From 57739ff8520de96a7375c12f97a2665338074bd1 Mon Sep 17 00:00:00 2001 From: Michael B Kuhn <31661049+mbkuhn@users.noreply.github.com> Date: Thu, 20 Apr 2023 09:14:44 -0600 Subject: [PATCH 01/38] initialize a profile for TKE (#827) Beare et al. (2006) -- used in Berg et al. (2020) and Sullivan et al. (2016) --- amr-wind/wind_energy/ABLFieldInit.H | 9 +++++++ amr-wind/wind_energy/ABLFieldInit.cpp | 37 +++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/amr-wind/wind_energy/ABLFieldInit.H b/amr-wind/wind_energy/ABLFieldInit.H index f66e59c0aa..a166c73dcd 100644 --- a/amr-wind/wind_energy/ABLFieldInit.H +++ b/amr-wind/wind_energy/ABLFieldInit.H @@ -100,6 +100,12 @@ private: //! Initial value for tke field amrex::Real m_tke_init{0.1}; + //! Multiplicative factor for init tke profile + amrex::Real m_tke_init_factor{0.4}; + + //! Cutoff height for init tke profile + amrex::Real m_tke_cutoff_height{250.}; + //! Top velocity amrex::RealArray m_top_vel{{20.0, 0.0, 0.0}}; @@ -114,6 +120,9 @@ private: //! Perturb temperature field with random fluctuations bool m_perturb_theta{false}; + + //! Initialize tke profile non-constant + bool m_tke_init_profile{false}; }; } // namespace amr_wind diff --git a/amr-wind/wind_energy/ABLFieldInit.cpp b/amr-wind/wind_energy/ABLFieldInit.cpp index cdc4c3f670..88545d335f 100644 --- a/amr-wind/wind_energy/ABLFieldInit.cpp +++ b/amr-wind/wind_energy/ABLFieldInit.cpp @@ -32,6 +32,9 @@ ABLFieldInit::ABLFieldInit() pp_abl.query("theta_amplitude", m_deltaT); pp_abl.query("init_tke", m_tke_init); + pp_abl.query("init_tke_beare_profile", m_tke_init_profile); + pp_abl.query("init_tke_beare_factor", m_tke_init_factor); + pp_abl.query("init_tke_cutoff_height", m_tke_cutoff_height); pp_abl.query("linear_profile", m_linear_profile); @@ -204,9 +207,39 @@ void ABLFieldInit::perturb_temperature( //! Initialize sfs tke field at the beginning of the simulation void ABLFieldInit::init_tke( - const amrex::Geometry& /* geom */, amrex::MultiFab& tke) const + const amrex::Geometry& geom, amrex::MultiFab& tke_fab) const { - tke.setVal(m_tke_init, 1); + tke_fab.setVal(m_tke_init, 1); + + if (!m_tke_init_profile) { + return; + } + + const auto& dx = geom.CellSizeArray(); + const auto& problo = geom.ProbLoArray(); + const auto tke_cutoff_height = m_tke_cutoff_height; + const auto tke_init_factor = m_tke_init_factor; + +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (amrex::MFIter mfi(tke_fab, amrex::TilingIfNotGPU()); mfi.isValid(); + ++mfi) { + const auto& bx = mfi.growntilebox(1); + const auto& tke = tke_fab.array(mfi); + + // Profile definition from Beare et al. (2006) + amrex::ParallelFor( + bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real z = problo[2] + (k + 0.5) * dx[2]; + if (z < tke_cutoff_height) { + tke(i, j, k) = tke_init_factor * + std::pow(1. - z / tke_cutoff_height, 3.0); + } else { + tke(i, j, k) = 0.; + } + }); + } } } // namespace amr_wind From 6544a6c8dfd299d82083cc2e001e254fd6dd2792 Mon Sep 17 00:00:00 2001 From: prakash <120606615+moprak-nrel@users.noreply.github.com> Date: Thu, 20 Apr 2023 11:58:24 -0600 Subject: [PATCH 02/38] Fixing 0 for mueff in ABLStats output, this call is needed to populate the plane average (#830) --- amr-wind/wind_energy/ABLStats.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/amr-wind/wind_energy/ABLStats.cpp b/amr-wind/wind_energy/ABLStats.cpp index 1e6b5e735c..c529f9306b 100644 --- a/amr-wind/wind_energy/ABLStats.cpp +++ b/amr-wind/wind_energy/ABLStats.cpp @@ -89,6 +89,7 @@ void ABLStats::calc_averages() m_pa_temp(); m_pa_vel_fine(); m_pa_temp_fine(); + m_pa_mueff(); } //! Calculate sfs stress averages From 022aeb2c72a747e1a6860bb358548226eb5bbb97 Mon Sep 17 00:00:00 2001 From: Jon Rood Date: Fri, 21 Apr 2023 11:38:43 -0600 Subject: [PATCH 03/38] Change DPCPP to SYCL. (#832) --- .github/workflows/ci.yml | 14 +++++++------- CMakeLists.txt | 2 +- amr-wind/utilities/console_io.cpp | 2 +- cmake/amr-wind-utils.cmake | 2 +- cmake/set_amrex_options.cmake | 2 +- docs/sphinx/user/build.rst | 8 ++++---- unit_tests/test_config.cpp | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e2a1b226b..d92e90714c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -163,7 +163,7 @@ jobs: ${{github.workspace}} cmake --build build -- -j $(nproc) GPU-Intel: - name: GPU-DPCPP + name: GPU-SYCL needs: Formatting runs-on: ubuntu-20.04 steps: @@ -175,7 +175,7 @@ jobs: uses: actions/checkout@v3 with: submodules: true - - name: Prepare DPC++ environment + - name: Prepare SyCL environment run: | export DEBIAN_FRONTEND=noninteractive sudo wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB @@ -188,14 +188,14 @@ jobs: set +e source /opt/intel/oneapi/setvars.sh set -e - cmake -G Ninja -B build-DPCPP \ + cmake -G Ninja -B build-SYCL \ -DCMAKE_BUILD_TYPE:STRING=Release \ - -DCMAKE_CXX_COMPILER:STRING=$(which dpcpp) \ - -DCMAKE_C_COMPILER:STRING=$(which clang) \ + -DCMAKE_CXX_COMPILER:STRING=$(which icpx) \ + -DCMAKE_C_COMPILER:STRING=$(which icx) \ -DAMR_WIND_ENABLE_MPI:BOOL=OFF \ - -DAMR_WIND_ENABLE_DPCPP:BOOL=ON \ + -DAMR_WIND_ENABLE_SYCL:BOOL=ON \ ${{github.workspace}} - cmake --build build-DPCPP -- -j $(nproc) + cmake --build build-SYCL -- -j $(nproc) Lint-cppcheck: needs: Formatting runs-on: macos-latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 04d14c3e88..582b964b8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ option(AMR_WIND_ENABLE_MPI "Enable MPI" OFF) option(AMR_WIND_ENABLE_OPENMP "Enable OpenMP" OFF) option(AMR_WIND_ENABLE_CUDA "Enable CUDA" OFF) option(AMR_WIND_ENABLE_ROCM "Enable ROCm/HIP" OFF) -option(AMR_WIND_ENABLE_DPCPP "Enable Intel OneAPI DPC++" OFF) +option(AMR_WIND_ENABLE_SYCL "Enable Intel OneAPI SyCL" OFF) option(AMR_WIND_ENABLE_TINY_PROFILE "Enable AMReX TinyProfile support" OFF) set(AMR_WIND_PRECISION "DOUBLE" CACHE STRING "Floating point precision SINGLE or DOUBLE") diff --git a/amr-wind/utilities/console_io.cpp b/amr-wind/utilities/console_io.cpp index 804bccba30..8189626d2c 100644 --- a/amr-wind/utilities/console_io.cpp +++ b/amr-wind/utilities/console_io.cpp @@ -126,7 +126,7 @@ void print_banner(MPI_Comm comm, std::ostream& out) << "(Backend: CUDA)" #elif defined(AMREX_USE_HIP) << "(Backend: HIP)" -#elif defined(AMREX_USE_DPCPP) +#elif defined(AMREX_USE_SYCL) << "(Backend: SYCL)" #endif << std::endl diff --git a/cmake/amr-wind-utils.cmake b/cmake/amr-wind-utils.cmake index c596d8f849..5b2c159b39 100644 --- a/cmake/amr-wind-utils.cmake +++ b/cmake/amr-wind-utils.cmake @@ -50,7 +50,7 @@ macro(init_amrex) if (AMR_WIND_ENABLE_CUDA) list(APPEND AMREX_COMPONENTS "CUDA") endif() - if (AMR_WIND_ENABLE_DPCPP) + if (AMR_WIND_ENABLE_SYCL) list(APPEND AMREX_COMPONENTS "SYCL") endif() if (AMR_WIND_ENABLE_ROCM) diff --git a/cmake/set_amrex_options.cmake b/cmake/set_amrex_options.cmake index df694e6a30..156cc05e75 100644 --- a/cmake/set_amrex_options.cmake +++ b/cmake/set_amrex_options.cmake @@ -35,7 +35,7 @@ if (AMR_WIND_ENABLE_CUDA) set(AMReX_GPU_BACKEND CUDA CACHE STRING "AMReX GPU type" FORCE) elseif(AMR_WIND_ENABLE_ROCM) set(AMReX_GPU_BACKEND HIP CACHE STRING "AMReX GPU type" FORCE) -elseif(AMR_WIND_ENABLE_DPCPP) +elseif(AMR_WIND_ENABLE_SYCL) set(AMReX_GPU_BACKEND SYCL CACHE STRING "AMReX GPU type" FORCE) else() set(AMReX_GPU_BACKEND NONE CACHE STRING "AMReX GPU type" FORCE) diff --git a/docs/sphinx/user/build.rst b/docs/sphinx/user/build.rst index ad3ade4b25..25098e2685 100644 --- a/docs/sphinx/user/build.rst +++ b/docs/sphinx/user/build.rst @@ -80,9 +80,9 @@ Architecture options Enable `NVIDIA CUDA GPU `_ builds. Default: OFF -.. cmakeval:: AMR_WIND_ENABLE_DPCPP +.. cmakeval:: AMR_WIND_ENABLE_SYCL - Enable `Intel OneAPI DPC++ `_ builds. Default: OFF + Enable `Intel OneAPI SyCL `_ builds. Default: OFF Dependencies ~~~~~~~~~~~~~ @@ -154,8 +154,8 @@ General CMake options Set the C++ compiler used for compiling the code. - For Intel DPC++ builds (see :cmakeval:`AMR_WIND_ENABLE_DPCPP`) this should be - set to `dpcpp`. + For Intel SyCL builds (see :cmakeval:`AMR_WIND_ENABLE_SYCL`) this should be + set to `icpx`. .. cmakeval:: CMAKE_C_COMPILER diff --git a/unit_tests/test_config.cpp b/unit_tests/test_config.cpp index f4d907210a..2ff4959210 100644 --- a/unit_tests/test_config.cpp +++ b/unit_tests/test_config.cpp @@ -58,7 +58,7 @@ TEST(Configuration, GPU) #endif #elif defined(AMREX_USE_HIP) amrex::Print() << "GPU backend: HIP" << std::endl; -#elif defined(AMREX_USE_DPCPP) +#elif defined(AMREX_USE_SYCL) amrex::Print() << "GPU backend: SYCL" << std::endl; #endif From 18e53327e31e940b1c6961f3cec8b4bf02512a3d Mon Sep 17 00:00:00 2001 From: prakash <120606615+moprak-nrel@users.noreply.github.com> Date: Fri, 21 Apr 2023 13:21:50 -0600 Subject: [PATCH 04/38] Add implementation for AMD subgrid stress model (#829) --- amr-wind/turbulence/LES/AMD.H | 225 ++++++++++ amr-wind/turbulence/LES/AMD.cpp | 403 ++++++++++++++++++ amr-wind/turbulence/LES/CMakeLists.txt | 1 + docs/sphinx/theory/theory.rst | 130 ++++++ unit_tests/turbulence/test_turbulence_LES.cpp | 126 ++++++ 5 files changed, 885 insertions(+) create mode 100644 amr-wind/turbulence/LES/AMD.H create mode 100644 amr-wind/turbulence/LES/AMD.cpp diff --git a/amr-wind/turbulence/LES/AMD.H b/amr-wind/turbulence/LES/AMD.H new file mode 100644 index 0000000000..ab913cce8a --- /dev/null +++ b/amr-wind/turbulence/LES/AMD.H @@ -0,0 +1,225 @@ +#ifndef AMD_H +#define AMD_H + +#include +#include "amr-wind/turbulence/TurbModelBase.H" +#include "amr-wind/core/FieldRepo.H" +#include "amr-wind/fvm/stencils.H" + +namespace amr_wind::turbulence { +/** AMD LES Model + * \ingroup turb_model + */ +template +class AMD : public TurbModelBase +{ +public: + static std::string identifier() { return "AMD-" + Transport::identifier(); } + + explicit AMD(CFDSim& sim); + + //! Model name for debugging purposes + std::string model_name() const override { return "AMD"; } + + //! Update the turbulent viscosity field + void update_turbulent_viscosity(const FieldState fstate) override; + + //! Update the effective thermal diffusivity field + void update_alphaeff(Field& alphaeff) override; + + //! Return model coefficients dictionary + TurbulenceModel::CoeffsDictType model_coeffs() const override; + + //! Parse turbulence model coefficients for this model + void parse_model_coeffs() override; + + //! No post advance work for this model + void post_advance_work() override{}; + +private: + //! Poincare coefficient (default value set for 2nd order AMR-wind + //! discretization) + amrex::Real m_C{0.333333333333333}; + + //! Reference temperature (Kelvin) + amrex::Real m_ref_theta{300.0}; + + const Field& m_vel; + const Field& m_temperature; + const Field& m_rho; + amrex::Vector m_gravity{{0.0, 0.0, -9.81}}; +}; + +template +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real amd_muvel( + int i, + int j, + int k, + amrex::Real dx, // Grid spacing in x direction + amrex::Real dy, // Grid spacing in x direction + amrex::Real dz, // Grid spacing in x direction + amrex::Real beta, // Thermal expansion coefficient + amrex::Real C, // Poincare const + amrex::Array4 const& vel, + amrex::Array4 const& theta) noexcept +{ + const amrex::Real idx = 1.0 / dx; + const amrex::Real idy = 1.0 / dy; + const amrex::Real idz = 1.0 / dz; + + amrex::Real cp1, c, cm1; + + cp1 = Stencil::c00; + c = Stencil::c01; + cm1 = Stencil::c02; + + const amrex::Real ux = (cp1 * vel(i + 1, j, k, 0) + c * vel(i, j, k, 0) + + cm1 * vel(i - 1, j, k, 0)) * + idx; + const amrex::Real vx = (cp1 * vel(i + 1, j, k, 1) + c * vel(i, j, k, 1) + + cm1 * vel(i - 1, j, k, 1)) * + idx; + const amrex::Real wx = (cp1 * vel(i + 1, j, k, 2) + c * vel(i, j, k, 2) + + cm1 * vel(i - 1, j, k, 2)) * + idx; + const amrex::Real tx = (cp1 * theta(i + 1, j, k) + c * theta(i, j, k) + + cm1 * theta(i - 1, j, k)) * + idx; + + cp1 = Stencil::c10; + c = Stencil::c11; + cm1 = Stencil::c12; + + const amrex::Real uy = (cp1 * vel(i, j + 1, k, 0) + c * vel(i, j, k, 0) + + cm1 * vel(i, j - 1, k, 0)) * + idy; + const amrex::Real vy = (cp1 * vel(i, j + 1, k, 1) + c * vel(i, j, k, 1) + + cm1 * vel(i, j - 1, k, 1)) * + idy; + const amrex::Real wy = (cp1 * vel(i, j + 1, k, 2) + c * vel(i, j, k, 2) + + cm1 * vel(i, j - 1, k, 2)) * + idy; + const amrex::Real ty = (cp1 * theta(i, j + 1, k) + c * theta(i, j, k) + + cm1 * theta(i, j - 1, k)) * + idy; + + cp1 = Stencil::c20; + c = Stencil::c21; + cm1 = Stencil::c22; + + const amrex::Real uz = (cp1 * vel(i, j, k + 1, 0) + c * vel(i, j, k, 0) + + cm1 * vel(i, j, k - 1, 0)) * + idz; + const amrex::Real vz = (cp1 * vel(i, j, k + 1, 1) + c * vel(i, j, k, 1) + + cm1 * vel(i, j, k - 1, 1)) * + idz; + const amrex::Real wz = (cp1 * vel(i, j, k + 1, 2) + c * vel(i, j, k, 2) + + cm1 * vel(i, j, k - 1, 2)) * + idz; + const amrex::Real tz = (cp1 * theta(i, j, k + 1) + c * theta(i, j, k) + + cm1 * theta(i, j, k - 1)) * + idz; + + const amrex::Real dx2 = dx * dx; + const amrex::Real dy2 = dy * dy; + const amrex::Real dz2 = dz * dz; + const amrex::Real num_shear = + -2.0 * C * + (ux * (ux * ux * dx2 + uy * uy * dy2 + uz * uz * dz2) + + vy * (vx * vx * dx2 + vy * vy * dy2 + vz * vz * dz2) + + wz * (wx * wx * dx2 + wy * wy * dy2 + wz * wz * dz2) + + (uy + vx) * (ux * vx * dx2 + uy * vy * dy2 + uz * vz * dz2) + + (uz + wx) * (ux * wx * dx2 + uy * wy * dy2 + uz * wz * dz2) + + (vz + wy) * (vx * wx * dx2 + vy * wy * dy2 + vz * wz * dz2)); + amrex::Real num_buoy = + C * beta * (dx2 * wx * tx + dy2 * wy * ty + dz2 * wz * tz); + amrex::Real denom = (ux * ux + uy * uy + uz * uz + vx * vx + vy * vy + + vz * vz + wx * wx + wy * wy + wz * wz) + + 1e-15; + return std::max(1e-15, (num_shear + num_buoy) / denom); +} + +template +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real amd_thermal_diff( + int i, + int j, + int k, + amrex::Real dx, // Grid spacing in x direction + amrex::Real dy, // Grid spacing in x direction + amrex::Real dz, // Grid spacing in x direction + amrex::Real C, // Poincare const + amrex::Array4 const& vel, + amrex::Array4 const& theta) noexcept +{ + const amrex::Real idx = 1.0 / dx; + const amrex::Real idy = 1.0 / dy; + const amrex::Real idz = 1.0 / dz; + + amrex::Real cp1, c, cm1; + + cp1 = Stencil::c00; + c = Stencil::c01; + cm1 = Stencil::c02; + + const amrex::Real ux = (cp1 * vel(i + 1, j, k, 0) + c * vel(i, j, k, 0) + + cm1 * vel(i - 1, j, k, 0)) * + idx; + const amrex::Real vx = (cp1 * vel(i + 1, j, k, 1) + c * vel(i, j, k, 1) + + cm1 * vel(i - 1, j, k, 1)) * + idx; + const amrex::Real wx = (cp1 * vel(i + 1, j, k, 2) + c * vel(i, j, k, 2) + + cm1 * vel(i - 1, j, k, 2)) * + idx; + const amrex::Real tx = (cp1 * theta(i + 1, j, k) + c * theta(i, j, k) + + cm1 * theta(i - 1, j, k)) * + idx; + + cp1 = Stencil::c10; + c = Stencil::c11; + cm1 = Stencil::c12; + + const amrex::Real uy = (cp1 * vel(i, j + 1, k, 0) + c * vel(i, j, k, 0) + + cm1 * vel(i, j - 1, k, 0)) * + idy; + const amrex::Real vy = (cp1 * vel(i, j + 1, k, 1) + c * vel(i, j, k, 1) + + cm1 * vel(i, j - 1, k, 1)) * + idy; + const amrex::Real wy = (cp1 * vel(i, j + 1, k, 2) + c * vel(i, j, k, 2) + + cm1 * vel(i, j - 1, k, 2)) * + idy; + const amrex::Real ty = (cp1 * theta(i, j + 1, k) + c * theta(i, j, k) + + cm1 * theta(i, j - 1, k)) * + idy; + + cp1 = Stencil::c20; + c = Stencil::c21; + cm1 = Stencil::c22; + + const amrex::Real uz = (cp1 * vel(i, j, k + 1, 0) + c * vel(i, j, k, 0) + + cm1 * vel(i, j, k - 1, 0)) * + idz; + const amrex::Real vz = (cp1 * vel(i, j, k + 1, 1) + c * vel(i, j, k, 1) + + cm1 * vel(i, j, k - 1, 1)) * + idz; + const amrex::Real wz = (cp1 * vel(i, j, k + 1, 2) + c * vel(i, j, k, 2) + + cm1 * vel(i, j, k - 1, 2)) * + idz; + const amrex::Real tz = (cp1 * theta(i, j, k + 1) + c * theta(i, j, k) + + cm1 * theta(i, j, k - 1)) * + idz; + + const amrex::Real dx2 = dx * dx; + const amrex::Real dy2 = dy * dy; + const amrex::Real dz2 = dz * dz; + const amrex::Real num = + -C * ((dx2 * ux * tx + dy2 * uy * ty + dz2 * uz * tz) * tx + + (dx2 * vx * tx + dy2 * vy * ty + dz2 * vz * tz) * ty + + (dx2 * wx * tx + dy2 * wy * ty + dz2 * wz * tz) * tz); + + amrex::Real denom = tx * tx + ty * ty + tz * tz + 1e-15; + return std::max(1e-15, num / denom); +} + +} // namespace amr_wind::turbulence + +#endif /* AMD_H */ diff --git a/amr-wind/turbulence/LES/AMD.cpp b/amr-wind/turbulence/LES/AMD.cpp new file mode 100644 index 0000000000..af052ccb7a --- /dev/null +++ b/amr-wind/turbulence/LES/AMD.cpp @@ -0,0 +1,403 @@ +#include + +#include "amr-wind/turbulence/LES/AMD.H" +#include "amr-wind/turbulence/TurbModelDefs.H" + +#include "AMReX_REAL.H" +#include "AMReX_MultiFab.H" +#include "AMReX_ParmParse.H" + +namespace amr_wind { +namespace turbulence { + +template +AMD::AMD(CFDSim& sim) + : TurbModelBase(sim) + , m_vel(sim.repo().get_field("velocity")) + , m_temperature(sim.repo().get_field("temperature")) + , m_rho(sim.repo().get_field("density")) +{ + auto& phy_mgr = this->m_sim.physics_manager(); + if (phy_mgr.contains("ABL")) { + { + amrex::ParmParse pp("ABL"); + pp.get("reference_temperature", m_ref_theta); + } + { + amrex::ParmParse pp("incflo"); + pp.queryarr("gravity", m_gravity); + } + } +} + +template +void AMD::parse_model_coeffs() +{ + const std::string coeffs_dict = this->model_name() + "_coeffs"; + amrex::ParmParse pp(coeffs_dict); + pp.query("C_poincare", m_C); +} + +template +void AMD::update_turbulent_viscosity(const FieldState fstate) +{ + BL_PROFILE( + "amr-wind::" + this->identifier() + "::update_turbulent_viscosity"); + + auto& mu_turb = this->mu_turb(); + auto& repo = mu_turb.repo(); + const auto& vel = m_vel.state(fstate); + const auto& temp = m_temperature.state(fstate); + const auto& den = m_rho.state(fstate); + const auto& geom_vec = repo.mesh().Geom(); + amrex::Real beta = 0.0; + auto& phy_mgr = this->m_sim.physics_manager(); + if (phy_mgr.contains("ABL")) { + beta = -m_gravity[2] / m_ref_theta; + } + + const amrex::Real C_poincare = this->m_C; + namespace stencil = amr_wind::fvm::stencil; + + const int nlevels = repo.num_active_levels(); + for (int lev = 0; lev < nlevels; ++lev) { + const auto& geom = geom_vec[lev]; + const auto& domain = geom.Domain(); + + const amrex::Real dx = geom.CellSize()[0]; + const amrex::Real dy = geom.CellSize()[1]; + const amrex::Real dz = geom.CellSize()[2]; + + for (amrex::MFIter mfi(mu_turb(lev)); mfi.isValid(); ++mfi) { + const auto& bx = mfi.tilebox(); + const auto& mu_arr = mu_turb(lev).array(mfi); + const auto& rho_arr = den(lev).const_array(mfi); + const auto& vel_arr = vel(lev).array(mfi); + const auto& temp_arr = temp(lev).array(mfi); + + amrex::ParallelFor( + bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + mu_arr(i, j, k) = rho * amd_muvel( + i, j, k, dx, dy, dz, beta, + C_poincare, vel_arr, temp_arr); + }); + + // TODO: Check if the following is correct for `foextrap` BC types + const auto& bxi = mfi.tilebox(); + int idim = 0; + if (!geom.isPeriodic(idim)) { + if (bxi.smallEnd(idim) == domain.smallEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = low[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxlo = amrex::Box(low, hi); + + amrex::ParallelFor( + bxlo, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + mu_arr(i, j, k) = + rho * amd_muvel( + i, j, k, dx, dy, dz, beta, C_poincare, + vel_arr, temp_arr); + }); + } + + if (bxi.bigEnd(idim) == domain.bigEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = hi[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxhi = amrex::Box(low, hi); + + amrex::ParallelFor( + bxhi, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + mu_arr(i, j, k) = + rho * amd_muvel( + i, j, k, dx, dy, dz, beta, C_poincare, + vel_arr, temp_arr); + }); + } + } // if (!geom.isPeriodic) + + idim = 1; + if (!geom.isPeriodic(idim)) { + if (bxi.smallEnd(idim) == domain.smallEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = low[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxlo = amrex::Box(low, hi); + + amrex::ParallelFor( + bxlo, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + mu_arr(i, j, k) = + rho * amd_muvel( + i, j, k, dx, dy, dz, beta, C_poincare, + vel_arr, temp_arr); + }); + } + + if (bxi.bigEnd(idim) == domain.bigEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = hi[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxhi = amrex::Box(low, hi); + + amrex::ParallelFor( + bxhi, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + mu_arr(i, j, k) = + rho * amd_muvel( + i, j, k, dx, dy, dz, beta, C_poincare, + vel_arr, temp_arr); + }); + } + } // if (!geom.isPeriodic) + + idim = 2; + if (!geom.isPeriodic(idim)) { + if (bxi.smallEnd(idim) == domain.smallEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = low[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxlo = amrex::Box(low, hi); + + amrex::ParallelFor( + bxlo, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + mu_arr(i, j, k) = + rho * amd_muvel( + i, j, k, dx, dy, dz, beta, C_poincare, + vel_arr, temp_arr); + }); + } + + if (bxi.bigEnd(idim) == domain.bigEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = hi[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxhi = amrex::Box(low, hi); + + amrex::ParallelFor( + bxhi, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + mu_arr(i, j, k) = + rho * amd_muvel( + i, j, k, dx, dy, dz, beta, C_poincare, + vel_arr, temp_arr); + }); + } + } // if (!geom.isPeriodic) + } + } + + mu_turb.fillpatch(this->m_sim.time().current_time()); +} + +//! Update the effective thermal diffusivity field +template +void AMD::update_alphaeff(Field& alphaeff) +{ + + BL_PROFILE("amr-wind::" + this->identifier() + "::update_alphaeff"); + + auto& repo = alphaeff.repo(); + const auto& geom_vec = repo.mesh().Geom(); + const amrex::Real C_poincare = this->m_C; + namespace stencil = amr_wind::fvm::stencil; + + const int nlevels = repo.num_active_levels(); + for (int lev = 0; lev < nlevels; ++lev) { + const auto& geom = geom_vec[lev]; + const auto& domain = geom.Domain(); + + const amrex::Real dx = geom.CellSize()[0]; + const amrex::Real dy = geom.CellSize()[1]; + const amrex::Real dz = geom.CellSize()[2]; + + for (amrex::MFIter mfi(alphaeff(lev)); mfi.isValid(); ++mfi) { + const auto& bx = mfi.tilebox(); + const auto& alpha_arr = alphaeff(lev).array(mfi); + const auto& rho_arr = m_rho(lev).const_array(mfi); + const auto& vel_arr = m_vel(lev).array(mfi); + const auto& temp_arr = m_temperature(lev).array(mfi); + + amrex::ParallelFor( + bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + alpha_arr(i, j, k) = + rho * + amd_thermal_diff( + i, j, k, dx, dy, dz, C_poincare, vel_arr, temp_arr); + }); + + // TODO: Check if the following is correct for `foextrap` BC types + const auto& bxi = mfi.tilebox(); + int idim = 0; + if (!geom.isPeriodic(idim)) { + if (bxi.smallEnd(idim) == domain.smallEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = low[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxlo = amrex::Box(low, hi); + + amrex::ParallelFor( + bxlo, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + alpha_arr(i, j, k) = + rho * amd_thermal_diff( + i, j, k, dx, dy, dz, C_poincare, + vel_arr, temp_arr); + }); + } + + if (bxi.bigEnd(idim) == domain.bigEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = hi[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxhi = amrex::Box(low, hi); + + amrex::ParallelFor( + bxhi, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + alpha_arr(i, j, k) = + rho * amd_thermal_diff( + i, j, k, dx, dy, dz, C_poincare, + vel_arr, temp_arr); + }); + } + } // if (!geom.isPeriodic) + + idim = 1; + if (!geom.isPeriodic(idim)) { + if (bxi.smallEnd(idim) == domain.smallEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = low[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxlo = amrex::Box(low, hi); + + amrex::ParallelFor( + bxlo, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + alpha_arr(i, j, k) = + rho * amd_thermal_diff( + i, j, k, dx, dy, dz, C_poincare, + vel_arr, temp_arr); + }); + } + + if (bxi.bigEnd(idim) == domain.bigEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = hi[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxhi = amrex::Box(low, hi); + + amrex::ParallelFor( + bxhi, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + alpha_arr(i, j, k) = + rho * amd_thermal_diff( + i, j, k, dx, dy, dz, C_poincare, + vel_arr, temp_arr); + }); + } + } // if (!geom.isPeriodic) + + idim = 2; + if (!geom.isPeriodic(idim)) { + if (bxi.smallEnd(idim) == domain.smallEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = low[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxlo = amrex::Box(low, hi); + + amrex::ParallelFor( + bxlo, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + alpha_arr(i, j, k) = + rho * amd_thermal_diff( + i, j, k, dx, dy, dz, C_poincare, + vel_arr, temp_arr); + }); + } + + if (bxi.bigEnd(idim) == domain.bigEnd(idim)) { + amrex::IntVect low(bxi.smallEnd()); + amrex::IntVect hi(bxi.bigEnd()); + int sm = hi[idim]; + low.setVal(idim, sm); + hi.setVal(idim, sm); + + auto bxhi = amrex::Box(low, hi); + + amrex::ParallelFor( + bxhi, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + alpha_arr(i, j, k) = + rho * amd_thermal_diff( + i, j, k, dx, dy, dz, C_poincare, + vel_arr, temp_arr); + }); + } + } // if (!geom.isPeriodic) + } + } +} + +template +TurbulenceModel::CoeffsDictType AMD::model_coeffs() const +{ + return TurbulenceModel::CoeffsDictType{{"C_poincare", this->m_C}}; +} + +} // namespace turbulence + +INSTANTIATE_TURBULENCE_MODEL(AMD); + +} // namespace amr_wind diff --git a/amr-wind/turbulence/LES/CMakeLists.txt b/amr-wind/turbulence/LES/CMakeLists.txt index 0de39f2154..1baced8e00 100644 --- a/amr-wind/turbulence/LES/CMakeLists.txt +++ b/amr-wind/turbulence/LES/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(${amr_wind_lib_name} PRIVATE Smagorinsky.cpp OneEqKsgs.cpp + AMD.cpp ) diff --git a/docs/sphinx/theory/theory.rst b/docs/sphinx/theory/theory.rst index 0fb72cdd37..47a87083a3 100644 --- a/docs/sphinx/theory/theory.rst +++ b/docs/sphinx/theory/theory.rst @@ -186,6 +186,136 @@ Using the perturbational form implies that the hydrostatic pressure is removed f .. math:: p = p' - \int_z^{z_{max}} \rho_0 g dz + p(z = z_{max}) = p' - \int_z^{z_{max}} \rho_0 g dz + +Turbulence Models +----------------------------- + +LES models for subgrid scales +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +AMD model +^^^^^^^^^^^ + +The eddy viscosity is calculated using an anisotropic derivative with a +different filter width in each direction + +.. math:: + + \begin{aligned} + \hat{\partial}_i &= \sqrt{C} \delta_i \partial_i \textrm{ for } i=1,2,3 \\ + C &= 1/3 \textrm{ Poincare coefficient for } 2^{nd} \textrm{ order gradient} \\ + \delta_i &= \textrm{Filter width along dimension } i \textrm{ for anisotropic grids}\\ + \beta &= g/\Theta_0 \textrm{ Gravity constant over reference temperature} + \end{aligned} + +The anisotropic derivative is used to define the eddy viscosity as + +.. math:: + + \begin{aligned} + \tau_{ij} &= -2 \nu_t \widetilde{S}_{ij} \\ + \nu_t &= \frac{- (\hat{\partial}_k \widetilde{u}_i) (\hat{\partial}_k \widetilde{u}_j) \widetilde{S}_{ij} + \beta (\hat{\partial}_k \widetilde{w}) (\hat{\partial}_k (\widetilde{\Theta} - \langle {\widetilde{\Theta}} \rangle) ) }{ (\partial_l \widetilde{u}_m) (\partial_l \widetilde{u}_m) } \\ + \tau_{\theta j} &= -2 D_e \frac{\partial \widetilde{\Theta}}{\partial x_j} \\ + D_e &= \frac{- (\hat{\partial}_k \widetilde{u}_i) (\hat{\partial}_k \widetilde{\Theta}) \partial_i \widetilde{\Theta} }{(\partial_l \widetilde{\Theta}) (\partial_l \widetilde{\Theta})} + \end{aligned} + +- **Implementation details:** + + +For ease of implementation, each part of :math:`\nu_t` and :math:`D_e` +is expanded in this subsection, these are used in ``AMD.h`` within +functions ``amd_muvel`` and ``amd_thermal_diff``. + +**Terms for** :math:`\nu_t` **in** ``amd_muvel`` + + +#. :math:`(\hat{\partial}_k \widetilde{u}_i) (\hat{\partial}_k \widetilde{u}_j) \widetilde{S}_{ij}` + is a contraction of 2 symmetric tensors, :math:`(\hat{\partial}_k + \widetilde{u}_i) (\hat{\partial}_k \widetilde{u}_j)` and + :math:`\widetilde{S}_{ij}`, therefore we get 6 unique terms, 3 + diagonals and 3 off-diagonals. The diagonal terms get a factor of 2 + from :math:`\widetilde{S}_{ij}` and the off-diagonal terms get a + factor of 2 from symmetry. This term is ``num_shear``. + + .. math:: + + \begin{aligned} + \begin{split} + (\hat{\partial}_k \widetilde{u}_i) (\hat{\partial}_k \widetilde{u}_j) \widetilde{S}_{ij} = \\ + 2*C* [\\ + {}&u_x*(u_x^2 dx^2 + u_y^2 dy^2 + u_z^2 dz^2) +\\ + {}&v_y*(v_x^2 dx^2 + v_y^2 dy^2 + v_z^2 dz^2) +\\ + {}&w_z*(w_x^2 dx^2 + w_y^2 dy^2 + w_z^2 dz^2) +\\ + {}&(u_y+v_x) * ( + u_x v_x dx^2 + + u_y v_y dy^2 + + u_z v_z dz^2 + ) +\\ + {}&(u_z+w_x) * ( + u_x w_x dx^2 + + u_y w_y dy^2 + + u_z w_z dz^2 + ) +\\ + {}&(v_z+w_y) * ( + v_x w_x dx^2 + + v_y w_y dy^2 + + v_z w_z dz^2 + )\\ + ] + \end{split} + \end{aligned} + +#. :math:`\beta (\hat{\partial}_k \widetilde{w}) (\hat{\partial}_k (\widetilde{\Theta} - \langle {\widetilde{\Theta}} \rangle) )` + is implemented as ``num_buoy`` + + .. math:: + + \beta (\hat{\partial}_k \widetilde{w}) (\hat{\partial}_k (\widetilde{\Theta} - \langle {\widetilde{\Theta}} \rangle) ) = + \beta* C* ( w_x \Theta_x dx^2 + w_y \Theta_y dy^2 + w_z \Theta_z dz^2) + +#. :math:`(\partial_l \widetilde{u}_m) (\partial_l \widetilde{u}_m)` is + double contraction of rank 2 tensors, and has 9 unique terms. This is + implemented as ``denom`` + + .. math:: + + \begin{aligned} + \begin{split} + (\partial_l \widetilde{u}_m) (\partial_l \widetilde{u}_m) = \\ + {}& u_x u_x + u_y u_y + u_z u_z \\ + {}& v_x v_x + v_y v_y + v_z v_z \\ + {}& w_x w_x + w_y w_y + w_z w_z + \end{split} + \end{aligned} + +**Terms for** :math:`D_e` **in** ``amd_thermal_diff`` + +#. :math:`(\hat{\partial}_k \widetilde{u}_i) (\hat{\partial}_k \widetilde{\Theta}) \partial_i \widetilde{\Theta}` + is a double contraction, has 9 unique terms. This is implemented as + ``num`` + + .. math:: + + \begin{aligned} + \begin{split} + (\hat{\partial}_k \widetilde{u}_i) (\hat{\partial}_k \widetilde{\Theta}) \partial_i \widetilde{\Theta} = \\ + C*[ \\ + {}& (u_x \Theta_x dx^2 + u_y \Theta_y dy^2 + u_z \Theta_z dz^2)*\Theta_x +\\ + {}& (v_x \Theta_x dx^2 + v_y \Theta_y dy^2 + v_z \Theta_z dz^2)*\Theta_y +\\ + {}& (w_x \Theta_x dx^2 + w_y \Theta_y dy^2 + w_z \Theta_z dz^2)*\Theta_z \\ + ] + \end{split} + \end{aligned} + +#. :math:`(\partial_l \widetilde{\Theta}) (\partial_l \widetilde{\Theta}) = \Theta_x^2 + \Theta_y^2 + \Theta_z^2` + is implemented as ``denom`` + +- **Unit tests** + +There is a simple unit test for both :math:`\nu_t` and :math:`D_e` in +``unit_tests/turbulence/test_turbulence_LES.cpp`` under +``test_AMD_setup_calc``. + Navigating source code ------------------------ diff --git a/unit_tests/turbulence/test_turbulence_LES.cpp b/unit_tests/turbulence/test_turbulence_LES.cpp index 2ca0451cd2..c9741ceb6f 100644 --- a/unit_tests/turbulence/test_turbulence_LES.cpp +++ b/unit_tests/turbulence/test_turbulence_LES.cpp @@ -38,6 +38,37 @@ void init_field3(amr_wind::Field& fld, amrex::Real srate) } } +void init_field_amd(amr_wind::Field& fld, amrex::Real scale) +{ + const auto& mesh = fld.repo().mesh(); + const int nlevels = fld.repo().num_active_levels(); + + amrex::Real offset = 0.0; + if (fld.field_location() == amr_wind::FieldLoc::CELL) { + offset = 0.5; + } + + for (int lev = 0; lev < nlevels; ++lev) { + const auto& dx = mesh.Geom(lev).CellSizeArray(); + const auto& problo = mesh.Geom(lev).ProbLoArray(); + + for (amrex::MFIter mfi(fld(lev)); mfi.isValid(); ++mfi) { + auto bx = mfi.growntilebox(); + const auto& farr = fld(lev).array(mfi); + + amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) { + const amrex::Real x = problo[0] + (i + offset) * dx[0]; + const amrex::Real y = problo[1] + (j + offset) * dx[1]; + const amrex::Real z = problo[2] + (k + offset) * dx[2]; + + farr(i, j, k, 0) = 1 * x / sqrt(6.0) * scale; + farr(i, j, k, 1) = -2 * y / sqrt(6.0) * scale; + farr(i, j, k, 2) = -1 * z / sqrt(6.0) * scale; + }); + } + } +} + void init_field1(amr_wind::Field& fld, amrex::Real tgrad) { const auto& mesh = fld.repo().mesh(); @@ -273,4 +304,99 @@ TEST_F(TurbLESTest, test_1eqKsgs_setup_calc) EXPECT_NEAR(max_val, ksgs_answer, tol); } +TEST_F(TurbLESTest, test_AMD_setup_calc) +{ + // Parser inputs for turbulence model + const amrex::Real C = 0.3; + const amrex::Real Tref = 200; + const amrex::Real gravz = 10.0; + const amrex::Real rho0 = 1.0; + { + amrex::ParmParse pp("turbulence"); + pp.add("model", (std::string) "AMD"); + } + { + amrex::ParmParse pp("AMD_coeffs"); + pp.add("C_poincare", C); + } + { + amrex::ParmParse pp("incflo"); + amrex::Vector physics{"ABL"}; + pp.addarr("physics", physics); + pp.add("density", rho0); + amrex::Vector vvec{8.0, 0.0, 0.0}; + pp.addarr("velocity", vvec); + amrex::Vector gvec{0.0, 0.0, -gravz}; + pp.addarr("gravity", gvec); + } + { + amrex::ParmParse pp("ABL"); + pp.add("reference_temperature", Tref); + amrex::Vector t_hts{0.0, 100.0, 400.0}; + pp.addarr("temperature_heights", t_hts); + amrex::Vector t_vals{200.0, 200.0, 200.0}; + pp.addarr("temperature_values", t_vals); + } + + // Initialize necessary parts of solver + populate_parameters(); + initialize_mesh(); + auto& pde_mgr = sim().pde_manager(); + pde_mgr.register_icns(); + sim().init_physics(); + + // Create turbulence model + sim().create_turbulence_model(); + // Get turbulence model + auto& tmodel = sim().turbulence_model(); + + // Get coefficients + auto model_dict = tmodel.model_coeffs(); + + for (const std::pair n : model_dict) { + // Only a single model parameter, Cs + EXPECT_EQ(n.first, "C_poincare"); + EXPECT_EQ(n.second, C); + } + + // Constants for fields + const amrex::Real scale = 1.50; + const amrex::Real Tgz = 20.0; + // Set up velocity field with constant strainrate + auto& vel = sim().repo().get_field("velocity"); + init_field_amd(vel, scale); + // Set up uniform unity density field + auto& dens = sim().repo().get_field("density"); + dens.setVal(rho0); + // Set up temperature field with constant gradient in z + auto& temp = sim().repo().get_field("temperature"); + init_field1(temp, Tgz); + + // Update turbulent viscosity directly + tmodel.update_turbulent_viscosity(amr_wind::FieldState::New); + auto& muturb = sim().repo().get_field("mu_turb"); + + // Check values of turbulent viscosity + const auto min_val = utils::field_min(muturb); + const auto max_val = utils::field_max(muturb); + const amrex::Real tol = 1e-12; + + const amrex::Real amd_answer = + C * + (-2.0 * std::pow(scale / sqrt(6), 3) * + (dx * dx - 8 * dy * dy - dz * dz) + + gravz / Tref * (-1.0 * Tgz * scale / sqrt(6) * dz * dz)) / + (1 * scale * scale); + EXPECT_NEAR(min_val, amd_answer, tol); + EXPECT_NEAR(max_val, amd_answer, tol); + + // Check values of alphaeff + auto& alphaeff = sim().repo().declare_cc_field("alphaeff"); + tmodel.update_alphaeff(alphaeff); + const auto ae_min_val = utils::field_min(alphaeff); + const auto ae_max_val = utils::field_max(alphaeff); + const amrex::Real amd_ae_answer = C * dz * dz * scale * 1.0 / sqrt(6); + EXPECT_NEAR(ae_min_val, amd_ae_answer, tol); + EXPECT_NEAR(ae_max_val, amd_ae_answer, tol); +} } // namespace amr_wind_tests From 933459a170c44c50bb588033f493e131063072ba Mon Sep 17 00:00:00 2001 From: Michael B Kuhn <31661049+mbkuhn@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:33:30 -0600 Subject: [PATCH 05/38] Abl stats energy budget (#831) * beginning of calculating energy budget w/ ABLstats * calls to plane averaging for energy budget * make functions available on device --- amr-wind/equation_systems/PDEBase.H | 2 + .../tke/source_terms/KsgsM84Src.H | 13 + .../tke/source_terms/KsgsM84Src.cpp | 38 +-- amr-wind/turbulence/LES/OneEqKsgs.H | 1 + amr-wind/turbulence/LES/OneEqKsgs.cpp | 1 + amr-wind/wind_energy/ABLStats.H | 11 + amr-wind/wind_energy/ABLStats.cpp | 117 ++++++++ unit_tests/aw_test_utils/test_utils.H | 22 ++ unit_tests/wind_energy/CMakeLists.txt | 1 + unit_tests/wind_energy/test_abl_stats.cpp | 263 ++++++++++++++++++ 10 files changed, 451 insertions(+), 18 deletions(-) create mode 100644 unit_tests/wind_energy/test_abl_stats.cpp diff --git a/amr-wind/equation_systems/PDEBase.H b/amr-wind/equation_systems/PDEBase.H index aa3a96dd7d..b3cff309c4 100644 --- a/amr-wind/equation_systems/PDEBase.H +++ b/amr-wind/equation_systems/PDEBase.H @@ -151,6 +151,8 @@ public: bool constant_density() const { return m_constant_density; } + std::string scheme() const { return m_scheme; } + private: //! Instance of the CFD simulation controller CFDSim& m_sim; diff --git a/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.H b/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.H index 8e4676e75e..abbcf5768b 100644 --- a/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.H +++ b/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.H @@ -31,9 +31,22 @@ private: Field& m_turb_lscale; Field& m_shear_prod; Field& m_buoy_prod; + Field& m_dissip; Field& m_tke; }; +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real calc_ceps_local( + const amrex::Real Ceps, const amrex::Real tlscale, const amrex::Real ds) +{ + return (Ceps / 0.93) * (0.19 + (0.74 * tlscale / ds)); +} + +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real calc_dissip( + const amrex::Real Ceps, const amrex::Real tke, const amrex::Real tlscale) +{ + return Ceps * std::sqrt(tke) * tke / tlscale; +} + } // namespace amr_wind::pde::tke #endif /* KSGSM84SRC_H */ diff --git a/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.cpp b/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.cpp index 1b97587a45..5e27b189c8 100644 --- a/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.cpp +++ b/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.cpp @@ -10,6 +10,7 @@ KsgsM84Src::KsgsM84Src(const CFDSim& sim) : m_turb_lscale(sim.repo().get_field("turb_lscale")) , m_shear_prod(sim.repo().get_field("shear_prod")) , m_buoy_prod(sim.repo().get_field("buoy_prod")) + , m_dissip(sim.repo().get_field("dissipation")) , m_tke(sim.repo().get_field("tke")) { AMREX_ALWAYS_ASSERT(sim.turbulence_model().model_name() == "OneEqKsgsM84"); @@ -30,6 +31,7 @@ void KsgsM84Src::operator()( const auto& tlscale_arr = (this->m_turb_lscale)(lev).array(mfi); const auto& shear_prod_arr = (this->m_shear_prod)(lev).array(mfi); const auto& buoy_prod_arr = (this->m_buoy_prod)(lev).array(mfi); + const auto& dissip_arr = (this->m_dissip)(lev).array(mfi); const auto& tke_arr = (this->m_tke)(lev).array(mfi); const amrex::Real Ceps = this->m_Ceps; const amrex::Real CepsGround = this->m_CepsGround; @@ -43,11 +45,11 @@ void KsgsM84Src::operator()( const amrex::Real ds = std::cbrt(dx * dy * dz); amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { - amrex::Real ceps_local = - (Ceps / 0.93) * (0.19 + (0.74 * tlscale_arr(i, j, k) / ds)); + dissip_arr(i, j, k) = calc_dissip( + calc_ceps_local(Ceps, tlscale_arr(i, j, k), ds), tke_arr(i, j, k), + tlscale_arr(i, j, k)); src_term(i, j, k) += shear_prod_arr(i, j, k) + buoy_prod_arr(i, j, k) - - ceps_local * std::sqrt(tke_arr(i, j, k)) * - tke_arr(i, j, k) / tlscale_arr(i, j, k); + dissip_arr(i, j, k); }); const auto& bctype = (this->m_tke).bc_type(); @@ -59,13 +61,13 @@ void KsgsM84Src::operator()( if (blo.ok()) { amrex::ParallelFor( blo, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { - amrex::Real ceps_local = - (Ceps / 0.93) * - (0.19 + (0.74 * tlscale_arr(i, j, k) / ds)); - src_term(i, j, k) += (ceps_local - CepsGround) * - std::sqrt(tke_arr(i, j, k)) * - tke_arr(i, j, k) / - tlscale_arr(i, j, k); + // Remove previous dissipation term + src_term(i, j, k) += dissip_arr(i, j, k); + // Calculate new dissipation term + dissip_arr(i, j, k) = calc_dissip( + CepsGround, tke_arr(i, j, k), tlscale_arr(i, j, k)); + // Add new dissiaption term + src_term(i, j, k) -= dissip_arr(i, j, k); }); } else { amrex::Abort("Bad box extracted in KsgsM84Src"); @@ -80,13 +82,13 @@ void KsgsM84Src::operator()( if (bhi.ok()) { amrex::ParallelFor( bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { - amrex::Real ceps_local = - (Ceps / 0.93) * - (0.19 + (0.74 * tlscale_arr(i, j, k) / ds)); - src_term(i, j, k) += (ceps_local - CepsGround) * - std::sqrt(tke_arr(i, j, k)) * - tke_arr(i, j, k) / - tlscale_arr(i, j, k); + // Remove previous dissipation term + src_term(i, j, k) += dissip_arr(i, j, k); + // Calculate new dissipation term + dissip_arr(i, j, k) = calc_dissip( + CepsGround, tke_arr(i, j, k), tlscale_arr(i, j, k)); + // Add new dissiaption term + src_term(i, j, k) -= dissip_arr(i, j, k); }); } } diff --git a/amr-wind/turbulence/LES/OneEqKsgs.H b/amr-wind/turbulence/LES/OneEqKsgs.H index de5678429e..6580c78c20 100644 --- a/amr-wind/turbulence/LES/OneEqKsgs.H +++ b/amr-wind/turbulence/LES/OneEqKsgs.H @@ -23,6 +23,7 @@ protected: Field& m_turb_lscale; Field& m_shear_prod; Field& m_buoy_prod; + Field& m_dissip; Field& m_rho; Field* m_tke{nullptr}; diff --git a/amr-wind/turbulence/LES/OneEqKsgs.cpp b/amr-wind/turbulence/LES/OneEqKsgs.cpp index 3287c98bd2..1164d49590 100644 --- a/amr-wind/turbulence/LES/OneEqKsgs.cpp +++ b/amr-wind/turbulence/LES/OneEqKsgs.cpp @@ -19,6 +19,7 @@ OneEqKsgs::OneEqKsgs(CFDSim& sim) , m_turb_lscale(sim.repo().declare_field("turb_lscale", 1, 1, 1)) , m_shear_prod(sim.repo().declare_field("shear_prod", 1, 1, 1)) , m_buoy_prod(sim.repo().declare_field("buoy_prod", 1, 1, 1)) + , m_dissip(sim.repo().declare_field("dissipation", 1, 1, 1)) , m_rho(sim.repo().get_field("density")) { auto& tke_eqn = diff --git a/amr-wind/wind_energy/ABLStats.H b/amr-wind/wind_energy/ABLStats.H index b9ecfc3e00..4d658e97c3 100644 --- a/amr-wind/wind_energy/ABLStats.H +++ b/amr-wind/wind_energy/ABLStats.H @@ -85,6 +85,14 @@ public: void calc_sfs_stress_avgs(ScratchField& sfs_stress, ScratchField& t_sfs_stress); + //! Calculate tke diffusion term by eliminating other terms + void calc_tke_diffusion( + ScratchField& diffusion, + const Field& buoy_prod, + const Field& shear_prod, + const Field& dissipation, + const amrex::Real dt); + protected: //! Read user inputs and create the necessary files void initialize(); @@ -162,6 +170,9 @@ private: //! Number of cells in the horizontal direction size_t m_ncells_h1{0}; size_t m_ncells_h2{0}; + + //! Do energy budget + bool m_do_energy_budget{false}; }; } // namespace amr_wind diff --git a/amr-wind/wind_energy/ABLStats.cpp b/amr-wind/wind_energy/ABLStats.cpp index c529f9306b..3640cf2a89 100644 --- a/amr-wind/wind_energy/ABLStats.cpp +++ b/amr-wind/wind_energy/ABLStats.cpp @@ -6,6 +6,8 @@ #include "amr-wind/utilities/tensor_ops.H" #include "amr-wind/equation_systems/icns/source_terms/ABLForcing.H" #include "amr-wind/equation_systems/PDEHelpers.H" +#include "amr-wind/equation_systems/SchemeTraits.H" +#include "amr-wind/equation_systems/tke/TKE.H" #include "AMReX_ParmParse.H" #include "AMReX_ParallelDescriptor.H" @@ -53,6 +55,7 @@ void ABLStats::initialize() pp.queryarr("gravity", gravity); m_gravity = utils::vec_mag(gravity.data()); pp.get("reference_temperature", m_ref_theta); + pp.query("stats_do_energy_budget", m_do_energy_budget); } // Get normal direction and associated stuff @@ -141,6 +144,64 @@ void ABLStats::calc_sfs_stress_avgs( } } +//! Calculate sfs stress averages +void ABLStats::calc_tke_diffusion( + ScratchField& diffusion, + const Field& buoy_prod, + const Field& shear_prod, + const Field& dissipation, + const amrex::Real dt) +{ + + BL_PROFILE("amr-wind::ABLStats::calc_tke_diffusion"); + + // Get tke fields + Field& tke = m_sim.repo().get_field("tke"); + Field& tke_old = tke.state(amr_wind::FieldState::Old); + // Get conv_term from tke eq + auto& pde_mgr = m_sim.pde_manager(); + // Check for presence of tke-godunov + std::string tke_pde_name = amr_wind::pde::TKE::pde_name(); + if (!pde_mgr.has_pde(tke_pde_name)) { + amrex::Abort( + "ABL Stats Failure: " + tke_pde_name + + " PDE not present. Energy budget relies on TKE equation and " + "Godunov " + "assumptions."); + } + std::string tke_scheme_name = amr_wind::fvm::Godunov::scheme_name(); + if (pde_mgr.scheme() != tke_scheme_name) { + amrex::Abort( + "ABL Stats Failure: " + amr_wind::fvm::Godunov::scheme_name() + + " not being used. Energy budget relies on tke equation and Godunov " + "assumptions"); + } + auto& tke_eqn = pde_mgr(tke_pde_name + "-" + tke_scheme_name); + Field& conv_term = tke_eqn.fields().conv_term; + + const int nlevels = m_sim.repo().num_active_levels(); + for (int lev = 0; lev < nlevels; ++lev) { + for (amrex::MFIter mfi(diffusion(lev)); mfi.isValid(); ++mfi) { + const auto& bx = mfi.tilebox(); + const auto& diffusion_arr = diffusion(lev).array(mfi); + const auto& buoy_prod_arr = buoy_prod(lev).const_array(mfi); + const auto& shear_prod_arr = shear_prod(lev).const_array(mfi); + const auto& dissipation_arr = dissipation(lev).const_array(mfi); + const auto& tke_arr = tke(lev).const_array(mfi); + const auto& tke_old_arr = tke_old(lev).const_array(mfi); + const auto& conv_arr = conv_term(lev).const_array(mfi); + + amrex::ParallelFor( + bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + diffusion_arr(i, j, k) = + (tke_arr(i, j, k) - tke_old_arr(i, j, k)) / dt - + conv_arr(i, j, k) - shear_prod_arr(i, j, k) - + buoy_prod_arr(i, j, k) + dissipation_arr(i, j, k); + }); + } + } +} + void ABLStats::post_advance_work() { BL_PROFILE("amr-wind::ABLStats::post_advance_work"); @@ -390,6 +451,20 @@ void ABLStats::prepare_netcdf_file() grp.def_var("u'w'_sfs", NC_DOUBLE, two_dim); grp.def_var("v'w'_sfs", NC_DOUBLE, two_dim); + // Energy budget + if (m_do_energy_budget) { + if (!m_sim.repo().field_exists("buoy_prod")) { + amrex::Abort( + "ABL Stats Failure: buoy_prod field not present, indicating " + "OneEqKsgs turbulence model not being used. Energy budget " + "currently only applies to this turbulence model."); + } + grp.def_var("tke_buoy", NC_DOUBLE, two_dim); + grp.def_var("tke_shear", NC_DOUBLE, two_dim); + grp.def_var("tke_dissip", NC_DOUBLE, two_dim); + grp.def_var("tke_diff", NC_DOUBLE, two_dim); + } + ncf.exit_def_mode(); { @@ -535,6 +610,48 @@ void ABLStats::write_netcdf() var.put(l_vec.data(), start, count); } } + + if (m_do_energy_budget) { + // TKE terms + auto& tke_buoy_prod = m_sim.repo().get_field("buoy_prod"); + auto& tke_shear_prod = m_sim.repo().get_field("shear_prod"); + auto& tke_dissip = m_sim.repo().get_field("dissipation"); + // Scratch field for diffusion + auto tke_diffusion = + m_sim.repo().create_scratch_field("tke_diffusion", 1); + // Solve for diffusion term using other terms + calc_tke_diffusion( + *tke_diffusion, tke_buoy_prod, tke_shear_prod, tke_dissip, + m_sim.time().deltaT()); + { + FieldPlaneAveraging pa_tke_buoy_prod( + tke_dissip, m_sim.time(), m_normal_dir); + pa_tke_buoy_prod(); + auto var = grp.var("tke_buoy"); + var.put(pa_tke_buoy_prod.line_average().data(), start, count); + } + { + FieldPlaneAveraging pa_tke_shear_prod( + tke_shear_prod, m_sim.time(), m_normal_dir); + pa_tke_shear_prod(); + auto var = grp.var("tke_shear"); + var.put(pa_tke_shear_prod.line_average().data(), start, count); + } + { + FieldPlaneAveraging pa_tke_dissip( + tke_dissip, m_sim.time(), m_normal_dir); + pa_tke_dissip(); + auto var = grp.var("tke_dissip"); + var.put(pa_tke_dissip.line_average().data(), start, count); + } + { + ScratchFieldPlaneAveraging pa_tke_diff( + *tke_diffusion, m_sim.time(), m_normal_dir); + pa_tke_diff(); + auto var = grp.var("tke_diff"); + var.put(pa_tke_diff.line_average().data(), start, count); + } + } } ncf.close(); #endif diff --git a/unit_tests/aw_test_utils/test_utils.H b/unit_tests/aw_test_utils/test_utils.H index 4652cd9c4f..32be1cfaa6 100644 --- a/unit_tests/aw_test_utils/test_utils.H +++ b/unit_tests/aw_test_utils/test_utils.H @@ -32,6 +32,28 @@ inline amrex::Real field_max(const amr_wind::Field& field, const int icomp = 0) return max_val; } +inline amrex::Real +field_min(const amr_wind::ScratchField& field, const int icomp = 0) +{ + amrex::Real min_val = std::numeric_limits::max(); + for (int lev = 0; lev < field.repo().num_active_levels(); ++lev) { + min_val = amrex::min(min_val, field(lev).min(icomp)); + } + + return min_val; +} + +inline amrex::Real +field_max(const amr_wind::ScratchField& field, const int icomp = 0) +{ + amrex::Real max_val = -std::numeric_limits::max(); + for (int lev = 0; lev < field.repo().num_active_levels(); ++lev) { + max_val = amrex::max(max_val, field(lev).max(icomp)); + } + + return max_val; +} + /** Return the minimum value of a field over all AMR levels * * @param nlevels Number of levels that existing in this AMR hierarchy diff --git a/unit_tests/wind_energy/CMakeLists.txt b/unit_tests/wind_energy/CMakeLists.txt index 94afed792a..84b3a1c7e0 100644 --- a/unit_tests/wind_energy/CMakeLists.txt +++ b/unit_tests/wind_energy/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources(${amr_wind_unit_test_exe_name} PRIVATE # test cases test_abl_init.cpp test_abl_src.cpp + test_abl_stats.cpp ) if (AMR_WIND_ENABLE_NETCDF) diff --git a/unit_tests/wind_energy/test_abl_stats.cpp b/unit_tests/wind_energy/test_abl_stats.cpp new file mode 100644 index 0000000000..b44c01773d --- /dev/null +++ b/unit_tests/wind_energy/test_abl_stats.cpp @@ -0,0 +1,263 @@ +#include "abl_test_utils.H" +#include "aw_test_utils/iter_tools.H" +#include "aw_test_utils/test_utils.H" +#include "amr-wind/equation_systems/tke/TKE.H" +#include "amr-wind/wind_energy/ABLStats.H" + +namespace amr_wind_tests { + +namespace { +void init_field1(amr_wind::Field& fld) +{ + const int nlevels = fld.repo().num_active_levels(); + + for (int lev = 0; lev < nlevels; ++lev) { + + for (amrex::MFIter mfi(fld(lev)); mfi.isValid(); ++mfi) { + auto bx = mfi.growntilebox(); + const auto& farr = fld(lev).array(mfi); + + // Give TKE gradient for nonzero diff term + amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) { + farr(i, j, k, 0) = + std::sin(0.01 * i) + std::pow(k, 0.2) + std::cos(0.01 * j); + }); + } + } +} + +amrex::Real test_new_tke( + amr_wind::Field& tke, + amr_wind::Field& tkeold, + amr_wind::Field& conv_term, + amr_wind::Field& buoy_prod, + amr_wind::Field& shear_prod, + amr_wind::Field& dissipation, + amr_wind::ScratchField& diffusion, + const amrex::Real dt) +{ + amrex::Real error_total = 0; + + for (int lev = 0; lev < tke.repo().num_active_levels(); ++lev) { + + // Form tke estimate by adding to the old tke field + for (amrex::MFIter mfi(tkeold(lev)); mfi.isValid(); ++mfi) { + const auto& bx = mfi.tilebox(); + const auto& tke_old_arr = tkeold(lev).array(mfi); + const auto& buoy_prod_arr = buoy_prod(lev).const_array(mfi); + const auto& shear_prod_arr = shear_prod(lev).const_array(mfi); + const auto& dissipation_arr = dissipation(lev).const_array(mfi); + const auto& diffusion_arr = diffusion(lev).const_array(mfi); + const auto& conv_arr = conv_term(lev).const_array(mfi); + + amrex::ParallelFor( + bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + tke_old_arr(i, j, k) += + dt * + (conv_arr(i, j, k) + shear_prod_arr(i, j, k) + + buoy_prod_arr(i, j, k) - dissipation_arr(i, j, k) + + diffusion_arr(i, j, k)); + }); + } + + // Difference between tke estimate and tke calculated by the code + error_total += amrex::ReduceSum( + tke(lev), tkeold(lev), 0, + [=] AMREX_GPU_HOST_DEVICE( + amrex::Box const& bx, + amrex::Array4 const& tke_arr, + amrex::Array4 const& tke_est) + -> amrex::Real { + amrex::Real error = 0; + + amrex::Loop(bx, [=, &error](int i, int j, int k) noexcept { + error += std::abs(tke_arr(i, j, k) - tke_est(i, j, k)); + }); + + return error; + }); + } + return error_total; +} + +void remove_nans(amr_wind::Field& field) +{ + for (int lev = 0; lev < field.repo().num_active_levels(); ++lev) { + + // Form tke estimate by adding to the old tke field + for (amrex::MFIter mfi(field(lev)); mfi.isValid(); ++mfi) { + const auto& bx = mfi.tilebox(); + const auto& field_arr = field(lev).array(mfi); + amrex::ParallelFor( + bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + field_arr(i, j, k) = std::isnan(field_arr(i, j, k)) + ? 0.0 + : field_arr(i, j, k); + }); + } + } +} +} // namespace + +TEST_F(ABLMeshTest, stats_tke_diffusion) +{ + // This test checks the implementation of the calc_diffusion routine to see + // if it does what it is intended to do + constexpr double tol = 1.0e-12; + constexpr amrex::Real val_shear = 0.5; + constexpr amrex::Real val_buoy = 0.4; + constexpr amrex::Real val_dissip = 0.3; + constexpr amrex::Real val_conv = 0.2; + constexpr amrex::Real val_tke = 1.6; + constexpr amrex::Real val_tkeold = 1.0; + constexpr amrex::Real dt = 0.1; + constexpr amrex::Real expected_diff = (val_tke - val_tkeold) / dt - + val_conv - val_shear - val_buoy + + val_dissip; + + populate_parameters(); + initialize_mesh(); + + // Register fields and eqs for the sake of a functional test + sim().repo().declare_field("temperature", 1, 3); + sim().pde_manager().register_icns(); + + // Register fields used in turbulence model OneEqKsgs + auto& shear = sim().repo().declare_field("shear_prod", 1, 1); + auto& buoy = sim().repo().declare_field("buoy_prod", 1, 1); + auto& dissip = sim().repo().declare_field("dissipation", 1, 1); + auto diff = sim().repo().create_scratch_field("diffusion", 1, 1); + // Register transport pde for tke + auto& tke_eqn = sim().pde_manager().register_transport_pde( + amr_wind::pde::TKE::pde_name()); + auto& tke = tke_eqn.fields().field; + + // Populate values of fields + shear.setVal(val_shear); + buoy.setVal(val_buoy); + dissip.setVal(val_dissip); + tke_eqn.fields().conv_term.setVal(val_conv); + tke.setVal(val_tke); + tke.state(amr_wind::FieldState::Old).setVal(val_tkeold); + + // Initialize ABL Stats + amr_wind::ABLWallFunction wall_func(sim()); + amr_wind::ABLStats stats(sim(), wall_func, 2); + + // Calculate diffusion term + stats.calc_tke_diffusion(*diff, buoy, shear, dissip, dt); + + // Check answer + const auto min_val = utils::field_min(*diff, 0); + const auto max_val = utils::field_max(*diff, 0); + EXPECT_NEAR(expected_diff, min_val, tol); + EXPECT_NEAR(expected_diff, max_val, tol); +} + +TEST_F(ABLMeshTest, stats_energy_budget) +{ + // This test checks the assumptions behind the calc_diffusion routine + constexpr double tol = 1.0e-12; + constexpr amrex::Real dt = 0.1; + constexpr amrex::Real val_shear = 0.5; + constexpr amrex::Real val_buoy = 0.4; + constexpr amrex::Real val_tlscale = 0.3; + + // Set up turbulence model and other input arguments + { + amrex::ParmParse pp("turbulence"); + pp.add("model", (std::string) "OneEqKsgsM84"); + } + { + amrex::ParmParse pp("time"); + pp.add("fixed_dt", dt); + } + { + amrex::ParmParse pp("TKE"); + pp.add("source_terms", (std::string) "KsgsM84Src"); + } + { + amrex::ParmParse pp("transport"); + pp.add("viscosity", (amrex::Real)1e-5); + } + // incflo.diffusion_type = 1 + populate_parameters(); + initialize_mesh(); + + // Register icns + auto& icns = sim().pde_manager().register_icns(); + icns.initialize(); + + // Initialize fields + sim().init_physics(); + + // Initialize turbulence model + sim().create_turbulence_model(); + + // Initialize ABL velocity + const int lev = 0; + for (auto& pp : sim().physics()) { + pp->pre_init_actions(); + pp->initialize_fields(lev, sim().mesh().Geom(lev)); + } + + // Register transport pde for tke + auto& tke_eqn = sim().pde_manager().register_transport_pde( + amr_wind::pde::TKE::pde_name()); + tke_eqn.initialize(); + auto& tke = tke_eqn.fields().field; + + // Initialize ABL Stats + amr_wind::ABLWallFunction wall_func(sim()); + amr_wind::ABLStats stats(sim(), wall_func, 2); + + // Set initial tke value and advance states + init_field1(tke); + sim().pde_manager().advance_states(); + + // Initialize fields for tke source term + auto& shear = sim().repo().get_field("shear_prod"); + auto& buoy = sim().repo().get_field("buoy_prod"); + auto& tlscale = sim().repo().get_field("turb_lscale"); + shear.setVal(val_shear); + buoy.setVal(val_buoy); + tlscale.setVal(val_tlscale); + + // Set up new and NPH density for the sake of src term + auto& density_nph = + sim().repo().get_field("density").state(amr_wind::FieldState::NPH); + density_nph.setVal(1.0); + + // Setup mask_cell array to avoid errors in solve + auto& mask_cell = sim().repo().declare_int_field("mask_cell", 1, 1); + mask_cell.setVal(1); + + // Populate advection velocities + icns.pre_advection_actions(amr_wind::FieldState::Old); + + // Step forward in time for tke equation + sim().turbulence_model().update_turbulent_viscosity( + amr_wind::FieldState::Old); + tke_eqn.compute_advection_term(amr_wind::FieldState::Old); + // Remove NaNs (not sure why they're there, but need to be removed) + remove_nans(tke_eqn.fields().conv_term); + tke_eqn.compute_mueff(amr_wind::FieldState::Old); + tke_eqn.compute_source_term(amr_wind::FieldState::NPH); + tke_eqn.compute_diffusion_term(amr_wind::FieldState::New); + tke_eqn.compute_predictor_rhs(DiffusionType::Crank_Nicolson); + tke_eqn.solve(0.5 * dt); + tke_eqn.post_solve_actions(); + + // Calculate diffusion term + auto& dissip = sim().repo().get_field("dissipation"); + auto diff = sim().repo().create_scratch_field("diffusion", 1, 1); + stats.calc_tke_diffusion(*diff, buoy, shear, dissip, dt); + + // Check assumptions in diffusion term: sum of terms gets result + const amrex::Real err_total = test_new_tke( + tke, tke.state(amr_wind::FieldState::Old), tke_eqn.fields().conv_term, + buoy, shear, dissip, *(diff), dt); + EXPECT_NEAR(err_total, 0.0, tol); +} + +} // namespace amr_wind_tests From 22e18d86e0b89135b81a9086c2ed0af71026f301 Mon Sep 17 00:00:00 2001 From: Michael B Kuhn <31661049+mbkuhn@users.noreply.github.com> Date: Mon, 24 Apr 2023 12:27:28 -0600 Subject: [PATCH 06/38] ABL wallmodel test (#824) * checks the effect of the wall model (with local stress calculation) on the velocity field after one timestep --- unit_tests/wind_energy/CMakeLists.txt | 1 + unit_tests/wind_energy/test_abl_bc.cpp | 175 +++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 unit_tests/wind_energy/test_abl_bc.cpp diff --git a/unit_tests/wind_energy/CMakeLists.txt b/unit_tests/wind_energy/CMakeLists.txt index 84b3a1c7e0..7cbc153c5f 100644 --- a/unit_tests/wind_energy/CMakeLists.txt +++ b/unit_tests/wind_energy/CMakeLists.txt @@ -6,6 +6,7 @@ target_sources(${amr_wind_unit_test_exe_name} PRIVATE test_abl_init.cpp test_abl_src.cpp test_abl_stats.cpp + test_abl_bc.cpp ) if (AMR_WIND_ENABLE_NETCDF) diff --git a/unit_tests/wind_energy/test_abl_bc.cpp b/unit_tests/wind_energy/test_abl_bc.cpp new file mode 100644 index 0000000000..c16e28e0ac --- /dev/null +++ b/unit_tests/wind_energy/test_abl_bc.cpp @@ -0,0 +1,175 @@ +#include "abl_test_utils.H" +#include "amr-wind/utilities/trig_ops.H" +#include "aw_test_utils/iter_tools.H" +#include "aw_test_utils/test_utils.H" + +#include "AMReX_Gpu.H" +#include "AMReX_Random.H" +#include "amr-wind/equation_systems/icns/icns.H" +#include "amr-wind/equation_systems/icns/icns_ops.H" +#include "amr-wind/equation_systems/icns/MomentumSource.H" +#include "amr-wind/equation_systems/icns/source_terms/BodyForce.H" +#include "amr-wind/equation_systems/icns/source_terms/ABLForcing.H" +#include "amr-wind/equation_systems/icns/source_terms/GeostrophicForcing.H" +#include "amr-wind/equation_systems/icns/source_terms/CoriolisForcing.H" +#include "amr-wind/equation_systems/icns/source_terms/BoussinesqBuoyancy.H" +#include "amr-wind/equation_systems/icns/source_terms/DensityBuoyancy.H" +#include "amr-wind/equation_systems/icns/source_terms/HurricaneForcing.H" +#include "amr-wind/equation_systems/icns/source_terms/RayleighDamping.H" + +namespace amr_wind_tests { + +namespace { +amrex::Real +get_val_at_kindex(amr_wind::Field& field, const int comp, const int kref) +{ + const int lev = 0; + amrex::Real error_total = 0; + + error_total += amrex::ReduceSum( + field(lev), 0, + [=] AMREX_GPU_HOST_DEVICE( + amrex::Box const& bx, + amrex::Array4 const& f_arr) -> amrex::Real { + amrex::Real error = 0; + + amrex::Loop(bx, [=, &error](int i, int j, int k) noexcept { + // Check if current cell is just above lower wall + if (k == kref) { + // Add field value to output + error += f_arr(i, j, k, comp); + } + }); + + return error; + }); + amrex::ParallelDescriptor::ReduceRealSum(error_total); + return error_total; +} +void init_velocity(amr_wind::Field& fld, amrex::Real vval, int dir) +{ + const int nlevels = fld.repo().num_active_levels(); + + // Initialize entire field to 0 + fld.setVal(0.0); + + for (int lev = 0; lev < nlevels; ++lev) { + + for (amrex::MFIter mfi(fld(lev)); mfi.isValid(); ++mfi) { + auto bx = mfi.growntilebox(); + const auto& farr = fld(lev).array(mfi); + amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) { + // Initialize specified dir to specified value + farr(i, j, k, dir) = vval; + }); + } + } +} +} // namespace + +using ICNSFields = + amr_wind::pde::FieldRegOp; + +TEST_F(ABLMeshTest, abl_wall_model) +{ + constexpr amrex::Real tol = 1.0e-12; + constexpr amrex::Real mu = 0.01; + constexpr amrex::Real vval = 5.0; + constexpr amrex::Real dt = 0.1; + constexpr amrex::Real kappa = 0.4; + constexpr amrex::Real z0 = 0.11; + int dir = 0; + populate_parameters(); + { + amrex::ParmParse pp("geometry"); + amrex::Vector periodic{{1, 1, 0}}; + pp.addarr("is_periodic", periodic); + } + { + amrex::ParmParse pp("zlo"); + pp.add("type", (std::string) "wall_model"); + } + { + amrex::ParmParse pp("zhi"); + pp.add("type", (std::string) "slip_wall"); + } + { + amrex::ParmParse pp("incflo"); + pp.add("diffusion_type", 0); + } + { + amrex::ParmParse pp("transport"); + pp.add("viscosity", mu); + } + { + amrex::ParmParse pp("time"); + pp.add("fixed_dt", dt); + } + { + amrex::ParmParse pp("ABL"); + pp.add("wall_shear_stress_type", (std::string) "local"); + pp.add("kappa", kappa); + pp.add("surface_roughness_z0", z0); + } + initialize_mesh(); + + // Set up solver-related routines + auto& pde_mgr = sim().pde_manager(); + pde_mgr.register_icns(); + sim().create_turbulence_model(); + sim().init_physics(); + + // Specify velocity as uniform in x direction + auto& velocity = sim().repo().get_field("velocity"); + init_velocity(velocity, vval, dir); + // Specify density as unity + auto& density = sim().repo().get_field("density"); + density.setVal(1.0); + // Perform post init for physics: turns on wall model + for (auto& pp : sim().physics()) { + pp->post_init_actions(); + } + + // Advance states to prepare for time step + pde_mgr.advance_states(); + + // Initialize icns pde + auto& icns_eq = pde_mgr.icns(); + icns_eq.initialize(); + // Initialize viscosity + sim().turbulence_model().update_turbulent_viscosity( + amr_wind::FieldState::Old); + icns_eq.compute_mueff(amr_wind::FieldState::Old); + + // Check test setup by verifying mu + auto& viscosity = sim().repo().get_field("velocity_mueff"); + EXPECT_NEAR(mu, utils::field_max(viscosity), tol); + EXPECT_NEAR(mu, utils::field_min(viscosity), tol); + + // Zero source term and convection term to focus on diffusion + auto& src = icns_eq.fields().src_term; + auto& adv = icns_eq.fields().conv_term; + src.setVal(0.0); + adv.setVal(0.0); + + // Calculate diffusion term + icns_eq.compute_diffusion_term(amr_wind::FieldState::Old); + // Setup mask_cell array to avoid errors in solve + auto& mask_cell = sim().repo().declare_int_field("mask_cell", 1, 1); + mask_cell.setVal(1); + // Compute result with just diffusion term + icns_eq.compute_predictor_rhs(DiffusionType::Explicit); + + // Get resulting velocity in first cell + const amrex::Real vbase = get_val_at_kindex(velocity, dir, 0) / 8 / 8; + + // Calculate expected velocity after one step + const amrex::Real dz = sim().mesh().Geom(0).CellSizeArray()[2]; + const amrex::Real zref = 0.5 * dz; + const amrex::Real utau = kappa * vval / (std::log(zref / z0)); + const amrex::Real tau_wall = std::pow(utau, 2); + const amrex::Real vexpct = vval + dt * (0.0 - tau_wall) / dz; + EXPECT_NEAR(vexpct, vbase, tol); +} + +} // namespace amr_wind_tests From 10cd537c708f5bb1472a6664193fcda5a661910d Mon Sep 17 00:00:00 2001 From: "Marc T. Henry de Frahan" Date: Fri, 28 Apr 2023 16:15:53 -0600 Subject: [PATCH 07/38] Manage stale issues and pull requests (#838) --- .github/workflows/stale.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..98990df4af --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,20 @@ +name: 'Managing stale issues and pull requests' +on: + schedule: + - cron: '30 1 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v8 + with: + days-before-stale: 30 + stale-issue-message: 'This issue is stale because it has been open 30 days with no activity.' + close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.' + stale-issue-label: 'no-issue-activity' + days-before-issue-close: -1 + stale-pr-message: 'This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.' + close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity.' + stale-pr-label: 'no-pr-activity' + days-before-pr-close: 7 From 257c13c1a634d841627a04f83f1923b2a8556ca5 Mon Sep 17 00:00:00 2001 From: "Marc T. Henry de Frahan" Date: Mon, 1 May 2023 13:55:31 -0600 Subject: [PATCH 08/38] Remove improper citation (#839) --- amr-wind/wind_energy/ABLFieldInit.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/amr-wind/wind_energy/ABLFieldInit.cpp b/amr-wind/wind_energy/ABLFieldInit.cpp index 88545d335f..b74b981278 100644 --- a/amr-wind/wind_energy/ABLFieldInit.cpp +++ b/amr-wind/wind_energy/ABLFieldInit.cpp @@ -165,13 +165,7 @@ void ABLFieldInit::perturb_temperature( // cppcheck-suppress constParameter Field& temperature) const { - /** Perturbations for the temperature field is adapted from the following - * paper: - * - * D. Munoz-Esparza, B. Kosovic, J. van Beeck, J. D. Mirocha, A stocastic - * perturbation method to generate inflow turbulence in large-eddy - * simulation models: Application to neutrally stratified atmospheric - * boundary layers. Physics of Fluids, Vol. 27, 2015. + /** Perturbations for the temperature field * */ From c9e76e5a3fa00d45605e73b950ae081262ef70c3 Mon Sep 17 00:00:00 2001 From: ashesh2512 <36968394+ashesh2512@users.noreply.github.com> Date: Tue, 2 May 2023 12:29:53 -0600 Subject: [PATCH 09/38] the default diffusion type is Crank_Nicolson as set in setup/inti.cpp (#843) changing the member variable for consistent readability of the code --- amr-wind/incflo.H | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amr-wind/incflo.H b/amr-wind/incflo.H index 0cdf2020f6..a2877973a6 100644 --- a/amr-wind/incflo.H +++ b/amr-wind/incflo.H @@ -177,7 +177,7 @@ private: //! number of cells on all levels including covered cells amrex::Long m_cell_count{-1}; - DiffusionType m_diff_type = DiffusionType::Implicit; + DiffusionType m_diff_type = DiffusionType::Crank_Nicolson; // // end of member variables From 215200b0bb19153ae5aa0f1edf833354bd5b3b22 Mon Sep 17 00:00:00 2001 From: ashesh2512 <36968394+ashesh2512@users.noreply.github.com> Date: Wed, 3 May 2023 12:48:48 -0600 Subject: [PATCH 10/38] Use start time index as default value for checkpoint, plot, and regrid start upon restart (#846) * Use start time index as default value for checkpoint, plot, and regrid start See #840 and #845 Any "more elegant" ideas are welcome * update documentation --- amr-wind/core/SimTime.cpp | 9 +++++++++ docs/sphinx/user/inputs_time.rst | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/amr-wind/core/SimTime.cpp b/amr-wind/core/SimTime.cpp index 08328b78ae..d4fbf1f01b 100644 --- a/amr-wind/core/SimTime.cpp +++ b/amr-wind/core/SimTime.cpp @@ -230,6 +230,15 @@ void SimTime::set_restart_time(int tidx, amrex::Real time) { m_time_index = tidx; m_start_time_index = tidx; + m_regrid_start_index = tidx; + m_plt_start_index = tidx; + m_chkpt_start_index = tidx; + + // check if the user has set plt/regrid/checkpoint index + amrex::ParmParse pp("time"); + pp.query("regrid_start", m_regrid_start_index); + pp.query("plot_start", m_plt_start_index); + pp.query("checkpoint_start", m_chkpt_start_index); m_new_time = time; m_cur_time = time; diff --git a/docs/sphinx/user/inputs_time.rst b/docs/sphinx/user/inputs_time.rst index 6e5ef1cd70..5865add21b 100644 --- a/docs/sphinx/user/inputs_time.rst +++ b/docs/sphinx/user/inputs_time.rst @@ -85,21 +85,21 @@ This section deals with parameters that control the simulation. .. input_param:: time.regrid_start - **type:** Integer, optional, default = 0 + **type:** Integer, optional, default = 0; default = start index upon restart This user-specified parameter sets the base timestep onwards which the mesh is adaptively refined. .. input_param:: time.plot_start - **type:** Integer, optional, default = 0 + **type:** Integer, optional, default = 0; default = start index upon restart This user-specified parameter sets the base timestep onwards which the output (plot files) are written to the disk. .. input_param:: time.checkpoint_start - **type:** Integer, optional, default = 0 + **type:** Integer, optional, default = 0; default = start index upon restart This user-specified parameter sets the base timestep onwards which the checkpoint (restart) files are written to the disk. From e18bd4a55ffddd40407e700e2fa8483cb337f6a6 Mon Sep 17 00:00:00 2001 From: Michael B Kuhn <31661049+mbkuhn@users.noreply.github.com> Date: Thu, 4 May 2023 12:39:55 -0600 Subject: [PATCH 11/38] Boundary condition treatment for wallmodel and symmetry wall; fixes fvm problems (#844) Co-authored-by: prakash --- amr-wind/boundary_conditions/BCInterface.cpp | 16 + amr-wind/boundary_conditions/README.org | 13 +- amr-wind/diffusion/incflo_diffusion.cpp | 2 + .../equation_systems/vof/split_advection.H | 12 +- amr-wind/equation_systems/vof/vof_bciface.H | 1 + amr-wind/incflo_enums.H | 1 + amr-wind/physics/ChannelFlow.H | 2 + amr-wind/physics/ChannelFlow.cpp | 5 + .../user/inputs_Boundary_conditions.rst | 2 +- test/CMakeLists.txt | 2 + .../halfchannel_symmetricwall.inp | 57 ++ .../halfchannel_zerogradient.inp | 57 ++ unit_tests/turbulence/CMakeLists.txt | 1 + .../turbulence/test_turbulence_LES_bc.cpp | 502 ++++++++++++++++++ 14 files changed, 663 insertions(+), 10 deletions(-) create mode 100644 test/test_files/halfchannel_symmetricwall/halfchannel_symmetricwall.inp create mode 100644 test/test_files/halfchannel_zerogradient/halfchannel_zerogradient.inp create mode 100644 unit_tests/turbulence/test_turbulence_LES_bc.cpp diff --git a/amr-wind/boundary_conditions/BCInterface.cpp b/amr-wind/boundary_conditions/BCInterface.cpp index d2616f9712..d0c4acb72a 100644 --- a/amr-wind/boundary_conditions/BCInterface.cpp +++ b/amr-wind/boundary_conditions/BCInterface.cpp @@ -76,6 +76,8 @@ void BCIface::read_bctype() ibctype[ori] = BC::fixed_gradient; } else if ((bcstr == "wave_generation") || (bcstr == "wg")) { ibctype[ori] = BC::wave_generation; + } else if ((bcstr == "symmetric_wall") || (bcstr == "symw")) { + ibctype[ori] = BC::symmetric_wall; } else { ibctype[ori] = BC::undefined; } @@ -206,6 +208,19 @@ void BCVelocity::set_bcrec() bcrec[dir].setHi(dir, amrex::BCType::ext_dir); } break; + case BC::symmetric_wall: + if (side == amrex::Orientation::low) { + // Tangential directions use first-order extrapolation + set_bcrec_lo(dir, amrex::BCType::foextrap); + // Normal direction uses dirichlet (ext-dir) + bcrec[dir].setLo(dir, amrex::BCType::ext_dir); + } else { + // Tangential directions use first-order extrapolation + set_bcrec_hi(dir, amrex::BCType::foextrap); + // Normal direction uses dirichlet (ext-dir) + bcrec[dir].setHi(dir, amrex::BCType::ext_dir); + } + break; default: amrex::Abort("Invalid incflo BC type encountered"); @@ -254,6 +269,7 @@ void BCScalar::set_bcrec() case BC::pressure_inflow: case BC::pressure_outflow: case BC::zero_gradient: + case BC::symmetric_wall: if (side == amrex::Orientation::low) { set_bcrec_lo(dir, amrex::BCType::foextrap); } else { diff --git a/amr-wind/boundary_conditions/README.org b/amr-wind/boundary_conditions/README.org index 2d7e476696..040c25b55c 100644 --- a/amr-wind/boundary_conditions/README.org +++ b/amr-wind/boundary_conditions/README.org @@ -42,14 +42,17 @@ The boundary conditions that can be specified in the input file are: if the user does not specify any inputs in the input file. - =zero_gradient= :: Sets =BCRec= to =foextrap= and =LinOpBCType= to Neumann. - Like =no_slip_wall=, the normal component of velocity is automatically reset - to zero. The value on the boundary face is the same as the value at the cell + Note, unlike the =no_slip_wall=, the normal component of velocity is also zero + Neumann. The value on the boundary face is the same as the value at the cell center. No additional inputs are necessary when this BC is specified. - =slip_wall= :: =BCRec = hoextrap= and =LinOpBCType = Neumann=. The value on the boundary face is /extrapolated/ from the two interior cells using a one-sided stencil. No additional inputs are necessary when this BC is specified. +- =symmetric_wall= :: Sets =BCRec= to =foextrap= and =LinOpBCType= to Neumann. + Similar to =zero_gradient= but the normal component of velocity is set to 0. + - =fixed_gradient= :: =BCRec = hoextrap= and =LinOpBCType = inhomogNeumann=. Like =slip_wall= the value on the face is extrapolated (during fillpatch) using a one-sided stencil. However, for linear solvers the value on the face @@ -86,7 +89,8 @@ possible for scalars and tangential component of velocity. | pressure_outflow | po | foextrap | foextrap | foextrap | foextrap | | mass_inflow | mi | ext_dir | ext_dir | ext_dir | foextrap | | no_slip_wall | nsw | ext_dir (0) | ext_dir | ext_dir | foextrap | -| zero_gradient | zg | ext_dir (0) | foextrap | foextrap | foextrap | +| symmetric_wall | symw | ext_dir (0) | foextrap | foextrap | foextrap | +| zero_gradient | zg | foextrap | foextrap | foextrap | foextrap | | slip_wall | sw | ext_dir (0) | hoextrap | hoextrap | foextrap | | wall_model | wm | ext_dir (0) | hoextrap | hoextrap | foextrap | | fixed_gradient | fg | hoextrap | hoextrap | hoextrap | foextrap | @@ -103,6 +107,7 @@ projection (pressure) and MAC projections. | pressure_outflow | po | Dirichlet | | mass_inflow | mi | Neumann | | zero_gradient | zg | Neumann | +| symmetric_wall | symw | Neumann | | no_slip_wall | nsw | Neumann | | slip_wall | sw | Neumann | | wall_model | wm | Neumann | @@ -118,6 +123,7 @@ projection (pressure) and MAC projections. | mass_inflow | mi | Dirichlet | Dirichlet | | zero_gradient | zg | Dirichlet (0) | Neumann | | no_slip_wall | nsw | Dirichlet (0) | Dirichlet | +| symmetric_wall | symw | Dirichlet (0) | Neumann | | slip_wall | sw | Dirichlet (0) | Neumann | | wall_model | wm | Dirichlet (0) | Inhomog Neumann | | fixed_gradient | fg | Inhomog Neumann | Inhomog Neumann | @@ -132,6 +138,7 @@ projection (pressure) and MAC projections. | pressure_outflow | po | Neumann | | mass_inflow | mi | Dirichlet | | zero_gradient | zg | Neumann | +| symmetric_wall | symw | Neumann | | no_slip_wall | nsw | Dirichlet | | slip_wall | sw | Neumann | | wall_model | wm | Inhomog Neumann | diff --git a/amr-wind/diffusion/incflo_diffusion.cpp b/amr-wind/diffusion/incflo_diffusion.cpp index e43b7dd14b..ca3280593d 100644 --- a/amr-wind/diffusion/incflo_diffusion.cpp +++ b/amr-wind/diffusion/incflo_diffusion.cpp @@ -36,6 +36,7 @@ Vector> get_diffuse_tensor_bc( r[2][dir] = LinOpBCType::Dirichlet; break; } + case BC::symmetric_wall: case BC::slip_wall: { // Tangential components are Neumann // Normal component is Dirichlet @@ -77,6 +78,7 @@ get_diffuse_scalar_bc(amr_wind::Field& scalar, Orientation::Side side) noexcept case BC::pressure_inflow: case BC::pressure_outflow: case BC::zero_gradient: + case BC::symmetric_wall: case BC::slip_wall: { r[dir] = LinOpBCType::Neumann; break; diff --git a/amr-wind/equation_systems/vof/split_advection.H b/amr-wind/equation_systems/vof/split_advection.H index 8ab4063565..2ec28091a3 100644 --- a/amr-wind/equation_systems/vof/split_advection.H +++ b/amr-wind/equation_systems/vof/split_advection.H @@ -124,7 +124,7 @@ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void fluxes_bc_save( if (dir == 0) { // For wall BCs, do not allow flow into or out of domain if (bclo == BC::no_slip_wall || bclo == BC::slip_wall || - bclo == BC::wall_model) { + bclo == BC::wall_model || bclo == BC::symmetric_wall) { if (i == domlo) { vofR(domlo - 1, j, k) = 0.0; vofL(domlo, j, k) = 0.0; @@ -132,7 +132,7 @@ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void fluxes_bc_save( } if (bchi == BC::no_slip_wall || bchi == BC::slip_wall || - bchi == BC::wall_model) { + bchi == BC::wall_model || bchi == BC::symmetric_wall) { if (i == domhi + 1) { vofL(domhi + 1, j, k) = 0.0; vofR(domhi, j, k) = 0.0; @@ -146,14 +146,14 @@ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void fluxes_bc_save( // eulerian_implicit(), so this sum is not for the sake of accumulation } else if (dir == 1) { if (bclo == BC::no_slip_wall || bclo == BC::slip_wall || - bclo == BC::wall_model) { + bclo == BC::wall_model || bclo == BC::symmetric_wall) { if (j == domlo) { vofR(i, domlo - 1, k) = 0.0; vofL(i, domlo, k) = 0.0; } } if (bchi == BC::no_slip_wall || bchi == BC::slip_wall || - bchi == BC::wall_model) { + bchi == BC::wall_model || bchi == BC::symmetric_wall) { if (j == domhi + 1) { vofL(i, domhi + 1, k) = 0.0; vofR(i, domhi, k) = 0.0; @@ -163,14 +163,14 @@ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void fluxes_bc_save( f_f(i, j, k) = advalpha_f(i, j, k) * disp; } else if (dir == 2) { if (bclo == BC::no_slip_wall || bclo == BC::slip_wall || - bclo == BC::wall_model) { + bclo == BC::wall_model || bclo == BC::symmetric_wall) { if (k == domlo) { vofR(i, j, domlo - 1) = 0.0; vofL(i, j, domlo) = 0.0; } } if (bchi == BC::no_slip_wall || bchi == BC::slip_wall || - bchi == BC::wall_model) { + bchi == BC::wall_model || bchi == BC::symmetric_wall) { if (k == domhi + 1) { vofL(i, j, domhi + 1) = 0.0; vofR(i, j, domhi) = 0.0; diff --git a/amr-wind/equation_systems/vof/vof_bciface.H b/amr-wind/equation_systems/vof/vof_bciface.H index 702a02d146..03d085d42b 100644 --- a/amr-wind/equation_systems/vof/vof_bciface.H +++ b/amr-wind/equation_systems/vof/vof_bciface.H @@ -41,6 +41,7 @@ protected: case BC::pressure_outflow: case BC::zero_gradient: case BC::no_slip_wall: + case BC::symmetric_wall: if (side == amrex::Orientation::low) { set_bcrec_lo(dir, amrex::BCType::foextrap); } else { diff --git a/amr-wind/incflo_enums.H b/amr-wind/incflo_enums.H index 6c12fb0d1e..b73ff06b11 100644 --- a/amr-wind/incflo_enums.H +++ b/amr-wind/incflo_enums.H @@ -14,6 +14,7 @@ enum struct BC { fixed_gradient, periodic, wave_generation, + symmetric_wall, undefined }; diff --git a/amr-wind/physics/ChannelFlow.H b/amr-wind/physics/ChannelFlow.H index 32a0cdcd82..3542269a80 100644 --- a/amr-wind/physics/ChannelFlow.H +++ b/amr-wind/physics/ChannelFlow.H @@ -81,6 +81,8 @@ private: amrex::Real m_mean_vel; + bool m_half{false}; + //! direction of mean velocity int m_mean_vel_dir{0}; diff --git a/amr-wind/physics/ChannelFlow.cpp b/amr-wind/physics/ChannelFlow.cpp index 9b694fc2f5..896c6f39bb 100644 --- a/amr-wind/physics/ChannelFlow.cpp +++ b/amr-wind/physics/ChannelFlow.cpp @@ -31,6 +31,7 @@ ChannelFlow::ChannelFlow(CFDSim& sim) if (m_laminar) { pp.query("density", m_rho); pp.query("Mean_Velocity", m_mean_vel); + pp.query("half_channel", m_half); } else { pp.query("density", m_rho); pp.query("re_tau", m_re_tau); @@ -159,6 +160,10 @@ amrex::Real ChannelFlow::compute_error(const IndexSelector& idxOp) } } ht = probhi_physical[norm_dir] - problo[norm_dir]; + // For half channel, channel height is double the domain height + if (m_half) { + ht *= 2.0; + } } const auto& velocity = m_repo.get_field("velocity"); diff --git a/docs/sphinx/user/inputs_Boundary_conditions.rst b/docs/sphinx/user/inputs_Boundary_conditions.rst index c8edf383b4..0f996ee0bb 100644 --- a/docs/sphinx/user/inputs_Boundary_conditions.rst +++ b/docs/sphinx/user/inputs_Boundary_conditions.rst @@ -9,7 +9,7 @@ This section controls the boundary conditions. Only non-periodic BC's need to be Boundary condition type on the lo (or hi) side of the domain. Current options are: periodic, pressure_inflow, pressure_outflow, mass_inflow, - no_slip_wall, slip_wall, and wall_model. + no_slip_wall, slip_wall, symmetric_wall and wall_model. .. input_param:: xlo.temperature (or ylo.temperature, etc) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0bd1c7131b..1848dee810 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -191,6 +191,8 @@ add_test_re(joukowsky_disk) add_test_re(channel_kwsst) add_test_re(channel_kwsstiddes) add_test_re(channel_godunov_laminar) +add_test_re(halfchannel_zerogradient) +add_test_re(halfchannel_symmetricwall) add_test_re(ekman_spiral) add_test_re(rayleigh_taylor_godunov) add_test_re(rayleigh_taylor_mol) diff --git a/test/test_files/halfchannel_symmetricwall/halfchannel_symmetricwall.inp b/test/test_files/halfchannel_symmetricwall/halfchannel_symmetricwall.inp new file mode 100644 index 0000000000..5c5853abf2 --- /dev/null +++ b/test/test_files/halfchannel_symmetricwall/halfchannel_symmetricwall.inp @@ -0,0 +1,57 @@ +# This is a 2D poiseuille flow when run to steady state + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# SIMULATION STOP # +#.......................................# +time.stop_time = 5.00 # Max (simulated) time to evolve +time.max_step = -1000 # Max number of time steps + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# TIME STEP COMPUTATION # +#.......................................# +time.fixed_dt = 0.005 # Use this constant dt if > 0 +time.cfl = 0.95 # CFL factor + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# INPUT AND OUTPUT # +#.......................................# +time.plot_interval = 10 # Steps between plot files +time.checkpoint_interval = -100 # Steps between checkpoint files + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# PHYSICS # +#.......................................# +incflo.density = 1.0 # Reference density +incflo.use_godunov = 1 +incflo.diffusion_type = 1 +incflo.godunov_type = "weno_z" + +transport.viscosity = 1.0 +turbulence.model = Laminar + +ICNS.source_terms = BodyForce +BodyForce.magnitude = 12.0 0 0 +incflo.physics = ChannelFlow +ChannelFlow.density = 1.0 +ChannelFlow.Mean_Velocity = 1.0 +ChannelFlow.half_channel = true + +io.output_default_variables = 1 + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# ADAPTIVE MESH REFINEMENT # +#.......................................# +amr.n_cell = 16 8 4 # Grid cells at coarsest AMRlevel +amr.blocking_factor = 4 +amr.max_level = 0 # Max AMR level in hierarchy + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# GEOMETRY # +#.......................................# +geometry.prob_lo = 0.0 0.0 0.0 # Lo corner coordinates +geometry.prob_hi = 1.0 0.5 0.25 # Hi corner coordinates +geometry.is_periodic = 1 0 1 # Periodicity x y z (0/1) + +# Boundary conditions +ylo.type = "no_slip_wall" +yhi.type = "symmetric_wall" diff --git a/test/test_files/halfchannel_zerogradient/halfchannel_zerogradient.inp b/test/test_files/halfchannel_zerogradient/halfchannel_zerogradient.inp new file mode 100644 index 0000000000..79640b242f --- /dev/null +++ b/test/test_files/halfchannel_zerogradient/halfchannel_zerogradient.inp @@ -0,0 +1,57 @@ +# This is a 2D poiseuille flow when run to steady state + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# SIMULATION STOP # +#.......................................# +time.stop_time = 5.00 # Max (simulated) time to evolve +time.max_step = -1000 # Max number of time steps + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# TIME STEP COMPUTATION # +#.......................................# +time.fixed_dt = 0.005 # Use this constant dt if > 0 +time.cfl = 0.95 # CFL factor + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# INPUT AND OUTPUT # +#.......................................# +time.plot_interval = 10 # Steps between plot files +time.checkpoint_interval = -100 # Steps between checkpoint files + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# PHYSICS # +#.......................................# +incflo.density = 1.0 # Reference density +incflo.use_godunov = 1 +incflo.diffusion_type = 1 +incflo.godunov_type = "weno_z" + +transport.viscosity = 1.0 +turbulence.model = Laminar + +ICNS.source_terms = BodyForce +BodyForce.magnitude = 12.0 0 0 +incflo.physics = ChannelFlow +ChannelFlow.density = 1.0 +ChannelFlow.Mean_Velocity = 1.0 +ChannelFlow.half_channel = true + +io.output_default_variables = 1 + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# ADAPTIVE MESH REFINEMENT # +#.......................................# +amr.n_cell = 16 8 4 # Grid cells at coarsest AMRlevel +amr.blocking_factor = 4 +amr.max_level = 0 # Max AMR level in hierarchy + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# GEOMETRY # +#.......................................# +geometry.prob_lo = 0.0 0.0 0.0 # Lo corner coordinates +geometry.prob_hi = 1.0 0.5 0.25 # Hi corner coordinates +geometry.is_periodic = 1 0 1 # Periodicity x y z (0/1) + +# Boundary conditions +ylo.type = "no_slip_wall" +yhi.type = "zero_gradient" diff --git a/unit_tests/turbulence/CMakeLists.txt b/unit_tests/turbulence/CMakeLists.txt index 030992c4bc..dc87c3adf4 100644 --- a/unit_tests/turbulence/CMakeLists.txt +++ b/unit_tests/turbulence/CMakeLists.txt @@ -3,4 +3,5 @@ target_sources(${amr_wind_unit_test_exe_name} test_turbulence_init.cpp test_turbulence_LES.cpp + test_turbulence_LES_bc.cpp ) diff --git a/unit_tests/turbulence/test_turbulence_LES_bc.cpp b/unit_tests/turbulence/test_turbulence_LES_bc.cpp new file mode 100644 index 0000000000..51eeaf2e73 --- /dev/null +++ b/unit_tests/turbulence/test_turbulence_LES_bc.cpp @@ -0,0 +1,502 @@ +#include "gtest/gtest.h" +#include "aw_test_utils/MeshTest.H" +#include "amr-wind/turbulence/TurbulenceModel.H" +#include "aw_test_utils/test_utils.H" + +namespace amr_wind_tests { + +namespace { + +amrex::Real get_val_at_kindex( + amr_wind::Field& field, + amr_wind::Field& divisor, + const int comp, + const int kref) +{ + const int lev = 0; + amrex::Real error_total = 0; + + error_total += amrex::ReduceSum( + field(lev), divisor(lev), 0, + [=] AMREX_GPU_HOST_DEVICE( + amrex::Box const& bx, amrex::Array4 const& f_arr, + amrex::Array4 const& div_arr) -> amrex::Real { + amrex::Real error = 0; + + amrex::Loop(bx, [=, &error](int i, int j, int k) noexcept { + // Check if current cell is just above lower wall + if (k == kref) { + // Add field value to output + error += sqrt(f_arr(i, j, k, comp) / div_arr(i, j, k)); + } + }); + + return error; + }); + amrex::ParallelDescriptor::ReduceRealSum(error_total); + return error_total; +} + +void init_field3(amr_wind::Field& fld, amrex::Real srate) +{ + const auto& mesh = fld.repo().mesh(); + const int nlevels = fld.repo().num_active_levels(); + + amrex::Real offset = 0.0; + if (fld.field_location() == amr_wind::FieldLoc::CELL) { + offset = 0.5; + } + + for (int lev = 0; lev < nlevels; ++lev) { + const auto& dx = mesh.Geom(lev).CellSizeArray(); + const auto& problo = mesh.Geom(lev).ProbLoArray(); + + for (amrex::MFIter mfi(fld(lev)); mfi.isValid(); ++mfi) { + auto bx = mfi.growntilebox(); + const auto& farr = fld(lev).array(mfi); + + amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) { + const amrex::Real z = problo[2] + (k + offset) * dx[2]; + + farr(i, j, k, 0) = z / 2.0 * srate + 2.0; + farr(i, j, k, 1) = z / 2.0 * srate + 2.0; + farr(i, j, k, 2) = z / 2.0 * srate + 2.0; + }); + } + } +} + +void init_field1(amr_wind::Field& fld, amrex::Real tgrad) +{ + const auto& mesh = fld.repo().mesh(); + const int nlevels = fld.repo().num_active_levels(); + + amrex::Real offset = 0.0; + if (fld.field_location() == amr_wind::FieldLoc::CELL) { + offset = 0.5; + } + + for (int lev = 0; lev < nlevels; ++lev) { + const auto& dx = mesh.Geom(lev).CellSizeArray(); + const auto& problo = mesh.Geom(lev).ProbLoArray(); + + for (amrex::MFIter mfi(fld(lev)); mfi.isValid(); ++mfi) { + auto bx = mfi.growntilebox(); + const auto& farr = fld(lev).array(mfi); + + amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) { + const amrex::Real z = problo[2] + (k + offset) * dx[2]; + + farr(i, j, k, 0) = z * tgrad; + }); + } + } +} + +} // namespace + +class TurbLESTestBC : public MeshTest +{ +protected: + void populate_parameters() override + { + MeshTest::populate_parameters(); + + { + amrex::ParmParse pp("amr"); + amrex::Vector ncell{{10, 20, 30}}; + pp.addarr("n_cell", ncell); + pp.add("blocking_factor", 2); + } + { + amrex::ParmParse pp("geometry"); + amrex::Vector problo{{0.0, 0.0, 0.0}}; + amrex::Vector probhi{{10.0, 10.0, 10.0}}; + pp.addarr("prob_lo", problo); + pp.addarr("prob_hi", probhi); + amrex::Vector periodic{{1, 1, 0}}; + pp.addarr("is_periodic", periodic); + // Boundary conditions + amrex::ParmParse ppzhi("zhi"); + ppzhi.add("type", (std::string) "slip_wall"); + // zlo is defined in each case + } + } + void OneEqKsgs_setup_params() const + { + { + amrex::ParmParse pp("turbulence"); + pp.add("model", (std::string) "OneEqKsgsM84"); + } + { + amrex::ParmParse pp("OneEqKsgsM84_coeffs"); + pp.add("Ceps", Ceps); + pp.add("Ce", Ce); + } + { + amrex::ParmParse pp("incflo"); + amrex::Vector physics{"ABL"}; + pp.addarr("physics", physics); + pp.add("density", rho0); + amrex::Vector vvec{8.0, 0.0, 0.0}; + pp.addarr("velocity", vvec); + amrex::Vector gvec{0.0, 0.0, -gravz}; + pp.addarr("gravity", gvec); + } + { + amrex::ParmParse pp("ABL"); + pp.add("reference_temperature", Tref); + amrex::Vector t_hts{0.0, 100.0, 400.0}; + pp.addarr("temperature_heights", t_hts); + amrex::Vector t_vals{265.0, 265.0, 268.0}; + pp.addarr("temperature_values", t_vals); + } + { + amrex::ParmParse pp("transport"); + pp.add("viscosity", mu); + } + } + void test_calls_body(bool do_postsolve = false) + { + // Initialize necessary parts of solver + initialize_mesh(); + auto& pde_mgr = sim().pde_manager(); + auto& icns_eq = pde_mgr.register_icns(); + sim().init_physics(); + sim().create_turbulence_model(); + icns_eq.initialize(); + + // Get turbulence model + auto& tmodel = sim().turbulence_model(); + // Set up velocity field with constant strainrate + auto& vel = sim().repo().get_field("velocity"); + init_field3(vel, srate); + // Set up uniform unity density field + auto& dens = sim().repo().get_field("density"); + dens.setVal(rho0); + // Set up temperature field with constant gradient in z + auto& temp = sim().repo().get_field("temperature"); + init_field1(temp, Tgz); + // Give values to tlscale and tke arrays + auto& tlscale = sim().repo().get_field("turb_lscale"); + tlscale.setVal(tlscale_val); + auto& tke = sim().repo().get_field("tke"); + tke.setVal(tke_val); + + // Perform post init for physics + // (turns on wall model if specified) + for (auto& pp : sim().physics()) { + pp->post_init_actions(); + } + // Perform fillpatch to allow BCs to operate + vel.fillpatch(0.0); + temp.fillpatch(0.0); + // Ensure nonzero viscosity (only molecular at this point) + icns_eq.compute_mueff(amr_wind::FieldState::New); + + // Advance states (important for wall model) + pde_mgr.advance_states(); + // Compute diffusion term to use BCs (wall model) + icns_eq.compute_diffusion_term(amr_wind::FieldState::New); + + // Post_solve afterward + if (do_postsolve) { + icns_eq.post_solve_actions(); + } + + // Update turbulent viscosity directly + tmodel.update_turbulent_viscosity(amr_wind::FieldState::New); + } + + const amrex::Real dx = 10.0 / 10.0; + const amrex::Real dy = 10.0 / 20.0; + const amrex::Real dz = 10.0 / 30.0; + const amrex::Real tol = 1.0e-12; + // Parser inputs for turbulence model + const amrex::Real Ceps = 0.11; + const amrex::Real Ce = 0.99; + const amrex::Real Tref = 263.5; + const amrex::Real gravz = 10.0; + const amrex::Real rho0 = 1.2; + // Constants for fields + const amrex::Real srate = 20.0; + const amrex::Real Tgz = 2.0; + const amrex::Real tlscale_val = 1.1; + const amrex::Real tke_val = 0.1; + // Molecular viscosity + const amrex::Real mu = 1e-2; +}; + +TEST_F(TurbLESTestBC, test_1eqKsgs_noslip) +{ + populate_parameters(); + { + amrex::ParmParse pp("zlo"); + pp.add("type", (std::string) "no_slip_wall"); + } + OneEqKsgs_setup_params(); + test_calls_body(); + auto& muturb = sim().repo().get_field("mu_turb"); + + // Get shear production field + auto& shear_prod = sim().repo().get_field("shear_prod"); + + // Check for constant value in bulk + auto shear_bulk = get_val_at_kindex(shear_prod, muturb, 0, 1) / 10. / 20.; + EXPECT_NEAR(shear_bulk, srate, tol); + + // Velocity gradients assumed in setup (init_field3) + const amrex::Real uz_bulk = srate / 2.0; + + // Naive cell-centered answer, with no_slip_wall (Dirichlet) + const amrex::Real uz_wallcell = + (uz_bulk * 1.5 * dz + 2.0 - 0.0) / (2.0 * dz); + // Wall-normal direction + const amrex::Real wz_wallcell = uz_wallcell; + + const amrex::Real s_naive = + sqrt(2.0 * wz_wallcell * wz_wallcell + 2.0 * uz_wallcell * uz_wallcell); + // Check for different value just above wall due to BC + auto shear_wall = get_val_at_kindex(shear_prod, muturb, 0, 0) / 10. / 20.; + // Check that the result is not equal to the naive value + EXPECT_GT(std::abs(shear_wall - s_naive), tol); + // Answer that accounts for location of wall at cell face + const amrex::Real uz_wallface = + ((uz_bulk * 1.5 * dz + 2.0) / 3.0 + uz_bulk * 0.5 * dz + 2.0 - 0.0) / + dz; + const amrex::Real wz_wallface = uz_wallface; + const amrex::Real s_true = + sqrt(2.0 * wz_wallface * wz_wallface + 2.0 * uz_wallface * uz_wallface); + EXPECT_NEAR(shear_wall, s_true, tol); +} + +TEST_F(TurbLESTestBC, test_1eqKsgs_slip) +{ + populate_parameters(); + { + amrex::ParmParse pp("zlo"); + pp.add("type", (std::string) "slip_wall"); + } + OneEqKsgs_setup_params(); + test_calls_body(); + auto& muturb = sim().repo().get_field("mu_turb"); + + // Get shear production field + auto& shear_prod = sim().repo().get_field("shear_prod"); + + // Check for constant value in bulk + auto shear_bulk = get_val_at_kindex(shear_prod, muturb, 0, 1) / 10. / 20.; + EXPECT_NEAR(shear_bulk, srate, tol); + + // Check for different value just above wall due to BC + auto shear_wall = get_val_at_kindex(shear_prod, muturb, 0, 0) / 10. / 20.; + // Answer that accounts for slip_wall BC + // (tangential extrapolation, dirichlet normal) + const amrex::Real uz_bulk = srate / 2.0; + const amrex::Real wz_wall = + ((uz_bulk * 1.5 * dz + 2.0) / 3.0 + uz_bulk * 0.5 * dz + 2.0 - 0.0) / + dz; + const amrex::Real s_true = + sqrt(2.0 * wz_wall * wz_wall + 2.0 * uz_bulk * uz_bulk); + EXPECT_NEAR(shear_wall, s_true, tol); +} + +TEST_F(TurbLESTestBC, test_1eqKsgs_wallmodel) +{ + constexpr amrex::Real kappa = 0.4; + constexpr amrex::Real z0 = 0.11; + populate_parameters(); + { + amrex::ParmParse pp("zlo"); + pp.add("type", (std::string) "wall_model"); + } + { + amrex::ParmParse pp("ABL"); + pp.add("wall_shear_stress_type", (std::string) "local"); + pp.add("kappa", kappa); + pp.add("surface_roughness_z0", z0); + } + { + // Explicit diffusion populates wall cells, doesn't update velocity + amrex::ParmParse pp("incflo"); + pp.add("diffusion_type", 0); + } + OneEqKsgs_setup_params(); + const bool do_postsolve = true; + test_calls_body(do_postsolve); + auto& muturb = sim().repo().get_field("mu_turb"); + + // Get shear production field + auto& shear_prod = sim().repo().get_field("shear_prod"); + + // Check for constant value in bulk + auto shear_bulk = get_val_at_kindex(shear_prod, muturb, 0, 1) / 10. / 20.; + EXPECT_NEAR(shear_bulk, srate, tol); + + // Velocity gradients assumed in setup (init_field3) + const amrex::Real uz_bulk = srate / 2.0; + // Get value just above wall due to BC + auto shear_wall = get_val_at_kindex(shear_prod, muturb, 0, 0) / 10. / 20.; + // (tangential extrapolation, dirichlet normal) + const amrex::Real wz_wall = + ((uz_bulk * 1.5 * dz + 2.0) / 3.0 + uz_bulk * 0.5 * dz + 2.0 - 0.0) / + dz; + const amrex::Real s_true = + sqrt(2.0 * wz_wall * wz_wall + 2.0 * uz_bulk * uz_bulk); + EXPECT_NEAR(shear_wall, s_true, tol); +} + +TEST_F(TurbLESTestBC, test_1eqKsgs_wallmodel_failnofillpatch) +{ + constexpr amrex::Real kappa = 0.4; + constexpr amrex::Real z0 = 0.11; + populate_parameters(); + { + amrex::ParmParse pp("zlo"); + pp.add("type", (std::string) "wall_model"); + } + { + amrex::ParmParse pp("ABL"); + pp.add("wall_shear_stress_type", (std::string) "local"); + pp.add("kappa", kappa); + pp.add("surface_roughness_z0", z0); + } + { + // Explicit diffusion populates wall cells, doesn't update velocity + amrex::ParmParse pp("incflo"); + pp.add("diffusion_type", 0); + } + OneEqKsgs_setup_params(); + const bool do_postsolve = false; + test_calls_body(do_postsolve); + auto& muturb = sim().repo().get_field("mu_turb"); + + // Get shear production field + auto& shear_prod = sim().repo().get_field("shear_prod"); + + // Check for constant value in bulk + auto shear_bulk = get_val_at_kindex(shear_prod, muturb, 0, 1) / 10. / 20.; + EXPECT_NEAR(shear_bulk, srate, tol); + + // Velocity gradients assumed in setup (init_field3) + const amrex::Real uz_bulk = srate / 2.0; + + // Get value just above wall due to BC + auto shear_wall = get_val_at_kindex(shear_prod, muturb, 0, 0) / 10. / 20.; + // (tangential extrapolation, dirichlet normal) + const amrex::Real wz_wall = + ((uz_bulk * 1.5 * dz + 2.0) / 3.0 + uz_bulk * 0.5 * dz + 2.0 - 0.0) / + dz; + const amrex::Real s_true = + sqrt(2.0 * wz_wall * wz_wall + 2.0 * uz_bulk * uz_bulk); + // This is checking the correct value -- without the fillpatch, it is wrong + // (passing test indicates fillpatch is needed after diffusion performed) + EXPECT_GT(std::abs(shear_wall - s_true), tol); + + // Shear value at the wall used by wall model + const amrex::Real zref = 0.5 * dz; + const amrex::Real uref = zref * uz_bulk + 2.0; + const amrex::Real vmag_ref = std::sqrt(2.0 * uref * uref); + const amrex::Real utau = kappa * vmag_ref / (std::log(zref / z0)); + const amrex::Real uz_wm = uref / vmag_ref * std::pow(utau, 2) * rho0 / mu; + + // Velocity gradient with wallmodel value included as dirichlet + const amrex::Real uz_wmdirichlet = + (1.0 / 3.0 * (uz_bulk * 1.5 * dz + 2.0) + + 1.0 * (uz_bulk * 0.5 * dz + 2.0) - 4.0 / 3.0 * uz_wm) / + dz; + + // Naive answer, assumes wall Dirichlet + const amrex::Real s_naive = + sqrt(2.0 * wz_wall * wz_wall + 2.0 * uz_wmdirichlet * uz_wmdirichlet); + // This is checking the wrong value + // (passing test indicates it is wrong in expected way w/out fillpatch) + EXPECT_NEAR(shear_wall, s_naive, std::abs(shear_wall) * tol); +} + +TEST_F(TurbLESTestBC, test_1eqKsgs_zerogradient) +{ + populate_parameters(); + { + amrex::ParmParse pp("zlo"); + pp.add("type", (std::string) "zero_gradient"); + } + OneEqKsgs_setup_params(); + test_calls_body(); + auto& muturb = sim().repo().get_field("mu_turb"); + + // Get shear production field + auto& shear_prod = sim().repo().get_field("shear_prod"); + + // Check for constant value in bulk + auto shear_bulk = get_val_at_kindex(shear_prod, muturb, 0, 1) / 10. / 20.; + EXPECT_NEAR(shear_bulk, srate, tol); + + // Velocity gradients assumed in setup (init_field3) + const amrex::Real uz_bulk = srate / 2.0; + + // Naive answer, assumes extrapolation + const amrex::Real s_naive = srate; + // Check for different value just above wall due to BC + auto shear_wall = get_val_at_kindex(shear_prod, muturb, 0, 0) / 10. / 20.; + // Check that the result is not equal to the naive value + EXPECT_GT(std::abs(shear_wall - s_naive), tol); + // Answer that accounts for BC (Neumann) + const amrex::Real uz_wallface_neumann = + (1.0 / 3.0 * (uz_bulk * 1.5 * dz + 2.0) + + 1.0 * (uz_bulk * 0.5 * dz + 2.0) - + 4.0 / 3.0 * (uz_bulk * 0.5 * dz + 2.0)) / + dz; + // Neumann is applied to w as well + const amrex::Real wz_wallface_neumann = uz_wallface_neumann; + const amrex::Real s_true = sqrt( + 2.0 * wz_wallface_neumann * wz_wallface_neumann + + 2.0 * uz_wallface_neumann * uz_wallface_neumann); + EXPECT_NEAR(shear_wall, s_true, tol); +} + +TEST_F(TurbLESTestBC, test_1eqKsgs_symmetricwall) +{ + populate_parameters(); + { + amrex::ParmParse pp("zlo"); + pp.add("type", (std::string) "symmetric_wall"); + } + OneEqKsgs_setup_params(); + test_calls_body(); + auto& muturb = sim().repo().get_field("mu_turb"); + + // Get shear production field + auto& shear_prod = sim().repo().get_field("shear_prod"); + + // Check for constant value in bulk + auto shear_bulk = get_val_at_kindex(shear_prod, muturb, 0, 1) / 10. / 20.; + EXPECT_NEAR(shear_bulk, srate, tol); + + // Velocity gradients assumed in setup (init_field3) + const amrex::Real uz_bulk = srate / 2.0; + + // Naive answer, assumes extrapolation + const amrex::Real s_naive = srate; + // Check for different value just above wall due to BC + auto shear_wall = get_val_at_kindex(shear_prod, muturb, 0, 0) / 10. / 20.; + // Check that the result is not equal to the naive value + EXPECT_GT(std::abs(shear_wall - s_naive), tol); + // Answer that accounts for BC (Neumann) + const amrex::Real uz_wallface_neumann = + (1.0 / 3.0 * (uz_bulk * 1.5 * dz + 2.0) + + 1.0 * (uz_bulk * 0.5 * dz + 2.0) - + 4.0 / 3.0 * (uz_bulk * 0.5 * dz + 2.0)) / + dz; + // Wall condition on w + const amrex::Real wz_wallface = + (1.0 / 3.0 * (uz_bulk * 1.5 * dz + 2.0) + + 1.0 * (uz_bulk * 0.5 * dz + 2.0) - 4.0 / 3.0 * (0.0)) / + dz; + const amrex::Real s_true = sqrt( + 2.0 * wz_wallface * wz_wallface + + 2.0 * uz_wallface_neumann * uz_wallface_neumann); + EXPECT_NEAR(shear_wall, s_true, tol); +} + +} // namespace amr_wind_tests From bbe0fddb6106d6db53979e8647582ae3732e6d6f Mon Sep 17 00:00:00 2001 From: "Marc T. Henry de Frahan" Date: Thu, 4 May 2023 14:06:11 -0600 Subject: [PATCH 12/38] Implementing a wall modeled LES channel flow (#836) Co-authored-by: moprak-nrel <120606615+moprak-nrel@users.noreply.github.com> --- amr-wind/boundary_conditions/CMakeLists.txt | 2 + .../wall_models/CMakeLists.txt | 5 + .../wall_models/WallFunction.H | 54 ++++ .../wall_models/WallFunction.cpp | 125 +++++++++ amr-wind/diffusion/diffusion.H | 17 -- amr-wind/physics/ChannelFlow.H | 64 ++++- amr-wind/physics/ChannelFlow.cpp | 246 +++++++++++++++--- docs/sphinx/developer/channel_smagorinsky.png | Bin 0 -> 128577 bytes docs/sphinx/developer/verification.rst | 90 ++++++- test/CMakeLists.txt | 1 + .../channel_smagorinsky_analytical.inp | 66 +++++ 11 files changed, 613 insertions(+), 57 deletions(-) create mode 100644 amr-wind/boundary_conditions/wall_models/CMakeLists.txt create mode 100644 amr-wind/boundary_conditions/wall_models/WallFunction.H create mode 100644 amr-wind/boundary_conditions/wall_models/WallFunction.cpp create mode 100644 docs/sphinx/developer/channel_smagorinsky.png create mode 100644 test/test_files/channel_smagorinsky_analytical/channel_smagorinsky_analytical.inp diff --git a/amr-wind/boundary_conditions/CMakeLists.txt b/amr-wind/boundary_conditions/CMakeLists.txt index 51527afcdd..601f8e4349 100644 --- a/amr-wind/boundary_conditions/CMakeLists.txt +++ b/amr-wind/boundary_conditions/CMakeLists.txt @@ -4,3 +4,5 @@ target_sources(${amr_wind_lib_name} BCInterface.cpp FixedGradientBC.cpp ) + +add_subdirectory(wall_models) diff --git a/amr-wind/boundary_conditions/wall_models/CMakeLists.txt b/amr-wind/boundary_conditions/wall_models/CMakeLists.txt new file mode 100644 index 0000000000..fc6006c54b --- /dev/null +++ b/amr-wind/boundary_conditions/wall_models/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${amr_wind_lib_name} + PRIVATE + #C++ + WallFunction.cpp + ) diff --git a/amr-wind/boundary_conditions/wall_models/WallFunction.H b/amr-wind/boundary_conditions/wall_models/WallFunction.H new file mode 100644 index 0000000000..5247c285a2 --- /dev/null +++ b/amr-wind/boundary_conditions/wall_models/WallFunction.H @@ -0,0 +1,54 @@ +#ifndef WALLFUNCTION_H +#define WALLFUNCTION_H + +#include "amr-wind/CFDSim.H" +#include "amr-wind/core/FieldBCOps.H" + +namespace amr_wind { + +/** Wall-function computations for LES simulations + * \ingroup utilities + * + * This class performs the necessary computations at the beginning of + * predictor/corrector steps. The actual BC population in ghost cells is + * performed by VelWallFunc BC interface class. + */ +class WallFunction +{ +public: + explicit WallFunction(const CFDSim& sim); + + amrex::Real utau() const { return m_utau; } + + ~WallFunction() = default; + +private: + const CFDSim& m_sim; + + const amrex::AmrCore& m_mesh; + + amrex::Real m_utau{0.0}; +}; + +/** Applies a shear-stress value at the domain boundary + * \ingroup field_bc utilities + * + * \sa WallFunction + */ +class VelWallFunc : public FieldBCIface +{ +public: + VelWallFunc(Field& velocity, const WallFunction& wall_func); + + void operator()(Field& velocity, const FieldState rho_state) override; + + static void wall_model( + Field& velocity, const FieldState rho_state, const amrex::Real utau); + +private: + const WallFunction& m_wall_func; + std::string m_wall_shear_stress_type{"constant"}; +}; +} // namespace amr_wind + +#endif /* WALLFUNCTION_H */ diff --git a/amr-wind/boundary_conditions/wall_models/WallFunction.cpp b/amr-wind/boundary_conditions/wall_models/WallFunction.cpp new file mode 100644 index 0000000000..d33a2298a1 --- /dev/null +++ b/amr-wind/boundary_conditions/wall_models/WallFunction.cpp @@ -0,0 +1,125 @@ +#include "amr-wind/boundary_conditions/wall_models/WallFunction.H" +#include "amr-wind/utilities/tensor_ops.H" +#include "amr-wind/utilities/trig_ops.H" +#include "amr-wind/diffusion/diffusion.H" + +#include + +#include "AMReX_ParmParse.H" +#include "AMReX_Print.H" +#include "AMReX_ParallelDescriptor.H" + +namespace amr_wind { + +WallFunction::WallFunction(const CFDSim& sim) : m_sim(sim), m_mesh(m_sim.mesh()) +{ + { + amrex::ParmParse pp("BodyForce"); + amrex::Vector body_force{{0.0, 0.0, 0.0}}; + pp.getarr("magnitude", body_force); + m_utau = std::sqrt(body_force[0]); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + std::abs(body_force[1]) < 1e-16, + "body force in y should be zero for this wall function"); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + std::abs(body_force[2]) < 1e-16, + "body force in z should be zero for this wall function"); + } +} + +VelWallFunc::VelWallFunc(Field& /*unused*/, const WallFunction& wall_func) + : m_wall_func(wall_func) +{ + amrex::ParmParse pp("WallFunction"); + pp.query("wall_shear_stress_type", m_wall_shear_stress_type); + m_wall_shear_stress_type = amrex::toLower(m_wall_shear_stress_type); + + if (m_wall_shear_stress_type == "constant") { + amrex::Print() << "Shear Stress model: " << m_wall_shear_stress_type + << std::endl; + } else { + amrex::Abort("Shear Stress wall model input mistake"); + } +} + +void VelWallFunc::wall_model( + Field& velocity, const FieldState rho_state, const amrex::Real utau) +{ + BL_PROFILE("amr-wind::VelWallFunc"); + constexpr int idim = 2; + const auto& repo = velocity.repo(); + const auto& density = repo.get_field("density", rho_state); + const auto& viscosity = repo.get_field("velocity_mueff"); + const int nlevels = repo.num_active_levels(); + + amrex::Orientation zlo(amrex::Direction::z, amrex::Orientation::low); + amrex::Orientation zhi(amrex::Direction::z, amrex::Orientation::high); + if ((velocity.bc_type()[zlo] != BC::wall_model) && + (velocity.bc_type()[zhi] != BC::wall_model)) { + return; + } + + for (int lev = 0; lev < nlevels; ++lev) { + const auto& geom = repo.mesh().Geom(lev); + const auto& domain = geom.Domain(); + amrex::MFItInfo mfi_info{}; + + const auto& rho_lev = density(lev); + auto& vel_lev = velocity(lev); + const auto& eta_lev = viscosity(lev); + + if (amrex::Gpu::notInLaunchRegion()) { + mfi_info.SetDynamic(true); + } +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (amrex::MFIter mfi(vel_lev, mfi_info); mfi.isValid(); ++mfi) { + const auto& bx = mfi.validbox(); + auto varr = vel_lev.array(mfi); + auto den = rho_lev.array(mfi); + auto eta = eta_lev.array(mfi); + + if (bx.smallEnd(idim) == domain.smallEnd(idim) && + velocity.bc_type()[zlo] == BC::wall_model) { + amrex::ParallelFor( + amrex::bdryLo(bx, idim), + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real mu = eta(i, j, k); + + // Dirichlet BC + varr(i, j, k - 1, 2) = 0.0; + + // Shear stress BC + varr(i, j, k - 1, 0) = utau * utau / mu * den(i, j, k); + varr(i, j, k - 1, 1) = 0.0; + }); + } + + if (bx.bigEnd(idim) == domain.bigEnd(idim) && + velocity.bc_type()[zhi] == BC::wall_model) { + amrex::ParallelFor( + amrex::bdryHi(bx, idim), + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real mu = eta(i, j, k - 1); + + // Dirichlet BC + varr(i, j, k, 2) = 0.0; + + // Shear stress BC + varr(i, j, k, 0) = utau * utau / mu * den(i, j, k); + varr(i, j, k, 1) = 0.0; + }); + } + } + } +} + +void VelWallFunc::operator()(Field& velocity, const FieldState rho_state) +{ + if (m_wall_shear_stress_type == "constant") { + wall_model(velocity, rho_state, m_wall_func.utau()); + } +} + +} // namespace amr_wind diff --git a/amr-wind/diffusion/diffusion.H b/amr-wind/diffusion/diffusion.H index 6b78f84400..7a466a7db5 100644 --- a/amr-wind/diffusion/diffusion.H +++ b/amr-wind/diffusion/diffusion.H @@ -7,23 +7,6 @@ namespace diffusion { -void wall_model_bc_moeng( - amr_wind::Field& velocity, - const amrex::Real utau, - const amr_wind::FieldState fstate, - const amrex::FArrayBox& instplanar); - -void wall_model_bc( - amr_wind::Field& velocity, - const amrex::Real utau, - const amrex::Real umag, - const amr_wind::FieldState fstate); - -void temp_wall_model_bc( - amr_wind::Field& temperature, - const amrex::FArrayBox& instplanar, - const amr_wind::FieldState fstate); - amrex::Vector> get_diffuse_tensor_bc( amr_wind::Field& velocity, amrex::Orientation::Side side) noexcept; diff --git a/amr-wind/physics/ChannelFlow.H b/amr-wind/physics/ChannelFlow.H index 3542269a80..aa0137a81c 100644 --- a/amr-wind/physics/ChannelFlow.H +++ b/amr-wind/physics/ChannelFlow.H @@ -4,8 +4,36 @@ #include "amr-wind/core/Physics.H" #include "amr-wind/core/Field.H" #include "amr-wind/CFDSim.H" +#include "amr-wind/boundary_conditions/wall_models/WallFunction.H" +#include "amr-wind/turbulence/TurbulenceModel.H" #include +namespace { +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real +analytical_smagorinsky_profile( + const amrex::Real h, + const amrex::Real Cs, + const amrex::Real dx, + const amrex::Real rho, + const amrex::Real mu, + const amrex::Real dpdx, + const amrex::Real C0, + const amrex::Real C1) +{ + const amrex::Real Cs2 = Cs * Cs; + const amrex::Real dx2 = dx * dx; + const amrex::Real Cs2dx2 = Cs2 * dx2; + return -mu / (rho * Cs2dx2) * h + + Cs2dx2 / (3 * dpdx) * + std::pow( + (2.0 / Cs2dx2 * dpdx * h + + (mu / (rho * Cs2dx2)) * (mu / (rho * Cs2dx2)) + C1), + 3.0 / 2.0) + + C0; +} + +} // namespace + namespace amr_wind::channel_flow { /** Channel Flow physics @@ -33,6 +61,8 @@ public: template amrex::Real compute_error(const IndexSelector& idxOp); + amrex::Real compute_analytical_smagorinsky_error(); + void output_error(); void post_init_actions() override; @@ -44,10 +74,15 @@ public: void post_advance_work() override; private: + //! CFD simulation controller instance + CFDSim& m_sim; + const amr_wind::SimTime& m_time; - const FieldRepo& m_repo; + FieldRepo& m_repo; const amrex::AmrCore& m_mesh; + WallFunction m_wall_func; + //! Wall normal directon - Default y direction int m_norm_dir{1}; @@ -72,9 +107,24 @@ private: //! initial sdr value amrex::Real m_sdr0{1000.0}; + //! flag for perturbations of the initial condition + bool m_perturb_vel{false}; + + //! perturbation period (y-direction) + amrex::Real m_perturb_y_period{1.0}; + + //! perturbation period (z-direction) + amrex::Real m_perturb_z_period{1.0}; + + //! perturbation factor (fraction of utau) + amrex::Real m_perturb_fac{0.1}; + //! Von-Karman constant amrex::Real m_kappa{0.41}; + //! Turbulence model + std::string m_turbulence_model; + bool m_laminar{false}; bool m_mesh_mapping{false}; @@ -83,6 +133,18 @@ private: bool m_half{false}; + //! flag for analytical smagorinsky test + bool m_analytical_smagorinsky_test{false}; + + //! Analytical Smagorinsky first coefficient + amrex::Real m_C0{0.0}; + + //! Analytical Smagorinsky second coefficient + amrex::Real m_C1{0.0}; + + //! Body forcing (x direction) + amrex::Real m_dpdx{0.0}; + //! direction of mean velocity int m_mean_vel_dir{0}; diff --git a/amr-wind/physics/ChannelFlow.cpp b/amr-wind/physics/ChannelFlow.cpp index 896c6f39bb..6a5af42f9c 100644 --- a/amr-wind/physics/ChannelFlow.cpp +++ b/amr-wind/physics/ChannelFlow.cpp @@ -9,16 +9,17 @@ namespace amr_wind::channel_flow { ChannelFlow::ChannelFlow(CFDSim& sim) - : m_time(sim.time()) + : m_sim(sim) + , m_time(sim.time()) , m_repo(sim.repo()) , m_mesh(sim.mesh()) + , m_wall_func(sim) , m_mesh_mapping(sim.has_mesh_mapping()) { { - std::string turbulence_model; amrex::ParmParse pp("turbulence"); - pp.query("model", turbulence_model); - if (turbulence_model == "Laminar") { + pp.query("model", m_turbulence_model); + if (m_turbulence_model == "Laminar") { m_laminar = true; } } @@ -37,6 +38,36 @@ ChannelFlow::ChannelFlow(CFDSim& sim) pp.query("re_tau", m_re_tau); pp.query("tke0", m_tke0); pp.query("sdr0", m_sdr0); + pp.query("perturb_velocity", m_perturb_vel); + pp.query("perturb_y_period", m_perturb_y_period); + pp.query("perturb_z_period", m_perturb_z_period); + pp.query("perturb_factor", m_perturb_fac); + pp.query( + "analytical_smagorinsky_test", m_analytical_smagorinsky_test); + if (m_analytical_smagorinsky_test) { + if (m_turbulence_model == "Smagorinsky") { + pp.query("C0", m_C0); + pp.query("C1", m_C1); + { + amrex::ParmParse ppb("BodyForce"); + amrex::Vector body_force{{0.0, 0.0, 0.0}}; + ppb.queryarr("magnitude", body_force); + m_dpdx = -body_force[0]; + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + std::abs(body_force[1]) < 1e-16, + "body force in y should be zero for this wall " + "function"); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + std::abs(body_force[2]) < 1e-16, + "body force in z should be zero for this wall " + "function"); + } + } else { + amrex::Abort( + "Use Smagorinsky model for the analytical Smagorinsky " + "test"); + } + } } } { @@ -46,12 +77,17 @@ ChannelFlow::ChannelFlow(CFDSim& sim) m_utau = m_mu * m_re_tau / (m_rho * 1.0); m_ytau = m_mu / (m_utau * m_rho); } - if (amrex::ParallelDescriptor::IOProcessor()) { + if ((amrex::ParallelDescriptor::IOProcessor()) && + (m_laminar || m_analytical_smagorinsky_test)) { std::ofstream f; f.open(m_output_fname.c_str()); f << std::setw(m_w) << "time" << std::setw(m_w) << "L2_u" << std::endl; f.close(); } + + if (!m_repo.field_exists("wall_dist")) { + m_repo.declare_field("wall_dist", 1, 1, 1); + } } /** Initialize the velocity, density, tke and sdr fields at the beginning of the @@ -89,38 +125,92 @@ void ChannelFlow::initialize_fields( auto& velocity = velocity_field(level); auto& density = m_repo.get_field("density")(level); density.setVal(m_rho); + const auto perturb_vel = m_perturb_vel; + const auto perturb_fac = m_perturb_fac; + const auto perturb_amp = perturb_fac * m_utau; + const amrex::Real pi = M_PI; + const auto& problo = geom.ProbLoArray(); + const auto& probhi = geom.ProbHiArray(); + const auto& dx = geom.CellSizeArray(); + const amrex::GpuArray lengths{AMREX_D_DECL( + probhi[0] - problo[0], probhi[1] - problo[1], probhi[2] - problo[2])}; + const auto y_perturb = m_perturb_y_period * 2.0 * pi / lengths[1]; + const auto z_perturb = m_perturb_z_period * 2.0 * pi / lengths[2]; if (!m_laminar) { - auto& tke = m_repo.get_field("tke")(level); - auto& sdr = m_repo.get_field("sdr")(level); - auto& walldist = m_repo.get_field("wall_dist")(level); - tke.setVal(m_tke0); - sdr.setVal(m_sdr0); - - for (amrex::MFIter mfi(velocity); mfi.isValid(); ++mfi) { - const auto& vbx = mfi.validbox(); - - const auto& dx = geom.CellSizeArray(); - const auto& problo = geom.ProbLoArray(); - auto vel = velocity.array(mfi); - auto wd = walldist.array(mfi); - - amrex::ParallelFor( - vbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { - const int n_ind = idxOp(i, j, k); - amrex::Real h = problo[n_idx] + (n_ind + 0.5) * dx[n_idx]; - if (h > 1.0) { - h = 2.0 - h; - } - wd(i, j, k) = h; - const amrex::Real hp = h / y_tau; - vel(i, j, k, 0) = - utau * (1. / kappa * std::log1p(kappa * hp) + - 7.8 * (1.0 - std::exp(-hp / 11.0) - - (hp / 11.0) * std::exp(-hp / 3.0))); - vel(i, j, k, 1) = 0.0; - vel(i, j, k, 2) = 0.0; - }); + if (m_analytical_smagorinsky_test) { + auto coeffs = m_sim.turbulence_model().model_coeffs(); + const auto Cs = coeffs["Cs"]; + const auto mu = m_mu; + const auto rho = m_rho; + const auto C0 = m_C0; + const auto C1 = m_C1; + const auto dpdx = m_dpdx; + for (amrex::MFIter mfi(velocity); mfi.isValid(); ++mfi) { + const auto& vbx = mfi.validbox(); + auto vel = velocity.array(mfi); + + amrex::ParallelFor( + vbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const int n_ind = idxOp(i, j, k); + amrex::Real h = + problo[n_idx] + (n_ind + 0.5) * dx[n_idx]; + if (h > 1.0) { + h = 2.0 - h; + } + const amrex::Real ux = analytical_smagorinsky_profile( + h, Cs, dx[n_idx], rho, mu, dpdx, C0, C1); + vel(i, j, k, 0) = + ux + (perturb_vel + ? perturb_fac * std::sin(z_perturb * h) + : 0.0); + vel(i, j, k, 1) = 0.0; + vel(i, j, k, 2) = 0.0; + }); + } + } else { + if (m_repo.field_exists("tke")) { + auto& tke = m_repo.get_field("tke")(level); + tke.setVal(m_tke0); + } + if (m_repo.field_exists("sdr")) { + auto& sdr = m_repo.get_field("sdr")(level); + sdr.setVal(m_sdr0); + } + auto& walldist = m_repo.get_field("wall_dist")(level); + + for (amrex::MFIter mfi(velocity); mfi.isValid(); ++mfi) { + const auto& vbx = mfi.validbox(); + auto vel = velocity.array(mfi); + auto wd = walldist.array(mfi); + + amrex::ParallelFor( + vbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const int n_ind = idxOp(i, j, k); + amrex::Real h = + problo[n_idx] + (n_ind + 0.5) * dx[n_idx]; + if (h > 1.0) { + h = 2.0 - h; + } + wd(i, j, k) = h; + const amrex::Real hp = h / y_tau; + vel(i, j, k, 0) = + utau * (1. / kappa * std::log1p(kappa * hp) + + 7.8 * (1.0 - std::exp(-hp / 11.0) - + (hp / 11.0) * std::exp(-hp / 3.0))); + + const amrex::Real y = problo[1] + (j + 0.5) * dx[1]; + const amrex::Real z = problo[2] + (k + 0.5) * dx[2]; + const amrex::Real perty = perturb_amp * + std::sin(y_perturb * y) * + std::cos(z_perturb * z); + const amrex::Real pertz = -perturb_amp * + std::cos(y_perturb * y) * + std::sin(z_perturb * z); + vel(i, j, k, 1) = 0.0 + (perturb_vel ? perty : 0.0); + vel(i, j, k, 2) = 0.0 + (perturb_vel ? pertz : 0.0); + }); + } } } else { velocity.setVal(0.0); @@ -239,6 +329,71 @@ amrex::Real ChannelFlow::compute_error(const IndexSelector& idxOp) return std::sqrt(error / total_vol); } +amrex::Real ChannelFlow::compute_analytical_smagorinsky_error() +{ + amrex::Real error = 0.0; + const auto mu = m_mu; + auto coeffs = m_sim.turbulence_model().model_coeffs(); + const auto Cs = coeffs["Cs"]; + const auto rho = m_rho; + const auto C0 = m_C0; + const auto C1 = m_C1; + const auto dpdx = m_dpdx; + + const auto& velocity = m_repo.get_field("velocity"); + + const int nlevels = m_repo.num_active_levels(); + for (int lev = 0; lev < nlevels; ++lev) { + + const auto& dx = m_mesh.Geom(lev).CellSizeArray(); + const auto& prob_lo = m_mesh.Geom(lev).ProbLoArray(); + + amrex::iMultiFab level_mask; + if (lev < nlevels - 1) { + level_mask = makeFineMask( + m_mesh.boxArray(lev), m_mesh.DistributionMap(lev), + m_mesh.boxArray(lev + 1), amrex::IntVect(2), 1, 0); + } else { + level_mask.define( + m_mesh.boxArray(lev), m_mesh.DistributionMap(lev), 1, 0, + amrex::MFInfo()); + level_mask.setVal(1); + } + + const auto& vel = velocity(lev); + auto const& vel_arr = vel.const_arrays(); + auto const& mask_arr = level_mask.const_arrays(); + + error += amrex::ParReduce( + amrex::TypeList{}, + amrex::TypeList{}, vel, amrex::IntVect(0), + [=] AMREX_GPU_HOST_DEVICE(int box_no, int i, int j, int k) + -> amrex::GpuTuple { + auto const& vel_bx = vel_arr[box_no]; + auto const& mask_bx = mask_arr[box_no]; + + const int n_idx = 2; + amrex::Real h = prob_lo[n_idx] + (k + 0.5) * dx[n_idx]; + if (h > 1.0) { + h = 2.0 - h; + } + const amrex::Real u = vel_bx(i, j, k, 0); + const amrex::Real u_exact = analytical_smagorinsky_profile( + h, Cs, dx[n_idx], rho, mu, dpdx, C0, C1); + + const amrex::Real cell_vol = dx[0] * dx[1] * dx[2]; + + return cell_vol * mask_bx(i, j, k) * (u - u_exact) * + (u - u_exact); + }); + } + + amrex::ParallelDescriptor::ReduceRealSum(error); + + const auto total_vol = m_mesh.Geom(0).ProbDomain().volume(); + return std::sqrt(error / total_vol); +} + void ChannelFlow::output_error() { // analytical solution exists only for flow in streamwise direction @@ -251,7 +406,11 @@ void ChannelFlow::output_error() u_err = compute_error(YDir()); break; case 2: - u_err = compute_error(ZDir()); + if (m_laminar) { + u_err = compute_error(ZDir()); + } else if (m_analytical_smagorinsky_test) { + u_err = compute_analytical_smagorinsky_error(); + } break; default: amrex::Abort("axis must be equal to 1 or 2"); @@ -269,14 +428,25 @@ void ChannelFlow::output_error() void ChannelFlow::post_init_actions() { - if (m_laminar) { + if (m_laminar || m_analytical_smagorinsky_test) { output_error(); } + + auto& velocity = m_repo.get_field("velocity"); + amrex::Orientation zlo(amrex::Direction::z, amrex::Orientation::low); + amrex::Orientation zhi(amrex::Direction::z, amrex::Orientation::high); + if ((velocity.bc_type()[zlo] == BC::wall_model) || + (velocity.bc_type()[zhi] == BC::wall_model)) { + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + m_norm_dir == 2, + "Wall normal direction should be 2 if using a wall model"); + velocity.register_custom_bc(m_wall_func); + } } void ChannelFlow::post_advance_work() { - if (m_laminar) { + if (m_laminar || m_analytical_smagorinsky_test) { output_error(); } } diff --git a/docs/sphinx/developer/channel_smagorinsky.png b/docs/sphinx/developer/channel_smagorinsky.png new file mode 100644 index 0000000000000000000000000000000000000000..78f220bfc3fc1a8629f6e1ed84c356053b322c97 GIT binary patch literal 128577 zcmeEuWmJ{j+AbiCC@P46gd$x^h%~53gAyW*3M?9=VNpv|5JBl?fuz&|=>|omyBnl) zA>Ew$;NIVU_ba^L&oj;#=Z6jk@vLXgdB=5M_jS*=4;7_|2`LFNFffQ^r0*+ZVBl_I zVBlQF#|7V!8GMDpz`$fOxp(iO**&RyR_0bVD%MY*8%h{hJu_66x_e7N;0^``bKrA* zePwBO=DMdB_4VtT*;oi|9F={1{FL=QTB;kWo2nb>>f^&Aw6({skc`z}VBe%9b1nN( zji11^@UrP^NG(yZvhurN#5yfqiy|j8>r;LGCXC$BMma+9MH)J-8!S{!x){&MF{$wl ztS~=!UR%7R&quOJof8#62cz3N6VM!vZN!6E{3++1gClPKse+DfHXE&?i1(?nV5aMEg^KoH{zs~qj z;wD}jk0%!4%h@!2EJ;kVrkD#7B$Ak+Ns}?QnjO>GBZo1Cu!!MUT_YIG;b=qHw)10p z>25Du89%y9>k&l?J`N@}i8#hT{P$IpPB2rdcI=;ji$uKoB3(}Hc`Ph^I!}z#|GPjZ zuFr)EjdC+V{n{^9kZ*T#bHpZ&+T!lxs_#KPXZ zK6dTa{r_b<@E3pa{L=!#JBwrElk~mw^!`S2;U9OF1Z-IAKkU`<^8f#a{l)P9|AzfG zz5go~Mt2zt_y(OS7BMAnz(nl_t?&%Bj4;*AC%06?rv*><)tJKSC_XcTqr-#8VjPOPWpM4 zg`fQ1KL-&y7*^8HrbwzR@Y;A-zkdA+3JEbNFdr!|=}J>!!mDV*J~eP4$}enBh3gp% z7FzUtZwz6%v?@W;M8f|2c+?>3UWp;XIoEThy`vvF#eu%*)ev|M&^TAMd`lnR#|?k?*Kk;_Nk=@VkF`%72^;vCkIIvv%8ZNcthyk?6DKvzXX~ zKH@euxf|$`6y^8pU;QeiJzo0J{^u|L`jw5Oe(ihe1X|oZwihp6Jk~7Q4;pA56|n(N zA@RA7d7%6T<(1B}86)|@YPeKwzANogyNLk7qCgz$;3~O&ZP!?IO7q>=J8o|;T{2Pf z`*pG_i=&8U@v2%bQcD9TW?4V0Rh2BYUu#f1J)Kgi4NkE!@%Tu1TT+QbJ6Us z3-o3fh&s}h*cVIa(H-d$9xQm^YSI;^lmGJ7eQ}lioy)OH63`Kl_{d|9=0^u!?V~4y z862MFHpTw>SRdGAuGOe$)r6da!UjCX>(AAR7ZZVqy~Xrx8~X5<*TnU+^@U-}7ZCvj zephbY{qcd8Hh1C0wUf6SatA_RjI=vM4Z+i*{|w`4{Ke#bm;&&L3i>pkA2dB1K?pZ+Lr5{o^psE{#5TO_e1Z#CcMzMhF5dz^moG| zp(BG~NUI)i(8<0Fc@AARPQ>T%UM0^3`cv~wfHxN2d72Qes{IZci8STpNnbWp&eoaX zCuI&<>`!v4GhDoIiIYI))vX;`yurlOm~YZq9(Ci!jkz=CkJv3->Ign){9=Q`?1)cm#kOiuB*1to6VJ>p1mnJ zZ8VeDDjGae0f+OoS3h}-s3;nB*V2VA@nzrESo-$;yG(`4M$$d+bF1GjDM@#ytL3FH z&kuq3+^(z?;KA-t*S1satlQ*?4l#%?$tp&6tI2rz{GTC=akG`)tFy zx>O^1O@h`QzAv($9*PmLwHZ3hDjI2cF(^)zl9W{EOU5?o&p$#!d|&i%d%_01iFhKC{a_Km>#cAl2yBgl^5RmO_7*HH{uo@r}5XQLCb-BnaZO> zc<+)cmGI7Fa7U#ZJiA6lMy4n8e#M)V^!>TixXd*s#h8Hc8t->4ew`sm!^W@;(;0`E z^0k&=5HcIU53*8hXIetPef#FwVdB0u?3f@S1>2d9w#w9xN$GAvv1{bsFSHmmQn$t3 zT=X)(AACW{|6Qs|*LsH>S3y~u z_%N^?Gc>GV+{z(N@Nj>vRf8|DRbbG}b9KnJwV*ZDvbtm0$3FmyOAwMgeOu8uyu*{g z&oU9l7^b`HbGlwpcU~m)Ru0B0C(CLmC?bVfR8r($bZd9spU;#~O4BaMh4HA5fS|}G zNToO`BB|m7g0~QI_-fT&ONcG*5cdMFh=@+*?mXKxjKy8fWsSnMLyuHECX1E%#3CZX5Kuja6oF@|CLnL`n!Cx}z5CKhx8>JP z&vUVIYyb#**YtT$jRg3w?pH^?Su0&Y1%LX)JlCI>>b3+NuMv;dRUch8Ts;1UYLQVc zoHKOPeXn_>)v|^HzYF6dZv5Rx^W7OtWUQ(=TvHnGR?z@2|BhriIq!4Si(!;GS~>cN z3ba)XMR{kuS*l{}qG>7P&r(HlyWc+_spWfyyo9tDa$R8H-zUq8s$5;0N5z)ZrK6k`R|>^O?9 zOZpo0hoj8GqoPc^+bQe>2bb)O&KWu1iVN0r{q;H)sj3uf+ae&!XH%Ggr~n7lW|@g7 zx097BgWoL>%DXVJTKu>i}%hjRJ&%HO+I?A1G+Y2y;Lx#&7 z8_$dF(*ZPFv}-(ES}?o#K(+@kLb;pc?58A}F-^J}SCY!Z5x7oKqQn>$UNfcnuJ5ak z>de8%>zR39jXqJfIfLx5_-nhRqx0Zt&71xiKK$*kL|lD)mbZDSXYQRBxlvzV-{ay| zY8i*S$IS*n_0O1o$AzpSqsM%+wc#9E4h?;v!lVsZF!sy^4|sf2UP!yrwFFeK8m3*J zUdSG`^gD!W+q`wF1g61lIdsR&1B9utR3Y_ z;zbsNg-F&OCw}w3 zeH@S2+WyVUbY!VTcyM4opdPcsd=~pt=|l`dM~bEAzLPv_&cY%2?s6y_s`%?dZY4sL z7|T?jlPMXX*p4hM{<1XNCE>mJ=g*giN;b{EL_}O6kfc4iU#PaQ~cPhnogq=BplAZfS z2rIHFn@qu?YOr^F#%uQntZ+oAS>ZJz98_ z_@H0M)YHD&6LYiDGUm>UyGE@@3+m4Na7yeoQXMv>-h0BJys?))<3#DY&e2khtm=2E z+&D8#%WE8-iNgQVizNabY7@WOJu(lN0 zB2h;l8(3rYFm`0Pjq-#nLN|nCr;ra&-vCVtdQrHQKxCO%CYP}cv5fO@az$6qyMVy?pC25+X}`x_L-rfMFLKjZIZXb8V(%+f|Xs1Ti>e#A4e= zubF@ilte>&q7Caj=Sm%|WD2FS=;h zA)1=Kvn#v>BP5jv=G&%aS@NCmm2*pM%bw2#`ui%Lf_kN4T05&#OO_jlc19m@w{7z7 zoUwJGo>`O`-fH^MX)#)G{(#yR7i*(DWow<(0v*#lBY^DOYKaw+19kcS?cRPLn7@0VGV0<+!rz!XCi>9_ga8rAGKSapIuULR6m?A<0^ zlX`#nMe@Y>H)e-3-5kxd(36!{)^0y}?&_U@Ik{G$cc|E~&ertwXa-|m>uHu2IwXx& zxzlF=V!sJyefcEnw(FFR+=M4S@K18v+tlUKDYKlc3v?Kby10faTh86GHA@7M{vs`{ zm1DM9gR^f+TNmF^{w(DyP(E$MsXZ>p##I(%isix;v5xDQ zt;dD8pTt;)5}7%%47MiR#_e?3m^W

*#-Us0*q;+uKDi6)V%|D_9NkI*Y9I%6t0RZ>Hq^`?;F)V(W{@RaDw7zMQb(4EWY^YW~Jm zX5I4Q0!1WD?~E?8E7ZkJ=@-9Nt8EKKPG`Xxd)%bUI&rPRKUI5|Iw>5!ut(AG555a&WiVu09kKs$6#a7n$FRtk=?HTI{pLDh>W{BeAU z6>^{3wCVhOX-C&TUgiGcsh}iEL2z$_^7(5mt58{qnC_`==K#k9ZW%|4$wq_zVNj$@ zfzqxaSCNj1@6IPgQA`Mgg-dLv?)SctlamYH8i6a(I8R=XQB({|Q%N1r5j^9$`9-_0 zO}{H6yOI@<;yJz4<><=2OnW?g;ya_cX1$z3_%ra95?XTdnIREFEo!^wyGlEUd+01a zd{}eTl)xlEaq3JG?&h2Gw3bWrXJv`;HP@Cev1-Blux zZ0oVj+TT{x=MocDTP6UsmZrXIg$s8_)3@jg)tMr#{#T_H3XGOFxSgxew@3Zn@(`W- zGf2^u$$X0_1()&#)%vox?A9v~|BGYO}5xi<^OTgqig;HBFEY zV}>Qpb@lC4K|5wC9u&xfxbeD9<2V^YO8QE@k%LEDFhfDxn;|BY(Yz+t&)}U43DjL~M+_J{a{+l-epU9HUUaqYH?g~xpq9C=5N|E- zcecIXkwLa@r8#=))ARPe#bwFfp}pqG2AW41VO{dlbtu7;Z9WG(#^jEW{rn9;n%IbQ zm+B6HTADe!dFF}cze=;uSYDX+V=6MR31=+0$Kg&YyH=q*qY~1*w*y1%BT1_@*ppA8 zFI4~%fw+c}xs>Q6h3MOx#db9U8Ep%%8e=J^^CXCwIdw|AOw;BR?ESxZ84=KOl%CTw zH=iDlHeO3U0>!Ad@SGf9PU@Vny}u|2WpOiWXsQ~^w#)X@~ZNwjsFMYF5hBJ(!U&ZcI8tmtS_tC|q&NyQ4O zJOU7;jR}(P7Ne`WlWq~`85PTDY{i4=wH(`4kzP@ho7>J*_@?`(4(kDO zUaBaEHBb~3oj19e2bdQJ7sb8%B>XWv^Str11RZk>SOg6Mg9~Si`A1zlX>_1c0>F|@ z_dDiY*?OZiZ&IGdf@B4XE&6VQn~B>{8)Ni!B7{Dru~{RnXe&(I(}s0FClv4C?Ii6? z{G8*^G6>jlkh@+rd3QCl82``YLz0HoA|8|Cyx&`OI5q#6HzyTSRB318gO~vz#i5+j zG8UUsE&Q~y4wVdmvQ3W$tYn$%3bAGRuLlB0`)f<6^+R3PUy^XKKC03dWZtZ%hvqZ+ z!fZLNiZ15{v_m+jPPx(T_tZi?sF}ecx9jKmEsMu-%@{wz@JqwSLH-me(?x?=NUESD z`-^L()#L49t1Xme#OcCuo;7uCyBiIc_bPa1Kf6t|uzPL_9^ATL8p>w)g0TwQZ6WW& z3B1?C9BkgHk38K|8@+m51f1bc?R*o0j?0TUGxAGw_SZdQX+&J4fBpQfls0#EJJyC9 z8;`emS{|{}rJ|(4!f89vA((+m*vYN3G6C3WR?w?I6wr9}ugP_d0jW|Slee=-=v@9N zTUVrHHsRgkBlE$+Wc zu}wuOLm@gWEH<@Ke5<89jB=U}-bAN#okS|t&0xiO9F{EFvd`HJS`@pqpr3GJ}LKF3<=$$m5>7YK+FSrO&fp z!IG(&qwuc6K;k~NKd7{3Pn7x)26`WZ_x1qolmybGO}CnQ{lV5K7rCD6=X}#{*}@2U z!;mJ>WikXr0JMrpEQY3p;zZNlu%igQY62rhy?BOURo5EqP2TFFPFJFLFHblPPfx-- zupW~!S(xuslJUxSf#Ah|;nv9{whVuObBX6os=2Zo1Fqdm%B@FDu|V~&>t;U;IUHZP zCl@)#m@|`&s?yhci04Kb%V#mrwJ`IjOwfJ5_|xYUk?!^As4z8;mWFUH4@&ihQ3Zm< zDfCR^@HJ2ISA_c+<{FXaM(A!wS1*PDEsbZDl1XU-V$nQI0rOpdBsvJf*J4rfM)H}3 zxp@o3W~EYt3R&8YS`Zcqyv|mgS0pLbShg`!*?qn#6LS7s!^X_sxbD6t&Y-I8N zpU$cfwjJ1HfcBF&)>K1iUh2UIDt<*!9>;kn5-fHMD(Ef^Ut+?2EI@9WoRkV7@9;GE zR_U@%C;jZtn(-9{re3d9%)bW&XXb!sVHFV9-HT5d$u%Y@hP@?7F)oDH%pU3Qg(o$n zqgp<3F3oi&_5!~pt{(>0^|Z9KT&G{pG(JCjofO?F&iAPwP(Rvx>?*yhwkhwkGz-#0 zm>*r2`f#_`Cq~q*w7U?X8E%V#{Hq(moew?>SV#*&bQ)ra+~?iD) zMu{~duC@e|%e+b(d*~{z<|sa*e2R?|YX$K4+GK4Mv4UnQdZdn^$!#|yX>sg#*ex6y z%A&lZXU$>Cn8hrhBn|_09RzG&6AV~RetLJ3#e(m>iUYjqS9Ijf>)6*d#n()^0P~L) zxyrLp^~i?vAtR@vE=o&1WwD>03*C1u4bwKk@Y;-5Jj35N z_}@g6IFbOg8hKne{lpJRw|5t^4fnTy?W|3I9YgVkANe!h?Coe8f}2k~=#L2@UHJqG zz~UFr*@_+O8N#uL-uwRhF@L=N_%(6XmKCRSepm=sdXh_*=%Z-+N7+V)46Y1G*Ibu0 zkga*sktI)|p4k7~XivwX->~PbXl%XINzQOga{62a-RQuz%quKd7p|=U#utPh-%<7c zTtoVwG}jFSUMrAnq*3xsmS`3vMf9J`pVfqyPU}YBwlDzPC*pP@&8Zte0}AKH3B2kl zfTIzeWV6bp`L1oB0(m(!yVTU>!q@{r3ISVWNast-WZ56!f&`mLN3P=iaIt##3$yG zJR3t3orG6sZ%LF|7>QJfH%igE-2hF>kosVzM(3GWa$}zPKl&5mq9i^n8p^&g&PLyc zWn4>aF7O*cj)EV90`ja*vLq3(7!o6_W?!=4xF3Fj-lJ~nDN1oPE3<{>{nx35971Yo zJ+zTdKMCHx_-v_HP5$xvAFF>AEB>}8EBPB7{d&0g3RGl!R;OWEau>!9MA09c##5Gu z9oSnb3ek*K6&qos_MbO(H8rEoQVXhb7LQ@|1ETNA2$k2#U1`wjO9!v|a(A-C3Y&zP z1j}uAJ#f_HNC(JPrsv%Oe1n+`2nO z0uHq5ex&_27Ke9}pc~+DNQuvZOvT=2)OD(QrH%pEU+#JmxE_S$Ga|&ELZjNEA13`zi$Q&{BPG$&{PFVv8;gdmfoGNrb4vGF7EGiKm?_Ca|Btbf#V9?rT1TK1d(oU`2lp^CDuFi5QG1RI%CokJZ+h;5p~Gd3n8AWP0XBdLYyd5VDbVO5N-EWn z8q7Ct|4Mjn0I&aKcZCkH6c}lIaK|;spG2`tq&_sHNYRFFoEN5J+1r8%<$#{WF^hfg z&Dp?+hzRoqIO|n0ckSikiF55^0E6I(csom-7!07NCxEBZJ?kKYo{qxu20b@2qs@_z z0=Bu-3ApP>mwA>*vw<$q(Hz`uA*A)#|IBOBao>Iam(Sr9GhSsk~@?q1v}=PZ7995AjvzDVNQVhG~%$=|Fia2B0f?@PUQ{ zLcv3zvy|ZR5Li%bFrm3SHFYjF{H)u;PURt=1s`Wi1e zaDuFMfu7_e&!%sTeVEzL3L}n+P{!i$?LBmVVR!Wi6uST=+gyms`#MlymaJ3e@Ywn$ z{auULt(p^dovlhMC4)C}r}J869DY$UA%asP&Ezz zO4=4lqI^RW4`=y9-)OO~g=eW4y=R)&Pyx*&mTMMS_iU%76@mxijVFFY_Vg5Ng4USm zT;J!!g7+ioY8l*F3-D*tO_3da>jy1D=sLr2ooRTK+n+CpfuJLBJv!L30iCRY3bkxq zon6qp08r-=@%q8`#D|_nm8SryKGtjj{Y$x*JFb2pM~p*}&lQP7FM)x$N&2ZT^}{Ti zvYd8*uJX)xa>G%kxVqc1uN~KotBZp24c}!R?j3g13376F7Ai#X297EaDiN>F6_sLf z0OTABu^h`KGc*)@rXd-T9td5p6AV#Yg4}843myXpNAt5;0Dj5=f2^Eu{D|xF!Rp4a z<0yyJ(c!)^D1o6yd_5?<%I80OVPHA40qj`5F*6VOh%|P?;ewvcbiRplxrLTvubfi2 z4&%e3bagHPWV3lW=wH@RS(RUd`Zgh)Is@JIhkK((vJmhC)d*_f5yeTzYlZ+)f_kWI zk_G&B*xO4?$ctT_!9_zh@BFCvTQYbKGMV`Jx)-hpfx4y$1hmzjxC|(tAfA~vktoQaDr9(vZn8X@Kc9fDB$Locz`uwbESeyvF5K>mXiL&1n zPNU(rSlN0D_$z5ZX6I)7aQ?e(UB64m!oqU>2L*wbACKIt`%>OV!l6w_XIIzq`;wAV z1RL=ZUItP1lXgIHSMt-BEtk3`Ib4^uv=EtU-j^dWP-KPfpP~lt@dU(c8Zit`h#HxE zE&h>JEeR=;oU=FVXgITwE%)Z#QR0iQNt0b&F`)`A8>@gLc@(+-h#Hg%O2WTh+0A#! zxJ2%Pb8J|BoDp$LA>!t>M!}fBrw2M^?Dw={%lyMnll;Vnet@8@*8rU6vSrOGrPAY8 z*YU5fJJQwIvUfiYq&@kZP+;ea%1D((N)z+8g(c*5bfFmWm|Dlk zFmz{8dax@?OFfF;iW8WTP_o-&JO}g$huHp#*Wzf^=zJm|_EF((_31~8Eewo|4WQx4 zSarCYk@@8FUDgQpCmRdB;qN$0>nN3smbz;M%07dbn1E@b^oUBygIdsT6a z(kk)yGly_r43Tkwj2?oNm{ULwR8_HMs~{`O0_n=q^Jspn%dy>Cb@U(VAewOkNla|) z4qDO)dh;#m`%k&$hFuo2HeEcCrMnb8peIfD{DShQSRgo`MEJkQcA^SKLdJq{uL?I$s z)a$WF5}pmajo$M@ur+5^RCK1wJ$UdSC51sQEabfCHeKiUNIr)9zyx?KS_w|tk~VyH z!1FHL71V0hP*zciE$e}af^*!U>gx2RTEiv0D z9@zVQ^L}OI?Z%qP2O5CHAhV_dadI>ssjK4U7S*8&W1e@yVmaDr^|;^5(WP%}#L%|| zU~-if4}+VROUuFnPmlf_s4J#~W9A&_wgOF_D`M*ae`=GNBLVTuM;O5zd^@j$+EM!6 zT2|~=V#?xDOofS(jglFIER(o=H+Y!EhL-v}a;`7{68bjQKYSe8O>C;fKM@u86D|%E zG_p+urF1q~x``ZobpVwEkmku)mP}oq8?8EGF_8k|1V(iE+WXOkY>m7JsPa`=0hjep zz0L}5&L9gl0%4nRa!u2`+>|b{&a&~mLPc20=sGF!^-BF*H8%jvSAovWDH+k-Z%tX( zbyiIy6tvXpK&!AkJ?6y^3f;WPmu~3N_X()LS?!B6^BLKf6>0>Z|2SI$S~N?fU%b!L zh5@qFfU<7=tZ=~IO4pPKF9^zaCz{kC)Ab=?Ha~n~X52HJp^s(#?4_G!!=qr%r-rS_ z+}8as!D{WQI92Tx;i}v40y0d`YvLH=sWQq%xofNbP4qQ%bLe zETGC1d$P1(SyZ1yk-!N&KyTd;QeJKC4)GFyN|EUHTp&}|ekyXQq6m~KTK0C$Vj^*B z1e2nM!KXPGT}>nv{hw)Ui2K?Z^xz>*Bi9t zr`4oj-k@%65-qe}cnlxR`wZIDY@w+zy(;WOzJx{R}*1sc9>63>VAul5YA?10>IzzRlL922=QQ>?vJTrg&f0xl71(v1EnLhS6s!n z33cwXRRcixsyUx)=@Zb*ICy;NYtwKlbwGvz5p83{t>?LmkG(=+NaqxtS_=Gv^MbeE zoTa#KI+u;TH&O+dIT<;*JkDfANSW!Vu@LcIMkUpB2WTC$wlWWyz>3nTXKbYCZabDH zt3!bau}?=rP0OXG5ardOQ(BjjiVRBP(88+BvI{163)Z09j^9a4D3uu`A)gd0$D+X} zJE`#F2F#?EAWv)Xxx7>jQ^`&}(;vy=zUW0kSTq1pePu|DD7R|!Ox=os61HA#TmN2n zOEkM~d0*xIU`B;S($GzbIO5oPM_?)MTHbeZdmOq7Jmr2u#;M(xnj#HmXr|hwXho)q zXT)>RZh(nEqOwBbK;2llbzouaqQVhXs?s~y*&lQ$>##MRO=p-=O$`w(4eZIm^nTLMWShi=Au# z#>836xy&li+%gW?=T5q0EsZU!=X}Q)l1y&?n_nvQEpZc{Brw~E=Z4j zrXL%WIuF{}EAS!Pn|)i$k%-g-at_V)5WWN9-VL|ju?)0`z=DAzZA+=S_XBV) z#xW&?SU3;FtEO_@4_vCYq5)g8bq9}LF1~mtF)6TKtkR;3x!-CvyNt!#s3jz^>c}#A zn%({jj%AcGG&uH0Jtd7L$b$wLx4?xBOld$}mB+sIboR8?{E(VcOm1lWr`#l_9^E@% zL7|6UpON-+AOwa>E_og9%3OIN+{VH|1{LE7!n zg;J4-VYlL?54))#7NFOO=YSUKDwY-K4^|T`2u6LTc#3t4QOt#y$1K~f7nKXjXom0d zz320jXztKa0mi&R+pR#g0xkOX7SW|YyX;V6&=ZHX4FhBjC9@~wo$<|pgrcvUW3ctO zapdtDu#%b!U2`LEh^cM}2x$DAn7C)N)7ST;UB54kLo0LP00crzo!-1k6+o_njV1ab^5MZDoGB;#2+893N*qZj z{OjALT-P0&s_chc?$N0vII}+mCr~{;7nJv2li%3%TK#E)@%u zHayMTTwmCMXr)Zu@q7fkN@P}KI19}MtvW(B!IX{Y24){as<@O&F!jeBl$>khJqWNmzFEis}x zvJQ*0yNp{=8Xj!Tixv0t4rxxB$EI__f>iPus8U&=>3^P~av=~1G=+H~`3*$(@3 zubxL}ock_IXD{gWyyU+UhXhpzQ;UYJOBU3Y)^%)E(dje*KYQ%3G%zJV8nOk9OC43Q zsaeJaQYCDh1B74E6sjncO6}ZPc3t6WhGRAmh z@7R=m0R0a}CS2f#%wI->1+pR3IIR+2PGh9+&*9`^!T8pym#8*tV{& zQM^4`stK>9Rvy`xP%Y6Dd#fmoU(9i04%sJig^8xN+ph~xwSBy2gV=fL5O27>GQQgP z`~!~Wuzf}Qblwf(91{%XceoT({Fdxc_5_sBJKT-+h?(*QB7@E`fE9DFoWZ0=k*R5E zGjI*x6xMDEe`qoe6wR{A%0a^_a~;W&gRzzbn-yXJv~ipDuPZqFUT43FAsfzJvXvB`TIlOb!d4?Lwc%w!DbOqje{-X{IvfM9n zlLK`$gWzslfL#0p(PqWM9iW+ES4mYYFrwLgud-zyGhAw)*F?_#WK4}Ab!)tvK&XV3 zB=o5)N*B=run3|G2W$ir(AVZ|{)qJS=Nyw|xq?*>)ZNoCWFTPRU%1r}FizcVPK^t@ z&Uk?NcV|O9#7_bvG{BH5IFn6w>~5b0zhR?92PhiZcO}oh6PopC1GX!RHwcKDn=9V! zu4i5g2FLg|Wpb$0J|py;YVUAo0fBAaWKV^e@1uh)d9)-<#MDKFSpG7Xt}wq9)8g)9 zJeVMeKN+%bFEZHhwt#Y@ZhfvZs}N|=!%aF4dOC>WM|gi`MITbm|qXSRXxF<1BF)q9r1Ww^ign{S?-s(6t#M!3(z#an#PLG_!%PQ?#F_p4AwTIG|1J!2!!vQnIG1%vumUM`I8zd zwq#X5-bFpI`I;pDq&p*g@wW3<9{HRPCnXq&@+Z9k`d~P7Cbm$)EgpeyEbB#Rw5gK_ zIO&9$mz5w}= z^#w5H!Kw7}1=Ll-QKe%Q)MVXQVk1tK5By3@RjOPkEgO_YFV!$0v>cGMG^kV1oVj{< zu$SzJ9?3PRzi85*n_LdOCsJ+d&hO3tYk- z8#p?g*pPF_xZZ}9gp7(U6z^=n8+0Wb0b|=tN>Vi4P#AEYf)uyaM1q z)d0bc>R!Dv6Go4Njd0=ZF|WwN{z8jrh^eFT*nc6TpwOI8!}JICJN^J5thi{sWnyCK z|I+<;ak6s3=l#3jbkHRuW|R-O+nuo&)9k{3F#D+A0Lyv-U8bbGxzt#iD*#`elqms+ zAgN+yq}I{d38fpG5$BnD(2~qcZxA?bMYCWJ6^dG!tJ@SA93Q^;0`9mTA`5IVJDwuUe z=M&8RwuBI@Tav!g{-DGx%`U(GVydyM)NUSe=D0t&)~O_C`s^o*1rW-yL-Sc+e&i3Zb< zAwcW%8yS7BAmrue$KtycwukfkYgzs9WI=~@xM+sgUQ=dOD3A*Nw5p0#Rybo<6*&QV zWHT%?xq>-h)_TsU2eW(-``nJ0yN1k*|DFoO*F#)4V!?+A=!ZG3C`?TYw;uQ7#GMEs z7TZi8NWFeq85OE0-y8wE@Z-Gq$73VG<|NTd59y*2(KHp=iuo2{!Ozg2)S_+n(s8h< zsmdpxgBh)TQ0M&FccDY$$c8YF^Z9`3f;s?s zUxmczvM)mB_mnMO??+!A>;sU-)S3BJ9#z6){XAn;f+eM@<0<|nI*B9*xotK?W;UW- z7(p5fy5P;3QKJ)ZMy0}mE~i}+79*nt0Z2Pf4}@MpU~0G#;Fg%w+#y{smesusG?M{< z*fG=y5B{5Bj?+IJVm}H92p9`}%W&(~Bj}EXU51A`;UcW84KA-+KT!}sP9pB&QVI}= zkNdS6ejFIM6D8zDNs?0W+lzffMND6vj$@vp7G2A}4eB|T$)4z*d||YPlgadv9gb#D zA(%NS8fGN3s!(GcaWA_cowkEiE-+PV=UYd*ZE8?(8%S4=c65nd2ckKyQP(wpdC=?l zdE~U>v;y5T0qxQ6|0W#~tZM0XAn-|w-Pk0J#k3j9>_&0@E))$2Aj z$WqVammUAvj#T_&x4K;7TxYUVnZvSW!+u=8&LyUXblr-{?S@cYz!Zx>Otb+QQB%%% z%#m%|zvK#79w=+Spiq4$zJego%k{v+H zrnS#8%NY2Ju_(HwV}AsfRaLq=r@?wkGdAn!FIp%k7qc+&n+K@zkFpDmy2x=it-HZY z1`D$w(E%jPof$VT0-6y{P(KvD^vY}u;2b;b71p7Z+J?Eb~2*;H>xYJFJF_mmo!wzk$?Y6nuAW7F~tsZfjzaYrF3xzzZ+z zx+>J}pevpmzUqmATsbWoW>)ET&9$RpKMGx&|4b>Uaza9-R z5gMXzKYqx*CL~J&fvP7;0cjs?x5OBWbN4vr;nQLsTzr!<*rn0CJLM;>R58aUr{Ajn z>bSRyaAkW~CtsAkwY!iDTQ%r^`qG|4Mh0_q;ca(@VYffO>=fpiq;i>dimAHqZh4X2 zyTOe3e_tq0Cs-+c0XIR|9GAM6L|mg9x)&j$58%`e56+p*m>K| z2X8MWcj!3EsA|V#?1>)E#N~EmW);oUoQH@yODfndg@8>`6OfJodpDpxZ!iIxS9wNg z4(;NbrzmE@vap!JT@o0gc9zF2-?1Z{1bM zPqG*+A8-KB1r(r+>blO!y}uS6dcaf-=+5Q5^eR0^ds z#3Rm9tWk7v5jgF`A&KwE8yk=!hNG!*E_`f&>2ZbnACO`Z%t-f^WI6z@!eC|W3*hd_ z`#(RIdaNdJ%H2vh@}Qu_gpUnzLZ;^FfndJWmSNsub32At=3T}-%Sm( z0VedoRJYw&H6me-uT^k?J$s^msR=2fMagezj)Rw%(fBtX{x`y4z5-5%@Q3X*=dqJu zkurDWeB_N0)@ayT=6I%TU&`{9=yDw)4;)>suIJ1r1O&qd;FgpQaGwVB5g?7|p-Sgj zfsje&lJIXQ{7=C8*H_|SE?aNCHT+WebNTo~z}+%-+A$EGXCWw1C#(( zp6bMFv^Y*>F_q$vcsfz-U1`cFsq?+D)RFv3kL}Q$c(CSGQ#g2HBRGd-z~$@K<_)&T zBXPGtcn#N4YdNV20=!(5j4mf!iI%MmU&8lFQT>1XAL;J9uZW=qJx~@y7naY5(>b<|ojh^sG%= z9`Bb8^Ftt%G+7__R{qgcY^!xHQUfjsaNl{cK83{QT{qBWir5G9vdU!+7JE)~RFH5T zpphqF?37yCe*Q0;^FOHsS_vqQ!j))l6dhmi6tIVN%P6*-(&(VP)Tp_vn2=0c=OJv- z!fEzezp#N%5+mb)#bCY(J=9awt#IkD#p(yD8gIL~j_-u$0Jy^Kcy-Ewkc@-HT;Ip! zc0CS0ZA2l)gO6Ky^R{I5T+$Zaq2q4ddmPmd_nv7j8VqB8bQhu)z|0j2y3I=zIs5>m zUg5pPJm{7RFk9~C&@FJ%umxDz1x~>0eWj(VIfmtRRER5=JTv&rBf0>O@~koB1)ony z1>Epp*s<~Qr4TtGhbGHGz^0jP>Vl5c(sP74;9nmC64wX^?@XE!^~rSwfUZD>yfbu# z4{}LR`-TV~xeD0s+OgQTOQNLm!CfK-h978xij4z@3xV3crxXsZX}PH5&D9oU+yOV$ zf2ddb=V;rpGa~kxNVb{Y?4U5(geo( zUI0huT4Dj>WZ)`q+wj?vk7Z@cuF`$asbgAHQP+f}U1%O@x6soJUDX2EZRpySo?ZCq zA#&9lXcqT>T`eIxKJl9ue1s;>rad?dTfl(I-V-`1NYVi$livu> z;VQ?SX2O-gguia}-{d}C5I(sNm!9r?RHQ>U?)k9U>yp~T&Gr6Nh8rA3buIj(A$}pG z8ClT4pCg!tDVa9|SM1q03~3;!`vX6>>z_yNU}NtM{95mpUY2*j`RRTMabsTU?Y%0p0Ri!azZQtOThmO z{GG(h_Fr6#D*#^N*)~Uq9V^3*3?D6ZMmsf@Q=>Ol3(wMWE-aVI1vxI|&d+C7fJ(uq z<)^I*;3sYX`Zf>C%W~k+N$o=r#O^}cMcx+e->3{>j^oQ~Oh?S56t>Dge8cEhx%q++ z+YL@_W;U1gfCD{u6Ka%&5!QSuyz~%>sv$R)k(UpNZrXrv%rgMsN^t4`4;?lZ!lQ%y z>{Q(TDs)-V$bd@HI6`I-CKH+*Og7|79C zQM*y5J%^qBaH7lX*4^(<8BGX)gP3B|8iqVh@g#Mh#oO~*!yYgeKW@qV$n@wDYFB{U z3JK^PAnbZv1_l?8b?M+18k_Fce+6Ogr(dzvmp(y59e+f{|JJz!bliE_hf`HLU1Zg# z%VUE0t)svMXLC~f&>g27bEoa6G9pe78$wv<kQ%faY>P(T35N*|~ulK`WR*_-<3 z?8osZP9nj{59KApejCQi>kl*k1yrV^%(S;0jW^9T+AGRt%fib34`Ejs7IoV7R|H$p zr4%F-B}8G7k{(w9Q9`7o6;N8FJ7#TK5a|$*?ydoqZjf%IyJ2YFGql2Uz29b6z5jQd zj^BZIw_YA&V{a`#b}EDm-DUvA*|mtsM+XGPF+PO84A{Q(hifriDxllHLoDk?v~}FYCWnHr0Ar_&`y;1e4oDbW$ZI_+Mgs=y2lb08}HpTU40a{15x;7%ph> zUsD&)Zvj3YI{rz$mcP=MRk$*JG|_gLlaADL%PO&1YrbE!2npX}a0o9!C8v>8QYxKi z*DubAY?aSwgS4DZT2j3OwJh{!V0ur~KN-H>cp-#3wlXx^ctgRvk)oxT@+x*;A~9cS zj#c8CzlCbq`*W_4rh-xTnIs#SEcn%|e|G3mqKZJH4jO8+=%BFGiC*S1 zCJnc+VA^O^{XcG3ACvDJCo#a;B))$ypEW7G@JMY|Jz}a({BQB>^e8PVC|lE7ns*;# z$h|&PQWD$p=%8}o{o=*t_@|);VPY>Zi7L4p+gX@ys-mGBI}*|lDs)omS`RQ?eFto- z*a#D^6)DHr{L^D71TU&N)C~5t@|(4 z58D4ucl?qz<2!8j;>sn_Y3=|~9y9&UMx)LFOI#ZSeIUtGyxiqVuQAR(slxS_04Gq1FBjLfyeWT4o z`g|LI?~i33Qe&O4F#0k4Wn=NcU&ojyT3C*BSX<2A>TxK>C-^$t6d4kZ?*1rHn5}AG z85zaaP1DBAVf5rX%eJqK2G{y~X&1p~DlUHtU?v z$*fJUd)~Ynd5uoI?*!HU4I#REPST88S>6%d+%5Z6my$Aj`jX+S#1Mq^BSrtn2}!NS zLXt)WA_l*g|3-UpvPs51`SXe~d*bo8{n1f0<0iauXnm=`bluMLzP^5q&wjjq|KEQS zJ&&i_3>XdC(jPAj28&5bx?hw2=Z{O?*q$79V0@ajudnWx&_TJ7Tvuf zXB4V2vD&##!c^$w$j7^G=FOb&2i)S#-&`t9Uq%v|E`lkJM1s zFEr+&6|>FP7N<&O)Cw}MKKLKIc`^e8^diliuVuB<`_ZA|ipfll1QorFD|MQOLCH5*Z_>`%LdB z?dEP1r(mfkaa~Tz;=f}Dt;I?Qk1j*Zw!PMgAl*834;ps0>;=EVcnbo>`?N=zvD_6GMLm(CUIHs4&5w>F#2ohqb( zy8ikHjW^`aud|OOo32JiDX&iVMd*WLR%$x<<()G0n(0ZpD#^%rT~j)MOp%2Yf$@#! zpVyqhA+2Ykynd47N^8>|>Ci^NnlB|Gkq?3T z^A_V7YS~kTBr<&`FYo8d>c=1^h|7eCk8TiexEk_5shFTnlUx$3I27v%fVP~QH=5}$ zD5LhnH;Bukza|~iqCNhz*CB_}>DJ}QwCqWep<=jverBdn3&e8#(L3$1s zn5abVpLiXW`tV{jV>90Y55P_X{P)T&v}}`BzZ}O;)gf7Rtv5k2WL=+r@Ic&1S10jn z{{w2@v$`FY1`@F{NsQ>6<#GF=+A!S}GXru=Z&VX>{`NqNNvHh#kpuXX_>Juk#b8hV zSDVu7^qs&}>t#MO{dvVGQ5z10caf3G020DDGMT4Rkh@MA2USPD=ED&(yeiOtlmJD@ zg%yH&4=E3N0UUUNk#b_9jeBbn!K&aeb~q%6j2TGFB{jPt8!}ul91@7`UYHVFh*Bze zBWV5f>C=WqE!TSMFh672Q4f3~s59}4p27ZmzaA(i08t?CMZ0=s4o%yHF@SzPWtm2>Fuk&sfG(S#!9!Rjwlt(|MElKuzD;L{l4@TJOS zZBxWtBvfxm(K?mBIzOn_@AZ-P+I<#R$s{#_5_AqxE=P~Ut>I!y@xDqQHU_8-3%CVA zPi3fZiO|P6Kmn?UgSa?t^nBKSQ(pXy6r#fOb4{9gE96If!?=yHJ^b3W?skju54zMc zxNW8?fN61UWF@k9L53_N^U%Iz^oP zCB2co`5IHR5|jJA+ll5AKUIAyv~t(xXz1xHInsiHF4JcY2A$_NH!Sz#WE9R9 zVTjC5f1N6SbGFSZs#eFj`Q}XxH#=LLxkY6VkJTf`B>e)rrkSCz9;mhIcB8Etvsjrb zQfZ2RIt8(aQ1h^ZNV)Y6;;F7hxCQm*steJrUvpOj(mr&j5( z+>+*umox2N@utqy3fh76s&5}H_)wA>2wA#l#=?UH)9Ks5CYQ?pM{?_;xt>&ZFK3wc`NgdY)1F?#fKSx{w>y=M8%O z%{D`zFs3F+3elGBr-3ayp00Ari-U#ZO4QKLRKNc64;s&&O=bMb6aQK%B()0dyf5^i z=`ySo?*P*^kO$Pj@SSGPEwvc)cQGBklVazZ@hNCBq#6BrwVtUZT9%+uQnJ7-VBHcQ zA79lk>v@Iut+C`zi}Om1)-6Q#+xVp)b`xccFk`L;m=5u29u;9wa2f>43&EUFYOCwZRjg6TMJQU`3B& zUg|Uy$2T5y8E$^n5bOA%#LMF7)mDp84JabDXRw~K*8>Su)Y5x__%}Y=)ZAmVU>k7H zp?o&)``(S}C8mH6)9=WgQk53|jcjvWSy?bvRFrqaN6V(De>8d_ndmnF&C5g()^Pw( zFWQ4IAT|ucfMAp1*Hrfjc*ZmuE3|-yJ=g zRE5FC?12b~s5`ai{^$Re`uD@8Rw-BiO__77E(_7hjdqwOU!`fe(pj7QqQa+Wtfavg z@5mX4VS|N49XhG#_dQOW^pK4q`rqP48xO8k=f~isP5%2f|2K_xE9=r#I<&cxwOKDU z-D08+j^OQy{GUJ5>1fA|gfoV9Taq;xp;OTm+G>)l{B9kfp2_79PbXCH%(sqFeHOcA zB1`v_7Z11WRkg{l8hLW54kab2z7|F!pQ&zuy2OKCN`PlizYGb)fCG(pWXw4@KQRmY zmW+9YPcz!Yc%;oxd^WS|zhbSL%OgCg*J$2rWg31sX^wUZ`tc)rCi0{?1ReAo$hbNB zEB$)P zHin%X?8Ae_YzS=gLGaZr1}?sUe6EmHXxpmmOr{ngx;) zmE#@UFNQmLTUd4djTDW#phK7x!Yl@uXi>y8_5r##To_oD^%DB{*?U(XqLjZb`9^r> zL{!UU=pDpB!3T)DhIBjL9#ilFz>4M^D?ei@&H~*J_AvvO>4_6q4vV zL`FtNcT&3?K)IhWerZS#*&(b58~`o(Zx9L}9F^+%cpc#{w?3e7i~ccUSq7(A}s3QlsZ~;|mOzX^ZLtrDX!_Gp0e;VTbwu`u#yIL^BT5W=G3rSTlJu#ai8AzhJ& zgGb--I;4$fGxhT>Jh@A#h9sos`~JWJ?Ih~{xlppiE2u1~ID+iSoQwq&*ITXp!nJu1 z<<$9ss&fGc8BmV4F}G^fKV0238i@T6RP1-+mP@FMx{i{)AJ*YW(y>U>A`&kvRaUnr zY!20aZvoO)1*|7MP!V4b)I5l%RRPOu#4eVnAlKZTP2Bxom&<^B&%2((5>~HcPc) z{?VaYSyenT&zQ@~?3pa)9ekG2&Z#A=ei|)Myg~xgWSnhQddMNuxZmqxRtE9N&z13Z zi-xr=HoV#ilN`L&Sd3SjS}}|h@S>d~^s1bJ)Abt_*Va}8K{YTM1DRsb@`EBHhgZvT z_De`+F<6<3W9sc@?CF>#7n0Rt{K6gAdW#qV=gQ&RV<%7cd_Huzdv?4%n8K~(S{R4= zSW61@5^K)`@u$U-`#CMLRJ4Pn z^%(|SF21P7D6^TCA8Z-xQA(@VAbmkt-^sF{Vne~6X=EN~58r%Cqz+B>xV@6YrP?ro zDDL^<;fTJgR~a*+ub)xxdTfnlk@c$K2_FMAwo zMt+>6jDr?jB`Mzka*x?@Qfcs0Zn#x7rw%90LrMzY%xsO=BgSi=s79!qn_(K@0Ratld0;0; zEgr`$si6KBRt4AqhjP~xR6>TMMbG;=0n9E{BhTGj~T3zbQd&y!okb#%GIr`}hWeot-dozh1n6 z@RXxpad7m6bFK9N<5KKXbgz~hyp!?ZS*`xc7#8WBrFMQGdI(_6Yy&OT%{38+@o0uy z*^YGD*cfsTg%n>Y$ZBbkc2n-SQ!zzVbwU*aH44tWJf&2^cHO;Ps216PprEF&$%c0x*ALe-qP?}0=Nog$ zv^vbAQYFAN*|XZF!|esEL+SG{@6x7P(!UdE+VzdbV)FZ-ew*Go!2||jxixfdQF(%j z$}dqHZz|#I`|r;QV$*#K*uy-Ve*}GhUO#>e&R6L2H|Kv4LkX*SOJ(Rk%Z8S*DKK=4 zT5wMO46)nNgL@vg7$YAp`rT^ZlDNdq2tj=(#pd3UnxSG!%9~yihvnw$#2IH??kG<& z*mzwN-5>HpjDu^}*Wo+FxVe|=W8gKi*pqcGNuA0>cc^zHsYRZPRm$N~%$oeMQ01jo zm|0+dpl&H9h26Ay(Q$8W)Y}$O_B1cZvh>rSOZ1NitsSd9e@bNwEszr>$3iew`WypDJn~fSh0=$LAn> z9g#-}q|`JMY_dg4c!V_V&pc=XSZ}nDzeH%hxthCJ@wVRL0l5v=K`~(DTZ|<8XMitm z`>si_r{$90cX-JEy{emW^#ov|etndnE|%@;+sZbOSf;#cF>zp$rg{<+@9gj$Jhw#y zU6fccymJvWHVePhmkSuIdlhwv2vW8@NSt`{xH&S55bK8j1l^STf4wWR zBT~HQN4H;*2d=?RhCACxwwbh5A~62WyFnX_dg!*NjC)olQKiVFCX#L1rZ?4~lg8Kg zKcawjalq57<`yJdM+&UO=CJIeV^nQPrfJtnGn@#Ib^wekq9iA-n^KkD?)1{kTYq!+ zdj1TNU=J$>%6QN-P9Eib??2#+2c#UKn7-8Kq0+!ul;4VO!;>T+I4~wzhq1NWNi4SI zZeRxvhZ89YCWlexXM7($4&mAPv9uz4$Ph-Zuc+B)U_uz(7==%$(zazSZBs6A!0#zF zAcNEQH>CgMcvdjL_6mfW%}7|uu^T)u=jeX#x!(0k%w4;F@z*@`Ar}BD04dCZ(Z%3= zBtQTL_uYcmHoxwi{Rs((GaZ$O&fD>3<|`XfRA-KBrPQKMuAc`2ARNOy0EpPwrK`&jm27?%T>nljA z>kl6(l0C!a#`kmLxOUmCIVk`A=NrK^49mGZQgNeM0Is0|NhC(KO5Ynit-^oYBjll5 za1z%mgvP4=-muj9@A^vyqgA*p)RorYxI?U9V zM_N(}zYtNi>iGl2c-zeoo3!n=t zzNK#mKh;aIzEbkvU7Q`NS&LF?zn&KcF*%Kr3s&_IKEuTFc;+HR3&6D^2UgH{RIrbV zypusFxcOVZ+J1CxaEC&!m@65Iol5Wf4DqOh!f(cJ-sl;uuj%ol$HhD$lq_Cb003!) zGHQMVN-Nz&D$pas@+N)1t37!Mc8iN#*iyUzFDZ_cDjCki#3GHAmVSn^tu2dV{DGPb z10kWxj*;4IGe<_BtOZQS^evUz@@h9xZqtF8taY&JW(0PGcc2vVzn?z`4f@<`7@B#K z0u{1|dyZ1mtx$n$WSwHQOgy1G-VISBybYlP?T;9tQ}HHa-wmrzV;tEv=fD2ie!F=r z9UgSXc~@{(o>y^e30JXAtu+W*!SQ;({$gjeqmOLN4yrVl>Blzq$w#y6sH<<8w8w7}aPU`t>%am>T zSgi`|>OERVa%)TKboCrhZnc;=9fg*iNDy$u9fl5&Hg@%gzD+4wA&UiC_Vox(#kYtR6SK)nyA?y>HTqD)cz> zz|OiJp6XI_5Rd{9WW7Den(;g$ZfU=_>W-i6WI~R zRM$z|LoX})?%WPJbhux7cXu&aqeg#Wk~m<-U3Iy#SZsXJ3~g|F8YEr%n*l#57B z9AIL|3)*sS*p&JcdADsWfIS?Hutfx8Jd$K_ttVUSBgd$#vn|l)O&b+zRvnlw*JdvY zaQw^)ry)Dfb<1hAZio_{YWNG{o{o8EIIlYadeq|_%f-+Cwr>lqo#mN1fZ_Prz8<-| zMI`FyvAl$+ILP(xeiHTi`zq(?&AH*Y1~WCC z6inJ#o|k(j9Q0o7)bT^Q5A|c0Ikr^=g-h9{x>SAB-eoc2*pD)|Ql+tg8Ut}RtS*ro za@LqVbHUUMM&9F@S{Cy4D`4Id65=5trq{4 zb)_IT3lcVQCS%0p-0l;UzepIOWmB%@tj)z00SJ~dAbXO;E${OniY0+|<^9-Kx2vw0 zZz8jUf(Feh*%>Dxpdgd>!pDK0!R-T{>yHlZS4a37L#jPOR~L$SoiLIoU~M++pm^VD z5g^>}!XM6T6Nz^!^fhn_V0d}$+hJZTM&@Q$gZqgr105xV5S9bj&~um|g}h-6pO~1~ z#{&pA@H?1{ES?e7=e92giN=|Ja6btBhN#e1PS^2=h+ju;_Ad(!Yis%TG+lY9e8(Sn zw%*tb+2(Y>2~q(fdoQhIW#p!|w4=hd#k$K$tmbv9D@6Rk&O2A{ct5bd_#>k#)Zn4} z7;IGKk0$GqyOos&anWLhYS=)8Uj-w4XiL1T_nA0=S_?0xkvi+X#b(vmu-ON}RRq#^ zVQ?VtiI$YsyMfaB^qs4(juwfL%hsm}K0M6}GjOh-fi{Tqyw;Z2m2$G*9`o2I6i$o6 z-TtchK(>9i$Lc7m`;!73FX8fD)R}M5a?5(-CFI)yMh+_&ISe#S;Ft0W`QBnQZqW!^ zfEIg2x$xzkQ*Ql$ibF_VwTB^&?tBoi2Oz(=q+lRCi`r0^=5ERDThhi0q|H1u(iMe) zt+N2OInnKce3Fw%9^1n9Se-j!`S6+d(?hkvhP}nY zij46gSE$_G3p~$|vSui@;zDDb17I`pim>;6dW-b=(k;ATV~wc=ilcTw9W`4<@6K$D90Yfa*5O$|Own%ZEf$ zw-e`h_^;RpJ9oED0ZX7Eqm*=YcNAQ>3bG&a9N#!69LUss&w2G%u;s)UBt6#~_=ePb z)EN8m`4|Q%xjV#7#O^RI*FON@l09X%U9hmb{xBqQ<8lg6&O3KdiF4CS$PheKng9r% zj>hK|qWi8kI*XpLK%XMJ*zWo-=mxidl`9zCfC<&thHNK}c5gtN2}G9;|IluZ?X+-U zxXHwM!wFQ)Sg#wU_~Y~hi#rhNp@nSraOU3O<+2P0Wswcu*DOc4v>S%gW2h zT|=Fb5PIut8$P7hKbL|6 zpY-_51DZhS6`b5@S69cz#Kp<9+|?Tx_Vwg4(`Ws)KVS1lo*e9**kh`$|0$;7iMBoL zqPu^>Y9h^_k5A7=f4rl{k2BYXHfjP!N!e4nCapN}EDf3hcy(gwxorz(Cr;WV3zr<@ z?k2E(0+%GLt0gB;!!hifi6mj4ZT zIO#Oe&Nj9cS<^Ml{*>KzHV#FJvZqLi`%2i&NO3u}dt6$m=;mJtfh|)TEWrFgYFZ~^ ztTsa2@~d3hjW_B0*P{Z-()zmQ&JLaF%TO?|tHcdJo$9x&38&}eG(R1M?Tm4$T8-z0 z#~bT1S+xiUKh#RqHabao7sQcemUP^CFW)Q^vuX0jpJSBB@m$@_^JG)oRDB8T ziHhhTBRV$U&SvgwWem~F7y6yJyZpZSS)?=N6S_+0U}}#Ywy!ynOw4}stx!PDmwV_n z3V}2YyxP*To?@s)k24yYUh8wVR9b0L!GyTG+o=Oqin)olkFc}49Nm+Gj)u|_N<<(( z(!g!zO`$WnQV$9edz>BMvk^~ZY~5uL!aA`#Ry0vFu0qkGU<5KM1onh{VZHJw-MK+A zEt5vnjL6EsC-I;>PsW5E3>R zHgBfQs=IJn9Eb^SKqXe34LU1fHZ51UzH>Nf=`;77#B9#&Pa0Cj%LkOBe>;k!U9foj zbil44(B==MXnvcd^r&gX$Fh$)pf)*0HlDTo4M9T zl@1-P;9PNiT=w5dPV7`2nKPB)vT5}DFF9}s@*ylJb~feh6C*j!4*v68=#!`q;SG}x z_V(JwIA=4H_LMK*(lqBs4KspvUt^O$sIEf#ASAT)$<7}PKMFKUd%^gRj8$V#ITNM0 z#HV4&C`C)g=cHz;iaX)p=aw@$?c}^?tfII}VHAuWd-Xi`@G6yn^C&jiipR>b6vCgn9h5 z(z>0Opx(>xcX0V6pFN9||GvXPn?L?c(ZNl(%22p@5GfQoJ8!pDut{n)reYL zSJexc$U>(t@pfv(7I7FK(^C

8Ze@=C83m6k+~2!`!GGOi|BON8S7uvzY^$28f-( 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�)JjqKW=0!Ig2&~v>_nw&0;IS#tqs~F(U)UvL64j4O;N2 W< 0`): + +.. math:: 0 = -\frac{\mathrm{d} P}{\mathrm{d} x} + \left(\nu + C_s^2 \frac{\partial U}{\partial z} \Delta^2 \right) \frac{\partial^2 U}{{\partial z^2}} + +The analytical solution is: + +.. math:: U = -\frac{\nu}{C_s^2 \Delta^2} z + \frac{C_s^2 \Delta^2}{3 \frac{\mathrm{d} P}{\mathrm{d} x}} \left( \frac{2}{C_s^2 \Delta^2} \frac{\mathrm{d} P}{\mathrm{d} x} z + \left(\frac{\nu}{C_s^2 \Delta^2}\right)^2 + \mathcal{C}_1 \right)^{\frac{3}{2}} + \mathcal{C}_0 + +where :math:`\mathcal{C}_i` are arbitrary constants. + +To solve for :math:`\mathcal{C}_1`, we set the requirement that +:math:`(\nu+\nu_t)\frac{\partial U}{\partial z}\Bigg\vert_{z=0} = -\frac{\partial P}{\partial x}` using + +.. math:: + + \begin{aligned} + \frac{\partial U}{\partial z} &= -\frac{\nu}{C_s^2 \Delta^2} + \left( \frac{2}{C_s^2 \Delta^2} \frac{\mathrm{d} P}{\mathrm{d} x} z + \left(\frac{\nu}{C_s^2 \Delta^2}\right)^2 + \mathcal{C}_1 \right)^{\frac{1}{2}} \\ + \end{aligned} + +This gives us a value for :math:`\mathcal{C}_1` as + +.. math:: \mathcal{C}_1 = \frac{1}{2} \left(-\left(\frac{\nu}{C_s^2\Delta^2}\right)^2 - \frac{2}{C_s^2\Delta^2} \frac{\mathrm{d} P}{\mathrm{d} x} + \left(\left(\frac{\nu}{C_s^2\Delta^2}\right)^4 + 4\left(\frac{\nu}{C_s^2\Delta^2}\right)^2 \left(-\frac{\mathrm{d} P}{\mathrm{d} x} \frac{1}{C_s^2 \Delta^2}\right)\right)^{\frac{1}{2}}\right) + +Using this :math:`\mathcal{C}_1` and setting :math:`U(z=0)=0`, we get +:math:`\mathcal{C}_0` as: + +.. math:: \mathcal{C}_0 = - \frac{C_s^2 \Delta^2}{3 \frac{\mathrm{d} P}{\mathrm{d} x}} \left( \left(\frac{\nu}{C_s^2 \Delta^2}\right)^2 + \mathcal{C}_1 \right)^{\frac{3}{2}} + +Finally, to make this problem well-posed, instead of setting a slip-wall +at the top boundary, we set a Dirichlet boundary condition using +:math:`U(z)` computed at :math:`z_{\mathrm{hi}}`. + +Parameter values for testing +############################ + +.. math:: + + \begin{aligned} + \nu &= 3.5e-3\\ + \frac{\mathrm{d} P}{\mathrm{d} x} &= -0.003969\\ + C_s &= 0.1\\ + \Delta &= \frac{1}{n_z} + \end{aligned} + +The hand computed values for the constants and boundary conditions for +varying :math:`n_z` are: + +.. container:: center + + +-------------+-----------------------+------------------------+------------------------+ + | :math:`n_z` | :math:`\mathcal{C}_0` | :math:`\mathcal{C}_1` | :math:`U\vert_{z=1}` | + +=============+=======================+========================+========================+ + | 8 | 169.90632344067848 | 49.63299783004268 | 0.5322727144609587 | + +-------------+-----------------------+------------------------+------------------------+ + | 16 | 2449.4399999999982 | 201.95839999999998 | 0.5576738677495996 | + +-------------+-----------------------+------------------------+------------------------+ + | 32 | 38115.76743991355 | 811.5733178848386 | 0.5646234542509774 | + +-------------+-----------------------+------------------------+------------------------+ + | 64 | 605551.4603806001 | 3250.1208744082833 | 0.5664029656909406 | + +-------------+-----------------------+------------------------+------------------------+ + +Test results +############ + +These are results of running the test case for 100 seconds. + +.. image:: ./channel_smagorinsky.png + :width: 300pt diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1848dee810..7a7edfbac4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -191,6 +191,7 @@ add_test_re(joukowsky_disk) add_test_re(channel_kwsst) add_test_re(channel_kwsstiddes) add_test_re(channel_godunov_laminar) +add_test_re(channel_smagorinsky_analytical) add_test_re(halfchannel_zerogradient) add_test_re(halfchannel_symmetricwall) add_test_re(ekman_spiral) diff --git a/test/test_files/channel_smagorinsky_analytical/channel_smagorinsky_analytical.inp b/test/test_files/channel_smagorinsky_analytical/channel_smagorinsky_analytical.inp new file mode 100644 index 0000000000..9bbc2250ee --- /dev/null +++ b/test/test_files/channel_smagorinsky_analytical/channel_smagorinsky_analytical.inp @@ -0,0 +1,66 @@ +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# SIMULATION STOP # +#.......................................# +time.stop_time = 100.00 # Max (simulated) time to evolve +time.max_step = -1000 # Max number of time steps + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# TIME STEP COMPUTATION # +#.......................................# +time.fixed_dt = -0.01 # Use this constant dt if > 0 +time.cfl = 0.95 # CFL factor + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# INPUT AND OUTPUT # +#.......................................# +time.plot_interval = 100 # Steps between plot files +time.checkpoint_interval = 100 # Steps between checkpoint files + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# PHYSICS # +#.......................................# +incflo.density = 1.0 # Reference density +incflo.use_godunov = 1 +incflo.diffusion_type = 0 +incflo.godunov_type = "weno_z" +incflo.do_initial_proj = true +incflo.initial_iterations = 3 + +transport.viscosity = 3.5e-3 +turbulence.model = Smagorinsky +Smagorinsky_coeffs.Cs = 0.1 + +ICNS.source_terms = BodyForce +BodyForce.magnitude = 0.003969 0.0 0.0 +incflo.physics = ChannelFlow +ChannelFlow.density = 1.0 +ChannelFlow.re_tau = 180.0 +ChannelFlow.normal_direction = 2 +ChannelFlow.perturb_velocity = true +ChannelFlow.perturb_y_period = 0.0 +ChannelFlow.perturb_z_period = 4.0 +ChannelFlow.analytical_smagorinsky_test = true +ChannelFlow.C0 = 38115.76743991355 +ChannelFlow.C1 = 811.5733178848386 +WallFunction.wall_shear_stress_type = constant + +io.output_default_variables = 1 + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# ADAPTIVE MESH REFINEMENT # +#.......................................# +amr.n_cell = 16 16 32 # Grid cells at coarsest AMRlevel +amr.blocking_factor = 4 +amr.max_level = 0 # Max AMR level in hierarchy + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# GEOMETRY # +#.......................................# +geometry.prob_lo = 0.0 0.0 0.0 # Lo corner coordinates +geometry.prob_hi = 0.5 0.5 1.0 # Hi corner coordinates 8*pi*3pi*1 +geometry.is_periodic = 1 1 0 # Periodicity x y z (0/1) + +# Boundary conditions +zlo.type = "wall_model" +zhi.type = "no_slip_wall" +zhi.velocity = 0.5646234542509774 0.0 0.0 From 48a2e3c164d2daa019c030a71bc8cb3788b52585 Mon Sep 17 00:00:00 2001 From: lawrenceccheung <15526007+lawrenceccheung@users.noreply.github.com> Date: Wed, 24 May 2023 11:08:46 -0600 Subject: [PATCH 13/38] Fix rotor rotation direction for Joukowski ADM (#848) --- amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H b/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H index 816701e093..fcd6067479 100644 --- a/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H +++ b/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H @@ -225,7 +225,9 @@ struct ComputeForceOp const auto point_current = grid.pos[ip]; // TODO add sign convention for rotation (+/- RPM) - const auto theta_vec = utils::compute_tangential_vector( + // Negative sign on theta_vec means turbine is rotating + // clockwise when standing upstream looking downstream + const auto theta_vec = -utils::compute_tangential_vector( ddata.center, ddata.normal_vec, point_current); // normal vec by definition is opposite of the wind direction From 65317045e9e56aa6ca410ba7d20a5cfb5f823857 Mon Sep 17 00:00:00 2001 From: Michael B Kuhn <31661049+mbkuhn@users.noreply.github.com> Date: Thu, 1 Jun 2023 16:54:36 -0600 Subject: [PATCH 14/38] put prescribe conditional within dedicated function (#853) Modification to make hybrid solver work for prescribed flows --- amr-wind/incflo.H | 1 + amr-wind/incflo.cpp | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/amr-wind/incflo.H b/amr-wind/incflo.H index a2877973a6..3105b2f37d 100644 --- a/amr-wind/incflo.H +++ b/amr-wind/incflo.H @@ -95,6 +95,7 @@ public: bool regrid_and_update(); void pre_advance_stage1(); void pre_advance_stage2(); + void do_advance(); void advance(); void prescribe_advance(); void post_advance_work(); diff --git a/amr-wind/incflo.cpp b/amr-wind/incflo.cpp index b8734ef697..93a96fd30c 100644 --- a/amr-wind/incflo.cpp +++ b/amr-wind/incflo.cpp @@ -275,11 +275,8 @@ void incflo::Evolve() amrex::Real time1 = amrex::ParallelDescriptor::second(); // Advance to time t + dt - if (m_prescribe_vel) { - prescribe_advance(); - } else { - advance(); - } + do_advance(); + amrex::Print() << std::endl; amrex::Real time2 = amrex::ParallelDescriptor::second(); post_advance_work(); @@ -311,6 +308,15 @@ void incflo::Evolve() } } +void incflo::do_advance() +{ + if (m_prescribe_vel) { + prescribe_advance(); + } else { + advance(); + } +} + // Make a new level from scratch using provided BoxArray and // DistributionMapping. Only used during initialization. overrides the pure // virtual function in AmrCore From 6d4023c8e5a2422c1627fd1ea6cac4dc7b5cfe83 Mon Sep 17 00:00:00 2001 From: Tony Martinez Date: Fri, 2 Jun 2023 00:32:21 -0600 Subject: [PATCH 15/38] Added time a start time for the fllc (#833) --- amr-wind/wind_energy/actuator/FLLC.H | 1 + amr-wind/wind_energy/actuator/FLLC.cpp | 1 + .../wind_energy/actuator/turbine/fast/turbine_fast_ops.H | 5 ++++- amr-wind/wind_energy/actuator/wing/wing_ops.H | 4 +++- docs/sphinx/user/inputs_Actuator.rst | 7 +++++++ 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/amr-wind/wind_energy/actuator/FLLC.H b/amr-wind/wind_energy/actuator/FLLC.H index aa15f4dd4d..081da12e6d 100644 --- a/amr-wind/wind_energy/actuator/FLLC.H +++ b/amr-wind/wind_energy/actuator/FLLC.H @@ -18,6 +18,7 @@ struct FLLCData FLLCType correction_type{FLLCType::VariableChord}; amrex::Real epsilon; amrex::Real relaxation_factor{0.1}; + amrex::Real fllc_start_time{0.0}; RealList dr; RealList optimal_epsilon; diff --git a/amr-wind/wind_energy/actuator/FLLC.cpp b/amr-wind/wind_energy/actuator/FLLC.cpp index 651711ddaa..7a2eeccca3 100644 --- a/amr-wind/wind_energy/actuator/FLLC.cpp +++ b/amr-wind/wind_energy/actuator/FLLC.cpp @@ -45,6 +45,7 @@ void FLLCParse(const utils::ActParser& pp, FLLCData& data) { pp.query("epsilon", data.epsilon); pp.query("fllc_relaxation_factor", data.relaxation_factor); + pp.query("fllc_start_time", data.fllc_start_time); std::string typeString = "variable_chord"; pp.query("fllc_type", typeString); data.correction_type = FLLCTypeMap.at(typeString); diff --git a/amr-wind/wind_energy/actuator/turbine/fast/turbine_fast_ops.H b/amr-wind/wind_energy/actuator/turbine/fast/turbine_fast_ops.H index 1c21f09e0f..56afb90c94 100644 --- a/amr-wind/wind_energy/actuator/turbine/fast/turbine_fast_ops.H +++ b/amr-wind/wind_energy/actuator/turbine/fast/turbine_fast_ops.H @@ -414,10 +414,13 @@ struct ComputeForceOp // by this turbine scatter_data(data); + const auto& time = data.sim().time(); + auto& tdata = data.meta(); if (!tdata.fllc.empty()) { for (int i = 0; i < tdata.num_blades; ++i) { - if (!(tdata.fllc[i].initialized)) { + if (!(tdata.fllc[i].initialized) && + (time.current_time() > tdata.fllc[i].fllc_start_time)) { FLLCInit( tdata.fllc[i], tdata.blades[i], tdata.eps_chord[0]); } diff --git a/amr-wind/wind_energy/actuator/wing/wing_ops.H b/amr-wind/wind_energy/actuator/wing/wing_ops.H index 34ae742854..45c3e38fb6 100644 --- a/amr-wind/wind_energy/actuator/wing/wing_ops.H +++ b/amr-wind/wind_energy/actuator/wing/wing_ops.H @@ -114,6 +114,7 @@ struct ComputeForceOp< const auto& dx = wdata.dx; const auto& chord = wdata.chord; const auto& aflookup = airfoil_lookup(data); + const auto& time = data.sim().time(); amrex::Real total_lift = 0.0; amrex::Real total_drag = 0.0; @@ -157,7 +158,8 @@ struct ComputeForceOp< wdata.drag = total_drag; if (wdata.fllc) { - if (!(wdata.fllc->initialized)) { + if (!(wdata.fllc->initialized) && + (time.current_time() > wdata.fllc->fllc_start_time)) { wdata.component_view = amr_wind::actuator::wing::make_component_view( data); diff --git a/docs/sphinx/user/inputs_Actuator.rst b/docs/sphinx/user/inputs_Actuator.rst index e675f234a1..6c0d43d140 100644 --- a/docs/sphinx/user/inputs_Actuator.rst +++ b/docs/sphinx/user/inputs_Actuator.rst @@ -105,6 +105,13 @@ Example for ``FixedWingLine``:: `Martinez-Tossas and Meneveau (2019) `_ The default value is `0.1`. +.. input_param:: Actuator.FixedWingLine.fllc_start_time + + **type:** Double + + The time in the simulation from when to start using the correction. + The default value is `0`. + .. input_param:: Actuator.FixedWingLine.pitch **type:** Real number, optional From d41b251383d7b519d60144540f35efbabe0084a9 Mon Sep 17 00:00:00 2001 From: Michael B Kuhn <31661049+mbkuhn@users.noreply.github.com> Date: Thu, 8 Jun 2023 17:06:51 -0600 Subject: [PATCH 16/38] Rudimentary approach to sharpening the VOF data from Nalu-Wind (#854) * sharpen vof data incoming from nalu (iblank < 0) * employs a cube-root-based filter --- amr-wind/equation_systems/vof/vof_advection.H | 11 ++- .../vof/vof_hybridsolver_ops.H | 50 +++++++++++ unit_tests/multiphase/test_vof_tools.cpp | 84 +++++++++++++++++++ 3 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 amr-wind/equation_systems/vof/vof_hybridsolver_ops.H diff --git a/amr-wind/equation_systems/vof/vof_advection.H b/amr-wind/equation_systems/vof/vof_advection.H index b1fdf93d45..75fecd6fad 100644 --- a/amr-wind/equation_systems/vof/vof_advection.H +++ b/amr-wind/equation_systems/vof/vof_advection.H @@ -3,6 +3,7 @@ #define VOF_ADVECTION_H #include "amr-wind/equation_systems/vof/vof.H" +#include "amr-wind/equation_systems/vof/vof_hybridsolver_ops.H" #include "amr-wind/equation_systems/vof/SplitAdvection.H" namespace amr_wind::pde { @@ -50,8 +51,8 @@ struct AdvectionOp // cppcheck-suppress constVariable auto& dof_field = fields.field; // - // Advect volume using either the Explicit Lagrangian onto-cell or - // Implicit Eulerian Sweeping method with PLIC reconstruction + // Advect volume using Implicit Eulerian Sweeping method with PLIC + // reconstruction // auto flux_x = @@ -85,6 +86,12 @@ struct AdvectionOp advas[lev][2] = &aa_z(lev); } + // Sharpen acquired vof field if hybrid solver is being used + if (repo.int_field_exists("iblank_cell")) { + auto& f_iblank = repo.get_int_field("iblank_cell"); + amr_wind::multiphase::sharpen_acquired_vof( + nlevels, f_iblank, dof_field); + } // Split advection step 1, with cmask calculation multiphase::split_advection_step( isweep, 0, nlevels, dof_field, fluxes, (*fluxC), advas, u_mac, diff --git a/amr-wind/equation_systems/vof/vof_hybridsolver_ops.H b/amr-wind/equation_systems/vof/vof_hybridsolver_ops.H new file mode 100644 index 0000000000..fefa83f454 --- /dev/null +++ b/amr-wind/equation_systems/vof/vof_hybridsolver_ops.H @@ -0,0 +1,50 @@ +#ifndef VOF_HYBRIDSOLVER_OPS_H_ +#define VOF_HYBRIDSOLVER_OPS_H_ + +#include +#include "amr-wind/core/FieldRepo.H" +#include + +namespace amr_wind::multiphase { + +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sharpen_kernel( + int i, + int j, + int k, + amrex::Array4 const& volfrac) noexcept +{ + return std::max( + 0.0, + std::min( + 1.0, + 0.5 + (volfrac(i, j, k) < 0.5 ? -1.0 : 1.0) * + std::pow(std::abs(volfrac(i, j, k) - 0.5), 1.0 / 3.0))); +} + +static void sharpen_acquired_vof( + const int nlevels, amr_wind::IntField& f_iblank, amr_wind::Field& f_vof) +{ + // Sharpen data from nalu-wind (in iblank regions) + for (int lev = 0; lev < nlevels; ++lev) { + auto& iblank = f_iblank(lev); + auto& vof = f_vof(lev); + + for (amrex::MFIter mfi(iblank); mfi.isValid(); ++mfi) { + const auto& gbx = mfi.growntilebox(); + const amrex::Array4& native_flag = + iblank.const_array(mfi); + const amrex::Array4& volfrac = vof.array(mfi); + amrex::ParallelFor( + gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + // In iblanked regions, sharpen VOF and limit it + volfrac(i, j, k) = (native_flag(i, j, k) > 0) + ? volfrac(i, j, k) + : sharpen_kernel(i, j, k, volfrac); + }); + } + } +} + +} // namespace amr_wind::multiphase + +#endif // VOF_HYBRIDSOLVER_OPS.H diff --git a/unit_tests/multiphase/test_vof_tools.cpp b/unit_tests/multiphase/test_vof_tools.cpp index d883eab5d0..3cad23a0f7 100644 --- a/unit_tests/multiphase/test_vof_tools.cpp +++ b/unit_tests/multiphase/test_vof_tools.cpp @@ -3,6 +3,7 @@ #include "aw_test_utils/test_utils.H" #include "amr-wind/utilities/trig_ops.H" #include "amr-wind/equation_systems/vof/volume_fractions.H" +#include "amr-wind/equation_systems/vof/vof_hybridsolver_ops.H" namespace amr_wind_tests { @@ -79,6 +80,9 @@ void initialize_volume_fractions( if (i + j + k > 5) { vof_arr(i, j, k) = 0.3; } + if (i + j + k > 10) { + vof_arr(i, j, k) = 0.7; + } // Set up a liquid cell if (i == 0 && j == 0 && k == 0) { vof_arr(i, j, k) = 1.0; @@ -199,6 +203,55 @@ amrex::Real interface_band_test_impl(amr_wind::Field& vof) return error_total; } +amrex::Real sharpen_test_impl(amr_wind::Field& vof) +{ + amrex::Real error_total = 0; + + for (int lev = 0; lev < vof.repo().num_active_levels(); ++lev) { + + error_total += amrex::ReduceSum( + vof(lev), 0, + [=] AMREX_GPU_HOST_DEVICE( + amrex::Box const& bx, + amrex::Array4 const& vof_arr) + -> amrex::Real { + amrex::Real error = 0; + + amrex::Loop(bx, [=, &error](int i, int j, int k) noexcept { + // Initial VOF distribution is 0, 0.3, 0.7, or 1.0 + // Expected answer based on implementation of + // amr_wind::multiphase::sharpen_kernel + amrex::Real vof_answer = 0.0; + if (i + j + k > 5) { + // because VOF < 0.5 + const amrex::Real sign = -1.0; + const amrex::Real delta = std::abs(0.3 - 0.5); + vof_answer = 0.5 + sign * std::pow(delta, 1.0 / 3.0); + } + if (i + j + k > 10) { + // because VOF > 0.5 + const amrex::Real sign = 1.0; + const amrex::Real delta = std::abs(0.7 - 0.5); + vof_answer = 0.5 + sign * std::pow(delta, 1.0 / 3.0); + } + // Set up a liquid cell + if (i == 0 && j == 0 && k == 0) { + vof_answer = 1.0; + } + + // Limit answer to VOF bounds + vof_answer = std::max(0.0, std::min(1.0, vof_answer)); + + // Difference between actual and expected + error += std::abs(vof_arr(i, j, k) - vof_answer); + }); + + return error; + }); + } + return error_total; +} + } // namespace TEST_F(VOFToolTest, interface_band) @@ -259,4 +312,35 @@ TEST_F(VOFToolTest, levelset_to_vof) EXPECT_NEAR(error_total, 0.0, 0.016); } +TEST_F(VOFToolTest, sharpen_acquired_vof) +{ + + populate_parameters(); + { + amrex::ParmParse pp("geometry"); + amrex::Vector periodic{{0, 0, 0}}; + pp.addarr("is_periodic", periodic); + } + + initialize_mesh(); + + auto& repo = sim().repo(); + const int ncomp = 1; + const int nghost = 3; + const int nghost_int = 1; + auto& vof = repo.declare_field("vof", ncomp, nghost); + auto& iblank = repo.declare_int_field("iblank_cell", ncomp, nghost_int); + + // Use as if entire domain is from nalu + iblank.setVal(-1); + // Initialize and sharpen vof + init_vof(vof); + amr_wind::multiphase::sharpen_acquired_vof(1, iblank, vof); + + // Check results + amrex::Real error_total = sharpen_test_impl(vof); + amrex::ParallelDescriptor::ReduceRealSum(error_total); + EXPECT_NEAR(error_total, 0.0, 1e-15); +} + } // namespace amr_wind_tests From dbc62f603734c896ddd4bd147c4992bc3dbdad15 Mon Sep 17 00:00:00 2001 From: lawrenceccheung <15526007+lawrenceccheung@users.noreply.github.com> Date: Tue, 13 Jun 2023 12:37:01 -0600 Subject: [PATCH 17/38] Upgrade Joukowski model to handle rated conditions (#851) * Updated equations to match Sorensen * Added more parameters to the Joukowski model * Fix formatting * More formatting fixes * Remove unneeded diagnostic output from Joukowsky_ops.H * Added more comments and some clean up --- .../wind_energy/actuator/disk/Joukowsky.H | 12 +++- .../wind_energy/actuator/disk/Joukowsky_ops.H | 56 ++++++++++++++++--- .../actuator/disk/Joukowsky_ops.cpp | 5 +- 3 files changed, 63 insertions(+), 10 deletions(-) diff --git a/amr-wind/wind_energy/actuator/disk/Joukowsky.H b/amr-wind/wind_energy/actuator/disk/Joukowsky.H index 036a5be6bf..ab78bb1731 100644 --- a/amr-wind/wind_energy/actuator/disk/Joukowsky.H +++ b/amr-wind/wind_energy/actuator/disk/Joukowsky.H @@ -19,9 +19,19 @@ struct JoukowskyData : public DiskBaseData amrex::Real vortex_core_size; amrex::Real current_cp; amrex::Real current_power; - // --- Sorenson 2020 equation 10 constants ---- + // --- Sorensen 2020 equation 10 constants ---- amrex::Real root_correction_coefficient{2.335}; amrex::Real root_correction_exponent{4.0}; + // --- Sorensen 2022 equation 26 constants ---- + // Note that Ct_rated is the Ct in the region 2 part of the power curve. + // Ct_rated is called ct_region2 in the input file. + amrex::Real Ct_rated{0.0}; + // Note that the defaults for S0 have changed from the paper. + // Sorensen 2022 had S0_alpha1 = 0.08 and S0_alpha2 = 0.05. + // Current version uses negative alpha coefficients, possibly to account + // for the change in rotor rotation. + amrex::Real S0_alpha1{-0.20}; + amrex::Real S0_alpha2{-0.05}; // -------------------------------------------- int num_blades{3}; vs::Vector disk_force{0.0, 0.0, 0.0}; diff --git a/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H b/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H index fcd6067479..a3bc010396 100644 --- a/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H +++ b/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H @@ -126,6 +126,12 @@ struct InitDataOp Sørensen, Jens Nørkær, et al. "Analytical body forces in numerical actuator disc model of wind turbines." Renewable Energy 147 (2020): 2259-2271. + + Upgraded to handle rated turbine conditions following the implementation + Sørensen, J. N.: Generalized analytical body force model for actuator + disc computations of wind turbines, + Wind Energ. Sci. Discuss. [preprint], https://doi.org/10.5194/wes-2022-108 + https://wes.copernicus.org/preprints/wes-2022-108/ */ template <> struct ComputeForceOp @@ -168,9 +174,27 @@ struct ComputeForceOp } } + // Compute the solid body term S0 (Sorensen 2022, eq. 27) + amrex::Real S0 = 0.0; + const amrex::Real alpha1 = ddata.S0_alpha1; + const amrex::Real alpha2 = ddata.S0_alpha2; + + if (ddata.Ct_rated > 0.0) { + if (Ct < ddata.Ct_rated) { + S0 = alpha1 * + std::pow((ddata.Ct_rated - Ct) / ddata.Ct_rated, 3.0); + } else { + S0 = alpha2 * (ddata.Ct_rated - Ct) / ddata.Ct_rated; + } + } + // step 1: compute Ct, a1 and a2 coefficients (eq 16) + // a3, a4, a5 coefficients are from Sorensen, 2022 amrex::Real a1 = 0.0; amrex::Real a2 = 0.0; + amrex::Real a3 = 0.0; + amrex::Real a4 = 0.0; + amrex::Real a5 = 0.0; for (int ip = 0; ip < ddata.num_vel_pts_r; ip++) { const amrex::Real x = @@ -180,20 +204,34 @@ struct ComputeForceOp std::pow(ddata.root_correction[ip], 2.0) / x * dx; a2 += ddata.tip_correction[ip] * ddata.root_correction[ip] * x * dx; + + a3 += std::pow(ddata.tip_correction[ip], 2.0) * + std::pow(ddata.root_correction[ip], 2.0) * x * dx; + + a4 += ddata.tip_correction[ip] * ddata.root_correction[ip] * + std::pow(x, 3.0) * dx; + + a5 += std::pow(ddata.tip_correction[ip], 2.0) * + std::pow(ddata.root_correction[ip], 2.0) * std::pow(x, 3.0) * + dx; } - // step 2: determine the circulation (q0) from a1, a2 and Ct (eq 17) + // step 2: determine the circulation (q0) from a1, a2 and Ct + // (eq. 18, Sorensen, 2022) - const amrex::Real a2_2 = a2 * a2; + // sqrt[(a2*L+a3*S0)^2+a1*(0.5*Ct-2*a4*L*S0-a5*S0^2)]-(a2*L+a3*S0) + // q0 = -------------------------------------------------------------- + // a1 - const amrex::Real term1 = 16.0 * lambda_2 * a2_2; - const amrex::Real term2 = 8.0 * Ct * a1; - const amrex::Real term3 = 4.0 * lambda * a2; + const amrex::Real term1 = std::pow(a2 * lambda + a3 * S0, 2.0); + const amrex::Real term2 = + a1 * (0.5 * Ct - 2.0 * a4 * lambda * S0 - a5 * S0 * S0); + const amrex::Real term3 = a2 * lambda + a3 * S0; const amrex::Real numerator = std::sqrt(term1 + term2) - term3; const amrex::Real denominator = - amrex::max(4.0 * a1, machine_eps); + amrex::max(a1, machine_eps); const amrex::Real q0 = numerator / denominator; @@ -215,7 +253,8 @@ struct ComputeForceOp amrex::max((i + 0.5) * dx, machine_eps); const amrex::Real f_z = - q0 * g * F / x * (lambda * x + 0.5 * q0 * g * F / x); + (q0 / x + S0 * x) * g * F * + (lambda * x + 0.5 * (q0 / x + S0 * x) * g * F); ddata.f_normal[i] = f_z; @@ -235,7 +274,8 @@ struct ComputeForceOp const amrex::Real u_disk_ij = -ddata.normal_vec & disk_velocity[ip]; - const amrex::Real f_theta = u_disk_ij / U_ref * q0 * g * F / x; + const amrex::Real f_theta = + u_disk_ij / U_ref * (q0 / x + S0 * x) * g * F; ddata.f_theta[ip] = f_theta; // eq 18 diff --git a/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.cpp b/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.cpp index 4b6228046c..3420a8af00 100644 --- a/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.cpp +++ b/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.cpp @@ -27,6 +27,9 @@ void optional_parameters(JoukowskyData& meta, const utils::ActParser& pp) pp.query("root_correction_exponent", meta.root_correction_exponent); pp.query("root_correction_coefficient", meta.root_correction_coefficient); pp.query("num_blades", meta.num_blades); + pp.query("ct_region2", meta.Ct_rated); + pp.query("S0_alpha1", meta.S0_alpha1); + pp.query("S0_alpha2", meta.S0_alpha2); } void required_parameters(JoukowskyData& meta, const utils::ActParser& pp) @@ -90,7 +93,7 @@ void prepare_netcdf_file( const std::string nr_name = "num_radial_points"; ncf.enter_def_mode(); - ncf.put_attr("title", "AMR-Wind ActuatorDisk actuator output"); + ncf.put_attr("title", "AMR-Wind ActuatorDisk actuator output [Joukowsky]"); ncf.put_attr("version", ioutils::amr_wind_version()); ncf.put_attr("created_on", ioutils::timestamp()); ncf.def_dim(nt_name, NC_UNLIMITED); From e041a0c4d7cb0faf0e4d8a394fc915b9fca33d07 Mon Sep 17 00:00:00 2001 From: Michael B Kuhn <31661049+mbkuhn@users.noreply.github.com> Date: Thu, 15 Jun 2023 10:27:34 -0600 Subject: [PATCH 18/38] Add verification (channel, burggraf) to documentation (#849) * poiseuille channel flow verification cases * burggraf flow to docs --------- Co-authored-by: Marc T. Henry de Frahan --- docs/sphinx/developer/Burggraf_error.png | Bin 0 -> 114922 bytes docs/sphinx/developer/Channel_Laminar.png | Bin 0 -> 95812 bytes docs/sphinx/developer/HalfChannel_Laminar.png | Bin 0 -> 113534 bytes docs/sphinx/developer/verification.rst | 24 ++++++++++++++++++ 4 files changed, 24 insertions(+) create mode 100644 docs/sphinx/developer/Burggraf_error.png create mode 100644 docs/sphinx/developer/Channel_Laminar.png create mode 100644 docs/sphinx/developer/HalfChannel_Laminar.png diff --git a/docs/sphinx/developer/Burggraf_error.png b/docs/sphinx/developer/Burggraf_error.png new file mode 100644 index 0000000000000000000000000000000000000000..27065336dee3957ae922bb705478b3e03ec914d3 GIT binary patch literal 114922 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 >rvVb47d909u5h+F}Ajx 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 literal 0 HcmV?d00001 diff --git a/docs/sphinx/developer/Channel_Laminar.png b/docs/sphinx/developer/Channel_Laminar.png new file mode 100644 index 0000000000000000000000000000000000000000..cdac4edefd93f6131e06eb273915360ee7dc4ccd GIT binary patch literal 95812 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≤4D7%SnGX(D1Kvqpl zLaGIPZb}7M1j;|aECF1FRnjAmdI#h(iDStYm;`+!Hn09Xa8C`=bNSURpdh%-!kwN| zjiJ60vmeC3UpL$wLrSQho9_?g!-=xk(i{CY*=J6jMou$NVPpo5S9V^8(N)z0XYJmi z$elFxB3lz#BE}}LQ1J7aH^Zta)6>;t0M&kk zpT*4d$y47uvz#{O$UME}8q()Jt_ve85M>YO#mo}(#{&pQKBEfbD-mq`(qL}dX@K6` zW!~Tml`la2&szCnfBaw4C7|w2n$K_pyQ4;7L9PKoe&;jFbu4!S&q_tE5rm`~)(5dJ zrjNrPU--z3PHIg}76{)Cg#lYKe+A<_6YPJ?M11Ie53#Ck`+)r((nxQd_RN0B@u8oc z;93Kejw1nhdRyLbeE-8VRb@0UK{)!?hVr$5HUA5uT2NtSrLWKcX>4T*SoGLagB&K0 z+kBxGL$$`PdSOv+{)JB-IBM+#2}Ao7mCTltNc2?<3#T+7^N^Ua)#xv3JOr(C!{zRh zG|*dk%CD=_1xzy9@wiJ=GNXS0sX2J86F4Vck9LoR96)*@e^LHANjWZ-O)>ILYc=$= z5ETPj1=iko*B56n5Rq-=Eh%EgaKP)Sp~|)ynm6}CuOeNJlT|a_-`GCgUt7D%>Q^*~ z+|R2-HzG!rV0470Um1MyM;(~z@YaNMBduzhuDAnfgwv5}3Q*_dlq|DPLdQ@m6xXQ* zh%!u8C+e{joe_03FbB2rWLwWbDO*Hi_6aH z`fCdp=4(M|UEdL+H<^S62iP9aiglIH*$-{r7DiBPW{FPodx9J{E>VB!# zToEd-?h9XbY;nHDveZ||cSx$K4kZ4!V<}PLcZnSF0X@ELt;4caG#RG(WzFE*zm#8| zrl9+;%7zAdNp&qh7*t6iHJ%`lhs8Kn0O)-+O8C_YRU-$>zy_}j~H--upW^Tqu{O)7@=*`$g|+q3~);E^<6Cu@D`-e#M$SeBLJV(AhzM zN$Svfd7lv3n_7+0p+)!&jVyy}eNgDRrX9f0uyX!lR@oFJ*e~rL#v$SY1l(vAp8rgS z_H>M-fxIR)_Y%Yn>!r)C+|EolD&jA*9;e>)ka|Lr`w-0=6uftABOK%;bZS`X!!&Y? zZ>}sYtRWhiWQl{>h3l@I^&0zus+?+Mj;A#)@XiZAFhZ;zj4-fQN()9A(4OI7S1&xX zwv&$a?vm?RkvOb9l9`UN*GaX@+0;g^sKSl@Y;?qFHWb!BD*mdcweq3bcBfbSn2(X$ zke8RYB7*@}BP17jg93*S8^hlqTh!yEtBuww$Ko``R$_E3o6>FUy`>IPKB7LS9*)@6 zfL?5?aS8gsPdU~2i{X+#k_`J{jH)nE$qQv21&cYy%1WjPJmL zzfR|Pz=mQZePPD~9ZAY-sZY|`s`Pa#9!MOtODH0K8COEDc%hjc1P;NJ&Sq%i^&W+d z>EV?{`v98RKQSxvdDj>ZuSY;gf8oZ3VKK<*_@RJvrJTr-;P7DhNKEtmCZsRl!DmS+r*RisTK%xvB<2J>l9!cKaDe z8|B|GiuCu{FCrno*r_@bm4Jj#%CN(ipc*Y->|=44?_1~H|M9Dgp}Asc2YD@0%~2$w z@80JzWjq$rIiDlSrUA>DXfUn|W*b4n;unENWh5ru$%I`U#0RgxM=i`4_ zwHymao+CBqnbB^?|MvMzP^>V}fBEN6jy)Z4NXUURN{X<&`8XbTtZ?E<0rQ3ePxC+D zJSzSjz-iP@+jg=4<@0kOILJ}&VLE=m9=iboF1Wkas*JV&_W6i(spcz!K>mIp$Npn% z40rdO-Yw=o5DtG`kqaI!7e(H&KRI60K}mrYl7Ic@yy$=XeB{K*%7ldZKfZxl1Mcpf zkM8t;D+K*xEbt$1{(nRNzoGx%sz1IS|5;`6|DRWVspb{4f9;qkdtwLRD7GOrrYMWD z2!x*l)Sf7(&Cf$N`c{ijXGnsG;C?p(uTX>kxT=~xZ=2FT+m7cZ-koJiqrLe}f`5MX zYez6nt^gyo@QWoszali*1-3&d>SS+8dSidMtt7-c2-u(rP~H|xOh;#x#dq#Ifv?ls zZ~k;qNeU4`&_ykpO({CuZ9ReELsOIhW$eOlA}2~4NvS(6=kSm1JR27+!Pz4jNg&dI zj~Sl|jpSsJ0I3N2W`5ZU=g2JnB`QbAUWgQRjTK_SV5C^qBx*cl@|&g@zXWH>oVkqM zi+`Rta>{`%!dBK}l}AQEvYSOHHcr5U&=9FP=4F!xh3g!-@jrhBH&>Kgp(rpI0=+9^ zk;>XqFWR5N`phNRpD!k;@gDmUn8Mc?gIJEXYbBK4M4|owiLLbT-c4g6q=@!=zZ{ev zIow-TE(f}0+o!v!MY1n12mkYWJOz9UQ+=|We@T_^QaC^*svVNlD2Nf&CNC7z{_}Y# zG1%@q)@J5t{`n1j6am*y_^xLMo#xX_AipJ8jQ?{Mg~I#qm`^JFxgQ8Y5-SjH-f3Vv zBh{y%&1>e~h^(Qe@fTL{*PU0S!>v7Goe(^BAxu*(#8PAyyq1-bj7=7Ld}~J{&ee&} z@F>G*vwshd2MRGw%X-oB`!T?OW*(qJjY^q4#`YGFlM~utp2=$*t;HRQitYcb;%L78 z=Zwo?g+n#W|2QZ@G=q(vwXT9twh;R2Y2lgpAc_p_;wyu&DmbiZk5M)HKmlkhUwa%4 z!m-8h1ZvYzAP6|S!yka<6Z)@v2T=hd`{=)98iRKj-%b?V2%twS ziWJ{a6&5@OaWm+4?M%QEsK&?T4-=ymFqu{b{XfqFhG_6Mqd+^OhC5QP1%8Uq>oa^w ziF^>QDI=+EJ3>VE@BKrhW`t+IFLHce0d%phiNDTnJb~fBSQttpHVje2<0TM#UlBXp z9&g$^=0{Km@X5bX7=Ukx!hx2&TX9l65Z3LjwkY;EmrG#BAi$+tRx%Mz_h+2o$~6DF zGTo_Y*UjEpMEPIpv~hlR(0#wwJrS8O^08Xhtso=*Yur5lQye#UR}VtvS%mQpb{m|t zW1waZGB2776qLc%cT{f%Q`7=TW{XmC*9iGe^_t*rssH^iWTZ2~@#thEcj z@6%WdR^B%{XR%{ZRD3fQbOa1Eh+1>Nf87Tfx>W`1Qv6C zWr^b&wfc?j-|xB_zy6kq^4Mbr^jY4sbhGcm!A{(fV40bVC$UtSn&uDJ5y{9Yp$PhJ18}8B)Yx zKnGC$Kier9U+J5U>?U=AXPfq{X@1QygoL&?^Lq=4uY?s%{kX#JUq=HhCpjeF7TzB5 zmgfOcK$v;)VjfxcX|jJFk>@v3n42ZM-G=}C2D$is$G_;XGX7dxNKy!J>b;WwhyT3A z6PpPB$V0O0+CRTR{wRd>MK87wy_0dF)aLKFg8w6I)83~`%#Y_zdLU%~#jWTA+*1Ef zqF0CPN>abq9Q+4`f%j`~$gDaoFXp_vmg#wPW=8n$=M#UgBIE0WQ~&Z6GZys($w4Cc zu2%s!_t=*G|JrrD@uI=NVHOHt2>IW_5_j&}|V zt)0UyCYdV6j5-oUWc}h_G_#Ldx+qQJ=Vbh3C7mk z{yc~cc9lyR>rk>C?CofJd;~1t`HWg8>0{r?)BIv4c@||FJECDU=+X z`fr1~-@6nJd_cUUng2`G-;?goj6&v;rxJqp+gg+VzU2S$dLKx=KQ>wYIDc#u{(L+j zY_pId$0(&={V%VpM8WI1LKijuTMjIYur1r`p|8Wgzu70zfyZG_4rkf?;^<% zWZ4QAp8CDd{rin5GdLkSrV5P7kMA7E-ZNth^HYgk?d*Sf{i7nh-bYKB_TMmw7$Bmw z&O&#MV|R7@yW|4hQzdHr{oOyu{ISaewAT)YuW*bObl&{uOaJRX@mJvW|4oAbO@jYT zg8%IV|Jw=vcO3j*kAsRMi)HbO#ZTX#25|d%w2}I~lMkgPx*-*s#CVO7hzdoP#gl*| zZAFhwn|SUN6VG)NN;;6~S_R`NoKx?rcrdW}ahR@NBICH4=@G(xba}X_MR+f^tVIZj zz(;Pe5LmSgZsc$EiZ~AE4jHw@PfwVupka}zp#IZ8o`+;SCarPPHKn)yxBYDoM{xw{9fhL&DFiqbKP zJ~Zy^s@3#3V_SQ|QDy&g#75)d8rh&p2VaG zBxyh-kjthZvRc^UdXd(ydc-g|j=?NSD21|oX5 zoKMStuBPj1XM7iP4;t#wo`w!kMYYgv;l@*FBEb&z7d)wkE@!DR!+x`Tg5ss3M?DXs z%nW+*@;cLI!wvm$kse%C(Bg_e#}`4`_SYniY~-J-Y1`c$QD<_%?n)C@FuOPK6F?)&2mR#(37Y5})dAMAqs(He6;XPi2x zqB8=p#OvV<7;Hv`IB~K4MGN=T!doOb0}NZx&IHBOCj0x0)tkVg#c`Zy@pS%cc2{U| zqZ-;l75;7L#I;upP#=*p!u4~1w7v(RFA2iH`P9(<{Qx>Q_;cx{=FVBXVAro~~N-U}zep0#meOB`aH<Q^90A)TEf4R5L_gTM`h*@7f+#~n&X(xo z5r}EDe`tErjoXl%Ok-h`2~@erQGVrIZw@d#)j~wxagzByHQ>gVW9}4T|4^yriEa~d zLd#155N;+gwhVkdPw_A~O~v_sZ=I0m)QZjIO(!)6xcpKzFXX;trg= zr-*Ya(xlF60n;hp1(sX})s|k*RX3PUT(}ck#z9d7RaD&`u`2ja6@>WYss=$Htq*{( zzjr*PKt%z^=9P1wwu^tyzyISjQ6VY-_L>hX<~iQr{G{Z4d@dDD4(;1Ljjb85>rP~< zrW?tV5x;8I{MPmfEV14alsqw{=6j&f0~AaH!q-x#h3=~dMt7CruSXH852PFv1>nn` zIB}UyR$B7T$F6>l6S~>c&oQOaFn<7;eI}SWguwK{c{YL>F_iq2sVqiwjjE;@IrO?| zd5SoZ=?5!bNjazA(YP6X0}X2qk29;9gqJw85}T%4vVHd2w-dGNhq9!@?T8)ID9FtS zN!gWXpGdG(qJqMMjFkSIkj%_HM|F?8(*8O^dF=5-kJ=vT_hrQ4ZHevQav0g-epX(- zc^&giaSyuw;m#E9qx1rqo+&YTpV!@5OeLW6GUL)XuUqT)N9;A)C{OO1<#1^f4Z|uVugz?_(qHW%Hfdw} z+N#yKv}r#^_&|5(yV}>I{g$JrQRYP^GY;xuvAAH1t?TnzvbE7^zA0E^mCx8Ja^`HS zLYs#UE|`L3L^bN^jYFaFl8Rr-5OT2Agf-9_Ms}$izRqluyI{w4FF0>J75z0P6;b$z z!1&8nz8Aw0GastNo?kN5O4XWnY>Bx(zqnP-;5742ALwBOVC!bUD5+^NrlWM#ljY%1 zw#U#Z79!b;Q}xfC=2tELJjNJuNP6xmcPIVYzfe-1!Q8I=1J*}2Qodd;p3}OA<&w5> zjw_v(^BHl3QJ+QF7P1#l>AxnY@U4I9G9>Astn%8L9N#!9iO_XMDvMmqZU@(;9Q0%> z${|GWKVwZ=X%H+V+pWpHT!R))yjmcnlPbl)d7um1v_6DFc;M^R@X%u04R~$5g5hr` zd(83&(J`2}3^a`2J^KoF1PmuBn=y7mw7+A_#q?2r!&iro7!OCA4VNt-nnp&zK_zLm^fv>oI7X2r%WtePdbeCk7*r zH8hiL*(_A6iD-KP1gzuVSqQe>tji;yB*ik?E6J2VI;`v@wKACObEf{`lCO)z z%gM3pV_H8&nSC1$-#CmGF-ca5J>6?BS$mS#bE1FzUNhmmj^ku#R9URmeSG{S8$s)` zGi&cP{auzVM|}?19BT_C*8|Vu4yX9q*$^0h-MH>WbR@a$Z~wh+qeMp%F)I;L?T0!}*a=Q^w5LtxFXi ziD;56PMAL#cKBD~EH&0CF=}R)(@K`UG+gBU_=dY($(PA%nDgyhHYQ6^pnXt9*LM#$ zZ9xdylj(6XeQ;*-CpZj=JqCpyxfqoUeFse{Bb|unVaW}2G#amaj*QP7an?UPmDb4H z)-ZEUp_I1LaZ4`g1a2{r<`dEmq`5T)uAfH#yRCY#sjj3_Ve;g4T%8EdG0?^=P=c?no>oJzM|qvT*4l8W>f%QePu!Q-(I>i>o`$qm$U3Q%c3^Jj z+&nCV_TBTxxKCx}?G~@=kJud?!40X#NLY+bF2QW8YIx_+=E(GNU{g2_(9WF|RDQaO z&%}Fwg@Uc)52KNm+dv+-w$Sr1&h@#1 z<-^C!3JA;l+sbo63V)7>g`)ZNm13PNCik0v#r^?U3ns0#?6umrk)Nq(J)KJNsOeZs z37bnd14ab`T27x_E;ChsP>)5!b;xMO%03y__4l%--SnOy%ulu>pbd`*5w;EhY#7fxff&=2- zH1{J;Y88ZO2N7*$?M%B6`JOoy`C-SA6-TxroFTo_0ul&M)DNbYoIx$B8)A~>i2m4$ zXuji8ksxy%U)@HNz%Xt1pwg}P1xoJAuTZ5!Sg-Rc?5c8Q-&-WSzH(mMEA7YT?(FhF zalz)xEp8IO_4l7HTP-JjzA4v3qL)-7yk+GzhFNsaUfe2Spg#k=R+XFQ;+1NbA18V#dn4YIbEWcF_%7?{N?l&ziL_1){Yr zuCExrjgD?35q#~mnbpv~z04)kW|k;a1<1AQ6*Nxax8h7(Q-T!f{^nP<~g`eez6TI-Wl+CH8Fk*8ary+BEL4 zd$i0In}8&>`{GsuqReOTqn|zt{wsz^kgQ0oG!~R!N=0J& z=`fv>uN*UvHZMHqefi{}$Dl_4(zof5PoolpJMsB2%#$3XX=~@UP?qPg+;LQij2UNi zmnGJl7M2kmJC%!~EJj!%v}-iLtJLtE9&?g4O^hdZXYQ4pWtO|UZq3AV>+uH#3xdsL z8d8WJNc$CUCsYRF?g!#Cas;wDakj3$^BDzjM0wYr#x%Y^)-Gr4S{k~6yJ2&PO}8ky6Eqh!ncVR7kN(tf9+~<05|ykwbS>x5=b)MFHx+cICf&u*W%RvT ztI3wvrfU=u<4$_CiAXozEKGxy-Sf?HZqvh5>(=oP&tx=RQPDd<*Z~D#7U08*-&Vw% z@)k#aZRgWpCwe*Ju&CSobnhMVGfyBozJp6=n({Nshxs~5iPSj0-h1?iHrxHFp4N zMV3^S(2KiMBR$zs_(1NZlEw{+hATI}KL8c{()!fcE)`EC{Taux=c;8LRvF~gNgJ+K zq8J)?r+we&WU~muV!z`JR!GlMzxAf7dL5kuB{l zH{`0HAj^}FNH;if+QQ4S3A?IIe5dao8i`u8E{Q};c$*-FVjO$6vLWJvcMzF+p1Oyy7}Nql^xhe#KVsQ$1d)#)FqAdx7#e zQ9N0fp97!5cofIYb#fT}=f>jW=JcnrPYfw=<|f&!_uai|>V;RLQqZDLzUR0Z5`<$Sf+@E;**2Y4);1U5VOQq%y_ARX5?b$d z(n-2i9qdMBx`%VjTc%77hZaYh7FFLxRr-8(+cq@v-wxhzqM3N|IpBnz_yfr@0o@h}>8dY+xTh4aD2Bf-;Mo>C&O|(ESnX?U^*m){JnRggcb1qUYX7ryA!0e{U!5$7jt_jvn3QF3g#o5Z#w)Tw*Ru zAy4aq-&9RgM=Q^qL7`uXoAInJeb-o#v#uu$QnYc#Nf(L2WS240II1VgEGL>>Q&n4| zD+I?G$bz4-_#BN(95RP7jXPFu1qTVQlcZ3@w*+L6F3VD_;=iv&iLdM@J`CT_x0!Hnm^Uc^t}bM>tqHIi1Jr9$b&FG%~oH`qb* zBIwiznd*OEGjh3G>-HNhbzmAA(jAI!$8fhdI|p6B%*J6{t#bSEeiKw%zGys7@+!mP z7C&TP_JOOYU{P*%(}TCNtbVIIxT2k>;&0`hRX#^lA&a4XdrMdXjH9Ln<0Q;{!s2S| z-qf^aF#~& z#o#ns2kgIf-+8P4<(2ao^`p!TXaacS!f;HzQje- zd}rguMw>ssIU+Nkx+T|A4Bskk%(-4H*wx@w#F!2G1*c%kq1C6HrDu7SCW+_G_>O4A zW@c`#7HW5VVUCuThK3co{TvG`3oy_jhU>blBYpZd)qNqyu zjvZxJdcg;SIs7aj3OADB&%XI%2>vUBiD&rlVgCHz9vI9)O|$rv7|x>>V= zkMlXP?KbSqr4%y$cjQq-0|{7Wv6f$-uHZZI-aLxZBdXjVlNKUxCh$#nVd|B7pCb6< zQ<$E{km0iv?DNCmnrKLrR)EhHf!p>&K8b4oeDnPBAbI>{>oj^6qx{Cp?Y`B2c76{ql?qIsNInORdPVQs-u`Bqs}{?ltQRD->SU+{<$ zWLI+N#Cquzt!$q4BNa$X6OAzs=LIBs?r~;Xq|ET4d!o+Bamz&~$5eZsmZvu z>VwD)a?Ppa@@^+>o2JW#t#QtGOLfq;+oH~M)79)FbT|~dg{+rB=>+(J|6y}F2=dAO!%$zgHe z4gmYNAg+)Xb>3Zzw5?#dLlTVX=!@?}6IlK6?ohINDVFr{gVU40M+qVb;1JQ<>YB5;fZ5H7u}$ zuxk>flKuH~3AuD}Z98fBZ2_V7XGNX!eB=KNr78}f2)m>(dP^Z*Jr&8ljpnngVqgbk zQ8U6|$Td-)nzJP7&ido)hNPZrqJNytTlNfL6QyM=Y@-j9r#$E0L>8R1F0~iNmHqN+ z&*6}EgbLTRy*)n0a7ssMB)Y;i&G#v0rMX6T^8E7=%iKizEiUwLJ6pktbX)08<6N(x z{rk|%!YjCX_Pf+)e_T}#$vL9Ez_pROarkMfLd>t)nI4}#zGY)zq55DpJMQ4Ii_(=- z{>Kt| zAFG;#rq@bX&u{K*3Lasu&S9Qiyct<+W-0&jN2uFrI z7#II=P~pO(GSVJPF$#w7=EO*E^*u*W50t zGfe4~4-`z(s#^XYPIk}lM_60!v4z}!;bb{ye;&jg@Y-fSbEhfh|M{|nC%L;y+1&m7 z=gh{dqcMgQrVb@FZPGc13D0!Xf=+!7^pK(M;WhIhk?df<7{#yWh@P7>pupAN4&%1X zRuQAz@uj7q{-?mEw{Z7h__@5p7 z?0r_ETWOS7T>C;6m|N}ABXOvQI>9AWJxP~c&nt$6B}o53pa$F1+tGGE&R@YI7IM?j zotCtodys8mA6&~gMN+XdR22dl-f@Rrvugg{()Z=4iFml#mU|xL zCZfqMjPbo|-|IGZ9T4!7#X$erB1z((OGSQG zQ5zX$*THpnUwmD(wq4tdw{PA&(&+A-nT^gx@8glLBY(e^k zRBm5Y&R#b<*>)*c2iK1hqbnyq`ObBEMy73$r0lVtK0JtB&QrlLU>PGCQgZSu{~16kC$!nE1|ue=C~8B(UK#V7RJxWn+YlH zH;O8bnd_TZc_ z{#@L-q01I!%9x1|S`T}Uec9PBBjoi^<3dYNw8z4UPFi*-)?^y3_sGV#j$%KQ+2(!0 z!#b*wqC33grb5C#e0sRWw7$on&10Dz*T<#z$=16@>j8|lL^NDV|CAxVoB}cb1asQG2!n=Pj1OL@xy6q zz~b*CO~zvg`^cemrH8m9>*8rW1(nDXg7r@|BrxBtoe=U-8VQsE5^YX+a-q!Eo%2IHh%Q8QCPAK zykJ&7s8$j=yMuZ;DoPaPbS|e@`|a&`P4oea`9ZbF-X$nkQ#A{Yzv^k=h>LSRgRy;U zERB+M>-B@OxB=(Y`|O@e<}5y%LJykJ_ClR_vpBcPRprN8`O?DoM|48A4vme<=bGeK zSvThN#PhUWo^)!Kg_h^G@x@1MSaUO^eDQ8th+F%6*H?rx{$PjQ;ICanjIzh}fLH%b ze*c^F?w_PxXWBdT>hLegoH!eYlsGF+gmw6(Z&g)%sqwvaqlQJ=D)xaJ`?(5nm6zgi zG9*MjMod&n7Ejkj;)o|c*gftIqWhd0>KnQBN*}e8C>k$`K8a*3*8*ORIn4^)#HS#i zu6KVp<}kMHW`;pa?M4R*4v|KhQ)6cQ&A%f6J;)|_ZW2GQob}@q3pi5(o9Lt zCs@7LfoaJc*sH)}!%zPxc{2i2I~A=>$;{bYhj8oLCGWG!F~XNm4=FmPxERoS*9WuL zI^R2Vlqi`#40e|&B=le33RYdNxn#G9nU~6nBiLnNT}C>6cF{&dEuU8QqG5ZA;fPi+ zS%yI76Ja)}ss4f(rO^`3dpoF#vBAvJyGYUFQkpf|OQ}mBQ9K{<^ZT$)+)Gv``2XYp_%@IwG757$B#k-pS#SMG;gDo~|KwAjmi^htE^%r%T zv%$vkw#_n_LFe$+k!IVpp!FdC@SJK`4?30vc4zYjg;Q2RcXhUcS$H5gU)jC zY3Ohef3=I!uxw9dEib#?V6|>7AxjxSL^EhPaYmXcF417_?AIq?yV`<+k<$D%d7!c zh8)KIvQ9>-+xBqcpDOHq5cTW^gR8Cah?IuTCF~QGX=5kqFemJRs1O^%MxB>^bcD33 zd=5+u2{z$dCtln_K`@Y^dc~LVGKPe`>3gk{z8Kgd5Bh&~?^d|~`BL%#5!cJxCw$5~ z<*74c`9E68p^y}9rAYs$rc+8{%RMy-|A!TBmqm!5vVIKp8pdqf9xX}_`)Tr|)LWE6 zXZ~}x60N-Vygo~<0Ar<0OG3!{Y@*2Ug)#}ug&WpiqgDk<^7s&&ciyb}Ex|adf~`lo zdm6MeCAt}Q=&d0?WPRn*FMF*GJi_e@sFv0fg{a^}X-Qrr|HwUsRj#-h#j8x&?WC3V z=*uY`x&m~vdTTMVEwp(x*)i_S>+^P9CKlbvzqV6UG9>-Ba&3RtY(h`edvnzW(EV`L zSf^X_;ZPH&I9#7r6DecZGtW2ZF|GO#d8*6aW!&s@Ama>J7pmQid@*uT(h2=klTycp zBrU~LU%t@JAAqC#ZDEs&q9#U)W$$Vv`W0j`R22!)nw_cpJz4~(JFz9FEEIAU z5aA+*m`sqJa2^QxJ{ufhu7|%hx;`)cGlS}tQLAwo<-rvtsq!=9Vc2!_DXz>!a+!JU z^(y`kLA*z*}@K#r-a|IO8CoQ1>bN92~io6oI(PctPmCx^BQ6uN{6K}ZEV9_<` zyQmM2bU=r+Ui6-hlhT_UC+Mg=Zr8Km>ityBZSayH@BHHR)ZLC-CpjW)h4UuD=wgdL;P<$uvGJaurw`DdPe>HoFkgkQkAa?f7DVY^=KYe#?F zsqCY^!dtQ-7}Vu`V+m~f74^wl1d?7Q;{284$YF>*6jh4Cd9|F=u0(Eh5ob(g^99MZ zniI;|^NMR`P|6wAUUpIg+xA1A4ySOmH|5Z{NcELb#XkO!P>^RrP790hj9DN%0o9S` z7ZfI4F)^lGzfy(?&;4erNWp0q{8ef6JWSuY*ODk-rf+33vKU_CEX1o;Bt6(`xtbSQ z7sqAFNk-0NKLa(+reN9igb{{r)@h;Kk94z!5j6SA%Jk_b8hn(RW^Y&Ga|P1MUUpxP zu-kO%MeI+1ag4s0J1#Slh#{>V#xLvSC2HjdaBT3`0lsW$8Acs-9^l3 zC^=EG%pdxcK0~UvNNK)aB7Ha3rs{y7l`fnkx0OAse>O#<%_G)?MVOkE0Ov4R#;l8% z?uHva;H1K97N^<&3J%DObs*u5%C}bf>t_v$9;0CU=bd4if{hZo(}&D%6{jyeGYZqn zoOC@?kM)VbV;|6n%*n0}%;{i7v@JMsG^WG;;O0c>*@&2$5=cQOxUBldU2L_HR3xS|LX5gHniI%quy;JZ26xGN* zdw+<>qSfBGm8^bx0@xg{((bRJ9+_7ztBOEduvL^}rN>{&R{00<-$4<&QrCiO!zAqo5GPKjIk3pe9_lE{Es# z&9nfVW6zUJ&Mq72I-aIjPFSB}EEsA$7@;iRzQ$-3sEpZB7*@=?@`_0^QWE1d1KUP7Z8}%)VeU+j+L|h1b zJjdc&I{n9?F9$wMM@!mA2{hims{KiEofM4Qik5}pIjN=62jx6&rzE@f!E{pE{UDT{ zhXucYeH!ht*E0U|64+)izdfQNAeB|Z_Gk`%$ASrB+;x<7MxXTiuyLfB+W95d&MC7k zz9pKDnHJ|RtGEN`dqKX8=NM2nc17ksLXjjFYoB?2o#`R8kErPtSryqjAh^;KKUT8l zvW*nsNaH%MtO8rCHjC$C!HAdNeXCr@wU(aKxRau1BqMp0#YD~`9gpY~F zr^#9;k+ourwLsdQ^?-X)6<9wA@H<$SX(bN@-;Wlw8q2g636T zK|6b|f-kW8PL=|I0)u1)*htHaE`_U>86SKGz_*)tD|EUQz}5bRq!{JDc7IRO87+xE z&x93^zZk7E(G?dVq=`xwxe{%&$WXP|9!H3zkx6RJRM^3JI^qiI&g-oBf->kbNH4;g zmAu=L=h8g%E^%?0)Wn{=j`NXZzfO}X$w_$DOm~+omfX&HWWv~Y$Cksn-*BNILjD58}f%Nrr0U4 zn5W!T&MX&1nlR$;^NZ+zbsYh&*6Xdk2XE)sU4V~r(b}S^JPu(58=@kX*}PTxK5`pH zIE6Nj3stuOo$1aBZK8dzX6f3vR?nzM*wKVD_Tkp#Dn)X!h! z@U@a1`yD?BU{r>gPP(2ONAbKHW|Je+1~xIdEC$Ji4u2hhd>?H_&vaM?7W5b=ydK+J ztqf@2-OzC=P4M=+gV}+T-W1Nud`R~CoZ{85o@k1N zN-@`UJUk>kZ=%1!G+EozNh@W(iNd7~JF`g2I`fGYzfb7m(jvZ~7ME3j^KPH^^DlBa zL+s(&v>g%4?7k%~C$@9w|ve-E>3GE^N&5d9OtObZ%RSZt(<0)&LnnLq>qI?OBfrPY1lBahWh!rK=7%n;IS$ULQ=Un8b8IL|| zF25Y7p6EAWH9;}PNZ(jdS7)eXN6!X`%eZX!=0WVp7^v&X5pXaPcj!k^L>paBq}df> zzE7Ule>4XRj_e0IB-&jbr?V6bCL3{@+Wmk@oc7LSv=mZ~jmd*VI%|@zOVDgq#08zZJ6O&JjmWuNKS;N7lJI~FUE79Hl{5J5u zc$xQ#bEWzhU;MUn|J?5~G^%6Q3DBbN3gA>!(rS-B(D@xNHvIa$m4j16 z$ie5S){VTqmz3YU7V_2+Y}U+V2&^)>61{9ku9u+U4du0SE+4tUb9M@g*XQY#OI zrJ`D&J&nw`=;f)|xBez)y^x%Yx0h9+T&ClxUr}>8u05N!BVk_r{GV2y5~9zSm)6f) zaq9W``{LU}|Jcqq->W{e4Csnu3Cg`b@4zgzar{2jNLGyB*Kg~*Sfa}s@k!x_^mS6`KGtG2#>9drTM zM?3FJ>%)S~R2eVZZK!_}ZhXFdOAm03J19E8>^@*~RJI1V*5m%1&yj!Th{eHIpgKr0 zdWC&(zR>c&LHNd-EA?w6wHt*LrdTa_YI*QxQp21g7M>|Sx)sk7<-0y#v9g<{YH?%} z3n#}slifPUy$?0c;|Gq~tMcAppTzn`yOWJcfW<#bR%ce!g&iB-zg@R@n!EA*;yWyB zFI6gLxEZXSn_$-Heyx?AQR|$3RopnzHp@+W`rGqT%lm|Va_?gA^zYnWd|nf{VD4E>;r@jC zJIX)&S-73;e9R<$mBbfMFP+;vx$dLP zgP84~WggU>{9%w&4m_plW7Wew8*GK*fGa<5j=!$e}iM@if1(0w#mT$IoN3^|?)C%**X*vJXgqoEAkVA(V8G-O3AHpdt^07H)LMM~9Z zazP26(d2?PZH^`v?4`$Oav4o7uwock8GXJU{R4P0%)0OC{~;Snu&0{E4iA9+_3SMD z*J`|=Yg3kmJ@SE7Mp$$h{9`^~>|V z&NNWZ{Ll#;pD14)#r{dLddBP2dCYpprQ9OE7Dc*l65u6mz@h>i#c2Zu!Q!Cg%p zobw1A9DJ&a=fS`1Jw2cVzi{0(@5|woAg?WhH_xo}6|GfNa5%y1i#PTnJ`RVhB zv~z#H#;3ymKc>7Q?EMhBJTP#_!QpGh{>5c~Y&eO7BZH%OS62H8?%MQ)Day~s?LWVM zO@H-(A}fUGDG5GR|8uTb{YS4G%rnaG_3LN6xWme7FMzl)@+qxZv6)h3g}V7_2$|HAe2q+nRxL*vDZGoC2* zN&L6_7JTI*`4}gzSl!*feXi#`GUm4$X^sB=&Z1?4nso{L#&7=T4FI<+;}35CXU`bk z->*DA9zH@T2+4i7b! zF`w~SWT>$2p{wXupig+qazC*pp%{`*O|=g!B+av3FZA;vyJDtd&R%nKbS5vX{} zh&$V;o>w|HHB{iIk}cckm|qnLiBiN zu!kj5;t||;g=2dzge*~fv%W%t?*1(zR+lzfoA}0n!$fYA+W46Uzob@D@$LYZLUZwM z^`Fjx+w|LGmXQf;sr~~~?!A(zq6%bZ8A0J$oXax-Pc^(zv)J+Lz5QKtWU{C=00%EJ zhod@pnvUGQdFBvIkBKfOyLZ30>MFVyciZ66M%|{=V6l<#cDwXRzhTZ5K6lT8XO>y7 zF%)yYUs4yg7P_WQ)6Ag9mCJqUA@t9C&DMv$6MpiH@O$0PwOLs>0-e`o)N4*XJ+jGU zTt4|QYU+nEdv6?yw>RTgYEbK8_pQUaC(#Pko9bK(AYsQX)YFn08@LZ^d6)34|KO6% zQeXND3F_Z2bIu6FcVog|5m}HeE!XYzcoUjb+IY|@xr`S`(kR_fL(&()EH z`%D2t{`+6UUVL?ckujN4oe~Dj1z+fngdWX5;091CT$`IIRKR#}(z(eI4n6VN#w@1R zHGJL~@(T!P=*LVwj#8PLv7(d9v+0Fn#%_9ESLX{JudqoHo_W%`RI3(4C+xIFnlfBs zB3^Yr+gDRpZou_;?t(P$D;geE0q@)tFSn4*6)UCANE~F1sA`JXuyV4!UsuJ<(;949`w`W3KQSwHzXZj+jzXX^9$SSoMx*@3|a+qeCT;;Bsp*$;xLYR)&(T~=i z{9L#_V#@8I(cAYOgFTO->JMLQlB3Y0X5P*?aramX(=LnHK(CylOQ8vtk&;4(M2(Z0 zWTyT)( zRf(T>-A^!!aYZ(7PW27AnSzh^W@j02Wo0#a?4Bq~MvrFRC*yfF`+G&^F7mch$Khg+ zvrG@6tsKgnh*X-wICZ1a(a6ZOR;<%?nnF#&VkTg3T77URNlw@%M>*l!Zu|gm=<(s6 zusrM%VN8!+8?Z>rk{T49H{-J;r&4l652+*_K>X9 zWO#+w zo5*@|#Bt$;6ko@l6!B4G-{ow?AK#hPz3_-uP>H5bz7kXb_fjoiM#?B5GP<%=8&IfQ zT!hX}9PBlW?+(WkvALmSAR`>J{Z>12y(Q6qNQGgX80&gBu0c=e1hmr{XCEsaz_Tvc zyjMxwQ2Q&1UF^!R!Q~8eQCd; zRb8K+M8U$fkWBR1E`HYtVl?vkl*M_5jtdle}G?EUoUrZvem`Y!|1p6 z_D8l)-Bx?wgt7__9`6UAKJR#hkdsV9?b&1|HKDkAtj)&`2WR>?f=Ve3A zhi-l8FvVG1Ti>c&fYCcGAy4B=5AxWg@yMk1&GRzYWiunCPsSKjc|ZL0rlk{7-S%hn z|IxCsXs|j`Tzs$T#v0nLrs`)3&uYG(YCnNkZ#BOieo6MSOtrB)YY^P7Yr`mPC z=y1$Dgd`l*EOM}vIX%N(OIzY7Ko-mj8O7hIn(-~vaOqAJXeT5LR-*2!H_$T+@cc$T zRJdAL+UVxTQgERK*!CiFA&ULS^v9P}x4L~6Qx+SApROvQ4I3DCOiyM{Pi%BcjO~PG zo>=ZlPFFgNY6|9a?!9skR9nTpaLv4=cBMc+#tTH^tz?yQ&K#E!ycLpci@0M&vd*=t z*}#+AtM6f)myliAPgkf4E#?Iv71H^8J^Z~A^T<~8ke1p_Sp4vmgDZu6WgAst{Z^vG zLs@0$fU(g9#wTggudL2C)!y*>NmX7V>f7A!n8ko=6j@BlAVx_tSG!umZ`mGEx#7Io zuqOmAS+@+l@xVcCt=M3p9?#x=H6=5*FE>#obsKJBj5~P7<5PE)CJ1@R*~^<1=p7&L z%&QSyv}>eByBrOso;+5SKK8Io(o)^zAZQ8%px{85XZ$^IW2;3KN{Ca#@jFP2#aH%{ME+uaQxWJJqdsgHvmfC}4NRFt;X(l6*Ll zaSStD8fXR1z9_%~lg~Qe1vfVyZqUoAlk2wVPok+fmCv7n3&HEn>dypS-ih8lTY+yx zwu~1hZ|tp4RK7CO(Ot+@NkYx+lso!&-)XN_le`dOhr+E;q>HCLDD=#k&NOl zp&0vTgG{EDh#Hj{9%-MDbES8%|ER@jD6K*9~f>*2DXaPM_fmWTkT5 zOK9yxm`v3>ls1f{+>oqJVfD}>yN&LC88wu2ueBlo0HO^EDpy4IZM+!vv>DvfgVCmN zuB*jK@3G&S7E3U+x&itlgX-XYHEkU|n+rnamTUb#){!Zuv_eG?NL~ym%c3DZ>BnMyM4yM_xZ2%k* zSgwQFVB>!{ifPg!jKQsVhH^!$uii2>@E~8DETM!$@(!aifTua)NAukY{L_qnHH=0a zyR;Ilo9HaXsHwfUl5NcPViGt;wTr5w8_mJzrnmKqnu|wsPBNaIx?VLqGp+Yyn^`|{y%P0#P%yXVqagxf zhnV&<@}A)`8aIbWSOQ{8p+N$j=}-^Q?uM~R!TpvW#?q-ouIs=cHYp3+?6`-=TuN!Zw z5OkrdxK&GS(7gSg1)G$$s%T*mC1(dREy+X=h9?b~V%{5Tfiznqi(7slpAn|lT%<~- z6Z5cbvqx8P#qXGoL?*kbB+d^VG79kL19)B~zWgb5YiKV~yFD)~ zUZ6V@HZ(Hpj~>|^DNgcRzvj^=%bFCFQDWJ^p-WY#)qQ7 z?-du~!Ng3B;^5rT1#xZU2+SPrUQy6OxOHPVu8Y_Qe=mKs6-LsPJrO=>?M5f*>(RD7 zRBF})`x3=wGo@E1lU^FKJeVsiBJB0fIAzWvgBBZILio*;2M*>$pZh_w$85d@H5$1% zwF!y1R|<*?3uV@irlf05Z!h4F7PT){s--#rb2n=R z;6U0r?k(8C30e@A@_O!HbG@786M7H~UKtz_(xJY|L+k9+QW#yX<#PPi^ zU1-U8J0^_Fts%c*!bdkvY<$f`-FK|SZg@9P;Mlr2O|UtZZW62ZF%Rr=ewdFCXLT&? zC1P8m@HzA#aIRU1&8qc;Adga(g%peGFsLN?Nqaw=z~Z|J5Sru*%0-@jsdfp0tJ`8) zoNb3ieX|ab_Wgl}JT=hL0orBf$>TqEn7%=9K~UAd0klNlHa|dP2!Wz zEiN8WzQmKr5#=hM?_W9?j#gVnebGM6Ld?%ih4f(OsNr$!~yGLD4rp|JE}w@U#HAH7grT^?h7IFS2(o;)2E4tH#A3$ zI`#^RX;$UQBq^8Qq$Hu8$dsTK0cKCWy?#8SguaFWp;2F!&fmyE?lOYtwA_y=BexS)0!Xrt{JY38{V?HBP+_#|=txJL8zT z*<4mwsSXX3iS6|TQMs$d0UOzj-RP2C<#8abF zQNV6lo`0~BbHlJP2>R9woiPRs;jmj0032<5g|H2(Y$(6}aGWbWD1<;ej1=8b?S0wD zNrHI~0$|t{amrT-SIF*|C7E|%N#5arN)>q4A(4hOJ}1r2RT=pe+x<`M>pP@3kmWv; z`%oH(RHW9VHAq5UUF-N!rO|q4B`5nS+wdEYsQy?10j+|pB7&`79~kCD)|4H{npK-YP(8Sl}aa94hFM z&z0@_30k)^U13hvqZ+rzDhBW z7F{ECq2b-{XLwV+2e8Qnm^C-ZFO+R{>&=REHW9hq2@!#7g!qQY9;zM{U3RmF{zsVf zQ45083+{xnbxMM~JEEf>IPN}=`gd8J=FmLcR2@fb>oA?^Ww6WVvgpd_JN=`5CPOyR7 zA;ng2?;TquvB)>rsERG$SgpKTR54|?ib~c7W8wxPb zpDuUtG+jbx5ykeeE~Y|fV%L%Ld5jGS@3_jW9Mp1rA*+JWnDvS~_T~>~{WN>>F{4c< z!y>JEbT-eeCrkKIc8v|jPmS_X7jKOm7LCn1P}(;9Y@^qzwI}UR5uP5hFQ{qCxY1*f zvq{yG)#SXAf9>Y7UpiM=wrM}&dcI!ndg2EHtA?$kU*;^6mP2qfPa_8P|DDKdr^qxe zc6C@s6jP{*FxyQXG0VdQMAT}8Y$cA(-C!H8i-c|`mMI@zZ--{fZg+{a#LclPmZn(P z$W0O~jt!hg_HcS{5cfd8Ub3Q?P&jmlT~m(M5IxRH8oim%eAzTBvFh8wYH4Jdr_()% zs+jd@q`Uj46!(rKy%s|5TKBB%#MCvA&nr#l0Y8>4g3w&k8cXEqg(B-0m`;9#jqJtE zNK5axkQGfDdk*Vx^ro)wLpBSesqPxT?OKd+Z{-qbe=Xs=v-H+&6D)1xaE8w|ibQzY zLn{QOI_NKDIxwZ;J?*I%;=bKin}Tw3_kHd2S-xpr1OS%7TCxDIgURpfcemdd2VxdB zj_kxXu$+jI)v-yDL%WdKt3m#@i5%?&4OHk)XZrT>N$w3H*Dk+9j@Y`Yrs^hlA2Nk; zJ%rQeC#5TgVHC}z4V%f#ZwDC@w}X^Ir1_4fi%@ICPK!HPDs{7Yw^Eo% zhv4MxO$ujRt0bPzOtS``39C`vz11g?ZL_|+gUrusNdJvMTFO<;N7o#k4I^$&yf z@@fagNj`|Aex!s6NomuGujcq1@iCKJYG%t`km;)RqXNqIerj?z*iB^}&p&3D^ZU}0 zUM_3iDP@>+9kMqsjNe#BLTfz{o0o{Le82VChwRDWZImDS6NTEPhEj9`n+Y9&Dz@~e zHmz#VgF%lZqmYY8e|aaDKh|NF^~B`Q!zLDYA|k8Nr!?*;d!pfaX;rS(!?dSwkjWOO zO%~xdB_+s<25H~jQ6M29`Rt!cwd(g8Cz%n=OmBb&@XTVS-ba^3^2gWi1p8^EJAweQ zBiTYt`RZU?((QXkpH$VjHN6AJj~&g=fM>Ag#eGX{%I&SYxRa9TR>vL4;N3#3?-&v z_1%54Xwo$N@JlS%64l^hP`DNpaGDbN{`HdJ_?s0%SDXFM>OSa8>O95lUlL9`C+g(Y z;&N+|^CBl8KO?Q=F)6KOciioy+@4@ATgVp{pd36t-J^5GkGtlD&Wp+VWh;?gQbAn-ZqcmMD2!<`rZ*`NmWQge}vcy`{ob#ll4n9SH*JJ zWnps>!nHX%*3E5R&H<*`&a zF$T3+$0=@|9O;e?wUA)0VejSaI2fXTc3jjQl{J|%=}_}9jyv74v<8*Z9vI(MJxF*P#%my+D-EU>}O~u?{28hu?!qR z5`|Z6qz?`9zG&sDk}SkHz_yv_y=SiW@MMdAOiuDn&#~VSp1g}jz80Ow|02JTlJv!T zyGNL8VUjP|6u)hG`m*5yetq};&v|)_E5AUg3a_aAOM;3=O{j?%DzR}Soa4vS-m`+L zYM(l!tESvu`7EV}ZGF>S1IgJ`#c_%+pqR86d!Z-WsnM$+c`ikRqKnkpO;{_NS?XdMmRvv#!j7A%**rer&NUr z&vFEJ(q`2`Kusp0xP8R+S8x<3>) zxZ1fCu&QPsGB`h$YGtD8X_9l$2q}K{d&vv5LFqdNu1`vgMcHX$1j*@*au;?%x)!>{ zDcG7|d3(r1C8H4(VnVlGz5Oa-;gHOHorOTbYtpf*4}Y%qtyNucti$BCD?{|RF`^nZ zn>>lk<_KxW_g`2QWZ`*s0N9dJrQESpU@sk_MN06u(RSYY>HhugeY#wmM2$HczzOgn zdCf;ySN6H9=kQi*Ika-F+T>;TiP?y>s+-n^W3#@IVT_9hOqzIj{nCPcvZ$&Y#+A>0 zdE&*q=F@m93ukcu77x~KA zd$+g0R0ryo0K(a?J#(-A1>y3+3cD%`Ni&FhDVx~AM@8JHpV{9Y(P~O(pt&wDT>8`w z@T%6!1KBNW8TBJ11UCC=4-N+?I*F$6wW3XpS{(TrknNstA}xuff|K*7L#J~2!!&L8 zkbRoz^&Ye-$)9Ck(DPo>(evg-};apf6 zC>=u3wL`&1a+S9k0`POX%GOs~hgVXk$;@A;uBY<*Yuh$y{95U68by4SD0u#THSImk zdOb3+r8MIj0OmsmSB}C@Al(@=skm}O+iGjA3-~egq}J63kRIG4QR&^gnVyXLppKRRGNNU$O0^F6(K z>QE8uUvCr1T~Hys2znk3(i2cFxzid(7O5#it#aD2*EWpR7jijG>2Hg0-!Ow1ReX>xH;y$k0T zelS7bibz_h)ksnq=nHL0zp#CIY(O60CfOBMgUz^mG}D}i@-!6W*~Hv)^-~MT9Cu94 zkE(Jed_?Kh*7@F7P^lHgR?8H);0R-mnL_(RyWEu6eeR6&E8AwX&|R>!S-a=Mm_1Mu zZqgwo^;=5wr{Oy0_Z!AO%%nnaOZ`dZ^8&`7;$euA9qZN+D8Tl4`=yAwv)42{-3f}| z%J)vfyGExuOkH2*NFiKmJY$17F|`!uwt;t5OO$sxh**(G7Q?+yvP_NWR)pgzmpoCe zN}WzXC{<)Z?&-L4ig)rI%7fW9_zj(7f!bwwU>v3YE8uo&%A9`1n%`?*lO@)-j;6^T zBxIybu_X}ZQM>A-Ip_@kx|*=(&W9~P7OzkbOWa3fG23u5mr+%E$E{!OIu`3#0N$Sd zCJ$1|QJAxmjG)R*v6eaCl(6=WiJhXnw+X1Y zsqVR=Q0_z-2*|e#2dRl{AoJu z=Nrc0<#LYqWYC3W((AFX72@55o6!-tcpJ3k_d z+vA&*rxpO`7b^5((w$9P_PTt7o}lu|%{b$4fz*CDTzDxZ~Nv|k4KYS`|#f`a07{r838Bb<@ZG{NOxD^Y>tv4if*Y+2srpt%by1%gd;{aQN2ZTYd?S|yE zN5qvOO(Te8K}Pqfg*fTt8;5+~+hYhURKXr5@4Qlv1xnsgf3p=&Yx;@qCPyw4@soLO zG~dQ}O0Z^wo?}b4`DOu`^if|5OBeL9>0Fo4eO;HsIJ3a<+h&bZO*OOL{SyBqN%$uPsz!CkQQ3UeFc@*S_duUIjj@aDI02$Q zy->hECQu-(6Xdb0$juB!L%IkaC-qJtX+1=PtYd5D1nS$DU4OeJO%il=dMY2ug*{No zrk_^V+bq`3k1a{jKA+vuE-8-2E>0`1YO2&Ud;CK79PhJEY;AC}WTdRDrT2V*6GqzR zX)w2=?PRHu1?lM+V+O~LG5o?cmmnX&{1i1n(dKW8Y3xbE;>SKvZdN?9 zqy3jqOkD%d_Z=D(cPbDDr^~}6t$awCe`F?p zl607UUe``Rv|8^E$NZT>x*099RD8eie49Mqmz)G)%tNzJN=I#TRa2&B^iFEz(6pK9 z`-n*=ac@7P%}#ql$TAKUdCl7UuI|q?W^0V4r%a-ciG~il?wfG$9v11(I(SGDXeDo^ zwm8n$k9fODnrHaTgG2AQ;{1BFYf|V^I-|tTk;5vL%H#sJ^!56jf)M{R3|pWB097U2 zAmR*AK?qnu7;YFZ!5IOR!*^Bo=?`{V4u_z|ag_){kQ#(=($7*Rc@_I`pe%gFF2j`i zBhi1E+l$f zr`pXJ+n{QTdS8?6UurKRH(kBQhJLSU{!rG6B!5k9hWD8%+*j@3oKffKE)j&oJf~^< zB+HWP(@`SAp|npDQ9nPh7{gVjlLuc$SaJ=`wmk|gEk`SH>zY4Y)NH+c{+v;wmU08N zX9PN-1pc|j1C*BuDh3(>IGR8@OuW=T(`3p}5%E%5P{FlH34bBbdV&*7FBC>MR-kjM zswx=%U72c#$Wc3+76k7q#&NUL13ibpY-=}$?|a?j@EHVw{#M=SN`4PDjz1_oz87n@ z8_Wqs+|oAhYGOg3Xq!J2Zofc6_uXuPd-cm6KoS7P0*ck?K~Aj?tv!UwigL2Sa7hVc zT8V}a)axgnTLMccgw=>V+J!Oi@D27<=oDF89HyT1;~n!V?ZKQhfQsP_->-+O2!i?J z)d~}C)L~IPV8ORb0Ihw;&bABElK~k8awPA-E^`BC5jnKfrCr2;5YVv%xI}#@)iQ|# z=gsk6(V6fzjR>f9b-pStoJpTMjpl$>f=uv72TNr5aq6`CO`UhHogBIPk>62C&O%Xa zYKW4;Ps$}BNAOBOI6dQx{z+rzf4P&5<#rnUa!b};5*In;?L}xYBmIe}cfDum)t7yR z;`>HwTdUS}>url5n+f@>QeMh%`lHa)QsC0#p|2N*GdSo8`n@och#?2PQLP~Xca(CKjn2?kiyK>thbC{=dUiIWE1j4`{I}ha zfn7vXE?p<@*tSSDkLhpnlNgJfzutBaKO;YHrky^ypf%(}rP;$oQB?ZQY!e)41$Q6( zC{}{eKAwlMw+TyL?ijXy1eV5-o^JVvqj8X&@deC1;K*(BKE~YAgTZ=;sQzfBDx1DN z`Vwr2Wb7MXWdg0PST15dZ)F$hlhI6AC&=zB^iH9eBTGL)KI^I~_} z+YA~j{wy5^@dNqsyDchz&FjVF9K+t>bL!7S$M$ZO);}uNbtJ*bh|eN^BH5Gtd+NClvQuHY=8hGO2Cg+Hw2QksNLLNH}r(>))%&@E%Y}4T}T$J zfSQcBYU~kl=H4H_4if;xKtQass)y753n-|21d|9)@qSS2Nr~ZDhIK8(nZfh*b&8}< zkG6~3(=DubXZ)NgK6jY~!*7hfF49ty{6*g@2!|@ARFiJ`ofkMwgkcC-Nh}OV7sba) z-294j4?HVw$Dd#&LxdEp_!Y&ao~-b$SI_Wz*S#rS|Y*b6=25cdwQ2 z=p%kSCFZ_}P8OXDI!ENS-3UEBNe_B9A#Bx2S(?BBb;#2RfCF|*b%T6>gV<1){f9eH z5r&Qk*bRI-cA_x+`q*P*N>sojU1~B%HO2lGV<#}r?p+Ms!l9witTTGAQ>bVbs4ToQ z;rh$(8Y$Ri99BtUoa*Mp(C;fqdNDwgFof?nyk5VJH0Z(D)v-Ftub-c6`;~U!;@ist zr_Ysa6><5O4=ciloH7sB<1eH1`=_V?eNlM@b9vtp9odzuZ5b%ybH4Ku6fbx>MUZ3c`PnJbHwv_6Tq+pnvUU&Y| zRdWI9*Sl0xYGqeQ_5=abzP}vrWeq_p@#L$DoaW~ zb!%4ZWoVW4fSp-!b}yrO`?REm@oo#IvVOjAw`GPE5k>~VJy#lG40T|y0;MN0?blq- z$!~w}xw#8m79GK?M=}5%<)l1Iv%2ooEAc=eFxTr9r=wOd*YwmBXfs@GlR({xE!w7w z9k>j@(*yCxP9HNAvk>Rh$EmX!hz^)g%BtwiVG^OS>jK`oWhq4d0YJ8h-FUS3 z^seYU&s4^}SJ7lu`&|p~A;hG22_Xd1i$g5NEwxxptJ%>v8WcFRUg!FJH|-4+J0Fh`{)GH{0ls+(n1X(EXq@&$G6 zcI!WmU0NeG={w8cku({9$DcOuo`PQTLzVMx$p?o-U^vQ8$e67dwxAWNo zkhixXiWJDg#n`|M)}G1qApLv_u#>$(C>>2_Mzqr<boc|2RUKu%UUBH{M^!Ril7PUAI)`@w|M7s*Hyvgyn8Ogv)_ zEhV3GcHKt!3I2@PYAE=6(Xlncx6$|N{g!-fMd)aVJk3EDC%XlG$)FkQsFjiVL)-=B zFC%_Rll~wOC@kJk)$A?5z~s+6@BI#_ZU3O8)Z)=4=m?Q@lYHRvnM&`5V5UAu4WOm{ zf;mlhpg>pkE<%ro@~*bu&YqFL{8kOjvshev-riJc!So5|*4p+>m zPD?WFZ3*|tnWRzuHQvrxL0d)VonP;xD}(Ta63;cqdaxXWxZ>rEbhz4JuKqB}%3;eB zI>5_6oG1W&`qZ!1E+sN6*Iuyvi6}lN2r8}$t)6*t&9f)F!op9eOb;C+d_U&xbph>? zwLn!^@o1#dN{)f3@vZ!bOmYmJm%8v0UqH!YBKs7VkAw>Hp3918{~OcFh!kSU{t~vI zWm}OHV{_II^U~th_CNGY`Lexopa1EfxWSx8SHz zZSO?_%7L4c5=JLmq1Rx2laa-vV?_oPe8{>Rd&-g?^#P9$icR(CIEA&u=EM2J89QIR z+nI~zqM8rTeYq(}H$2zP-}`~#eNt}NACMO2V(lOyC*A=B3;OxPILiG_)_fXMUOR4$ zp1}ua=+Sz6os<_t!0e6E5%0qc8Q9rO~~=t@Uyx4=2I#d9gCup#er%2Wsyu z1aDuuu0L^<0(JTx5dIf-=g?aN$0I+SY6%NPY3GmD)l)Q5jt?X2RHn900;mS3n`Yhg zk)Mm>H+7m~@UHs)xS^wYUDQM~GD^FJS+YK}*&sd27m{CP!@HcG;h`v*F+~3W+7v=Y zmpzGWa_W`b^H_W$1~{>!agI2&f$y~EXedgk!Z8hQoUeIj=9esiArskRcCtmYoiuyY z*44Rh1XUa2*Ci7Zbe@s0#3&OCiTiZCVIFz3lAq0JG=5{UQa?P+S;*0f{wyC;Vg}hh z<;ohxugIK*2c*Gv11IPqS8m~Eaqo?5MAdhfEj&P|sl!bIJgG>&WD5-)TwStWKrO_H zZWZ=U+U^uY)GZRyWy`vR`BM4QWwIHMfxwDoletpiNvr9hw{gCsOV-bxY9&l$SdR3R z16(=1!~sD^JP@W-7E*1QCp5jk@^cWdDdy!_qo1oum0W?8D3?jMbvXcN|28nABxA~> zuMoqSfx-RS;5_DU0|c5|uKp*xIfjS;Q@e|Ga=D=4V8H+AXFCe?PZZPgn#;6JFN=+8 ztx~Y5diYUVP0iKexnA>}*K{9A2A+UpadvxC54RXAg=ZMhK75f#6tKQOnJ(0b9x<{8 zp`F98nLRCg`;Ns&e0RXz)(Q`Xr1ttgHG?u~W{V(A8C8ja5 z2fShI(D~!ZEFl58xJusrTXU%RhuP;7p<91`sh5U#xi7JWut?gC7?HSL) zBzRQR0pY4B*=jn*M(Xs)-MgBCXFu909-g-RF_d^Z=|>`yg76Sppb<4b0T_vC!uI0@ z!J?P2$$_%a+63DAmSvt*u2ty{`&8Ka0CYmfJ7?Ff8Ua)$-AiL0t!Af~$jOJzlThYB zhX_8Oo>b}C+xlhA3FGM}$N+w3dbZQ@`eENkvHq&X<0;?`;usC!?A%>~CyV^81E3rDm_0VO^QiY0alT^S# zwKRsVB%gd#wNIv0@O~tUVZbI4>0RfwZfr!bRe4RBGq+bZfO~aYRI4`xl05H3QRV+5 z{;2fT!p5vk!Gb^3=Hdohs#BBK~FuXX_A(zaql!nbaV8mugEvkLZd(6eZ-NGkGG zjKDQV7j~I|h^1p1@2fgJ!y*92?;RKJ<&>BO(DeI95JjwrE9PvPXha&JCPqO-o15rT zIaF8Im>vC=CD+tTglS+Mc2$K-jFX71_?1Sekp(TcWP&4RDE)9)p`pD&M$cHjQohxC zQUPDxh4ablZ2Kx{Gu)Bm-O1k6g+TAo*%$sM_Z4Hd&w5dV`6z&l+tf|rC*kL^58Tm; z=3OlFzv;p=PW2bS)Xofotq+Ds5h-D!e6{VWWv<|95DZxwNVXj*k{RYLx(fy8VYyO( zY5UDmCcEy$$7u)A{3d1J%0xF)%U{;*CtOtjEuH;m=k4{1Am62HZXL|&Pq9f6d*mg2 zO~SmzHLS?XFaTtL)>K0S9hU%Y>XIkbW2W#skOZHC{0@CeefxKp!S9Fyt_Vy%FIRvAaI zSt9bWZ@(4O|Fi*|;6y4B(2}N{Di!c90yo zzS!E@@}`c5RLA}~3m{HCmj_TwD}_JeMgG)^|G8-3k*>d!iDFfm zs(FWHCxOpgCOt-g*l>R$R<4qxY*^PQAv02H!XK2dvDLR6ASMCq5rQ8pKh?dyA}ZoI zA;1sloQl9>Uu^Qk?=sh1wDO4MY_aTR(=>iuPm#_dF*TiS3~0C)c*f`a$5v-`-e92Y zU|ZP$v>7`rJLb~8`0I~wu>t#ER_A}dO-lx}SAPqu!vyS~2k0+ey#dYSF=^)iy)j(;|E}0sE$Nr>{ny6+JgNUZmH*;`{_8IO z;lBTWaThCHHLf2>g5?N{jH-~~wES(|pmTHjUKlAuH=tG>5`x16Jhb6LsT$3phz>=HF65@Df=*A)B?Yq zH$A$ryObH3C{HT-DV53&lNLhCmw7)jLR&~D9ox4n0kjJFx{Ei>TI#XDb_!6C*uHeT z)hWuMQ(r| zJLW5V1v=)SPXI;Jv>!SSvPU^tE`|y`Y1cs@4M=bY&AiD%j)qev?VP1>Ei9#u0cAq? zoxJRq(g#Z{!ex~p;kwFTS^MG9XQJ(j)Z^}*^~upAKz@F?I;3d=6o7)DfG7;wQF(zX zQAJO3^*@7)2N(M+Z^^dE2@+$~6DZIf>3L2FBY}iGwLlyTJ57znro`2QmfafJEYOwn zsbmuf2qHzlJ!c6IbP$m>68rgyqNkNi8e$Z42dm=ADJel7fll~d{n&pA8vh%%jBRSq zKb48Oc-ypoEE#%&xkr`^0V29`e8FT3=79tBYES=tCP#>D@oV9kv1_0_qOfhkW$^wJ z=vwbj@xDD45ohZA1=}Qy>bzBYsl?5!>0qUB-`PidHc6)C>yF2_ zUepUVxQky87f6S1!S{iD_rdmz-{wuzivI`{gMw6oL=h0q?>hBCcpNMFl$Y%7I+{Ql zGkCNW*HpA`8SaLIFx8b0BNfCl@9cTm2aS6Tn+ffVe%%5fbI$keQOm(pK)$S|AVBh4 z*OKqI*)WPdWiQfV5gI`cahrb5wg-9rHbcux<6b%^POVhWRstZBP4?*AaFFEofzBG_I_*f6vzY@ePX$2TD9|*$XUZCQi(E0X82{=%uLrOXMze)Qmy;6s!R+NPw|x<;A@I8kN+(V{0BIm1DWF1jeQdxJUX`@ zpfj6y)C}5?AQezsPGDcK62x_Z-?}?qeFKyW7&|Mhc7Wi+w&SC@rVH=r5SUsq2uN0S z!5M$!bgI8u;~yz9l2Iw(T~0 z=NnWSZ+J&PfDt92nwqFCxMBb%yZz71;h%4>r+}XPf#ovNU?M7g&^yfjB#TkMCrLQc z|L*Ytnm8s!($9;X$Eghr?07U%YQ{7X-mLrl*}rG8zr$Q`5E!t8bCKtA-g(?fWBqgI zFFp6PO%%Tszjb-c7jSnW!?n0oJ;JjR(Q3cDi~njj194%0>^PK#V_kuM*>#T_^iI_l z>^vq4dmGd8g;?E4=c^=FV4Gn=;~=?L0Uhr0XBLVf*Kc~Fnvq!hy7oq_!9QZ}zxcU4 z36KrNcq46H6NtkZ#797Ri~Fr%&A4?^0!UTwank3$DQ?%DYSsml1mxqin6BRp{Wf*hwz73C;XdF4Ub3yG`^Vb4aQ z{>*m&^(oaGLHOz1o9cT{M5SKJO5l=lBc=|>6;(ip%%F@kW@QR41}+P>8~4!M0W?{o z!ugRGhsUuKPC}TDuAi;ko7c^qG|v17igA={hLWxy4ER?W1|fa2IiEMARrHcQ|Mh}@ z9$Kp#&DV^qJos#t9Epld8^ks*oB-j^vy~~(wJ+zh%m)PQMm!}Rp8KQIK&WcH_5BMr z-3Ikbuf;K<;_K&t4LxTrvqp(qFQ?=_zGROn)DC(mTlRT$-T8y$S+V_pnpxTrHbFfV z%?Q=Goi?X&xVyd`_zY}TR}Hf5HGSBMe)*$&Sat(cG9iJ0+kW+N_!a;rRhJi{L6gyB`1iZ_ZF;eDzEne58@8wFbZNr z5e(vnez$tyl9NXuU(tlU{f&Or^@Z~W4}L<|36PvS`gS`qUR5X zD>?d_piutD9dD9JPe9xe0No&|e~kVFOv?=d1ZdX3JmCeV0AXdzh%IaxxK>4Om5z?C z`}t)SIeBs+`=Jgj)?jrCNN0$)XagsnqL@y^#r#nM0X{i~IZH%`FB_tH1gMH$ zqv~JMX@e;XnzpAvWKOg|zz>EQF}kA?Y|dBofN@NZ83T4S27wH%PYg>823O;)=;(BO zu%?zo5G_BP{`cLJL4fCsFTOU(tlox4$5#f}KSeHMFmNXU06|UDTAIxJVSe(L7{mmo zS+NRcEVy$yXM6JW+yznrHA!?dA!))APb(h)UF7#=8VZOKh)kf&ld6ApDM2tzumDxj zRX$u4)&zb8y*k}Fz>P$@jI1e+&%0{xSkn`^e|prx#9r4uFV!A;IObIWv6(WG8|qwH}I{ zi`Zd@Zwh7R5uY)}22CEp{coNXvPB4)oz80ucU0~mwJv%r_~MN-{!bT)^%)(qX;NX1 zeXu6yy63mYjX+7FbJ^g_28-H(=?vHlT?9DP4!q&SSi#`A;07ri#(1>uAcY~22-}Pi9b3ycon^kfz z>LKZ#Xg`4!skyTT~0`RQ5(^WoxVgelqFUG z`oHq8O|x+))@sk@gwK<^T3HE;q@dlYA#TZZNa#9kk4tB3N|Ij^5SSEYb9v^iZ31q zC$w`Wf~(88nY!ak&X;A_#VIgG<@Hl(a9}!eRz3+~>>V=XWal9fzC?r{B9c5wT@x z9Mj8q$t6pFK_5(o>!5|p>7nFnDcRIN+E*W-! z7nj#o0(pBaL{z$8kH1r)^vSzhljOG1v%I9SVxRzGRDQIA9e8K%hkC*!J~vPogbk*E z94OLsb-W~rE(Sf()TB%B8Ln+kswLw8cu}8MKATQiwqHaq4Nn&=FNgN_?%(RhnSFQM ziJ1)4Gwo?{tB)$PQWOjDmiyHp;g13S$IJ3OvA6Y0wZ6*xd-+GA3j9jt2)QAsXm`N$p?h?#}?bR({4B?JVCgKQ&|Q;)}~Y}JFH%})52GA_JW%y{`2dW%7ExU z{T6ZNS3RgDIaU|e8f&0bQcG^RDb{`NnpQFtd8cpkH0*lsT|&%-3Hv#xXSzTrcI@VN z$#C`-|KE>FfFJdbH9zyypT@WP2{L;w&gJ}E?$HxFR#Zy7Zs>aMTU|N5K*(uWx@VZj zv;*CZ1@X06vz$vvVnl`jlH3b|^m=+^D}SPQlpIl{QI?z%$1Bh_EajX*Ku*W|LQTS5 z0==aSHPJNQ?2&ikBy^ED$H|dOvSz)Qp3k&qJ%Y6tr-ZQ-)`bE0z0jBvxZjiHx*31U zY~SAh#|Y&Uf){9Y%pY!5lT`Jb=;Rt|d8*cOp3tQTeW9_3Wo<&`n*!^I7ues9Aa6Ye z?4;~Bn*Z!A8{9L2n0@b5hsUpLBg6h}W|{Kyrdtek;g`s_G%1`qMd339aFUO#H^SS( z*s$s&u6Z=`Nz$6Pi!%GJ;tfh4IS_qG^#))!b!*E&j5CHJC} z_o}AKbxKR#SeSQwI@M>$!(*)+pS+gxBp8AXUmDgGlH$TnuFWn#L0EV|m4m*q>7I6XMjvA(V*yGRsnyc48-ND9rABffX;yZgt6JxbLUMX zok!ezi_p%WX#AqOo@ed%Ja10j&`6!KO25l&7l6LIiX$nh8AwQN9Q1@% z5~eD}zfI2s{jC!&Mrfoyju6bh{F^F(YLi}1=d+rPQB-CxGtE5F+vQvzZnKp0^*#_h z6>yPC?~ovJV973}Q<2^0rDA~CcbtaHpx7V3e0;d~hr9I;f%k=mWlH6%F%rK~6qNTE zS2{%wVpfI3HV60yD(;iqOB%C1EV=hl%~$4CJmo7&!+g%+RO}!rEEO*=wzf;6nkT>h zSGisf0{$~WjmjZfxuExTaP-7V;g#HJjTEAiXVOcpZ7FVRvup?y{wFn*XcvB5$ENV} zI(ZNyN_lsm{|76(TmS((>3f-n(ekmAUsg7=v|BU6(cUDs8N=gehMD2{J~~J?Y>H>t z6Q@a6+}-Z1_+nFC25ic(t{fqTZMGvJ|Ig}wK82-#4=oVkFpEA;V4Mje2V4ZpQ1i^|`?=A7o0B%$QY0}$U zP5+7Cd$L0fd`S;^TsB36`9alta4(%4J=oIIZ};n3i^Dlz0h5*IEjNtyj*gel2=an- zFA-s8UC%nbx?vmu$@p(Cb^37d;cDE!dW(2M`$^I@)qM#(d@YJhW&7z|6KEnJ*c22} zAWfrkAYr?Ft5hxR=Q{{7y2pQr{dGU_Aa{w7MG&0+~Tm0(3?o2UQXO>f&sC2-==|Lq)%sp}plP|@4JlIAoY5Bp`yKXnJVkPa+ zJ{W{K$NvIZmvi8RsifF`eVUiyNw-SWEw3B_J(ppTLQH9fU3-sph2+`7aeh&S@lXD~ zj*%3FSDvEe#VOcwqP7Bnsi<270-0q9p`kvg2RnMPdVnWekZL6UYkk+fp}qct(b}q{ zRnvfHzc)C=?r)(rGjA6V_5BCTbPJ=AnjlM4XtV#;6jvgu0BBB+hB62t>99-Qwv0?px=(^ zVCw*r$9Wt{Je^(N-JI`up$50eV;Wo{7)sv8e0~?3#O~(N@V{b%A}72%K7B8SKc~j( z@sa!h39`qaLV`R6Ut0c=8|befeXnajgxfh{_B}kIiuL$-&s4j7=)N-LghazQ2$6SYLfss%u4e`zqDlsp!z=^ z_RmxEsV;%7^5%xi8e~a^I%3XM+x+w|IhG*9iolFu>o*+PZ@*C%(hHun zijHP+v{LB&G{2%wz`p6=loCcV_>0yZ`apBuKE;JtcYZO7KA=c_m<{MNfJ#DnIt>kLTiU`>_#T z{<%1%w9MnlhqFVUp7L6~jpE)@_q4A* z4TL;#V1C^R*>purmI)wj>VG2VbGQQ5;8*{niJ|KR_W|LM7hb^y!*j`3!8U+^583r} z&u6gQirss|>+M!P^)pDiV$JDgr3%|=rOK&Hf zF~WARbIf*^RPY z`&!Ja5s0?6(THBME1_?)B{%62^gxI4Jdz9xmo~}=@`*_7WW&2EqiVy z7Vs*OqSmHjg?ojk1#XTEddPPcS`A{i?;1Y_5Og+rdUa7w0P!H+sk!yru>Mq+U-8^r z%fKg^=v2HqhweTH6jO8fF_{IOhYKh<|d=e!t z;Ws7_zcH_6)rlu$!Q$GeIJn^gwy+|oMrx|77aqD#oJCj=f2e);2-S#!m_|hfSWQe;n`H=?!oU3i#z?8$9LdX(;CWpZ(+g!BrRqhbNT`vfy6>;rd z7CYQsu>W8qg9d&(1c#Q9189@F5Ys#0zpI_W5x&0w8kt}T@Oq?D57SV-KbmH`SiWV8 z%Bn|Z$$A++jECfL{i&pNk{(X78mMOtW4Nt!U zbq}Ce72Mnpp-oq>Do+tOROwony7RXIVtHiGqW`hC#h2?ksJR)7xYk{hR>Lb-z0iHe zif7S+rmZc`elH;$PN=MW9wp>UuEVa-zxAZ@BKYv8y;JSCLE}6jupo73IYD|tfcqAy z*jqL??%cG}fN<+`MB3@x0!J-1S{qrKp5kI5RsYQ=Qjo-+)x`nPp2W_BdZsIvLV~zy z{b0)B;I{uUeE)cL09j}A7j~5d1Ot9ewb$asrBU~YrZ}_~2{EQmX>i+P@`cuuLOiaI zc-0CBb1jFBxHa{4^( zG({hX|GarX>Uhzc8uM^(tkiVvWBX#@FO+2=goFNnpvQ}wf_O+h641$G{X?K{e}qM$ z1ts`Q<+q@;g{^JnZWhoA#h$?A=2^WxzuLr%?KVoHFl|$)lBp=%H(FAx9>>h?>_6q> z8OZ4_;fKYTH0NK2-7Fc065Qgj?Fho=?GnfmGk*&TdQ~m%nash-3>{>$HgT7Uc}ecq zC{7R6vJ~IvS0Ff9GCmYADH&nBP#{&t7b3MivZRjY?(j>#H1pZqboF){PqeVJ;PjW? zxQK=;aSncrwTx#jIho3-=%BN8@>@<}QY$>4kBcE+-WPIz@V*z4m12S1J11als){@I_uwbPhEB>j`_;Sb4mdxutRo*i+d`G9%^)xf?N$5GtaOzQw z>n7N_3%Ts_5s=ocz>TU z{_2(D1cD0$c!CgyLdMyR2+Ad}3^$?tD8_iT^nbFBb>|4^9!2S=UvIC;Eat(3GoIL zyo)t&4WgQJXq8)W2uZOtiLf3Ck;YOm7207b7Ta|Pm`6~}7WedV4W^fi>90_v{q10q zDta1N)ri{L0`)uv1J9N5|12 z8Utf!W=D`bshFnu$g{0en2W6x1BGMOf9Xs5VX!}AdnX;9f{%A3AcWoDkih{7kO+nv z!K%s;i5f4rhdPFbE&Xt_$QLjA9lzDT@-bG2Q-qG=@Y3=&tO>{~2FC8im4)~EAt zJALaMIG4~9jW*z1=JDt9xiB#eEX|#OD!Od)Yom~btu5vBLuB$TQI9yvzXSmkb z4nhsDwZZL)bVnd)j}ay+qtF@hXX3NNqcKvlPLGpQJoQg!E*NaXGKa03M$tfeza z1sD+}bh(?0*1(%Ixg3;La$#d^<&7_@RF>G3q|zcXL|v!A8`r=6gHK@pHd*l2`y3@$ z=#5*gS3Rqrk(V1))L zx6T5GGqTLn4(s@lxO_^bB-b+BBnp|DV`$LF%=o6iz_N~8woR8KruV`<*V`xRdvMg5 z{dS75bh=;?46UH#7~9~-E^oJ!f4#@+;(4%~{1Z~nKuFf{ySjhL{scI;GJ?X>7!_T{ z<>AcjAiMIX*Y8V(7MM8J*|TzKv(ynqUG2@sLh_^#GuyhD9My|23$;|KFiiqcR*v~n znzxEG{w6*n%l@h9`Eoh~Pt*|0dK$1ZK@{;G=La@2Yi#&iUXFb`_uAN<4&g|qla2lM z{Y{4bI_eNv*->p$>(7_{6G?!UdKfs>F<9~p0*?nb3)xr=MEOYa2E8R-!l&+I&k>So zf#f|H>7=YfJXnYoNV&1d@4-cjIF1T)*#)&sJQ%dK>PpY359J65qt%9Kb7)=?LxLLK zoF9*K%x^!3q}_B+V2p9>8e1!?b3dvOpw@W7{^XE|1ivc{-tlPlMkxglL91h@G0{W+ zCN7n&7t2}6`<8cWnKe&hWtx6)i;M-0FSryWqqIuw8bdREICf~>6jJj|bQtEeQ_P%$ z`%U>2a$R`$7b$o1Do~^61K%hzctS-^f>(BSPUw+A4X&OvuU_kDZso>y(^Cz?n7UnL z$*qrL8|L3}QOUsc3nr#7=6<=r+MvLKBk9n74#|b}PFh}oCjspsb4&4O--VP6>HfXbf zzI&R}7CfF4B^Ed58e&%+Zg)>#20NK`{_!AnJ6}h?F}jmtF@w}m;-|r;*o?}f`bA*= zTv;R#U@MlZ0r=1$KPhVB0JW|jA8<@AOB8Y$HA();WnCxR^#95jLSIW~_%qRqh=3L^o7Eh-jPI&xAUQgAO zP?x=s+3|jfwO;5I9Pj!Tv6dJTXar{PLSV*m7hYz!Pp;hP`+K&b9#G|DSzvL*5?Vk= zK78fH6BQp{L*-vdpNU@x0qAG)^>}{;A;x7DlHe4RS*~&lLBwFrDGPrSN*?dDx%gS*-<7z4*s1(zDFYRC< z4PJ=9!2z?0e>+>G3gFlhL?11J)0~2X=iw{IF5|b^P!akZZMxx!b4_Q&^iUz1r%V=u z7PT4kY?XX&sT52uH<1Hv!~#LGCks)IHLhGjNYRKl zM{>`B$~y(6Js{R1?^lD%q7=)D!zAX(^HVy0)|y6K^9x{+NjrxqRdF!hGm*j;C#c0L zP9u;Un?@Q9JZgpd2!FIpAkIeuo3fFS4Bvl;J5IjuO|h9&-v8FPAVrQU}EiprERr_tmkq9E{bW!3&5q$7baWU zWR&+@Y?qAEgNoDRbp|!*^ADF17U{{%`PPLvYPi!0dF1*obd};>dnJ=yxvWZV1$$(t z;9)TA*R9Y1TdG5M2Jbqr<8OtXod1E3bDjuDeJ{?EKb7m=_gJLk7MN00@I4gW)*WA< z^O9NnmtLf?3T(^d{o2v4tHW?Ju^A0wiP>H~*3SeN#J5}Zi$slm+w6#)59b0M{IV+0 zjS9@Q_&^%{J+Si+o~7vEH^qyUh!;2Tv`C7YW)7&95aar-kyy)!zh4?t|UEpTVR~(k_0?+aY_O*H6D+cJRjnZRsHu>Z~Q!`>^%ccj}}vhI5FSB5xf6BDm8FogPtc`{t0Z zmyhZKQ=sPfX!y{0Y%+y^{Rt6>BwnKv`$Aj2eeG8#g);2SLlDw|OoA1KmS!6>A{IEs zxbHduy&vG|RvA-tYszTIFCFkx>6nV{eN}T#9o1NOuZo+!vGX@TU6Y;ItpFGI+}+&YB!6*x0NZllTJU_zH=oc;Gx!lok5%O}C56jSuU zr9+xl33|YWVi2&j8-B$hPZr>bTqOA4!T<2VA7nnz8#NK^VI+cosar!BD$zIt*UV=2 zS)lp*>=O%X%LrbxTqYr@9P#m`rxigwS<3S4=8s&BH!3w&QsUj8L!=xsUO#PL%MTG4 zHm+Pv{|WVfR74HYwbkgyB-M~N*QY{b;hegENfYrVklL-7ydz@mMZ}m_kPTf;N!c3n zL^wM(yy;x)ztj_sGwmx3OZ^WAmv) z$xBC_re^#Mkp3jB!TsoBMS;AwuW;Z_9xfm3!3vvl-YrQIN-Eh&ABMs!oR{e=oX)gM>ydPtcKLRD38 zPbx4RWrf@Q(FmXBIr0XemduWM$j9DM4W{`%a4l_F(8C1sO;E>cE#>yt-5v^^WK!_&t~O&4td46>R9O({kmBJcyPeF zasSj@Td|lMl)cA&o4JSuj_flu77u9p|DAL?V&aIAo-cI=9adUq-JheU0*(o~l?W%F zgHH;&^Eh<|-(d6A{p6D5uHuk*|1<4Vv6q)GjRoH;HOx5`|H4cv1ku6} zXiqJMbSQ#_q`*F3uA=Xj-e;)&e2)>=6x5r|&hrdCMzqUIFoJn0r;(TZ0C`+ND7tyj zN$>dEod;iit-lQJ#XI{jzSD}8x)Z`b+z<`pA5ftxc8$UC>u*eATAfZ}7(OdKKXS_?Dxi*zYmcvJ=ISjVAIykJ zS^J;Q{|iewk!Ueu5WrA_s|!cUFL#KB#0eS0Lf@RpWeUJZZhx`g{-(XP|F)T2x`!tBrCI_ASXvFw}HBjVrB>H_)g^2+nX`n&g?k*i3@MCtj{xI81|s%c>m=Wtns=C zCdupX=l43ZT)Z+mcLSNqq^v7)#{3|H%1@=7mS{+emVf&3 zT4IBD&tiCGsBb^5)>u(yUpb9g1!~Sd+>Sd<`5wm=njs3yirAlhKYyv1@9Y31R<%V4 zA3?(OnM5SWQ(=!e{9(j&0;#hqR z#iK~g3k6)BXNF^c#c*CHLuO?M1L@E>=5f}K{NLJ0ahVy3JX@=$i3=Z~((;4@HM z4lKz8zt0kHe%?VxL)2JxRcv{axPntlJX~fZ;!eTA1Vt;~WN?ufYeaZ|B-vqEPf$C2 zy7LM%N^@Z7viUG$CK504@rBR#Aq9T(6o!uv)deI47PC*t#qPtR#9QXy_eH>->t zF@t~tid@Z#Z^%$h`?NowUr@kZxfoG~H;ErO@|>53zk~5GgZ!1Phs6UgG*S7*ZNwPM zR#a9(ioE53--yis&qtnlV)@!;sxo8sVU%&2u<>{_Lq8@#jW*z05k&;ew+uCD4M&eX zo7=8aB~jnKr2kbsm%i|L$lvrU1sY{#-l}gKW_j%2LVv;K+nlX3m^B}uoX149#kixJlzn(Ho(-s zHoFZUzW?f!pdY;gxA8ber5{r%K~BEwF;tL6TGE@;d2Zk)^Qc;a2I`E}0cRlrys#j7 zdBc9wt2l1f%_A2Y+6DBj+!%!(kSYF-0b^Sn`45g|`KpSu?z8%{6 z*XU<{$4V;;m?w5aGa-$R!y9v0)bt1 zu(*mV*s(Z(+J_jT1}v3m`lB`;o!Ee-mfTl4_fzYhqxg0jqvJPY=qXuy*b9IZ_T{bT z0u^;lk7FTm35zEGSI5TJ=MXs@`YgWO$cCw6qEMx%-ZqVN-tG%tLIbQW;|ibVq#U|b znS`v?EDpGve{45*z4G|2a@z3J>*Zj6WV`u;h3nx*e1gAtg419N`W`E;{+vMW6-5Y4 z&q%v2q&|JOYK@8|JC{!tUPD{O8+$$JjkZduVX0bJctgRiWQ{Imz5}n?GKQwk;)RHi zyFTv)faty{;C*7vEtXDC6gzb#(duCu41mMKKsAglys-z-9Z#1r_dfXXGs>aqWLmDjf6B`8VN`}{hOS% z0{0b`?u}xPJsM%kQwHy&YzmzMqauznF?eDBz=FEkTV2g8h$zwlzCLqnyP0dl7Q`iM+=)G)>F6>;G0$^s{$AIi0C0MEJo&p_1fr;I*U*{Z61e@(zT|5 z7%xJyv!TTNG3AjJ|KnuTtFLlXq}_A$SU5hAa7@$QG~vq+=Uze~TJ@bN z9-JYsrkXni?$mEP4%h{7wZ7LTSbQ!+$vXi_2+Dxh))- zb(7&IQSj3NB!q{?6CW{al$pTYyz9RF1@wMti2={$?VUG%?#oTahY6*yGjFp)r>0CF zopF}+=^XFf0KCNVU3c`zkN$Z)*=zOgfD3z*R6S2zob6FJ(7>5B5;~G&LF)LFNeXce zq2NJqM6GJ2gUuyfYjhX^_|*-40^6;0<78%e6Uks&x0&kf)GM)>(QzXdoeIo@K~fQj|Wn1u?0SP+Oepqhi> zOp4=muhEgaO<0TH=FLkR@JX%Y0nK>FvLFAf5pY-(TmAnLz(_~i^rc+RnV{x~^%qaH z8>R!3!y6W!Ix95v)-@4nVEJ?%pyi)dy@QDlU=|@ff0;7@FqMZ|tPl@U{dFR9OF=Uh zleV{_+`PQTB}L}#45gqJb20*Ed(xp<4IrCfJZ<-a8!+y8fRUF6vMP%%N<=4D zFWNk2Ve%@Ric6LJxND~dHl>r&@{QVIjHm}kvM%>^5eDXNKBjAWw;6K>-!m{Q zv^p~i*%&N;ifKWv1KiqS6ZZl8IdRS9jK81F)gg8&NtUfIrsOoI3Bec zoZyVlxsg{g3cAHqOhs?TCDv}-?muE@|M4m%?V~`nR$S}s4Z6X*({9q)OS>nXMvD0W z-~tIII}iMU2eE|k_WihoSATZ52HaU9JHBZirTE*I^8#q>Xm?B)zlu2>aS6b7b`k?+ zBNTn$R4jFQuf=7k1P=-*D6G?U+=qqT^fD*G#DXInxN_8m&1VDnvgTTE@ASTWGCE%! z3~j8@!(RBk$KaGcW&uY>2QXoLI7_Fqk)2eF1HEkB3;(K=CJPw8(;xgA(P1KZ&WcNe zdKtLNnRhwG`F13GaXzcBfm2)8^zNm{pYPNHn-n2IIE;kAXM25K!bL?zLxO`xo&7HV zSuJ>~1|zO<+_h*D#`%E|GeX@g)?8^k<)Weks4nX(o9~d5} z=NTs_C&l--cz9I7tX-zb$;tEUE5IfiDj6&dSB%FE16;VtPjk2!Xwk{tiEDRGx+3ZB zC=1!p9PrJFRyvRs)Nx%Qn}mpmL~zr|++8d{8{v3tgUmfOc=$j6LoiB~jz-hvI)RqZ z9+x8Mj6e3=y1yxaz|^$4P4O&)8j5@7*=1$0;cqVm8qn}EV^F1eKH&Qw6MaD2yn?np z#2CNd?LAhKl`~uu^>he?ds~8a2d2)K&DBdIEUHtInu#&H@h^*SBk9`j!-4t$4E1q6 zr`-0GwOK2zGq0zyJW>tn<{-GdeiEbRb=WNtm?Vp1RYkt%V53h7!N}@YBB0#fm!)t7@>s6Ue&AQcasu**U#{oG}nA8&B>S)4gp8>UfP5R zUdwmlqm|!cfiN!m3+ZnMPnAmX_}Cw!0V+dKL1WiinDOB35yNFS8hgtR(~=^MoHRL zoikxRJ|yik3Ys}O%%8|q9+jH@ARX3rHl*WX>Wh|ki~glgyn+ZVX}&ywo6VWkN66;Z zSME>ABmCsL?E}Lm1t$wATteQKYVqebo}GD1HP-k?k+O{#D?VtThLe7W)r+rveMGW_2xX=sZlT+@e6&`R;%6J_YAA5d@Va0Pxqa- zYR-DlSMIl6RaRivM27{s&l@eb2YRRhM`3vhifm|4lAZ)(3Hdoe3LL>(qlK=e4b7@; z_4{Ry*#TBrmni*}TRsN+`V07!j7(T;7`iNeVHlikm7$B6V}~(`T0FiR=Pv%~N-DES zqX|WwiiQdki2t1rSgFwmDGETdo8E}6qi&bPQYf0t{?^fB($yOy=rkRp<|X~?D4!c{ z`ktes4;;GW;Bk9Zw?22xfx$iv_%6tu7LVL_J5uDA>$p7bSFRW5zPat&I0*()MU~IN zm!ivE2ig`E777qE9TsgTr%=ab)0E=ROm%6;IgEZFTG_e-CbeIM@Atf`R7c!@bh({h zy`682mv9Gcgz@nb)i^5iec13aC4P{(Njt;Nk9J07;ovXQc@(lX1BdzUr6joYoc*nl z{X)kaPI%cIT6h=&yt6{rS8Yy>X!jiZkB{SRNTNvo^XdFqV=7^gGE97n2t2zmLeQd`R(bkkig>oM!S%sl!!)?_g}D*zn$>vu~vm zpW|1!^APi$y%{BOG>mI={L23Ic%aEz?;96pdiowxEPx?qQpRvdqg_{Mtm8r+f^s#} zc!UB6C$QYcI}sv02Nq~4fe;QcBKcr&m(zxwv5~k_3YLt?JwYdgXwY@Gwy$?2?6~t|{7zHVkGU$v+r}O}Yeg(naISgK$RiAl zsFr*31S7bX(9_x8esfFB1FJt`K37mAE zLwM#hZ+t*&K7(9tO)EtEVx*llVn|KJ>#yL-1Zhn7bhE%pnROV?Mw(WU{id=d5iERjDByMa!Vo zw&HCtgi_AI!9l`q4~!Zu!#C>iGmlZ_TLV9#d?xYq)^VnBI64%#mio2-JrZ*mN|uNk`%SyJ4`E%3{91wcO>&B>pz@1 z1h({s1n>5T__&c4CmpxNE@$wmr&Hjhz~_Si3;>cpSy)%M*}F?J&v+4OP0QKxUYRGK z=->U2K9PLU(N8aA1;f=~`Z{ED|*w_VA|Auu}EJ4qJG2su&z1Ma=J6AI9 z@3!sFJ1gA3AB=0bb-EJYPDEBgVNF=t+5CE{RmDu$Yc5de{lX!DPE=@==gOZ;!@y;r zAip_ZIeyiDnEIxa>yQbdkptjDkv%C^h#CBv6nm0oKUH69l)Dsd#WUV3?1y5nHQBZ0 zA7D2NqFFLNzV2~@e<9inzx+t}Fgr;m9^eq=8`Kv1I^GX?qWuvhx%@YI(&jJq={RvR6l z8-*EV)7-mkp3iOHg>CBt-iT?w0U1#&JArR+ni)TIDP!utX)et(?ign5)KoZaInh#w z=C|H<90V_o>QnCA6Z&`)&KXf%;~*mxBXk zWM$us{Ad(jd_R6>>jPD#!M1k2V(hJ}oSdAgX~jw-*Obg~t{m8PP(i)xG>3Sp}ay~F#U#cK*v z4OvX}fLaKjT#c%U=ouMXk35F7y^~+&akn}pfkq73bdLit>$Anq{wIEdseAet13)z` z`hlRqOE@)R#vB5?mh>oGp(EiJ9F!jBXe_IzY_N#-KG&VIHMiXh>OBH8nqM3%;8D$l79WAj-0WUI?iVrj- zu=jX>5|E9TSa$Oc|BB-C0WsJ9hxk{eiss(^#rXput`8{C_4upKH@vIsZ?EUG27KHH zQ|p~@*o2b;(Xp`{xvoQVEb}powO0B&I#mW)7|aul{4=3-FgJZG?j7ik3$*6eo*DD}>DAK=wt0pE?{zQm$u>A_bly;Or%&9q+S{ z+@ru}E$Ms!5la$Gk;ku~!3|@7hvd5)$61=HrO7l|Lkb z-U2(tsxTPX?5@irJHs2hkpB0N=C54YJ##d50}uylVH_0h+LJ9?l!AKjYyJwYfO!g+ zs84rnaO+i`n=Fw$zwW*K0E6;v*Y#ospNK#pi&HheW{Hnvt!d9W%yj=moA_=1!|6)k zlTo|wFbKVd;s`K$-4g}CIy9E0?&)f4&p5lGYa$fSZh=OzJ-Ndt=pI+wT@gMMBw(EL zR1HkDD}O~F7H5M(&8R1=#nWi3=v8{7OwAm|fs*U;21%MG3N@$;7n9!iepl{Ch#`lPO1rEu2YHj}dw+I}SRx*ucp!S-0iF&(cmwDVB`G}&ti7CyN(jQ) zSzbdUVq#)KMoi4hy1zh@lB*|!|2px#3+6VLV>U*d?b7G`D5$7*^lWXN5s1wNQd4O- zJkD*=AwCp9#m%@M@1E|wvj+-rXTj{mp*01r`Y8Eztyl&kU*BZ=I<1H89izecRE`}8 z<7*-NlID_BuC}EJ&gLPbwM)-G&Sui2lsu_IxT<^%4q-&*LLzc#nm$gB^{zDH+An}e zgVl29=x;X$_?f%NmEC?N^U|s+eW%4Qtam^sIAc(X)zR6>GrFmE1)IRTf+qmE2zIyc!YeZb|c0;(VB|ZqF@+& zHxYHLcU1F3PAN!`VHGcM>1R)fX!B>jIH=wDFGJEnur~HisgjUCsyRHGagK{UI4iU1 zbxlT=~Jf#Gc<`V_;al{;H`=ned9vkR$^2@z$TtK=dIXU(cqkl_v&(DG(c( z_}0IOl16Yg(*_YL5jUW0*>zq* zlBejr(yJT`f>42BrtJcm&a0q>X!b;S_drh{pVctTC^7^fx*40$00xqha z{hWHsA0UZtD?)gMiV?>@PTM$A?)et7K;tH>o^Wbas)&}rEKv;7i|`ifr=rS#h9-quj9q@@+j+D3MRT^o$H|N5dd-o^GRRm_4KA)Lk3iN@ z1XJ2%Q*`%a0YDZW<@Q2$FFgSeFa%L&ko60d8(DaZxuJ|raeqk7C58AN{yXA1PeI>T z(94rP07jlpf|2iM< z^BE~EE-qdMuo{-Vmbt4KStKsr;as^AH}3jx_odl}Jm|ENfXSHj?d5Z_a&qKxdaAzb z@T$2dT+^T9LJQ5>0yf$Pu&8^vJ9GN`Z2N2a`z8xhM+1_P25!GXELLNMqvsDb9T}k9HPQ|FWB1=Ex!t9vsi(xmp)m~ORH1_9Ee?%WrtvyraO*SGkT-(t9IwNj*8l?0BVXlkDD1+n?asY+WIJyiK;OU1sky=U3VWYn_ zK3Oc-hlh$KaVdI+WLx$0r&=3%L!-QWim4}Q$>`{n&+(zEqQ2G?(Z$hySlno zJ2-X)2E2Z7ImuJxei={LTIMPX%L#3-o_dm^#U8{zadB}TuhK|Mb`{?ijn8J~MvMSg zdl?K1iyB#yebrh#ieNHARVv*^{kTqc8P()Sf#%vTz4?$8nY#K)+Iu zKizqqC8BL1ka=PD+S+e0(taI}ebbh~_%Qb-%Bs~Au14TNXLzc15@BSdABk>$eZCyk z<|gFQ`Xa?Q)vTs>soBCDy<@<#*cipLMrWS ztnL)KwYH=Cx93;F@J4%LA7*Z2!`D01@sHmhDiNe1ld6SHy{RnX)u#=5yQnDm2%{Gr zUH=T-e-9QJbfdTrAx4!{N>Bz82l_>$-lb8rc{1b1hQ?Di$4cV;%yZcjiUgb^_=fYBz!q{y<@E5wq>S|4E>7R3JPReOfw? zZ%B3Y;^8gwgqEMYz@ZrYtd6K4v%I>zc*+-U5AtG6D=VR23wF|3H+Ofdt zUE9nZ5)P*j>s8}cT?$P758xqH2XOY8k_P|;V@@=ryNYa}Uk%c2t6BeTk-%yo=&WB> zzPsE!B_vg;X_dZKf9J=2+ww>ATorDC`IqAeSXo(@7oDqId*VnhSXY347p6gBH-_Tv zw~HQcetQtFWE3MLhYbjciDAdHd-!7P^u@6{-}ATeoc1B4^YUf^Ib+?#7?qR)Lyq_6 zmP^R!ZBq-r)I>Wgn_xAws#|VSW-ij_C$?7PLCQlUU$`AMWzGSg5N{}FiXzi>dVkqmj^D;IxkfIt;uOaZl8sw8rtL zy(|S)HNV$!k^H)D2#dG8#zoN{q>K?Pz8q1m_)>I8#XUh^@77!pU7&l_fK@|^WbgF$ z$R1}wejg)IRBed$?ss^1H2KfWjs92Wrjw7>YpF%U%fGXdO6X ze+G?p9jVs-0>ZF&!j=L>iP``(tUi3 zv3u`{;8qLq`&;qX=1Esazt%igfx(1Fv7psca(1@$hsu7BZ%-RH9e{=Cf0w~A&O(cZUkzM(fr9(e;z^)Mg zO9Z3gx6Z3Lkqa);-mJBa&Dvmnz$WE;&_;6aSq^CRdT+66|Hq};uBV%?o_?z>1vAKY z*Zl(m@Ju=rGxOHv7~5CtMg|7eOJV*r0^18lzjZJ^-guAmR}}D*((f!1EAa=tSqVLl z7YxxGle8eEe$lGIJPxmXfDAYwL|!oBb8xA% zcCb8SH}$9y7CVaLl30_DZ-<%ihPwExb~Z1od7o<<(R8a!9wh|x+v;N_m527?wpY{c zh+c(a9*#Z}0TZC{hTc`q@5+yFuM$-?w<(d$%vbg|>)oBB20;KdSLDa$_<^n9J;e@i zPBXOXyt@+)@fzQptL<%mD;_gEx>-fIQ}dTMM@9%Lc6K82NBBU=5b|a*D#8E{OIP>B zi=aaf1*Jan4X}3fvYG|n@ z8rl3L@M%MvMySFBWkDQBC(5?Bx3`>Q=Pr-$JJ^0>0UxS)S3b!(h7ihWZgm!23YMk| z1})+G%1u9rjn^UpvCvqyc{u#zK(9Mo0Qd)At60?275&A}nd!kY8g=`cU4q+lY+#$ls!4|}6W%k49J&M{VDHL#xT~J69S1KD0*x{(7L*sT0<0%**vwh%fA4kJxutxMJG zOx>4CFrPSJ!v%Ce93Qm)ZQ((<&Yz%?5#uK9kkh;r!Hz7mSz1mRmAItP64K;j*x14u z#I7;bH!M}IZVs>!;9l}U1a3YBU$i!P7>Nq?{(UZ^7Y?G69>1cKtNWU}-{t%AVD#7v zUidhY_1Tgiz?^P7iYi|pbykU8*aX<(@{RPUS^hL?QK|-qOd-=U{WRjPbw1s~!mzNg z1`svGqi$qy@VTnYVz|44I$)wFX42x%T(q3w119R!MeDV-wMNiuv87kd5w_#V)c16^ z{;Ad#m!mz+y@*Nf@g-VqR8k5$Jd)9XtnVjPmm;{j`HcN=mPk^wfs}ET5VtwAx`I>@ zO~mZSXR#OBe<-uylTx#1NI`PUcfm}UfJm^gv~DJqxNC-) zZ@1lIeM)71)(UmodZZHWv6iLZ2s%}>+Su55DV}_9H-vG-v#dUqO6qiO-^4Owx_tjt6CX-5vK?D@5V;^OGwbT^$_w5>>%Z*Ic3; zuGy=eNO8MgI6o|4UH+-(pI`Xc6sxWt%s0(Hmj7(#8Tz5$6a&tu-tOGS&+b4_Ykc<6 zw=H~&Q~(m@1UO?AYuCqoxqK9*1(4iALYC+Z{4CchOLXP7WtIE)%5ep|C!zCT=FaDL zThYDcrcFY;v0FfO+MqAe**ty2g3ffE1J;7b&$+on#Tox61P?nk5 zv`aSxIw&S?2*BGjPVEC;qX7((nz*ySa;Md^Ir1~2Ngko|Au*8wAHkgfQOraR*r&T4 zs)mp7q3&NKpqC3JHi-R>fD)VBO3Pb(73~UDbSrB=%-^EF6Xd7LM8dG@vF;Wh;Iq;C z)b=0~$RKK6oR{c-<4eS8o+G<2vhyhvZG-UxiVgY_lS=i#RKv#4pWiy3xDAfC`Ca8& zj%V*2wKTwt#E<>rfUS^zHS@$uh$!K>E(YBb!YtoXEnCGXRPj|8L|zJGMO;*LV6awX z07KVWNs*p=>xGEclm+wm5qu?yqpa8ye^wzw%v-$HEPrj*_?ybBqIbpAjF|1ZO;|}# z6RP*B@EM8|j{ZQUe|U{Xy>AKqlgB|){~A(wEdENRDiya)!r4U;z`#=`j0?1a^nL)g3gxY}`4XZKpY^byS_85c9;_evqmFbr> zF-;F!p9>cmn-TtyR7h-ai&kLP{lmbGDdh9-lLP5S(I-|7qTTSZInW+`H7spvV&b*p zziFy7;bCDij~;#ByiOHon^#dWL~D`MNrfMeSO5kPv(9z=TeVrog&)oRcAXZK2zlbm zAJ%WFkp2PY^Q5}wGr{5@bIrJA_ABjT#D(eV7crQ=c&0MNXdM4i3e6~O=nk&;Fc5Rp(y8kLZi z&LO2sVCWJMR8mx0LXd``YbYh9duR}l973e~y+4dq*Z+t2%P#BgGIwq~dCqwR!6yh_ z>MBg{!LS-Bm3MJ*@m@xO_?eE8QDSIl=%JI-?K!kI=%O@3HK~M-SrNdlg|KWR`=w+* zFD#7*)Kmq>ljzGo(bQohX{O-^6b7`45juj? zI~11)Ku8qYPS5@Yd#l$Ml=P#upm?UfZ6@&jjW=0kqx@!5r zN~?$CUYi%*79K;hiGFdGcAj*zWVnQcp2{bJK|U6cjUojj!tV!tqX&RWKVntTDM% zF+O0zkj1DrG@0f6qm>6QuU<85F=UEp2_tW&}Vbx|4|<92|lD&V8#i(<-adiBn}Dv1|;1m>X*lCroI$ z@LyFnfCL0>T);1$=1WA=8`Pon4L*mDCcCn!Q#1|cd&u}!Z2I3S&kSkV{l=}k@&WDO;9%k~phDsse)j}XlGmQ zANf55C`!1aS}?2@(c(1j`pKJ+AvWxW!MaMRq>O6tn~GAU3>Bj$z%o)${!r-qL9N5M zljwX>+4^Dj-8mzPFW=Pd-iv^=^Es6pgwLp90jqG(C;M)Npa-NY<~0*=()QDUGwEXW zwWTG?mosxfB7u|@XZ{H+buBrMufBp_$k-wp!1F(y&3<8=W>gJWjG_yP6V1=LnSV?Pr4C|~wd2iYU2W*d5`3!(zz z&2eZ;kXyeVJOvvvK3`}=&}vHEZGAYIHH>?s>3bzWUkZF?b6h_N?j8aliASYNhwB(E zmLuC00G-wjn;q7=#eQbJZ||aW(#C=wS7!Y!5>KFJ@#z93O9pf?%bYgdL;|` zhUi#MB>f4jZ4)dQZLEyZC(f8p*My4{m!oAmli#XzOi{<>x9j4vxiDR#Yd;obcxLRS z>&>jne^O+)a_7*e#CjaGTFAyjEPev8e%wT1Oe{(4f_Lv8`E(^#i2#I`?e?@rdZzVj zKpq5sa9|`yNGzz|%kPc)Qw)1M=6usvtioB zxc5V^p$pJqVPpyO-?4WMCWyOsLw7BOI-jxd-DS?!9PZ^T15EBqWyv((%l;TZH2_Qa zY(E0ja876KLUQ({@v%SF2_YJf0lXKSy#V+#ku{X5wZVL0c-mwGi^rx)YBQERY}rdh zlMe*CWDeO{BTP);=Oi4&@|6(RUaq&+=R3=IWiQp=gZT2k$Gvo7v$Nuz$tNO+!Ltrk zeCYS!10vN>g{>Cve93Oyq4&e_tzpvk&)@OdIEvt8y*ny%ayoK2VFsD+-ql$P{n|17 z_V8$UF`EM-o>et8;E>WHzI&ou-f6TA<*;dHCe7sA=x#OphR$5S0?V1rx7GB`CJMz_ zKQv@$dJyzeDRJ}OEaxjZZsZq4(q?L(MJ{IbXGo#@bEU;o<-gl?-9+GkMM82tG?4Ig zodW<%SYN*{Cg%5BPyV|j5?(($2lx7cX8PPs)y_G0y<^Fhvo{*vV*31E1I05-+ljy` zT*(z(V3&b#_JjyChM33 zARrtso?Qe@|B4=vMv{*k{kAybl*h7~ z1B+B_;tn$0_7gMHR_gKN0>z1_w$tHP8|&kX>4DML*EhAN@|`eP9gC?sWu!Og1V%DXd!45GlIaSh?jgUWUha^6 zd``dWzZ~Ce=r65L9|H($@e(t@1-y(?|4Rhf(bC*F1^6nD9Y!b_AaHEdNn^V)HV@{$1Y22j^YTm&1a0$C5pQ z2y%*4c>O0}j!b|lqAma=7+$9z)3Yc;Bg~VR@4kA+=SnxoLt)D* zmJ&6eTfY=K3WT^KR4jfr8b0jb)af}H4Ci_dV9`d@>!UxFZ=NAksqr!XZSUkI(;!=UP{`dsdu`+?3~P5j(o}tg1(rNV|JFEC8F+YUKebwlQa5TGZT>i zfLMXvd9C^P?}a~|#2?7)p;q|Uuzdds1Xn7rZ{YvLkFXx&N9MvrW;5@ot|Ulp#<^~2 zp`#y+eaE(>vTEI@8&q2}tMbWi8Ux-SXyM9S^o=@WC}{Rl1Gr@WPXyAS8>(wg5F;rk z7hmcl_hY~RbwZ99Of6qUbu^+`aqr7TXv(4rKTg;keBDpSUk#=oaOqG1oTb{Je6Aev z@P~oV4$13=zNv6Tb$H_BZHT3&-5?gbC-V(qqd!5F^2zvkKJX!%m&EM!AVqz1g-v2OpSG_3lft5?aeIPypBz9)@SPUq$ zX^qP*=C!OA611w3qA#<$^pYwSR7bBA_3b4OLtJ=3h*m$gaMS)dp8o&RT)mjXtHMl1 zX=8(`$}I-tSmtcW(D1hYA-&=eim+q1a^sBG_HNGeYn~6G_wpTxXChZLnvszwYD7E4 z7W4~G<=aIO?maWd;WeY=^#>+HGhj5wPeaf#)1Ws0Wso7MSWNP!Qo$Qf=Xf}v#4>S; zqjZcgVdRqa^YC3Q{FoSC$_b$7rI~uQx2XKq%6`bIt`vHU!mRsx{C%-v&}gE5$=X6Y7l{+Cnn>ba1lU{Z=*cf;Of{( ziyS+O=C!#Rcx0#0S;C_)9WXE}6VX+7BV9w$3=>8-`RKNHm0+}NZ>TaLl*d@Ynz!F4 zodDH9hl*t}kkzVoa_-Or_U>;C*j&v7fY+*MhTu_#YA&7D3?ERXq~ws1`Uvj(R$qq^g(Q?^hx=fKWulLptS$^0_u8PChrexQWonZk`hg&Ei{mgQ}#;tMzpnzG4cOno?(-17t>&d{ebno|`{4vYCRUR1B;Ky3uGMMl0=_V~mJwaS~-*gB|{mo}w z&2iREh9)o6MfH*^;=tv!7S*f^kOlZj@w!}p-K9xpGT*X%i~2`9V5{eyAe8Rtf%s8$ z!mjEUI@qRFSE8sbm|WEiW)@xatxT2z`NKDaz#p9VD-QsvsFtBa%KaWNpDtIM=8HB^ z!5qd-UVe+Ky#I>g87~JEC$wOPr9@wOAkIf;UD4z;Y7*>1R-MOYrRIKIMWB65F6c=U znEl*F$9JaC7o_uD_@;KGLBy!wV@0CJ;3a^Swk2IcfBb(W3LwX$eN>E)369Np<<~$j zVZSb!Z)b&mR79Rs9LK{`+@P31wRZ}J#ltw0h7g`@>lZv92CgBXu7=2tlbo-|Msm3I zTue`BQLbv@pqcacvp(St8IeROnn>LiA{Z=qWo#Ry3M;YNc5R$h)W7k_zI$nX?Vlne zB_CHI0f2u+M($L)ieF3-$ob)J;^qMVwAKW%Tcu>+QH32U7}~9RI!?w_ZbC5a#PC2` zUQBDsvFK3|a`A>nJa9xaQs^R}lm?_-z=i$J3TTDoTTZ@+eR|A8Lqa?>>XpZ*2O-(z zUn(t-iK+8?-<LKkN-K+e!FP1F8oJv0}pF|Yy@-|5GZa6;RnYB8bg{SjxrW2ip-H&y%WUDa6Rr75C9Gqz*yF zElo)W3m4@`etVgyxXw*eOXsY8>i)Itk ztsh9+S!ynh{v+^{L311i07b*(T3WzEk+5rgygJ4#9%#we96oJT#$P?0bA83c8%VJP zrDGH%n`Ps}Ew6<=!L0Tai+L35r!gy6#ipl|Mh$nV`P^EQMp4PxQwfmh_GqM5kRA?_ ze(Qr{5X(>f0)53}<2{wBnuEJ7ROP{Uwz#3qV!Sg{op(vs*m(8{lAWD>y7TQl1#N9X zJs|)R2df5Zfh$p6KgSvzhuZ=m+$O^*=P$rU<}3JprbIW0QMO%WJJuBNbGoXiU4T$3 zwu&sxdP4a&yzlh+l0^5<;eGw{W1Pp*Pc@v0EhX-N{0v&#;qQXDu>tE27ebsJCl#U- z1uj4G4)oyH=@!F&1Ps7mHNMCHkOpO}v8ddy;RKBb)VTfDuoZ)Y{Fxz#)jXc4=Ebnb zQ>GtU0HmNbX{!;eq;ey|hn4b->F$Rpvc5-XAIA#Ulpg(sQsAlJTv1-Cw^LZwvAO_e zLKaTO>dm4w@Gie!p55994is%onrc zArPCUjNoogd^eHy#G*+9bKOX|ec7ZMow ztZ^!|t4rQ_?(NghA5K%BQ)~NwW*k6a4#+?pt}Db6mYH2;u)kI#a0XF!_=N83+bx#X zDEDJK3W>*oNQ1KLXQT7Gf}E}yNVd`~XQn^?OfX<|!{d1dPe)wP+4$BGr|{KGK}n?TC+$0CSc{Iy>1S5OzPtu>4}QFGOJMlr_DK*Q5a&EUkOyRZbE}zMurO zbY)>-;r-|WIrvb`&b4&J9EAiIHSrHhRre+kKYApsnMPX&>F4MS#c?J|ebchre0rRx zu?c}MgAb>^bmJH(-%8^R&dsAn&46mHn&Y8dwZ3592O%vI62!U7Y20_msFIM+o~`S zKM|?l6E(>0MbaenoD>w&lXDVWccDLAg8{XmQ(BBHIi)eOO?K+{UPeb>kuZfcT5W-r z%WZzj=Mx&7b6?MwXKj?Nht~k%w_Pox(VVO<5M>sx{L6ju;yEk5uhYPzTwnj;%5AQIQqhPoBu02j~XQw)|_RPFYJ`Qdiuqcw7s`WT|ZZ`@!!2*5y9RweB9JuvSk1r(BxX__v>Bep3^uPKD) zUgDoehR;ap$$MF-^Kr?Hy=x&}7avbx+88Qt@Ig#KNRX79=f2f3y%AUkeN!{|!n)z$ z0Teu$g2Y6jUH|njUcSr^?A6oxsT*|B>-Fo8-%E9$KDmrn$UbNqxInh_Vhn}LGwPxc zyy)0uGC5G_J0BHbxE8%H9Kk#X5SSoY&i@voT_O4bI^jeTua|_zfWZU8;A6d%z6Je1 zUfGXhcz+H9*^VkOrJJ0~0o<%kym$xJ@FujmdeOOaJXvio#uWjKrAVrF&|z)J$5ZCk zOl`}{KBAW7iic5zBoF=OVT#b0}=+T8hgA52+^)E=?ppI$iL~N!( zAhu_mUQe1RzwzF4^7FfgJ|BaSY0h{-kdpW!koc`ya{aa`Ku}XLq4)#y(OUJ9Fxb?-gyqzd^$6Qu9& zeD=ld%a%9b^TW|!O+s>p@Erv!c*17{6{BvMd1|qjT&|Aa2!Wkm zNKCY{xS{pP&lHF6`LK(KDk`Z=a`f)yIZ3o#@|C&k$?t;y@jASoYdhT>t{E@n=^^hI zhD)rBU98c%c!5SZp#{S`o8=Xc*FPsPFwcNLcnp3x(5CSPQrfc&Q38!w>@3fvNa9p_ zb+C~O8ra4C0vBZ)owCar9fhf34);N%6ag64Zye5cDoD)o(Jvj?xtmB1(w;(Hq3P~tmxzd zP7R$^^}%LZdirxfT)0dOMqpR3@m2Q(kgj$J!X>gW5} z?afvL6@0|3I^@ITC3T0R!}@A{Rqb&@R^^_W{Is~UV%A~T8Rw#%nqNf1C1den$&V#_ zlM~R73^XFllM*tYgj@$(f2P=F-tiM?aRh2wl_gJ`+1uN%0xg8kCzHdQwMQUi$fUT? zu6zvB7$BrohrLXz^RZyj; zdPBX6o*GU$P{8b`LTmt(C75kOZ_V<AE zag+YtG?%Mvb_)ZMJRAA1}A|~pg z4-PaX=&+hM8%?3i`<%`xKi+wv@2R5*Y}zZ_AlV^ql&hlWtqO1T+dRf)7bm-_%ym%G zz>Y`FU00Ds>k*;qGBlmsJ+7)o7qf-gO2eEU?xiHTv#Jxylrdw$=699LWIDrASAG8R z@}Qv^SfFK{2Lt4TOR5i+@=SYDMviJbDePuip9A_Mtt^0R2--fi^7(a#kH1zz0fzWV z1fg1Ngb*DTjGT+Rq4zA~_I0@z_KB`Bg3fU1csDDl0#0&rgLy^ssU3TAyNTiDv~2zX zTZ^Slz12>nqDGaYajJdiMo>Ab2ouKmDLOKy3ZUJyqVJ{a_rY5{#u@M^a@@^Ku<{_r7;+!U*@5BAPvxzVA{k!kqPQuHB z>Cuh31-=kAJ4u#@EtdE=U%5B-cP&@zGW?uArB~(|Y1&cnc7rjAa_bL$6VOdW=98^w z%O_Ru1&g%3z`tp{*?URQOiRmYrpxrJY;T7y?-=hKnM>cWrWl-3&J{p9vnOjA(P2P*ZJ_$y+T;8jlcEAZX)EGFUe4(ccVAze&qo zBW=p`0Gxjk61;oU@27qWmVU%xuZNAyFpiIpujwR%Nr+w+PACBnrjmKt<<|v#U(&9Z za5T%81x5P0J`&lFy}db9Uq29+?)(1MeS1R zMQ7WCflh=&2W8f2UTmbooU)oC6H|Pz4q21(r9R~|$nHYl^fSo(mnggw7?fwv6!Ui5 z1penTJ^sh5LKvOEVDNz+t}k=4$li5Bv&KSxO-D=k@o=!k-foW zYgy0KBvwHZ(86FJJ(s~ni3L*+nUMtOnL8WkX}z=Sq$fV8#5)sNJ^Ak^2_Id!e_8dq zFFuTMF2j0+dcL4C9cPO=BM{8n_V-^FnEE)R6^R0e*fjL1u8TdvY4v{&ugAFtU9R&(!lE z%d1^Q59n7(3!TB26zt(`h#PV1y%RDwQx<&HkY*390!q;nZ8vQ!U^gP|I7TkQ!&GGN zsG@V3u(V7$RIgX!!-7tcLJIgo@}@az-!FW;%)kL+a-4=qY`vi%`yzbkNr2o`Is^CrKi( z;Jcp;MO1N=TV(ZyYLw(Jb%l8&XotC{Pv&Ha!;?J}O*QhbRVODfjowqXB7jeu?$Zc= z;>Pf1uEzdj1;=Z8qvS>UO1v?!X?AA$vL(Jb7e@nR{EN_B^|{Tko9}iXR8d?Vq?1F5azeFfZ-iRvXtbe0k>H_OEUW_FSMv+J+~)cFmPg- z%gE*(Sytqj4dySm7b$O)qP&I)gSOl^RLc~6e@vLOAD0mJ9aSo%dqpeti!$Dd~lvlxgJ zgD9V=A!+0*cD@=(Hu6qv|m3=%gB1Ra5-rTShPl9(fol$YejHA00ZdfB$LXq&CLuq zD$=WKcD2$w?skTyFiVSeh8chHdH)!?f%BXmd&A?wKuf^^xfRBwN?cS4(C~?XQkHfo zLZn%2!6Q%5c=mg6`OC?iyX1AZbHl%dmaLd_Iv%~M%s1IS((5f{7canEM!|%L>c5bR zD>UR-RXx*|w;WX{K*opHD{43zdE0I*u=F+wysx)+ef{Oh<^5o9M3%T6T?P1i#>l$w zyayo$=5DI+vQU#{i$4)lH%H;lE$gw08U&!gYj5U9)O#Lnc{+AZex}waG?9nKWiuR! zjhyg0!SV&d-%g*+srcF)?*6AAecyx+Y?=Giq1&gv(FF5NM%T&aK~27mf969n`J(+q z`d;MOZ0^tRBxE9ow!H}^h){EqfPYFm$9uS&EeEFGCSA`x7%vOwk4+ z8*y5lMGSGCW(HXrhv?f%Jh!6tmEJhcw2nMOkjl%;+djDaHsH>ddL(`BZ)S8c41^0a zKVr;_@bgHWJ9s0Fz&(>zBmkW0ITuE2l7k7V7N!I+bnQRJy&kfya1I$5ct*+Df+ZmK zbFmu!a@+bVFtF~Wr9LburD<>*W@6<@_@>Gj`Wu@K@#@}Y$mcQ021RIdutN4dKmd1t zhH=h}(t!wEX*um+eCTyotlZFvpG|dPMl1OcUnG4Gno8aheK(3FSq! zU%II;Vlh_1tlLN^8)C6?h2!NPkDVXlo*ycbcm#|QInu``GyYEp*(@Esjo|Q*8u;6dP zXBk4la}mgriSN&e3;EYRbv?}C8Y+`6$(U`6A>kf*bPt+)syWN|{2wm_2sV&2$(+CY zhB<4Dj+}8~sd#My^M31QU#;>a#KHrkkVmCuR}Ky43kN*s2FSFsPa-&w(w+AD%Xd5N zO*WEjZgacqSp(B}Y!ewggfuS9-3QrRXs`h|n`=Jc8l6i+Uj5w!xvPCDv)7z@PGoK{ z>L+uvP+!#wSwky;9OjFUxMr!M$I0oip1%(Quq6^yubHdL zR(0}W_mGzF%M=!A+V3G`uF+;!7tD7`@a%=T=*Q`2%D?R`FCm+L<~^#@zZ!ROvm9zc6y>pHjF~y5PMH{Sj#W3;7Wx8_x`3y=!XTP(w(UTQTeEK7~K6xt-WR&nI z31(a4@kv-CiIkNGPnjAzKYleb-*gTcIK$h-IzH_R19t`endScD_qrq(EY5F^|I;D& zyU9VacIIX?QXR>Z+8bL@X5a8eJTH5I9&$b}zY`Oxm%Xxl-%fOqhyO;jc|)ByyX~F+ zGWMkW{3*^Vjd-C;TZ-SUThF9u*Dx&-6Xvs8V)aO%K-c_HrL~+C$d-Vk+VG0_MEtuY zdH)YSaQGX;o3j`>u>ka0UR~P@A>VSgF(HUv}WEO;= z;oX+=B;E*)j7&jpWF|W;vH@41)AsPWL$`~LavNU~i$jB*93mvFh3eTo<)lQ1fq_WZ zp~BMbH}eb&`kj<=KhxZAUV%}()Bf*SuEB*fg!M~+%5fX1p|?B%>;(6jGyGS;N#_%= z=`-<33H%wmQsNMTg<0xdPxeNjI|g2BYG$rYsEDibKPtVBs~Y03C>ML_6}?Pg04Q?E z+cnx^_->Rb(1c;+*vi2o!T%Y}bbNn9Jm@>4@HM@&JvJ3aSx#WMYl2HqG9FS+`@5Mq zsh(ip+4mx*Di@^lM)CO!h&Po*H$q0?rMx8YY1Az5)?ymvEq=&g-{>d3A4}|wxR*&C zR0|4Au1s?o6nu0U#~dQcTh zHhYk;)(kho_tX1{=IySYSBX}0&(zqA;act!UGoH3FoqeHpC*s1M_(5Q)PgqmHsF|N z*6)A>i`tFR>iHj`_eUK5J-2)OWww?UBTCe@*SADu`a)YDqAaM1j|vugxejC~H2W{Y zWytuhc1P<;HbQQ!P)AKMbE*L8S;%Iux3RdkW^UitD##n8x8(|__lg5%++}9g0(cd; zI@m$OTMU1OQVe$z4t)>u?8FUjcp6#uaszrXurU2*`}NGP!MM)3PkS6%~&jHpEfh$4kAf^i-Ke_r0*r1vS=kfD*uE7P;H z7#q1zo3SZoC_3wd$aXa|0J~SdKIe^Kv6&{1Z~c}VPDe7rs^`yj-0#r2#;~W2y*0tE z3AU{`&IR0n_h*6mVYu4;yA=Sny3-isC5u7zE&UB%mBadIR9hKHPsKz<_qaWd)}|(- zNW==qKVgc4ct%n$hy}RFf5Q2+&N%;D_RHi3bu44)4piaxX}VNH3xS#}?&aA!Yteu> za3fr<&$&TcDG#<%l&$S|s|^Z5mvi;jmfbpwDs7||$19n+6U}uFtMDk(pV5&w*5&)j zM+u5!BkALAU8SE!O(S<}&R(A5y=jf@h0C2{Ul#0l=Rja?XlpG;Y=~D;;k|Onm)%RC zOv2Zr74u68gpCb+_mgXtE>Pv~*TYQ6e(&cunzug4y!sh=GS77Q1IP}=hfk5ZNhw~A z<1;mGB`x7I%Zy1Xa}nXF|ET7K%7QFH8U91cbC+8_?H*JHK)I7v`g#M%qF9o@nEiZNFvi}Y z4OY+Y1w?t3`MOGf+io|hcXrh}*9guD44=CoZMmjh@br(E0aKO+lya8cKvB;Sh-=B) zKb_p_7<=TMNx|p#>5(!8f{HRVj`uRXTpS@u9Ov}F9alOmP)-P4iup=gj%2@^@vw18 zDdjyQ6j~~EhKZ&rmllR?UO_isS2(a&-(>1|$m zEn1qol2w~)*5n#Wuhs!~SxAdg9Jrk~gNg=qX{$CLqFxP|rX!)%;%0kr;LfJpT+B|$ zv|W6i7H98Kl1hDJlPj?Q4jjI=U zo9SIz22ZI1RpZNU7T~N1D816$H-)#h$Y|a+Z|IG@=?ph=$Loax1&w&7Fx%6x=|>dc ziqTq19^ubP=PHkSQZ>f@)%QGpFMJ`=NfM^z-u$E#+}xc;9>F=Y5mf@T!?_?1Tc&_p zS-ZL-Ybdqq??tZLTCQeVZXbB^9xXGfYw5q=2t_p>S5~F)`ayMa&lJ11N;{hPh(?Gk z3j{}u7xs1!kk#!_k!!X$N2Bsf4UP@y?TZ*18gg)PHGtG=avzmLRnzvj<*}Jyf?$a8 zv!r1Y`>=k!Vn}1;LPHwDqPt`qNTUY1u2hz^t)8ZKV`BmGgq zWm^w$9*=MVCFaseeC|eqMd)&OXj|aIc~>*us1= zsh<~$^*2#;^--rtFGd%lyt8lZh}>TTbuA9uxN zC}$S6FH-Hk9O?6ifjIVm=ghnqQ*lEWRo`^!L-tdx!cE3vyqGR}(<0!lGUbs>@pRqz z;v!Gy>i9z`EEs_t>BU#wyn1DvHJ~_OVIqQErofm5r9HJlrZ7Yh1KpftKl6y<>S)Hi zH3sDoYjL%J40Y+a&cTA}YIsQnU7W}6yzFQjkcXZmAKw=9h+)&a>$Ye>9pG3oMTvos zoj3hkLJMNgNM1b=>DWgqIJLXGv3c9+Me;g2)XGmC>Rx0d(dKH96%GwL=3bApra)!P z)Ri;NNe*`_i>I>Q$m$4dCxnM>O1)LMtR|_aiXGi;f~eJu3pv>GtX!8cJ6lLgGhlW) zg|Ck>!DTIGoI*U9V!nNTXGafG#Ay`TuNA6;cC&ObzXjPB^6EFM|Kt9Fh&)Ax40JzY zV_o+PMYBc=X{kKTWU{o&Z;Gj`1S6YI?(Al}nom{DHa3F#>CVdNLFnD%KyAL@0-Q)N zXzqHr%w^TfpDtPu?WN7D0S}iaml|Rqm@u}_Asi`6JoFI^eTfy`I6F$$lBhD%|MmCzq&gh*fqiZIl z9U2ToOT;xovUS-^yWia(t|!r<0PQoqnG!($2;S%HxmN#KM^!aK$9VaTU3*iT*NBhA z3-)S7%KyIob60;B38@$@@zJ)l$T&9-WB%QN%{20tF~yH`_Gpn`8Q`nH^HOOPUuKCO zyA)zQNF8#Re?8hZ&b=sv2#6>HonmQ1m3VfT)h_|y*rRPX$?6AgzF5S!rG(Q z8LTQ+8=U&f$HFA$3c<^FYLe2v$k@wU_0DlM7)d9axrAJ;z0p8?UB7bQk&?tOygMUr*WQz799_GjBE#YQL9b(7c2}vd2|SD)j^8D zMxBZ_BzOYr4?Nae0Y@a1T0(K;u8c9(=F%`p{B|9~B#^<RNE-P%L2 z8cKGf)@Mt@C9|oXbCquMFp9FR3i-0xj9(4EA($3yW4c%bgLs4*Rld4*f6oIj%5|&${;ZTZeX`-g)h( z%wSb_{u7)7d-+i z=iorCZ4sw=_YC80#Z%$JTZak)x#wKgIojIVRw3B{y1h%c9^QAGm(uZv6i4LMoh?q( zaO~2_Y&h&pM>K*)ahDU2KwRsoJvbOYa`#wzs}Q?1UM(~RG)%Yd44Ue^p)*Boo5Kq{>u3WOrl9_R74KQ3IqL{l zSm@u?pggnWhZpxI=Q&D6n*&IJs`1M$-SX~wfhD@O;_c{WoGT>W35HrOAo zQP`8{xYL%7qu8lDUMeKyuBR7-NaYF%be?h)x9kH@VVB*kBr`ib4LkQNm^`()3O~j0 z7GBWITg9ob0W*!>J*OKAeNC16K;iZwFP)x7ITMy<^Lv8ZT!AK^1T=H_yTX|@CADY$ znGhdyzlz&odCz?Sw z-OBAVEw!ng_U>08hF6k?Nb)DQuDFRy)!K%)A9G8;?%8&$v7qAi!mIzD`TNHNALTS7 zamqYa@_QzixV5NN58F3xnl*dhzV%RYk#A&^`qkF@L`VG4rI-@#`fkwkKs@Dc+_O8O z3aU>Z`>1^oHLsrt*h|cY>?h@jmh~?;PYP~y37k;Gcj(BT&f<8PtR#_?dP^ej#f9(ED( zOF%DP4AoxH%2V!f5-aDLCuixbX#<2lrHcLoiTc%7IIm5n#8~;$iu=z#lu0sCDBXZ0 z$K+KV;U4`jxj8;w?|JQGN>=f3vk^$^+%)S=yF>qCPe`i~OX}>Wj7-GB>)O^Rc3K(r zZGgEcG>u1n+Vv6I_TjQ`^pvO$F=Z}A(H-Dc1FdXJnBm`g+zAAcf~HE+@o?GYvL50* zhpA1c{dCUNePqzhd>6T16{?T@jx&uDv_yGF50szEfPOU8>6Ch}e5PVozf(CfJlAwJ zaR8JJ9JbxE@lqbNuKQ$*o?5Qb&3w92T=|bVn1OQB9F;}2>9M|E7zcFJUrh#CyBTnB z5;1H;QOFcfeh66cJlaFCn$g9j)R1S<1u1FfKa_}Rl>0CX#$ybnAd*;FfgoFecQ)wI zIlv_k2gVOZ$LrTRgvL`~*d{&Kc>iL-{_!%vU{48rUMNZtdtz~CV_1K_$6SADSLSAg znPaaqB`<2bv@*3MFp$v7KDm$tp8Pm7S6fYWmq~TF20*0NQBcPTy9`(0$OM3p8>QQW zz90pKnwesg&f$Ve5Z?IOYsTcG#(e#He<$G0hHA$`V&lY`0;z=r5!Hvg17JEq-pT60 zazG`X0>To{sSoDFMH`jQ33@d&G;pCc>AW~F%s;+p@0jBAJlwxh%4v=w>?=a3Rl3-} z)6KMZ?#t8;I~uis8UN+b{_)cPhC30x(Ms)RHHvc5uOCt?(NjE^IY0!^UH8h^n0z%t ztS)Je+#xalR+kT}xsS-SLqAEj%?Hi!+y#?I_U?xs!4~6}o|1Em^$jpy{@DELTr;(I zHdWOoAz^DQA9tMdOca-Zz~+E~WtH*jG?=*%2#M4Lo99$=0S#o8-f1w*gqqcLutk!J z$t+7>7zfB#5DJV(8ONzB^~;M!3x3(QP1I$Q;VuP=FyTGjM}mS!I~^r%CHMXlNP*hj zES7AHpdje@qV3G#%sS<)RO4lI!5k~sl#Dl{yb&AbD)smFr~@X7o=&~pvnych3l1iU z6%;UPi+mX)W}{@(zaT4)-aOYf+A)vsdc`Qk%c?m60F4a44E1n4Zai8&g!Gl&fIMb! zD6P2BBVPlUmeYi<4HQhS94@bjxw{`gC*}&+1_2AuLAAw%9O49^b*kLzSR1k#b>t~JLM;jn&g-;uJEH(;N@a($tpF3K(yg6Od9eWiuK2kv-6q%wTm z^_>eo%v)g&9#O-alo`)wqPuKn0%q!xapcVHMk2hJXa<@;jY6XnZeSM!{c2Jm_Q~y!{G8Xl`$(ey{ z6#&tQkrHbid058nN1ud8;5{;y!O5I63c1ODt3kY|6xJYM3d@v>x?sBSo)F&0z1-pH zz&*$7sL(hu@GCSYETo}51(`;6i>p1FTYv=K<{up$-Ac7SzEUc5eG3uPtQB%^SmM@K zJ=Haoli6UflT}-T(^Mwkmg8Wnm4Y~fXhN*c(^s~v2b+lzIv&pNx|qQv2=ldb@nHVJ z6tlIn)NAZBLmZ0@5roeOQ>diT&W!HjG(l#&)2uz!B)4rOmbzCYG=!L2UV@6d#XxSN zrWxyiBrUNjLTfPPppAQ+;@;C4zf(+q#6-s)^EC`a2|Kw=+=@B?s0#J8r9mHXMN-pP zN)$0MhXh}{F`8&&b-yR1ZZqYwyZ~JK%~~RY~RE495mkbSf{dVA{4TF}_SJ55hZdT@$u%Brf3rv<-wC3dMZ&h3J^Riu!sjQODK zUmnS2sWAd7LpE)k{dGXp%AY)Ko&-ZiM*QhHW9I^ZTVPzUs(XenYEP;B8oOB1w-D$; z9tTsFNXhC~InHh7L%ln9|Mm)3pgIy%f@Hs=+Vi$~z%%W-pAz*lN=8MAqjBJJ8EJg< zR{ffsrT=WW5O9WgEiy#6>51QjdLyFb)uS48igXmxHue?qX6|Nh4#VMBv9#ovU3=%U zgI}C_pJ#RGa_%uh$2R`hE~;dvzgMH$!#yUhCoQa`a>>$WcfKsL;+!FHk*iQ97>vP%HPa^mw3`;O) zK=&J(@4#ojoSqs-cF*q|qgf0}wj50TyK-;{PpJS=LeTip!ML@lMn(rB_#9K;;Wi_| z3eYty1y~ggM2rJeB5By)SsVTzXF7h-gc6?2CuI&#PREINc2GViPu>!nAm$Bel)ZBuGc6LX}A6?|*z*KnrLL`r&^gCw#JBNO8I@QeKf|c|r z#5iGXrtL|-3}iJ$^T6B2yZQL`Pi^0-td3tPcwlmiPLDXy9f1d6ldg0E)o%f|o1r^p zUB?S33OUZt^gOFX(aG32xq*Qd;MP-mcrKUC`UjWb77GG_#1fvk6d?*uGbKnH?lIHm1*6`at*64d`deYig*fWv+QsF62w*~jicv(<`tSg{_O7OIhX zrTxtnb#jAP!p021D&pL$F2`w=v2a;sgy@BpC#7V-JMFjci{2RL&v>K(dQ#r2YbpRgqyBT#s}}pS2mdV|UZ8oA z_x_DK0j*_vZ*{Ja!;(qhbVpWz%z4JSA%ecJwmeH6ODoIlyspxml<5UpGrXe32m^+o zA%#=nfOxK3*|Fa<0<_A2-OGEu*Uq;R2sH^1<;%zu+l5r=uZ5#;JsK&sS*^igEu^rr zum~h#QmG5jUQY~120MUipb~uhr4&n3f0#yXW||E*Q!oYN0V@x3Ft+C}InsqWLobOb zzq)reuK;{m{Akq$bk_#9YvKm-4<2M$4N+{A2L~rgqRltN)C=C;1fa!X@(9}?Z3w%7 zVQZ~(C8$~cREaQIn0-BwY8^E6i|azksDtMIw9cVn3{pXdm{pqscr zuwVfD6{-`@v}rMSgg1BA1)_3fj6i5cm z6lX!7@h{dl&tQk#?L*d40}I%xB?}pMW@Z9=L0=Ma&#z0NvG#LJ?OP zedPhkvr|vZ7$MYs7+&9+X7%Lh7JwAvgMr9xF@-spI28g42CHC*VpTFCcMUX5-WrnC z+>Mp{+ozvdSJLf&I|1c|a^PYToX89?M*PDmimeRB};raQfuF>M!U@o5qk zCg(C)_N2%JfT!0048R)NFw8be(A~ui%(PnS-Qf5OViaT$s+jb0v83hX;f~XjO%>ugj{jHU`sQHs4ntwRq^!ZIvG@O zwf5W_UIoM0_=72U6nbGutDzw#Q)UDtIw~u$(YqO)#_*p@2d<%T2^MjMiP3(3;0F|- zg!C=B+UbuI<~FE|yRj}`Kk21#N7fO!Wa9r`K1oaz+tWoOAK9<4WdnIsD1Kpis%RW* zuL5cL*c|T6Ml7gM{{0f%c!8?sy=5U5vd7qW2P!KjKiqYU)y?#*7IGZ-dJokS9Um30 zJ@r7Bcw~+?+!8pV zPiX(qyS-+lbTmi0OoU>JwCURD-^yi}ry_*Ol*3R%&H3*RA)lGQsG#F6k}$cwrjQiHP}EDEXb(xU5Dl&9fgMsHov#8C5<3mA}T5ZN;gP1(p?51U5k`%79q80IOB1@ z`xV{peSP1L^W&V$UoLg6C&nCe%n|n(_icH3B6j^oO3JPXpTFnE_Q4q7So#C=by8Hf z3vEmidTiQnCZgU*?+Ph(@6uEf{lW_UX_vOuPfJm)`7mnq&l((VTXy>+ZD&W#e0Y=V z9{QuG>OcOh^z>^{Q}s<--k}Bf$u3pFud>zOj0T|E z>A!>^4NGWKft}@$uAIA4uTuNvN3#L(*6tZX8K`Nil)l{+J;HD>9(sS>R*Qg7Y7NXo z@YP%04p{lx{6>(g^Vnar0RDbI;e!Zf z@fZ+~MqFhbLqp>9_yZ_V1kPjGU-;GD|8|At$HG>uceE=shk>Be7NJjiF+M&nxSF~& z-rxcJDQ6JG?ZjLI!OyU@@20-!EMP5OD)E_Qlwx{rhW;u<`_IG52tr~tph>Ce`;Y%7 zX)=JuuzhSN{-quH_HAgkz*ZZ}MzR0PSAGrO+T)TP(lD@}C{@`+)wXD*P79Z?XLLa{s?_{*&uEb0ZEftGj-1C#+Vt=_3y1 zG^rgJ-3u@6Tb@RxXg5U&^zm|qUx6wBQW3~+nxB4A4uw>0AZE|J`{5aho0#tfksbuP zT>zN!;`T8pJ_BZe_|2VSRR%^zZOC@Y%FDN*a}mA>}Yf zU^MbX&=Xx_VLX<@$gmchQnWfWxwhQxJ!x1vDZ=_vKf*5lgEbYEPO8AD%-hgsXh+XjlB2>B)7LLM) zwzejOye*pqxS(9W!*Z00*RPx1itY%b)dJL3=klzez(`?=bnBi8# zf;-!0gy=K+{|fNO4};es+dy=fr{4_6d258GTCIOEATc9j9;I&!I1pl_X*NS6yVPU5 z(`8#3nI|dY-bSu%P?|P!Lp)Aa9f3}HJbu^CDW%QrytNixCa#FUp{J*(#i|T z`azPNMyuvotjleYJ2{giJIl9rujthW$tt)wej?!d(mbrpo#(i|2o0(i9aqGD{Vv${ z|7j8JqQEk!tz>l^!bcM7f=cOk$l@M{^cQ&h+Id1%CS`I_S=~xeRz2w@WZy+Yvc9hQ zyubRZ5*w0>31f1d_O-g(Nh@RO3J|``FM*TU+X)<1pkS@zJFv9FtrDyz@|`V#q@ZfZ zz@;1mpFjTi=gaLsmnR)if0o08x5T?29Jp;Tn?lF*ZNP(JoJnxT>%pZL$x zWJaVZR(#Y|rGI{6i#L@%es6}Mndo6hv+v%B>7YqUiCyJU7+ zGD=Oaj7>UI9N~kq@6&bGH4oUt>vol09 z3{>>y#U_;hw7GF$;qgug9uC8HJxL9$o-9j@G=h!=)lM+lka#5%(9h9DFuJ)4Slv$n zNxkQlzPt7jIq@9;rq*7a>q9WYUGMH*F5g#xjYS+9zBO=wAszQWJB|sE$@^;e8z_R7I zh0XHaO=frT6%_^HB$an!-t}H}D$+8NDR(B+Jo+7& zV71+}yVbN%*yNPn=ClR48v`O^0xI5|$uD56NKsY_vdP;|4ioY6jy>kkuP7*cnnzE<0H|pM7>5H|N5Ck!EqF#;XtEU~G-oq0{nAn_*hH zewzIoh+xHPFNi=c#?zkMm^ghkj@5}?nr}AeeP=*ZHH}M&Zh84!tQHH7E#6;h`r}P> zShYSXVdCx2H*0^x0wj44eQ?h=h>YjvFN@7hFOq|yv4zp&DmZEele4;k>Lk^g=RD2D zoLFJs72O77;Q6ixf7 zIFp`+FrMs&)n#3Pfg(Gel7=@f4{n&0k=@A4=#)cm@bbN@OzU&zn7o7ULzj>IN|Tm3 zVDD)VK8-hYxAkshKFe4=k>(XJ3md!KO%W7(v?nVBktmyVWu>iB!a$wQ$rYx@b;` zNi8pRvYqM16Jo3M$3IH=uY+`>6pYV6`5%PBQMXTf))ql$+{>c~XNZVZ9K+u#+niQ? z+diUR0*f{0L=u`Jh06cg!a>*6EsN~5Prll(#2n%oYFDuxDS1BG;}9#`112w~aibFnx}&{Jzf`%c`CY^^OU zumb+kWZLl;tvlab%xZiEO3h60$H)KZ0~>``EO>aC&()@EDIAK}jxnQ7^Zlj8iGrS{ z;^%z5SP@#~5yabDK&{Uc5D;93_A#I8BDvtlZ%=ONr~`0?0-uzn-rYEEL;4(eQE6)V zRe)ZI_N!Z-Y8z>}LE|ZZOCUGV194$pG|&eixbSY1=3|bR4kwSf;q3(j9>3v#TkgU6 zXV90U>jH}S0BW_lzlO|$GP>Q;8mskZCKLb=ar6;JDyAJq=Q?`pL(68aG+TqXv~D4o z2*cf-ZCXNv$deYGohteE)~{;aB|3?I>7jX;S~$ao!K-5>GmNurT^Om05O4q>CSI+y z0&KLiX%n>Vyl?z|n0@*EMULyiTC?NN#%A#;}r2)2cGCw4~@uJFs&aDXNJ z)&d8OGpob>4)dNKEJcZmX4bztT0j!Q)mS1+L{MPXzI5a8vW@E%USn`#*Xa*&ne_NY zrau6sysTU6x7BkSLdGe~xw*b#t^pfWW%N^0jptN+)}uFG#;t6$2BgRQ(`J&x3RlsV zc&8{X{cC){pLM+qyoVRM*og0Dl${{`rzj4r?PNMwu40SEfBT9e*xw9`;#0pqWst`d zwgG>>&)<^zU%rxoOfc~8{q&P<*!znjKrc_Hhb{ffSN@CM`N5w5aBMWI!}HpVh28j< zucSb{r7>pxPdWv*!F3Lvw}^3q{~y#2`HBV%Ay{F^_xJ~w6nV9W%78Xf=b8AIuPi6S z5-1m{|7x`2f20#t49`1P%j5m);|Gyflrk(qY3=O2A7rs7j)tH1!Sh-vH`@KnS4tUR z2^39#tIW@mZvKByWp*P=`CQAji*|*aPe`1#&gy#XBO(!_$Y7EbN32>TlWa(Dn4xMo zkQCX~87XI&)*+{=kQ=bG5tEqf6Lsaxp|cm|XnnSxZbq)IHE;nm<_jOYjb7Pm%XRB@8ui#{%`(x$GVdJLc@PGR{r#V2W*cLpMK}B z>^1w-MC+ga%0PVkU2AD3J4^$Qs>1=w;dAVEi#kg~?lgLWzB}8Sp^#$>c}k*saVgLE zznA`glXj^nuE=a8pwR=Yx!%U|lwQ>Q<9(5F}bRkTH*uP0=*U|Db zZD0_+eEbz%%FFkj@C-gWP`|S|ZwAfBoe$MX&OQ>(#d;`mJB`cX+CEnn5~TmN7te62 z-5>YxPXo{4uajsjMIn7aK`GZ?2sOTUlxOsHz*T1TP)J8L5*nM7t&fjGP&9)M^jNwP z6@z`q*s6VOPzxT&7iYXNycaV>Kz=20mcQobS?b88tz8)+v+Kin+Kh8rM8|=Jx~|?D zg6U9znK#yla=CLA)sG`(oe3%30K)OazEUvx zX#WAZa9MC<8UV8zY^bh;5XJta78@bLWKAl_K*tci`4V=~LLd}g8!_oFMJFnD0xp&u z|A~PyiixG}3uXmF&&Gjoj}uz#%OI^I-Lwd>>ri~wlIAd&YAmp1>$VT8 z^jyU$wbGGBjlCo#KU`h>WC5hR8X&4X>u?)V7Y&en8a!f%!{|lEUPbA&+)}C2g!bg> z?^=og-rZ&gEbKgcNIm~@B0sVSe&@|Wo*ylck=qT>xJ4Oi*3<uoGn&kEY+@r7RjW{pcFHd$21Ca33_+=o)Oa=?0_-!lwO+Wrrmy0SD z93&kHf-6aq4pabqH+I5OyL>|Lv`k^eWB>U#RA)hc94ESrvm5GXy0aS~DyY9fWCJ7dO~(cfkjv=#lpfGzTd|XHHWjgf(}G~MJW!!m<(et-s!|U zz1heAP7ozS=b9Cny#-k2mk*fpy6$D#HRu&!siiC3X;{=E!#Z@5Rap7Sr%)f@mTFhf znu-hLguxB3?S@~_y$VTZEuP8R2~zc7Ei14ChO>IeD;Yw^ohh}`vg{IEvZtFDU9!~3 zu4bJ1l#I*tj+8$)^Uu2KQt_0-##Dm#QZVey@2BT~J{=>i zSql4f^d`MBPIA`y&J8fB4L&$%Fp`JCB8UsqB3whKk3n}0e!XEz8i^#PL>h3s69Q&` ze~JCrs`xr~jl<+LbsTd_*`+(11G`?#7l0W3?m;Yn#QcVZ!DbNFJaLNKJgQsEjo@Aa zKLEz_k`Jv(k-6j|;WQz$Tvz?KD2l~rx1hki-1f&=G4#-*=j)AGz&sGcz zTE_f!)c01^zWaI9$m{?8ndIP^NCX^Iz&4*-hon}UWCTACTSoUsll(psHGb=vu5WUI zlN>uQc|{L+bepTO74Rov7>h^v3edd&!^^&AdOgM?0am$Ql)~g>-3(Y|?*_|;pi6fj z2SbmaP79=A8=%db{F@Rfu)^N7x5U8SbhRQ{UuP} zNg~{ake8#H(o)hG>5Tl6n$s$txejON->mD#V`0T66&O&$$8J7YacGJ$qvW0!_we%? zeurUr0X8J1q=R{k&vO4L|JRBd{tL*_fvl$&pMwbaJw)77vA$cUQPU|IrXXd~CFtjG z0przaM*=OnVciA^;q^#w1kx|3qW^WUavW($uGaoFPIR$~4)7%XBOoI$^8x)^}FRZ*e`jOssdhnGb7wrqAAOg5$9rBrNdy7ZLYn z*}(O(cGW2d^HwM^3;Jg>j*+sG9-V%xmPSj!zyPBWV#%=T>`URGFPC<#B>K9DHLil9|MrX z>?Ls!o38k+XGn7B)4Lq=$_Ng`pEz{-^~Fo~o1?n2RI>Hfaj5wQiPc&{-|m+{aj{=z z4m^sGcKs-oDAvOY>tVX!HFCmH9=_hwSvH>fJQc5YLqeRl15zNtQA#I{Eghf4*>Ma|)o-qv!xgIhNAj3NYbG&M9ajLHX%f8p@8-;A1`U>d3NWY6!nHyR&o zhn{x>YP-?nOGp}BbR!H8tB>`VQI&}DlRg(&o&#X_ZVbZw6E0;sf0*V(}JOc?AleYu48gLHF{}6 zB}UAdvue)1KxcvwUXtEyBR=DrHr)65_@|x8dD>0de zSGd3{6pX5%N6!DBR&W+yVLMDjI|g0oRdib6k;I7Mlu9EWW9p!Hf*>HnF}Ge)g4qq+ z=bCoM_*cUDZY594#lo^mDm4ul*#*AOBiIE3Cyx zN(Vj^Za)pjtWIa{-nNpkEfT5Qd-hfRyCkyhZ$~p zOBcXr`0trEeGx3Lm$JX*Pe%2=+TiPJM=ulde|=u=JO_!W-E-zs!SLdg5thTaKlxM5%vhEa zjT|8ShxwE&)qCing*)LSoZgVAIs9K;{__Er<11D=HO(MiZt3U8#(3^ok4Q*iqV!of z=n#i?&(CC@3CFZt0;ldinTjtE#(w{=+u#N;9lP*zvZ{vRYiIcm3gKB*;nH^6vl_>0 zU^y7iol5yB9|ibHK#v6bE2(}yZ2s~1jUbpt%M*B+=r5CmEayuk!}{u6;rH8r`?S2@ z6-ngAc=6WS#rx9T;tbTVPqHLq-}_KeP?1vm#O;H>_u)a_G{@Ek-3aRmUDmE;FV-^h zHDO|XrP=1oHq(E;fD>F`mcr-2&lfnpHW}kx4?17}nJ#jMe*|(lAHtwLwNbZzFB7OM z2ArH+vztfv2oHIBr|ZJ5%yH1q`WYEFWF4^rk-d?I-54?8D)zdU!bl#kXGz;=uPh-N z6+GX3|H@W2>b9B~b2dyDGVubH6O4VIwCBe%;h=2Osog|BJ$oQNHgn!Xas>9&014t@ z{uqE-RzWxK=bs{z*P7sPIP>N0eIZyFARN5vkv5nELkOALGBnGzvL+Pwzq>{#kfR?0 zsmJd-UFH=$z&*;(ID1zUVmd-RDCqL;*s7(=@4^@t4OAo$0s1Am}3dgr(?_Go` zkj1Ldh9LRBX6W0e^Y1>MkO(4@=B|7J!Qv51Ot(JYPfDJJxVht>diKf>d2DVROuvH} zyZ!S0i7c3l@}UINWAw9*EyHbF)iN*qxX$~qUSQ#~6h!d=5F)$|MSNGzgJt6258Amu zVV^5A9sUnn2C;HAEML>ioa!6_Yhi6_3r($|36ah{KWm3HXa_G{$4~ZE__Tk8ihj5* zlrBisRzDC5T>-)A=#&|i*5FC*QI1E#*Wve1+z$UNtRAAqjA3z>b*e zpSNW$XjX@4>dzT4;R~({Bh{h%4;vbMMB^-`gwI7a-^p2DiU!_@AGIom~+t!~`kI#v`y1Z$m06 zO#1;3m)71NA6kQ7ubtj20>s?PJg$(FVAk~#+OgGd1mx+J^7RI@dMtdV!8?sV=(RZ* zc8lUmq(AKl*E}2ypc#>*v%a9%Ro02xAD0;RFbNIz&q5YIar^$;r}dCA8LxM$^`jpL ztezE%_7Bfn09&C@J;>w7L*bF|87!nmv)&q5Hd>E<>rQRasQ*9u-CZnXc)?w}H+y4D zCl$~F&9aR!z8|$$m=+5rEpxy?1t;3gdvtrYMLY6~@C=rTA85-Vn|2usrUY<7!o|Fr zk`Q-K)?JtjmVLS^rRwrer_aOi3dn)#VFp}w)A>w~p876YS7`_pQDVw&+CLR~5_2nH z>bpL)X{(|wM@RQ;^o#QFT){=7drsDGk`unEl@u4irXTh|^uE`o2_*8q;O`ZxFbyf( zs#<7YnBY$$$i)JU^5};95!ob11r#x#|G`Awf&2Z+jRF(%@nnapQCe3?D*MR-1Zq}) z{08M+63C6$jDvjMkqJ4Ms5q~Wom&dtYp7hkaqc-$K-#kK_~0vY4hGvg#vh*u&Mj#z zq!>D09~=(@`}6>(xuf~!kf-Q}63CT|!){;BRtHp{=VP_=dsJ>Cx&mrA_RFVAp+q>@4kJ1kr*@*F+*>mMR^8fQqq9S0I zP1~vSF#hYetl?XR|C?*kw`UChH49*`%)YA(@+s=(%A+RgjfZOjKr{yCJ^LXn(;aqF6A-#{Y1l{gIIHY%)A?mGVf<}D* z*-)xKV{Msd`*i2o6h1>ndYY+1;A(h=>E#Jgt&~$hm-lUzqL^Kwe?J(8IU^ zJzxO25GacZ)Epefzk$EtA+2$ecgm%^i~AU>Fp%eXfwc=&kOo$lbqVMpoDTvNTLqFu z=7e57{o%Lq>X8nQum3^v1d8xaFWLT?z7aM%JX1VhHV%NOneI|mnQa(9HyO;b*r4q# zKyuiSets-av#dt_H7QmuQQ{(>DqqPFx}5iTjMqCLO%RA+O|`6{7rMp~FgeI;-@g%6 zyNmWK?JbluhC75anRn|31_MTo&*C}X%sV#~rTuvI_(2@|)i33|o6 ziZrgB%S)>}ACzCqkaFcH_u!){XlLEMQlh0*x7DkD*NuSlIn}@b2%^3_!ewVY)3M~V z*tla(24wR}H zOyxLq!480IoH%QqhD6Q1TYLlSUIRWnVZME>S4v*39$L2AU)x{BN+E}d82E6#XC7O3 zK1Xi7GiD}yz>67uPI$dQursTceJCPIe0J^&scYI?i76!q78SEOz=l^J((2PBKIYP> zV{oueo&sxkN;!K>YXl(YA<-XM>EJcR5-yqt1i1-oaNm|tb`_WPkE$f)w9)| z^5h_v#(S8Ae}j+KYW;pjXJCR=DU4y&ynYCvaE#>?wKbApGEV33E0?8dOJYI*;z2OT zxN}|(6XkYP+@uJAccFdOz06r<)VEWNv`%0h`iOJUK^_x7L$#~%@I7U1f4Dg@_cQBS z9g!(nK^E~Wv#}2XR|ksv?HU6rl&CrKuI_V<$HK$okVp&Q>diy3ewPfy@w47hQ-B1F_eoXRw}r_s_xuJ&6ePK?m}i<> zLUp6Ie`@8TLqdLXU5H;P+Jd%tlfQ@Am-qq3&)Y+0Ce+USRx?+ILr?aFcm7H*S3r{f z6_67*xuf3pl&cInZ(kKj(x2M~t-pd;wBFhcS6u2#z+Y|1NZ46502~Uh4(4k4J2~AMCtVr+-()bHKwe*bO{onzEs+{R99vz z&HD*RS1X3eMKv-L=1jEA7cDH-v2r05G!>}SgrV6i%N}jZ+~$ZvNJZ*3U7_SH^Bfj@ zmOVK)ox$~q?=}UAMwJ8hMlzhGql~w@`MO0VE;?|n(%(zkcJauvIi5V)k_JYvc%^n_ z?t$g4=Hi|3vQ=A6>2T+dlr&g4_$51-JC+kM8JmUDAsaN=U)X58C)kar_Up|amk7yK z*323d=-}hSp_s|^x)R`96pZiFzYFtizE(FM8Xt3_;(eHCH{!{4Hk9$^GBV+sjkWG$ zlQSl}W*RyKt|0s-$mD1pXIY{;=KQd3XufSAbpCo^2}Xx>CW7CmV#G*U~7G5 zUeQuPV`zCoXtx{AIFcci7xST3T$2P}?`zmk(IVAkI#U{rsO@LIs&{Fr+|yrF37?B2 zm01l!Mj7ZFu(Ih+MQQ2iBAX%?WR#Q?lAgG`f2iZMp<=4|nL5^T$F|wqUENsIylVh{ zTqRtDs{Q=BP|jqu2f05QXjD78>6-7}xY5nGiSu@5JCjUzekL-UNexVB^rs@Ht6(-Y z3u?#0&aGmP6?B|YRy9TqF<2Ze#J(WqRBdcsb3iXao|?V$%SzVn>JzT{^cw?TcdyJh zdqlADZFg$#JWLN?uJoKqmsV0U;jn}w|I_)Im3xe_VVRKNr||r6Qrhr124Im-_2Zl~ zaG9(dijdYHLgTo`^Aj(VD{TPt8UpwX-hdA2-{6ON>QbI2xfPZcK;@qc;E4U$EAkS zhgW9u)c5;mDm2RCL;$e9byt4uJWkj=&`GVDSb|ur?azc9J=$%%HdgD@JD38 zjFB9s(X@&L$_S4n9ywqI;w_yQ*79h&8hMiYBmQmr`~DY71ufHgmNxXag>vW4(&A5^ zc~Z21pMcCx(hD`oyPOutG5`5qI0y~C{ zBb_i}#df`D=$@)^+}#}T9z(p&kK@Fbuk|%g#S4ZlYl#s?s>UH8S>WSJm@+gdKKIbd zV3OQDYO1m44!2o9e|hN5z9+T9xlA77(-v~B#n3U(Nn;4sS4fq*h$NftRTJJf)YtvocvEu=dnOn}b{y>~EdW z9N61UfzdMp1JTcI(sz~Z>iRc!`E_egmQyZwX?y#3_})A|VKVWSEt|n0`AC9=bI*WP zpGOkDi~nZ(Y)P$~ZmhIG8AF}RY*C(kV^gu&%8?lTTN}nt9dK(yH8p$FUv8$Hl`5pW zD3`5acf2dPb7j<6^$>PiPCFZ;k(@;OH5PlH4mGJkXRwFW0SxbQ*M>bp0e8)iCD}*Z zZ~lScjh!7GGU2|8Gq<=CfDA!z-&+>colAp>7N?dzu{yV8TH9sMklgR_Ez)2(mqbxS zz}a=$C%0-veCg}wqoM@V{M;QqlIyXDk9DiRoqTjB?epUU+c5V;TNG$<^CwmBJ|(UP zB8`x_8yp{n7GB&60Z`&Tj}u$L$Yc;NHje#jb=6?v}P3cyI3WRB)idr)UhJRbk(q zvX4FNWjIiDoClys4N&|W%YW6L64oviot|05)(6B)4KVhqWk%*)olkl`HC~wc`@^D< z!A*|i>^p1sc!hF(+ArsbRe@*jWnyHn{p-N@b%?WTFgP)^!obG+Ow`B>CI{$Tcqp-NolF}JbG zlsvKIlD7kQ9kxCKhT!Am?#U!R?8i!~*7+?4UK&>^{0Z-u`lpJ(adM&VL~TEfku0Wh zG0C9RG$PA}TX`iXDlFmF zVMzdbf-+Y^8f3Z=RwQTYX{_72m;B>C&?jvrSShd|opDZI)61JcH$KI#?@8KMg(WCzT&E~ffpZ|_Y08@G5Q1Afsl{9GSa5)<{wd< zSVWPVHX|uQh2^h>#zbD?+dkD7FqmFn!Xp!41lEjlbTCco2o8aLK!V*8l>a|1LL6{l zORCg8D^phk9dxA|lRCmHI$xPw{sn*E^~F_c>{a6l`w?EH`Tc1|sIC&gJTPRj+ED@j zQs&CbkO#m8UWLg=rx)kd_cJPp<}E!AA~1XPu7;ng*hapSjg_^TsJqeOb>CO4doOzJ zD&uQ5dXSMvk?m+ScPZ`=K}KkO)maaw6LpMhuMTEFgO~k>l)FJe<)szH-G;XNI|J4k zTv%nAZI>FjD3E|d$H^)3r8qBj21@7rt`ui?tS$x_J9M@eo7zC5)ZK)6sT{mJihe1v zX(vaZtv0sJB~=?YauIx3y0YE{XA03OslWm&g>mizBi{&nkE45k!7O z5c$nlg4xVJw3~6b-^QYwZ|%ARaT2-4r-H8C@<-MusEpr120@(a?Lfa5 z%92hwobg!YJfYdv$g@r`alz{SvcYPr<_Y1}svpqA=d+yFg4|X$bl*Meyq8ROD??7_ z1Ab5LmqvXd1P(*N>_`>Obf&3Jdjq9n!JJaMw@%R{!T9b zD@A(hLfmKO4r9G2TO$9MusrR=8uo%jO`1fnLw2wp9l<=4^RhnWo!j)-t9DQSq8o?k zay#&R4EH6Y#JQ7FGNTQLlJi2sXF8NI77nUu^6$lRKes2ponA(0nXHq@?R9CDXC;;K zlfUkutU}e%P56GNSj~@VG`I{HhB41aDx~rejE(fgep+NOe5U#aJQdS%h&tG&dEY9^ z5nM)vS)iWho!cY)E`I1!Pya zox`;gp9f*GA?K`h;C&lrH1C^|@%{tHNBUkR%*9{m$}f7{ZWdf*V&Koaz?ZC@zTqId zJRg;A*FV}9L2Ej_ktRnP9otCyPK`0g@}tN&s`0_Y);zybk7Dxl-Ff(PNl{v4x9KpA zI^B+@S;7oL-$9lkr#(IQc4fDd#VEO#g>M_8R>?ly&|1Z53{RK>iBesro*1)jyEqyW zB9f?Nab`t?{bWrgxth4|TAaayn7PANW&wF>9|nW?7S?Jn?)ocUz^7aaSaO?fE3@|( z+Md7%GG=c%O`|@yWRzTidqB>nk&VC#_X%lxy=WHhP+F%8>gdVM9tMLhTqo!EV$5AL z3ZJ(9OPko@;rzaqySc^v0~(ERddd_wAGV7Tl$MVB~m`42fioE>?H+ZnWYu; zR)VLJK0c3qtVxd<+Rx(|=aTE!5npF4_I7dNED7<`wZKbh<5HSYimvQwZ6kSCEM-wEU zNS+a=Yb=11+ioS5(*9yVmdX+};1TVDyW%i}8aMq!qn6$jmTn+t(HEspU`jXEu(gPN z@_Fq>MP_%)Mb^h^vS(`hWxY$}{Yr%bAgQoQG}_N%)~z;nr*o}wxV4 zF`8s0=m;`5lKp5Ds9JvlOcR+0FQZ|x!S2rVT0(A1kg z87&+l(4l9pg;!EIH}QBZy;GXMjIGv1*Z(tCTglj#*5;Lr4W^5eN#arIv74e+YLO?6 zb0cR97BAXIbQa%alE5(wrM1eO%~kbCi^dDalxCIspbOs=J;;|QURA~j)&-46L>)oR zHThO3t!iFtjC%U03e~!HTZC>bsYHy1kkgFRSusY!$->>2TD14G;@8|xG4?d~$>bL& zq!~(#w*0P$X%}`G3^=Xk>ihGih45;SKFZ9zcJdN~Ll|pkgR$HhxkH#W)uQfJn4l$V z?Rt&5_|DVZeQhicr_bp3yZXEFXO|`C22%&o+s2gxlUDP5b${ao1Vf%EjUyTC4~DPn zr<`6nmq8(h;+2mY0k3_<=-!3C#HUM4!%qF7MyQAKG_%Q{!r^Ddhv`;oS*~wh*alFRvpWd z5y$jFakooG{6*%iMTWVFN-op$>RfMB?dw6P!-9oA&h*nD9PunZClJ+raX4EwtrSr1za{rb(!vF>r4!8 z2#syH=$R#m-^4iiVyQFWCi%3&EZuF5GYZb~w@xtN(`&_E&Yy|h6z7X8Tvbx3XBkMr z3PkS~g_jGYjmQ_O6W2VXc{rVvzmulQKHJ+} zrzW+ES?~v+ECqbB;JmQNoKmA5e`SrS)UPQ~;d7`J0a@>>8?PocB#Bqv6BDT6JiW7N zY!kuwZVXTG>FP-GXv)}zahJg8ll->TF)zNkLrsg-D;A291TOit1?4X(ufHHlACT!Z zGkM`I+|hUX*j#z@=2zcillHFc>7jBp1*+)_g??N~Enf}aCKA4_iR|oCd{tF^*v!`L zu4qS_b-&O|S5Z1ydfAfO<-DA-RbkEmo0QCVIpL%=_n9(3o_J%z(J#y0t&Ps9?X*hO z;8G#TkQrE-5U4k@C45iOe_*i+0(atWm%%CGM)Lk^U%tNeTik)>-xsr*BL`?{Gtlle z8ybN`0x3H%weST~TVX-fXv@J3Nn7;_k&aGVgEYAg=U{a-Zj}Z;;lx}?hckitgjVSw z3a$E~uuO06-G`%@E2WP#hq)!laivFI;Mloqs(G&a*&g%4*_e5{)UgSC+uR-XoOS4D z(L(~Sy~e_IlE0`&5_)d7Gud&?5zN1p&=JzO#L-IUV!Nr==}b6fnqW7q8s>g1CV;%jYWxvn+b9G$Yy62sjs$Hu2!PuE-&0J4bZqulTjx+v7tMl z8CRNKS*fco)Fqwqh3E;#^sXRf%wid7%eGSI)^sZMyDwH7GXPp zgKr;@#3zs&{qa4PP?ehxeQ8hqyNG z*dM^zOe1c!4khasoOM90W%;Ib#FR>>8031jEV%pHu;2B9F+g8aBUZDP4rb2dzn7XZw(R!ngH5sLe5*x@iZ@qj${aji;v_WsAT}Rn7-ZY%@qtm^typtsn zx?CYjWeH4^(QC(#-yn15JS!P#0y%eKr39%+$ev70P!Tp4A zoBr+y)WeNo4^~b{HsxDVWn}pcEJ`vL%;Z4AZ?hV`;i+syz?`p^lI$3mYvU`;UDnUC z+l{e{&kk5>o4Y1ZlsMsKF6T|QVca8N_oTDsD}iA{qLR#Xe;JognI-Qt^VaQ{l-xJ9 zb`7e!Q!ZV|T;-?P({}TQhsW*vqX}wD%BOjvUiL44a(Mjmez7$dc3u&tRncLvykv>i zvO%3yxwws`tGC`p=#sm}vH~e((&jODW`T-SfqP<>U8`9(jKrgfE>##eRe_UMR_;+I zB|VFo6#;k`r%y08d>Gd%7t7EvIcYih)?Jh&MJ-#;mARPWj|`kQG><%V&2*1WFW|JhZ{@i~7okP_nr|g<$ z>Z3M$&6R@enJ;yZ!Jz(%-JJOJ`&Q}$!rEtP(`Xt$q;wM>HZ}+@;*bbEsi3sVXrCB* zf&OOFoa6_w&TJJK#*%c?o)50f%tqEDC;IayS0Ty&QYT3(n%eq`#D`}D9!z;OIoC-p zNa}Fhs)?U6_B^gfCopIpC%x^38DEW_9Jm_jaC}Lj?*TKj7517M-Jm_dhWQWP{u^!8Ho|cZdc|$ zUmdY<3&&EGow{NF9Mj;jt*`7aw07~*8Jc?;rl|9iCl04;x7d6-DoTGS4lmn!QS~)> z2HkN7>!utAlH@4J`c_za>buj9APtFj3cF8V;STp(8lNb_-DMM5CO+wH?ttqj61$}|(CSFLF z>vdGWHO`rtKT-@S4IT3ssdycjujiO*>6y6 z<)(hR-Zuk_%UYrukOOsuuP=;E*vo7}B96mgrN*(dKsvoiK_{m9iD|^F5t%Bb1DaPU zqxf}~y%>A@VsqKymIDLOsYGic*W<`192g(>jA~!~QfLs0pHO%$pRTLlIp}2leEWPN zI$H^as7Yl-(WLSwmjq$|%s^!HYPZ@t@L9^o9n5{Gu|ZW-ZM0yx%X#uaW3sD;b?dd$ zidpm{>`?;-1fv>CG7_8{jcDEpDZkRCT3WLk-Jw}FiwWljd_W(RZ&6K?UDIwJ7n|;` zpujtwm89x>e)}M>YdY<$WeyQsIf{z9>h?Z6q%5DYtWYVhdbooZm)bU}rBpdPc2kiFdaurR*2cOC&6oN(kvu1RJj4~s46Hf%% zyJY%CM0N{IPWET_y-cZe1`WXJRaof>oI9M%HjCMAzL(OQp@mbesi*8rOet7=^GuXc zN!|F;0U~r)o|=(Cs2)fCWB#SGgX*pH&3gHBhZhy{Wehp2TgB*bvQKCxNQLUG?Bq<^QIZ!Lcp4e5+msp+nHALzfRG6rRYUM$XiscP~RKNbdnzG%PMp4Y?DaBWpf zND&YYGP(I~qbu4pC7vbIs$KN69}FCdqZ5|;@6b`XtFjxIqs_l1pkeFWU0!?qrSsUU!Y4V zg+RGg%CGOKXoPm7;T$hXCx725!>IeTRKNIj~;POMSzFUR=i;k_#YPOCNqxs~C4dx&vWu)=HHSMoraDdofnf+5ZMIEPce& zhPWHn10h2XIwPqj%Nn5KONMdVy^tbG(tVYe`AF@PH^jYRiNk9}Z?`&MWu{t` zVUm_p_&Q**8X8$Z+c#D3bNF*ha4IFm#nAca}Z8CF5_9Kkr_ll&@Kd=cSoue{+ST z#=%pOulA_L{nYqa=duGL%35e3Z!Oha>R9n+=wg{gLkjvhnEGPPtzl9)tEkHo`9jTG z3z(+K(_h8DuVT*sGOVAb6Kx8K zSZdS5_xu%=E)ThvC+DV;w^UlS*b-rE1%?)KA3LGr>!leUJw2e7Yyt(ER z?}mG}(xUhnS$)2mTjD1PvOa#CO^MCqIf4&d)jcc{X%Y@Xh6I)s`*FLVm~>rLK*t&- znS;NU`6{&jCLSAS_xY8xn|kuwcKTM?YQ_c=_CdjPH~ls$s2B}rD$V6&)XOi6x!%MX znW3nSrlEHCrkg4?ebS4aVlulxiN|z|LF})|0;E zcYRI|YcaYx(c-*Z#YSDRD_QloHfVxry>rk3cNIk=(w9CL)718}G>qE$mC`%Vx$xGi zwMuuZ?Vs27;;B-pl=ZJ&PH^DtlSeXrFZ})9>nHTzoT=ylz`=yKCGlLV^4Nz^(<1tk zB-u|K%CuEA;yf2r``FLxl6~>Lz|3msOkCYbFjgMTpFK`Qt*qF~5Y=tquNfNdyR-V_ zfSRiEHNq$J4lBD~$_9Fn;cw7Tbr<1>j+jwwx$PhHAuV&p^pN5O>y@%LU$IH4mR04q zsf>2LU~d@qr&UUCbPiH-KlQ<>O6@PA>GdIWosl3pmEYfd$3cds3v=Lz6+6kr zB+o}*aoki_m$iv4?SVPMEZu#5+c$-9#9Z!Oo#|9gli=nPW%}9D4U%1$=slAETCVQU zW@WauWAjy9p3qcxLI(70lC&bCZ2aqZ&l8KIPbZ;8H$#fC)QX0(GF%@=4ej&jBY3+Z z-wzqa}) zNv>)+YSDauIjTq{0PPynr&+#jC+!+{PIFV~zN73H;;Ni4Q4n-NMfeY@^o%t)hUF9kp zLhX(EXBq?@jKUchulr?Yz-YM=wjw3sq(#b`WEav$Y8+<#O4UQA#$_>_Z!#aoiFWh# z>C>TAX9G$R_M7rQ;?1PDqfIVy+0H? zH;($G!fjA8WQuq3iFaw+fX%h_46Y+q89J-RG5d_TXa#68>#mX8pZn7Gq)%01K)!FY zi`Yr=Gxryc7~`3U2RY?RR|m4@GLp+!oTeh840}!cXu#PkwIHTm9U7oe8S3v+G&?j( zV2l(CyHL522c1n;CU|ctJEyOxHpmb4nx?0ju#_&hMo1BB!8~~h`}h@6!nZ4r%BEw> z-lJySObb4URUWWn?_zQqCW^eD$DPNNgwE6w;d>XiK2#PtyO7&n)PQ0T>FvJCKwL|0 zKY50E5xkxazzCYPGsLU;s%NLln_^i}T37ZE-;o|k$FeOr7@Q~TDeCZ5Us=QcmK!V*J_(aBlONJYL!q5r47w~mT(?c0VG#Q+hMlopW=DQQ93bl1?$fDGNj2nY&_ ziYN_|5(2{v-Q5BbB14CONQZRKdtQ6r_w(N0x4EA`zqP)#wttMSQCZh@#xIZGaj?Gn zMPQjjlbRbzKS2L@Tp{zOuq@o7^`_)#mfr=X4Wv5LannpKo#z%v}d z$r1`RCVeN^AN|cWpO)JaeaH<^+7wyr+G~8v)Ai5_a`wQIaYN(c&s4c zrXE_AS=i<@uf3e*Womg=)jCWOKYU%#?VBeXGGOK5i1=Y88zOh2sNDa~K$8-2zL2T9 zmR7u?No)?vN1PGvTnt)Hqs4El?p$N7K62pZMCH*vqI&S5l_1D{z{^5Qte96tUk zx9t@DNkLWi_1qT9YEURLtIld|yUlW`OmGKlU^BSllS#YVM7S$#pQmNgJ;0=HDY{;D z-N;vZt*~^U^9%j+%!yg;F2>RbeY?!)(eBRmglRw`_f8@a_ND;LW;YNgXDc@Vek-A( z6woAi`GIBfL-1Yo?2vbec{JCK2cBfiUQ_!`e=Rd^v*WY!8`MYsp(H7{=l~QJwX1aUqr^;QUKB3L!v!KNK4CNg&%J>XC*DE!F;hU&a})a-I)TzUjXsq|f$Ap3gs1Gt z&zeg0*Wu0pNF~KRGQ{<11$}wqP`mXJegW3Aexl!0K5LM6DP{xWdYB1Bu$|kfBAGk| zaNF(mu;<8nh}*sB1oX>w_eDZJ&}~m&?F{IkG$S{8-fLX|)1CFcbi_HMYxRCnoI-8s zoeR)b*s={yDSRw34YVMu#HRAKdEb0Y|NG$FYWj z5V25McvbMV-l6+m#Yc=OPCnQRdoHmzsfASo!!lB>qvta}E^q8V662W|wy_J&NL>bV z#3Z5)Sds})+qtuqpqh>9cQIE^@dV|0H`?w^UPQbT32etuV4;{HV}r4-u?Chu?M^g^ zZ63~zG(?5iA7)O}(+54K<-w26RXD7-y!=CFR`a|+mdeXBGvRIl7Su?b4or1^-%=QT z3Bt?4b}z@l499BMx*+<-=iyEgr#J4%MNxpvx*Z-COs&5h5D`S56vKw343Gt8qxQ)F zf&0n;ZkdVg(+ZtNE>qg6PRA0c0t!`y6HW0VBqi_Z-R;RJjMAMpN1@C3r+-8n`eh>p zC7%sR78`oL_zr;8w8=mH8u6lVA9Ry$Pvfq)+AWo(Hf@PTxC|L4^*#-56_cR?V8-S^9BL22k8R z-4WT;#vb6G&))FgVbl+v;|jny>96qIXgd0G=ns6_E5}c+ zr8@UCmJ3|8R*faUtaWG5Fb?sV0fL-p`WVz)RMj1wdmP5@#zRj*l}SzFzT~xXhOar% zF;1UWU}8(^qiaW!e6!b0ejx0!fBzD@y;uzY44C-3lA}e?%U*ztQ$B|CN9wcC2bb+4 zCmu4Uc) z63}NYKMX`3``6Q$3CqV}FkIM1t< zr|NKaWOdZenD40F=Xf9p-zoZ_7)49o)2^$@U$F<1?R3#{c{pqTp_6oBb<=E;-@u1y z%e@R)p^^F3iO^}0-X1`>69cp+tLt$m>;BKgj6?M~fh3pNtEi90-PtV zkEMhCuQ5}>OzZhfm$e%&?p-8j^d8}PT!@8JzHRR2NgcF#xq7dk*7uwCh_F*p@a76i zapKzuKHKe~`c(Z^;QoH?c3+zyTld^ho98a!Snv-8(~b2T;f^JO<1SQ^Pfr zafN{0@go59^A_2cnrjRh>xLPnHN;fqUSTD#TcHt@G>&jC&Uj|=#F$$$PE(_c=W7n} z64&_%o1+ypJXZ+8ihb=cn9Hqi);I_x`dH$)j&+5bynrHI5d@K687Z{G@@qF>oh&|6 z7(+yn=qZPg6ThLVT$9|j`{LXq!XL#R;J1B$%bMkh71L~+&XCwiP3_EB-UE%{KfH3* z5M&D1t2VTUQVGVR`6kt@+qqd*Xn6fT14iO83ZqQ3apwg2?BZ_jC2Y+j z#z~G??sq#69n(J1D}9EiL6A5Wtv@PxeQrdL6u!QRkpLeKT|JfBxcudsF?M9R+xPt- z-!G0*$K}Yuf~{}OZl07h^x?{T>r1zPLS42zOy?U+gN5~7meHHg&L;C47_lZ=Sm_1N zLICIc4l*HJNVslk$Ti}*RP#@D)zf@mJ~M{Qz(B?rHaIBg%5=t~=O~!Ulcd~96p}9NA^dZ0 zTg5kjse=ynwoBGS&Fnkt^lII0Lj;4`=3Qz4^aJO5HH)}w<2V!vP@iMiqcy?y5bVaoaUFve6?$3tWP}`I+ zZ=pmoW>5lpEbPbYfV25deWyXqr!5yE8D4du+PcHi?<26SQT}C6mqVb6p3HkPKGyw@ zsM;=9T@3gv<`$m^RX@y@)900>HdW%&)j}K3^)arTkGj?5gAgE*z=8TDsHNfy<`@S8 zSf6ZkM<;oFN;PWP%d_zo{pF{pqu;yA=?n;ffDxrD>`eIFHxuSEoiHBPlOl}U^DZy#63NH6 z+f!z2-{Y5n;jcvj{nwqB`4sYq$|zf7F7mZi8gkDZm+f!zwue2O`j5*Q-_Tm0Cxuu& z{-oa!&4EW6293^A{{h=%v8JG(Qt!gjwSJJNx63y?EIG6+ul`K~s5&$QRvG*$b!y{!i5!>$l>wG!^#c59NENW?=P4RDhH^+0Pf?(7&}vwK*j1( zr-Y}*^}3j;5<#CrwNg9+Ir1BVdh~vkr4--_N|l3M6y~uaPC5)QnP2*|5li9qP38!w z*nG(*+<+<0T8u=?4n&}IM?oTOg&MBKWRJ%Q!t@-hY<^N?7ai;A#B=R+*oM` zFK_t|Tf7viwH=TwHKgg>1vza`#n6nNp6D0M&-8Vp{Zl!~TMKnx)pob29IXQMj67xZ z0Vu?89IjUFFJn?)3a7u|cp0&3UEGeyU4Q#%j~5dk&{gaO z7r1;2;`SO_z3Y!A^feSGXh+e{HQpo#jB{H&`dQj1%0==qIO4ecK+-BaOiSjh-1*byzP79q<2d>MIoqIurb z>Y<%PJ^y!5*8F;vHIZa2nEvasaPOLp2Itc4=RrFJR3nNhfq5ncBR)Ul2b-@>{0NT3 zlUkZJIWL00vAyvKeF>YHSH(svgoyfffTBosx+YZcguH{^Rz2{Ku*)Djn*^B7Q|BXq zlS?@46WjSS-x?U6e2sa@hDf{R6#sesZBTA^P%CCM9_&0=$PIQ5-yLq_aW7@I$IjJv`tR0ebMpQwWz~QmOm`{jNXib~${c+|g_W1z(=f>9c*5{3;u?~dRy%q&{ZH(ag%GB-|2 zmSd1oBR2cdltH~GqZyxUd=$PF9cJj9P?4L(HEz@4Ujh9=7M3S_3G<;s7->a}m;0z5-NH{`eIw zl!g~Wprweu?@|0~slUNfV(Fa*aav@A2@N|ylE&o^8c+|6IJrIn(1HX2X`iUq)Gfaq z{^0QOBD$o>6md0Os`MQ}#HD1|vpYFs_wfsyfnzt{Nge`Pgj2>KHk16W=)8Sz3+Pvb za>LFWKl}#TY{Eq<%RscTss6U6wBdyN7tQuO#t1{wV5xB4x_wLQBYqaLY@OI3geaMs zZ(l0rO^k_`K{_n-4R=}G)I@Ml6lU-dLTIi^lyKhQ*AbwM9&o6fi;=UU%2F*F5_xr~ zy(*1m9%z`!_0flE3Fj?IJxBy)Dh^!ZJA~6xb$ah}!yOjMm8;InzEgi0iV9ZK{NVqSY?i~W~h&$|>M%`}Q?M`=qvGQ7t@S$T%{SgzSeYgs# zZ=Dnx_tR$4BkaVh?|LLSE|*;gW+k5ue1vt4%6vXG>($=%pHDjIWg+VuOgWv(d7AON zWP8I1t`+@U=YTbF+h{D8-=d(-(D4L-60WjX<$K6b-C=i>(*?D-PV3BvBK)vru2S<7 zPTl3YKj0VrSUuLW>9q#NUYOkAG0qwI;0G_>u33!IR#O}|>lXll2{et_-Zo3X->*Lp zRau{Rv-3B+My+T zTYSNYdVT@mf%lAbth;%a4>=TacI33mHSK^S+g+r#<!sePeSz= z5T~trOb1gQa$~YoKT;TE3|bt%UC!6-cUB)!6gnB0Q4^{(L3da`_D0(=M2_#A`=lEt zj;v<-EVRGZW{N^{1#%;C1kCIrS)(oR(t-h*n5pAm!i5ihlfS@rJKo5N1X=rLtpbvc z>`Ncc%CEcxeAMC?6Xet+>KPs3$lB9@%_=;mEWpX zOE%r+#?V`ANIma4@L_B6Gg<5a=Q~(bUWG1;T*IvtI<0yNqBjuNf#(=Ke(XoYjHdWtpaoUp7eP{HG~u?? z_y;O#08gJ=yqQ}lIvg)AV~tFsL5X-s$7|rZF4l4|rPDHDFr|u>C0hu02!<4$HFw23 zC*g##u>sI6H{6Bk1#u8sE9Nd_7HVUKEJib83KYo+>-TEcu&4^Z=q%4Y zLS?wKAs*~qlvNz$WxzAqsm@B&R6CDfwOw8|w944QZC?IfBTet*S^38SL)qDy5+z?o zOQ~bG9m>^C`SzUE=NWh*h}=aXRjoHKT@a;at6fgm)N|@F8JE9!*~fCUFKGT~@cc@k z1;6UBn25FPsZ;FV1_Qwfhtw}tSJv*pC-P)f26sNLcJ*-9IBl2`n_`(Bs^TIe2#S`H zH$c?(+u0JE`Dou4)n@%cF?h=5w6oeE)dO&(IZ8^y8hR$p_Meiuz%(A2kmTa0_S73_ zg$d62eC2DW3e!v-%CI&|hoyvPJPkS^m_*Uk0Zz`Wxr&R2Ndq#5r9ZWPD9lQ2+w{*X z`}Mqx#B*G_sS+q0c9L0G=8;!LzqF|o2=iPn1QW<1!dp{gCJL80$>10sHf&~kn=m2H z#$Kv%z0?$or#hc%o_I3y&9+>Z<48PVC|$dra`#~PyW-?hk@nP(%`D+Upqf@e=MGKl zvt7l}31PD-*OPA(=9{JIV4NqUXpd!!Ppc+Y% z>XK)3Hve}`c{=jEh(letv|i^ZQJM+8g3&KnW~_@nX|4KJz4RW%t}lWpGD_@ZKWLVx z_?U7fJ&E7IwB3BF(LVXLl6OIC{HjS?|D9 zvUq`d%&vUGtJqdH3#%N6Vk+af3Mm&CITeS3D5A)8C~tXryn^+ySMbqpk^k-@a#mk3 z$mj4TkRv)5SkM5m~s-Oq+oqp*o|_I0j@Or9NCj<&tYt5PpC>lYP+bAM1h*#B`MfTk8<=7UFG6jK>N zVDGbef(ZR<%CbqjvRUXvsmRn#s}vHOH0AUUhMFP*m)t*VwQmjC^rJ%sam2 z!|AOAFs+Uo(Ru$!Sl#PAPYve8)Py-(>ai!BRo<~448Y%XT6_$!=+OtTLY9;hWL_T_Ds&?_R7I}_gkPAZkUd-sBx2`Nz)rnl$;uYs1q)VEI(VsprEJpi@-vhF+e zwt>~JMck)TVta|&42s%OIbw?v|M%NR2VhRS$mXjh%<{W7b#smY{s2D*tv7o14`xQ{QZsd0hl%9`9bB^i<7!*5eUb2nwa;wqWj zviaa|UxzAk@U`8&nO@(h@1)7}x+0pe?GOAgLIiWgh1Z{6UFqbwQt$V`)@>*r`O}Yf#_LW+e&`W8y{C$^L>I(v0M!2!l0-2C5es(cfz}cb5}lDx77}q3gs+mv7fbF z+DCfiq{z>#ll*qqHCD_*?yZIS-$(EKCU;lc+N&Q&3eI^KWr_WoM0(D)1Tci6>{{~o zi%BAxU2pa5-{M;B?WwdfOWG0{+^_aHeoESYOG-KVFrLr<*IOJi6!(rCcfx%7V0^e< zmYl{u^MP6Pk!jcJy1>Ii;nAvPfju!C|HF^5&9Bu{^w?FcjPLJ=)+L*O-t26rdA%VA9x=g>P`q;s&;IPMxz6IU{rk>s#L^H3rtf60O>lVoG3YI`NNzSh8VXZx{~rcW%C&4uLfE{?u`)_c)$ z0X{E*U=QV1zVRzn^se+XAcKh3{qgF~yDvw}n2PPheqa0M=L|-cx2*`weCO{Q|D2G zZIDuSS#sP>BlNt9@6A7DHv3Yg#|5~(c|VF+aq>8)mZ$}e-#wa1ws94$&{7(=7Q<+# z7jfVUPb2kYK_atWyP?hfakcv}N% z51{ACs2dnJ`~*~KmaVP>#lD{g5<8TlkBb@reL>Uhm$VTMc;i1A!;Gnfr~QG3R#S#z zWQ63vj9E1~2Q)pkfJN!)gR)U1QP}KCZN){4G`B{4$`_DujYgyxUJe1R=YXcgc!lc@ zA2{nt%vEBZOFCxre=gq@XwwcRj)0m9A(){4YWb%Af`hJ0+aUe|V^Yg<_;0*6E-}wmf>d$r0pX=by zxIGKDQO@*_Rono0UD&v1$5bi zcUQZPu+Bh3cp41cUjbCLrPqu{c@=7c@E|Ee{ko?NLbJf5U zS$Del^b69>B^{E-+<2ZTFF8&muvO}rDd{mYM=Bc1&gow5mTj=rI+{CwsqT{Bc-0XG zWy;h z2!6t>yI!%B2j?wtY|%euVf%b5g}N5z)Bi;e89ETulx*ODY!0T|9Vh!jJNVZr`THNT zUp;Gq$?D_(bt%u3sZ}tNw&Zlw{Qc*ip8az;m>3InZ@?b@FHh7@@qSpzNaueO_kDdu z!9n+huEw8+>C8tCEWu|k>!%?9-&A>-@NOEBTaZVc{iJ_hbjS0d=n~o1|3!iMpJh4o zDEzZ5|18VD1hN_bxkP`rrXYy;|880O(wH7LH4@IX z_a|;M{oNY(#Q;-0x;eJDa#4#+zZ9YS1hl-HArym6cLMoj0PdtOP3Gl2AWR79=KZvf zRK0(7b+lCZH>UIkVy-jV;DSM+rR-p3WdB8xm|q!`|2kIx;iv-3xEKquF;jK6tnY&w zwimxQgNb7LwlEq>AkZ;k-V*nt3h-YwgDTaSxm4Jh7ZZrIzn4CG6ukt@eqUSzaMR7V zY!h!>x<*9;R6!>mYjL)H1e%XjK!$=!)XjQ-`@1?#bub{tJJZ*_ziZwGqj*V*cTtHP zl1^*_(;fzhy*z(tp7ZfkogWnx8`pxrKl8u$y?=Jgh-mTg9qH8U1^^i`C_pR6@>l@X z;A>!N%SazY&4jz5(Zb^{GEYE@>rI*cSqBVSph~cBl}tTXN!S$4!BXy=hP^8HobVVOTaQ2u0Q-vQhdZdv2AYwC~3X~ zd?M+2zsZ-60ohA1z#i~E1v5bwXfS;L(V<9j;ro1m?GAZ$mf;TAaMd<)+n(0Dj6;B* zuMee{Ph$JMb?)|e9_1LWtNHb(zWe*L;gLwddc)QtiB)39dL;@wo5o2Zz2d~Zoh)y} zHoxUDsrKTASKQq*po@%f{&eT>SLZz54jxEtKD+c$T%6%T`%wDT{>*@7breyq6YvzU)%QjlvG~*>EZ3RiltoCQguCez3I0CJqn}u zfl9?apf+0o;@OvL6V)#z_r?Vh0r>Mai%5c&Q`KS?v#AP2>%|_Rxg5*K{x4CR{*g9fOK1e7;64*{CLJ^-e;_jG^51<*!C zdA_k6l>%yqb ziX2(pu z2cQ{74D}NULNH~s+Uk; zSCW$IheeB^IM=3yfVgs76^ml-1!9SNZ*K`?eXA>M1~=vA480ZKf##yy$<7dKafT@% zyex9^1pIg`R~tY>AwO@~a$e-lDmdlDK#yAEoBeDadi-WAM_!E8vCk{TauFkEAcs*Y z!|wY4q@v6>U~AR(xr*NcAD?9nQ@PF~9im$^-IdApJ%+MLY>hH?BjSzB+E#p9dcPBl zEX@L4-3RNpW|MVYLo3`mD_my@yw(TNM*Pp&d9blYGkNJy|8g9h!%9_ydA?9mTRUU4*t_O{f)Od@5V&&bnM)ExoDqn z_9h8B%dH7Z%kH*atsw}pHUl5m_!jVbwQm*MMNqsv^0!EXTmuTe-kh2MIwX`cZ|knp zDIL`}XC+uq{+A7+yk~`4^ujpY#&aEMsE~q<-fb&kwph#slD0_G`3hp4abkPVP5@YB zNcKNmB#G4f&B^ny0t+*m3$6hb?1&b1(`G$#s1w>4ujuMiT`@ihl;$GuoGhkhdyYLW9a(tMoHPfl&F%)K8TB8xspf z`W~3cPgPKHKZ+359!0u4}Wa2FS3>{8FSpKUZY1(Bgb(I;O2BAqeP5sJUf9l zFd5XADF7zxu7zb0JC8UODK%6&A)t$W|0Xomat@I4b=!|xzTu9r9s&jQ=&!F{)6J}8 zizqJux=kVw;h0Ve+36i04%PRJjUfQS@4EVf1M4DYE8xeMokud2V+%k-o4Mn2MYxVr zC4iqjQjrkKqy=(s9pY~l(-%&jGaNIv%n{A|7 zxOC}UOucB1Z13pOaDdB1y3PvHn0g&}`rbDTkpfVUacfB#{YoIj(k)XXRQXa{=8;>Z zI51dj1ClB!4K;Ijgh1R|sJb<4;3HKdlpzK6B4Irfzl5n-EgxWgdk7)9 z>X(2n_CbCfhKc9nKEUjV#&<_oN%bBBj<+uc%!w3< z6EO#Qt|7WA*=_SVoREWja{Rl!`Qc?c9X;&Ao|Y;#QO1R{Oc4C40=UCiFLB<3BhTCZ z>?3%bl>W32j=FHxdS$7_yOQ?_=#Gdm3#JwUfMzV>B+ShK!ibUV4I`990p;3jmeQq} z)}OK}Pq__>GB6wlMfs=9vp+xw=_){T%F3x;0rQ-Mu5B=xyFCPN6th5F8q)fPQh12oZ^i!oc@JkEG^eAg18*+y$S+8-mtVlKaNRG-I|L(9{L2!_QIW$O?ab?P9B0L=`rPLQ%3yEu9gS;1yioQ#)p4JF zR(!1n$DTvba!bWkDhu2XE>5TMSdN~VT)7dz1lJ7OKn4LQ5ETwE*wTMZ6cyMK6y*_8 zjPSO2O&)KRPe`-Z$5GlcM>ZSb16i-Zy8zwc)zMJ_u*cRWV||K3bc7=Y22xPZS zzWJg0VLcNJ_8?5gcm5#$osFk@2n1b0ekg`qZyEibiEGJe;VMRm zQ6v<40@EK0j~uDg?-wWzw>m4LPcQ?O&Tiawg#Sgl!&T!gE5QaV=|YLs_gH)`kNJw1 zn&S*$vjAm-#O9arrPoRAx0ExZ#^dE`k$czqt_~&MT|*1m59a0Fb|~s#F__Ox{K>L6 zBUD+upcJaDl!6fZ#d4x_%S^@(#P-7)#UMrd`z9g&0OnH8^bc<0^syUK`E%2^j0o`T z*P=`~5(;{vFqfQ;RH@#Z}Cj@UMdkLdj#g%__Ac72wj zj8UyDZHsad%J7vzNYHD6m;5`0bSwjIv1;s(YHhiriK7bVf^^EUHg`mO8PU zw7N!fpKBbV)O$e4;cRZE3k9GS!0+sI165A6loRH81=$RkDIn*M)PRomNOMO`-P#P0 zs~n=sI;=H($9Dr;?mV>ts>y@l^`N|N0Tf4f=I3Zz^ns8m`}|G6Go1|!CMw4fJ0r$_{ai-UFE!kQ&Bq!mlOGg3ai z>xP4HJf8>s8X@KclyZHVp_o0Tl-0oWGWl7XSMmg$FA5xiq;w8ml9!mE$3kpM1KZAF zl|P@{(4GF2^DxT#%%=fBrnY%wx>;sCoTIn}zQ&@KNCi3!kp?E;+xbL?s=e3q_}Xf7 z)I>A)wIW^DQ4qiIr>-eK2ysp07*+fB*YUwtGn8}BBx?~xL(yzJU+jo2z~vHwrlqNk zZf-l)_SNBJ~DGJitR&8&uLRAlleQ*9# z$0I-`F4qeuNuFIw!aO+=>5Ro$OD>+WYe26|OCA8j%y>0z-%RQFQX`n>VbN`D4kP1SP2 z>A8Dm{jECtla%*B1le!C-bwsx&JFA60wEOEx$ z=M~-t#t|EUnV9zp7DN-2USRk7&_?AXw%=N<$8vRdaJTa7ZN94tt6F*hy3;zIJ#$Zx zN9m5a0(2xHG$6C&WQOyD3JxF>_Ozso<$haSG4mF)Z<_pcvmyWX}T=b5o z9lggw-|d!ot9Dvz!o%aM#M*tn(a|5Yd^!`{ zH@k8ak-v5aXZC9TH|JVrshBb)%zRkr20xEztWBtLGxm|_?-tT#K#(Of-;<E8 zNs@sE0F(nn4b(087W&($aZ}#(`wU zE!Z;7)M1PaQyoo*ruO;Ki{!#Jq;5Yyu!zRP@HnYrII8jbdWrf?30+KXk!fZ_Wu?c z2&yu!_l~U=Vg(iSZZK+y27*|S8EMt}LUlY6m3Vgtgt2rK+=fxi-oVC@&h-0d#w>pS z{4xOWJ9pfC2CS{x?(is}5^es`_`LY00pBEK+(5P}Lks0)T~+EivmGmxu`X|wS3xEw zf15|1iqxL3aS)hE>!-_k#cDQt5#f66Rl}+Y&90L*KGPsURo(4@OwuEj{?->(#|I;z zZu2OX$K*AUaDLrQ79U~Fhi6;}Y0y9A4&|0&JL@g~_1@h)8cLSS_4c+xM=nHX{EYwc zpVF?t6U^KoLI2FNJ zdDdUA^3TKkw=Ml=W&T;2|Ga+x+@`a97i5_JS(*R8tc;kTV9cMjuDr1SaYujugEaOc zwTeS`f)EOE!S&96dpF3%5ncVsEn9)W<^~9Q1fE%x__yQgzWrXL#;@$ Date: Fri, 16 Jun 2023 10:35:39 -0600 Subject: [PATCH 19/38] Implemented Crank-Nicholson discretization for SST and IDDES sources (#852) - Discretized k and omega source term based on Crank-Nicholson (CN) scheme - Implemented limited version of production for omega equation - If time discretization is not CN, the original workflow is preserved --- amr-wind/incflo_advance.cpp | 6 +-- amr-wind/turbulence/LES/AMD.H | 3 +- amr-wind/turbulence/LES/AMD.cpp | 3 +- amr-wind/turbulence/LES/OneEqKsgs.H | 7 ++- amr-wind/turbulence/LES/OneEqKsgs.cpp | 5 +- amr-wind/turbulence/LES/Smagorinsky.H | 3 +- amr-wind/turbulence/LES/Smagorinsky.cpp | 3 +- amr-wind/turbulence/LaminarModel.H | 4 +- amr-wind/turbulence/LaminarModel.cpp | 2 +- amr-wind/turbulence/RANS/KOmegaSST.H | 5 +- amr-wind/turbulence/RANS/KOmegaSST.cpp | 54 ++++++++++++++----- amr-wind/turbulence/RANS/KOmegaSSTIDDES.H | 3 +- amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp | 54 ++++++++++++++----- amr-wind/turbulence/TurbulenceModel.H | 4 +- unit_tests/turbulence/test_turbulence_LES.cpp | 9 ++-- .../turbulence/test_turbulence_LES_bc.cpp | 3 +- unit_tests/wind_energy/test_abl_bc.cpp | 3 +- unit_tests/wind_energy/test_abl_stats.cpp | 4 +- 18 files changed, 128 insertions(+), 47 deletions(-) diff --git a/amr-wind/incflo_advance.cpp b/amr-wind/incflo_advance.cpp index a31140d471..9c6b9c845e 100644 --- a/amr-wind/incflo_advance.cpp +++ b/amr-wind/incflo_advance.cpp @@ -196,7 +196,7 @@ void incflo::ApplyPredictor(bool incremental_projection) // TODO: This sub-section has not been adjusted for mesh mapping - adjust in // corrector too m_sim.turbulence_model().update_turbulent_viscosity( - amr_wind::FieldState::Old); + amr_wind::FieldState::Old, m_diff_type); icns().compute_mueff(amr_wind::FieldState::Old); for (auto& eqns : scalar_eqns()) { eqns->compute_mueff(amr_wind::FieldState::Old); @@ -517,7 +517,7 @@ void incflo::ApplyCorrector() // Compute viscosity / diffusive coefficients // ************************************************************************************* m_sim.turbulence_model().update_turbulent_viscosity( - amr_wind::FieldState::New); + amr_wind::FieldState::New, m_diff_type); icns().compute_mueff(amr_wind::FieldState::New); for (auto& eqns : scalar_eqns()) { eqns->compute_mueff(amr_wind::FieldState::New); @@ -636,7 +636,7 @@ void incflo::ApplyPrescribeStep() // Compute diffusive and source terms for scalars m_sim.turbulence_model().update_turbulent_viscosity( - amr_wind::FieldState::Old); + amr_wind::FieldState::Old, m_diff_type); for (auto& eqns : scalar_eqns()) { eqns->compute_mueff(amr_wind::FieldState::Old); } diff --git a/amr-wind/turbulence/LES/AMD.H b/amr-wind/turbulence/LES/AMD.H index ab913cce8a..6db42d131a 100644 --- a/amr-wind/turbulence/LES/AMD.H +++ b/amr-wind/turbulence/LES/AMD.H @@ -22,7 +22,8 @@ public: std::string model_name() const override { return "AMD"; } //! Update the turbulent viscosity field - void update_turbulent_viscosity(const FieldState fstate) override; + void update_turbulent_viscosity( + const FieldState fstate, const DiffusionType /*unused*/) override; //! Update the effective thermal diffusivity field void update_alphaeff(Field& alphaeff) override; diff --git a/amr-wind/turbulence/LES/AMD.cpp b/amr-wind/turbulence/LES/AMD.cpp index af052ccb7a..7629252fb6 100644 --- a/amr-wind/turbulence/LES/AMD.cpp +++ b/amr-wind/turbulence/LES/AMD.cpp @@ -39,7 +39,8 @@ void AMD::parse_model_coeffs() } template -void AMD::update_turbulent_viscosity(const FieldState fstate) +void AMD::update_turbulent_viscosity( + const FieldState fstate, const DiffusionType /*unused*/) { BL_PROFILE( "amr-wind::" + this->identifier() + "::update_turbulent_viscosity"); diff --git a/amr-wind/turbulence/LES/OneEqKsgs.H b/amr-wind/turbulence/LES/OneEqKsgs.H index 6580c78c20..19c9f3fdb1 100644 --- a/amr-wind/turbulence/LES/OneEqKsgs.H +++ b/amr-wind/turbulence/LES/OneEqKsgs.H @@ -56,7 +56,8 @@ public: std::string model_name() const override { return "OneEqKsgsM84"; } //! Update the turbulent viscosity field - void update_turbulent_viscosity(const FieldState fstate) override; + void update_turbulent_viscosity( + const FieldState fstate, const DiffusionType /*unused*/) override; //! Do any post advance work void post_advance_work() override; @@ -110,7 +111,9 @@ public: std::string model_name() const override { return "OneEqKsgsS94"; } //! Update the turbulent viscosity field - void update_turbulent_viscosity(const FieldState fstate) override; + void update_turbulent_viscosity( + const FieldState fstate, + const DiffusionType diff_type /*unused*/) override; //! No post advance work for this model void post_advance_work() override {} diff --git a/amr-wind/turbulence/LES/OneEqKsgs.cpp b/amr-wind/turbulence/LES/OneEqKsgs.cpp index 1164d49590..4660c5ff34 100644 --- a/amr-wind/turbulence/LES/OneEqKsgs.cpp +++ b/amr-wind/turbulence/LES/OneEqKsgs.cpp @@ -87,7 +87,7 @@ TurbulenceModel::CoeffsDictType OneEqKsgsM84::model_coeffs() const template void OneEqKsgsM84::update_turbulent_viscosity( - const FieldState fstate) + const FieldState fstate, const DiffusionType /*unused*/) { BL_PROFILE( "amr-wind::" + this->identifier() + "::update_turbulent_viscosity"); @@ -289,7 +289,8 @@ TurbulenceModel::CoeffsDictType OneEqKsgsS94::model_coeffs() const template void OneEqKsgsS94::update_turbulent_viscosity( const FieldState // fstate - /*unused*/) + /*unused*/, + const DiffusionType /*unused*/) { BL_PROFILE( "amr-wind::" + this->identifier() + "::update_turbulent_viscosity"); diff --git a/amr-wind/turbulence/LES/Smagorinsky.H b/amr-wind/turbulence/LES/Smagorinsky.H index 580c50d56d..40239b163f 100644 --- a/amr-wind/turbulence/LES/Smagorinsky.H +++ b/amr-wind/turbulence/LES/Smagorinsky.H @@ -25,7 +25,8 @@ public: std::string model_name() const override { return "Smagorinsky"; } //! Update the turbulent viscosity field - void update_turbulent_viscosity(const FieldState fstate) override; + void update_turbulent_viscosity( + const FieldState fstate, const DiffusionType /*unused*/) override; //! No post advance work for this model void post_advance_work() override {} diff --git a/amr-wind/turbulence/LES/Smagorinsky.cpp b/amr-wind/turbulence/LES/Smagorinsky.cpp index a232cba127..ec5c75718d 100644 --- a/amr-wind/turbulence/LES/Smagorinsky.cpp +++ b/amr-wind/turbulence/LES/Smagorinsky.cpp @@ -19,7 +19,8 @@ Smagorinsky::Smagorinsky(CFDSim& sim) {} template -void Smagorinsky::update_turbulent_viscosity(const FieldState fstate) +void Smagorinsky::update_turbulent_viscosity( + const FieldState fstate, const DiffusionType /*unused*/) { BL_PROFILE( "amr-wind::" + this->identifier() + "::update_turbulent_viscosity"); diff --git a/amr-wind/turbulence/LaminarModel.H b/amr-wind/turbulence/LaminarModel.H index cdc49ecfdd..9617fbc084 100644 --- a/amr-wind/turbulence/LaminarModel.H +++ b/amr-wind/turbulence/LaminarModel.H @@ -27,7 +27,9 @@ public: std::string model_name() const override { return "laminar"; } //! Update the effective/turbulent viscosity field - void update_turbulent_viscosity(const FieldState fstate) override; + void update_turbulent_viscosity( + const FieldState fstate, + const DiffusionType diff_type /*unused*/) override; //! No post advance work for this model void post_advance_work() override {} diff --git a/amr-wind/turbulence/LaminarModel.cpp b/amr-wind/turbulence/LaminarModel.cpp index 33bcf0c525..745073de5c 100644 --- a/amr-wind/turbulence/LaminarModel.cpp +++ b/amr-wind/turbulence/LaminarModel.cpp @@ -81,7 +81,7 @@ inline void laminar_scal_diff_update( template void Laminar::update_turbulent_viscosity( - const FieldState /* fstate */) + const FieldState /* fstate */, const DiffusionType /*unused*/) { // Empty function as there is no turbulent field } diff --git a/amr-wind/turbulence/RANS/KOmegaSST.H b/amr-wind/turbulence/RANS/KOmegaSST.H index 0bdb8af54f..a274730529 100644 --- a/amr-wind/turbulence/RANS/KOmegaSST.H +++ b/amr-wind/turbulence/RANS/KOmegaSST.H @@ -45,7 +45,8 @@ public: std::string model_name() const override { return "KOmegaSST"; } //! Update the turbulent viscosity field - void update_turbulent_viscosity(const FieldState fstate) override; + void update_turbulent_viscosity( + const FieldState fstate, const DiffusionType diff_type) override; //! No post advance work for this model void post_advance_work() override {} @@ -91,6 +92,8 @@ protected: amrex::Real m_buoyancy_factor = 0.0; amrex::Real m_sigma_t{0.85}; amrex::Vector m_gravity{{0.0, 0.0, -9.81}}; + + DiffusionType m_diff_type = DiffusionType::Crank_Nicolson; }; } // namespace amr_wind::turbulence diff --git a/amr-wind/turbulence/RANS/KOmegaSST.cpp b/amr-wind/turbulence/RANS/KOmegaSST.cpp index 4427442999..3a9760a197 100644 --- a/amr-wind/turbulence/RANS/KOmegaSST.cpp +++ b/amr-wind/turbulence/RANS/KOmegaSST.cpp @@ -61,7 +61,8 @@ TurbulenceModel::CoeffsDictType KOmegaSST::model_coeffs() const } template -void KOmegaSST::update_turbulent_viscosity(const FieldState fstate) +void KOmegaSST::update_turbulent_viscosity( + const FieldState fstate, const DiffusionType diff_type) { BL_PROFILE( "amr-wind::" + this->identifier() + "::update_turbulent_viscosity"); @@ -182,24 +183,53 @@ void KOmegaSST::update_turbulent_viscosity(const FieldState fstate) f1_arr(i, j, k) = tmp_f1; + // For TKE equation: + shear_prod_arr(i, j, k) = amrex::min( + amrex::max( + mu_arr(i, j, k) * tmp4 * tmp4, 0.0), + 10.0 * beta_star * rho_arr(i, j, k) * tke_arr(i, j, k) * + sdr_arr(i, j, k)); + diss_arr(i, j, k) = -beta_star * rho_arr(i, j, k) * tke_arr(i, j, k) * sdr_arr(i, j, k); + tke_lhs_arr(i, j, k) = 0.5 * beta_star * rho_arr(i, j, k) * sdr_arr(i, j, k) * deltaT; - shear_prod_arr(i, j, k) = amrex::min( - mu_arr(i, j, k) * tmp4 * tmp4, - 10.0 * beta_star * rho_arr(i, j, k) * tke_arr(i, j, k) * - sdr_arr(i, j, k)); + // For SDR equation: + amrex::Real production_omega = + rho_arr(i, j, k) * alpha * + amrex::min( + tmp4 * tmp4, 10.0 * beta_star * sdr_arr(i, j, k) * + sdr_arr(i, j, k)); - sdr_lhs_arr(i, j, k) = 0.5 * rho_arr(i, j, k) * beta * - sdr_arr(i, j, k) * deltaT; - sdr_src_arr(i, j, k) = - rho_arr(i, j, k) * alpha * tmp4 * tmp4 + + amrex::Real cross_diffusion = (1.0 - tmp_f1) * 2.0 * rho_arr(i, j, k) * sigma_omega2 * - gko / (sdr_arr(i, j, k) + 1e-15); - sdr_diss_arr(i, j, k) = -rho_arr(i, j, k) * beta * - sdr_arr(i, j, k) * sdr_arr(i, j, k); + gko / (sdr_arr(i, j, k) + 1e-15); + + if (diff_type == DiffusionType::Crank_Nicolson) { + + sdr_src_arr(i, j, k) = production_omega; + + sdr_diss_arr(i, j, k) = cross_diffusion; + + sdr_lhs_arr(i, j, k) = + (rho_arr(i, j, k) * beta * sdr_arr(i, j, k) + + 0.5 * std::abs(cross_diffusion) / + (sdr_arr(i, j, k) + 1e-15)) * + deltaT; + } else { + + sdr_src_arr(i, j, k) = + production_omega + cross_diffusion; + + sdr_diss_arr(i, j, k) = -rho_arr(i, j, k) * beta * + sdr_arr(i, j, k) * + sdr_arr(i, j, k); + + sdr_lhs_arr(i, j, k) = 0.5 * rho_arr(i, j, k) * beta * + sdr_arr(i, j, k) * deltaT; + } }); } } diff --git a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H index c392380740..c109ffc187 100644 --- a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H +++ b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H @@ -36,7 +36,8 @@ public: std::string model_name() const override { return "KOmegaSSTIDDES"; } //! Update the turbulent viscosity field - void update_turbulent_viscosity(const FieldState fstate) override; + void update_turbulent_viscosity( + const FieldState fstate, const DiffusionType diff_type) override; //! No post advance work for this model void post_advance_work() override {} diff --git a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp index d2eabbb751..9e6a9c235c 100644 --- a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp +++ b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp @@ -60,7 +60,7 @@ TurbulenceModel::CoeffsDictType KOmegaSSTIDDES::model_coeffs() const template void KOmegaSSTIDDES::update_turbulent_viscosity( - const FieldState fstate) + const FieldState fstate, const DiffusionType diff_type) { BL_PROFILE( "amr-wind::" + this->identifier() + "::update_turbulent_viscosity"); @@ -194,6 +194,13 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( // fdtilde * (l_rans - l_les) + l_les; const amrex::Real l_iddes = l_les; + // For TKE equation: + shear_prod_arr(i, j, k) = amrex::min( + amrex::max( + mu_arr(i, j, k) * tmp4 * tmp4, 0.0), + 10.0 * rho_arr(i, j, k) * tke_arr(i, j, k) * + std::sqrt(tke_arr(i, j, k)) / l_iddes); + diss_arr(i, j, k) = -rho_arr(i, j, k) * std::sqrt(tke_arr(i, j, k)) * tke_arr(i, j, k) / l_iddes; @@ -202,21 +209,42 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( std::sqrt(tke_arr(i, j, k)) / l_iddes * deltaT; - shear_prod_arr(i, j, k) = amrex::min( - mu_arr(i, j, k) * tmp4 * tmp4, - 10.0 * beta_star * rho_arr(i, j, k) * tke_arr(i, j, k) * - sdr_arr(i, j, k)); + // For SDR equation: + amrex::Real production_omega = + rho_arr(i, j, k) * alpha * + amrex::min( + tmp4 * tmp4, + 10.0 * + amrex::max(sdr_arr(i, j, k), 0.0) * + std::sqrt(tke_arr(i, j, k)) / l_iddes); - sdr_lhs_arr(i, j, k) = 0.5 * rho_arr(i, j, k) * beta * - sdr_arr(i, j, k) * deltaT; - - sdr_src_arr(i, j, k) = - rho_arr(i, j, k) * alpha * tmp4 * tmp4 + + amrex::Real cross_diffusion = (1.0 - tmp_f1) * 2.0 * rho_arr(i, j, k) * sigma_omega2 * - gko / (sdr_arr(i, j, k) + 1e-15); + gko / (sdr_arr(i, j, k) + 1e-15); + + if (diff_type == DiffusionType::Crank_Nicolson) { + + sdr_src_arr(i, j, k) = production_omega; + + sdr_diss_arr(i, j, k) = cross_diffusion; + + sdr_lhs_arr(i, j, k) = + (rho_arr(i, j, k) * beta * sdr_arr(i, j, k) + + 0.5 * std::abs(cross_diffusion) / + (sdr_arr(i, j, k) + 1e-15)) * + deltaT; + + } else { + sdr_src_arr(i, j, k) = + production_omega + cross_diffusion; + + sdr_diss_arr(i, j, k) = -rho_arr(i, j, k) * beta * + sdr_arr(i, j, k) * + sdr_arr(i, j, k); - sdr_diss_arr(i, j, k) = -rho_arr(i, j, k) * beta * - sdr_arr(i, j, k) * sdr_arr(i, j, k); + sdr_lhs_arr(i, j, k) = 0.5 * rho_arr(i, j, k) * beta * + sdr_arr(i, j, k) * deltaT; + } }); } } diff --git a/amr-wind/turbulence/TurbulenceModel.H b/amr-wind/turbulence/TurbulenceModel.H index 26c149c037..bc70c88df9 100644 --- a/amr-wind/turbulence/TurbulenceModel.H +++ b/amr-wind/turbulence/TurbulenceModel.H @@ -5,6 +5,7 @@ #include "amr-wind/core/Factory.H" #include "amr-wind/core/FieldDescTypes.H" +#include "amr-wind/incflo.H" namespace amr_wind { @@ -47,7 +48,8 @@ public: * \param fstate State used for updates to differentiate logic in predictor * and corrector steps. */ - virtual void update_turbulent_viscosity(const FieldState fstate) = 0; + virtual void update_turbulent_viscosity( + const FieldState fstate, const DiffusionType diff_type) = 0; //! Do any post advance actions for the turbulence model virtual void post_advance_work() = 0; diff --git a/unit_tests/turbulence/test_turbulence_LES.cpp b/unit_tests/turbulence/test_turbulence_LES.cpp index c9741ceb6f..4fd1ec9076 100644 --- a/unit_tests/turbulence/test_turbulence_LES.cpp +++ b/unit_tests/turbulence/test_turbulence_LES.cpp @@ -176,7 +176,8 @@ TEST_F(TurbLESTest, test_smag_setup_calc) dens.setVal(rho0); // Update turbulent viscosity directly - tmodel.update_turbulent_viscosity(amr_wind::FieldState::New); + tmodel.update_turbulent_viscosity( + amr_wind::FieldState::New, DiffusionType::Crank_Nicolson); auto& muturb = sim().repo().get_field("mu_turb"); // Check values of turbulent viscosity @@ -287,7 +288,8 @@ TEST_F(TurbLESTest, test_1eqKsgs_setup_calc) tke.setVal(tke_val); // Update turbulent viscosity directly - tmodel.update_turbulent_viscosity(amr_wind::FieldState::New); + tmodel.update_turbulent_viscosity( + amr_wind::FieldState::New, DiffusionType::Crank_Nicolson); auto& muturb = sim().repo().get_field("mu_turb"); // Check values of turbulent viscosity @@ -373,7 +375,8 @@ TEST_F(TurbLESTest, test_AMD_setup_calc) init_field1(temp, Tgz); // Update turbulent viscosity directly - tmodel.update_turbulent_viscosity(amr_wind::FieldState::New); + tmodel.update_turbulent_viscosity( + amr_wind::FieldState::New, DiffusionType::Crank_Nicolson); auto& muturb = sim().repo().get_field("mu_turb"); // Check values of turbulent viscosity diff --git a/unit_tests/turbulence/test_turbulence_LES_bc.cpp b/unit_tests/turbulence/test_turbulence_LES_bc.cpp index 51eeaf2e73..fec26db781 100644 --- a/unit_tests/turbulence/test_turbulence_LES_bc.cpp +++ b/unit_tests/turbulence/test_turbulence_LES_bc.cpp @@ -205,7 +205,8 @@ class TurbLESTestBC : public MeshTest } // Update turbulent viscosity directly - tmodel.update_turbulent_viscosity(amr_wind::FieldState::New); + tmodel.update_turbulent_viscosity( + amr_wind::FieldState::New, DiffusionType::Crank_Nicolson); } const amrex::Real dx = 10.0 / 10.0; diff --git a/unit_tests/wind_energy/test_abl_bc.cpp b/unit_tests/wind_energy/test_abl_bc.cpp index c16e28e0ac..811d1a4c8a 100644 --- a/unit_tests/wind_energy/test_abl_bc.cpp +++ b/unit_tests/wind_energy/test_abl_bc.cpp @@ -2,6 +2,7 @@ #include "amr-wind/utilities/trig_ops.H" #include "aw_test_utils/iter_tools.H" #include "aw_test_utils/test_utils.H" +#include "amr-wind/incflo.H" #include "AMReX_Gpu.H" #include "AMReX_Random.H" @@ -138,7 +139,7 @@ TEST_F(ABLMeshTest, abl_wall_model) icns_eq.initialize(); // Initialize viscosity sim().turbulence_model().update_turbulent_viscosity( - amr_wind::FieldState::Old); + amr_wind::FieldState::Old, DiffusionType::Crank_Nicolson); icns_eq.compute_mueff(amr_wind::FieldState::Old); // Check test setup by verifying mu diff --git a/unit_tests/wind_energy/test_abl_stats.cpp b/unit_tests/wind_energy/test_abl_stats.cpp index b44c01773d..b42e073762 100644 --- a/unit_tests/wind_energy/test_abl_stats.cpp +++ b/unit_tests/wind_energy/test_abl_stats.cpp @@ -3,6 +3,7 @@ #include "aw_test_utils/test_utils.H" #include "amr-wind/equation_systems/tke/TKE.H" #include "amr-wind/wind_energy/ABLStats.H" +#include "amr-wind/incflo.H" namespace amr_wind_tests { @@ -180,6 +181,7 @@ TEST_F(ABLMeshTest, stats_energy_budget) amrex::ParmParse pp("transport"); pp.add("viscosity", (amrex::Real)1e-5); } + // incflo.diffusion_type = 1 populate_parameters(); initialize_mesh(); @@ -237,7 +239,7 @@ TEST_F(ABLMeshTest, stats_energy_budget) // Step forward in time for tke equation sim().turbulence_model().update_turbulent_viscosity( - amr_wind::FieldState::Old); + amr_wind::FieldState::Old, DiffusionType::Crank_Nicolson); tke_eqn.compute_advection_term(amr_wind::FieldState::Old); // Remove NaNs (not sure why they're there, but need to be removed) remove_nans(tke_eqn.fields().conv_term); From a75d2ec013e15c567faa5873098a35a7b484c06a Mon Sep 17 00:00:00 2001 From: "Marc T. Henry de Frahan" Date: Fri, 16 Jun 2023 11:55:27 -0600 Subject: [PATCH 20/38] Update SST and SST IDDES (#842) Co-authored-by: sbidadi9 --- amr-wind/incflo_advance.cpp | 4 +- amr-wind/incflo_compute_dt.cpp | 2 +- amr-wind/setup/init.cpp | 4 +- amr-wind/turbulence/RANS/KOmegaSST.H | 2 + amr-wind/turbulence/RANS/KOmegaSST.cpp | 45 ++++--- amr-wind/turbulence/RANS/KOmegaSSTIDDES.H | 4 + amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp | 116 +++++++++++++----- test/CMakeLists.txt | 1 + .../channel_kwsst_sust/channel_kwsst_sust.inp | 66 ++++++++++ 9 files changed, 190 insertions(+), 54 deletions(-) create mode 100644 test/test_files/channel_kwsst_sust/channel_kwsst_sust.inp diff --git a/amr-wind/incflo_advance.cpp b/amr-wind/incflo_advance.cpp index 9c6b9c845e..a230c7a7e0 100644 --- a/amr-wind/incflo_advance.cpp +++ b/amr-wind/incflo_advance.cpp @@ -157,7 +157,7 @@ void incflo::advance() * \f{align} * \kappa = \begin{cases} * 0 & \text{Explicit} \\ - * 0.5 & \text{Crank-Nicholson} \\ + * 0.5 & \text{Crank-Nicolson} \\ * 1 & \text{Implicit} * \end{cases} * \f} @@ -475,7 +475,7 @@ void incflo::ApplyPredictor(bool incremental_projection) * \f{align} * \kappa = \begin{cases} * 0 & \text{Explicit} \\ - * 0.5 & \text{Crank-Nicholson} \\ + * 0.5 & \text{Crank-Nicolson} \\ * 1 & \text{Implicit} * \end{cases} * \f} diff --git a/amr-wind/incflo_compute_dt.cpp b/amr-wind/incflo_compute_dt.cpp index ad1e7c6f57..23841ad92b 100644 --- a/amr-wind/incflo_compute_dt.cpp +++ b/amr-wind/incflo_compute_dt.cpp @@ -27,7 +27,7 @@ using namespace amrex; * * \f$F_x, F_y, F_z\f$ are acceleration due to forcing terms. By default, * \f$C\f$ is always used for computing CFL. The term \f$V\f$ is only used when - * the user has chosen implicit or Crank-Nicholson scheme for diffusion, and + * the user has chosen implicit or Crank-Nicolson scheme for diffusion, and * contributions from forcing term when `time.use_force_cfl` is `true` (default * is `true`). * diff --git a/amr-wind/setup/init.cpp b/amr-wind/setup/init.cpp index 34bda511cb..1181470147 100644 --- a/amr-wind/setup/init.cpp +++ b/amr-wind/setup/init.cpp @@ -29,8 +29,8 @@ void incflo::ReadParameters() // Godunov-related flags pp.query("use_godunov", m_use_godunov); - // The default for diffusion_type is 2, i.e. the default m_diff_type is - // DiffusionType::Implicit + // The default for diffusion_type is 1, i.e. the default m_diff_type is + // DiffusionType::Crank_Nicolson int diffusion_type = 1; pp.query("diffusion_type", diffusion_type); if (diffusion_type == 0) { diff --git a/amr-wind/turbulence/RANS/KOmegaSST.H b/amr-wind/turbulence/RANS/KOmegaSST.H index a274730529..fba60bbca2 100644 --- a/amr-wind/turbulence/RANS/KOmegaSST.H +++ b/amr-wind/turbulence/RANS/KOmegaSST.H @@ -86,6 +86,8 @@ protected: amrex::Real m_sigma_omega1{0.5}; amrex::Real m_sigma_omega2{0.856}; amrex::Real m_a1{0.31}; + amrex::Real m_tke_amb{0.0}; + amrex::Real m_sdr_amb{0.0}; //! Buoyancy modified constants bool m_include_buoyancy{false}; diff --git a/amr-wind/turbulence/RANS/KOmegaSST.cpp b/amr-wind/turbulence/RANS/KOmegaSST.cpp index 3a9760a197..5ebbc21fdd 100644 --- a/amr-wind/turbulence/RANS/KOmegaSST.cpp +++ b/amr-wind/turbulence/RANS/KOmegaSST.cpp @@ -35,6 +35,8 @@ void KOmegaSST::parse_model_coeffs() pp.query("sigma_k2", this->m_sigma_k2); pp.query("sigma_omega1", this->m_sigma_omega1); pp.query("sigma_omega2", this->m_sigma_omega2); + pp.query("tke_amb", this->m_tke_amb); + pp.query("sdr_amb", this->m_sdr_amb); pp.query("sigma_t", this->m_sigma_t); } @@ -57,6 +59,8 @@ TurbulenceModel::CoeffsDictType KOmegaSST::model_coeffs() const {"sigma_k2", this->m_sigma_k2}, {"sigma_omega1", this->m_sigma_omega1}, {"sigma_omega2", this->m_sigma_omega2}, + {"tke_amb", this->m_tke_amb}, + {"sdr_amb", this->m_sdr_amb}, {"a1", this->m_a1}}; } @@ -67,7 +71,6 @@ void KOmegaSST::update_turbulent_viscosity( BL_PROFILE( "amr-wind::" + this->identifier() + "::update_turbulent_viscosity"); - auto& mu_turb = this->mu_turb(); const amrex::Real beta_star = this->m_beta_star; const amrex::Real alpha1 = this->m_alpha1; const amrex::Real alpha2 = this->m_alpha2; @@ -75,15 +78,20 @@ void KOmegaSST::update_turbulent_viscosity( const amrex::Real beta2 = this->m_beta2; const amrex::Real sigma_omega2 = this->m_sigma_omega2; const amrex::Real a1 = this->m_a1; + const amrex::Real tke_amb = this->m_tke_amb; + const amrex::Real sdr_amb = this->m_sdr_amb; + auto& mu_turb = this->mu_turb(); auto lam_mu = (this->m_transport).mu(); const auto& den = this->m_rho.state(fstate); const auto& tke = (*this->m_tke).state(fstate); const auto& sdr = (*this->m_sdr).state(fstate); // cppcheck-suppress constVariable auto& repo = mu_turb.repo(); - - const int nlevels = repo.num_active_levels(); + auto& tke_lhs = (this->m_sim).repo().get_field("tke_lhs_src_term"); + tke_lhs.setVal(0.0); + // cppcheck-suppress constVariable + auto& sdr_lhs = (this->m_sim).repo().get_field("sdr_lhs_src_term"); auto gradK = (this->m_sim.repo()).create_scratch_field(3, 0); fvm::gradient(*gradK, tke); @@ -99,17 +107,13 @@ void KOmegaSST::update_turbulent_viscosity( // Compute strain rate into shear production term fvm::strainrate(this->m_shear_prod, vel); - auto& tke_lhs = (this->m_sim).repo().get_field("tke_lhs_src_term"); - tke_lhs.setVal(0.0); - // cppcheck-suppress constVariable - auto& sdr_lhs = (this->m_sim).repo().get_field("sdr_lhs_src_term"); - const amrex::Real deltaT = (this->m_sim).time().deltaT(); const amrex::GpuArray gravity{ {m_gravity[0], m_gravity[1], m_gravity[2]}}; const amrex::Real Bfac = this->m_buoyancy_factor; const amrex::Real sigmat = this->m_sigma_t; + const int nlevels = repo.num_active_levels(); for (int lev = 0; lev < nlevels; ++lev) { for (amrex::MFIter mfi(mu_turb(lev)); mfi.isValid(); ++mfi) { const auto& bx = mfi.tilebox(); @@ -138,7 +142,7 @@ void KOmegaSST::update_turbulent_viscosity( gradK_arr(i, j, k, 1) * gradOmega_arr(i, j, k, 1) + gradK_arr(i, j, k, 2) * gradOmega_arr(i, j, k, 2)); - amrex::Real cdkomega = amrex::max( + amrex::Real cdkomega = amrex::max( 1e-10, 2.0 * rho_arr(i, j, k) * sigma_omega2 * gko / (sdr_arr(i, j, k) + 1e-15)); @@ -157,7 +161,8 @@ void KOmegaSST::update_turbulent_viscosity( 1e-15); amrex::Real tmp4 = shear_prod_arr(i, j, k); - amrex::Real arg1 = amrex::min(amrex::max(tmp2, tmp3), tmp1); + amrex::Real arg1 = amrex::min( + amrex::max(tmp2, tmp3), tmp1); amrex::Real tmp_f1 = std::tanh(arg1 * arg1 * arg1 * arg1); amrex::Real alpha = tmp_f1 * (alpha1 - alpha2) + alpha2; @@ -167,9 +172,9 @@ void KOmegaSST::update_turbulent_viscosity( amrex::max(2.0 * tmp2, tmp3); amrex::Real f2 = std::tanh(arg2 * arg2); - mu_arr(i, j, k) = - rho_arr(i, j, k) * a1 * tke_arr(i, j, k) / - amrex::max(a1 * sdr_arr(i, j, k), tmp4 * f2); + mu_arr(i, j, k) = rho_arr(i, j, k) * a1 * tke_arr(i, j, k) / + amrex::max( + a1 * sdr_arr(i, j, k), tmp4 * f2); // Buoyancy term amrex::Real tmpB = @@ -190,8 +195,12 @@ void KOmegaSST::update_turbulent_viscosity( 10.0 * beta_star * rho_arr(i, j, k) * tke_arr(i, j, k) * sdr_arr(i, j, k)); + const amrex::Real diss_amb = + beta_star * rho_arr(i, j, k) * sdr_amb * tke_amb; diss_arr(i, j, k) = -beta_star * rho_arr(i, j, k) * - tke_arr(i, j, k) * sdr_arr(i, j, k); + tke_arr(i, j, k) * + sdr_arr(i, j, k) + + diss_amb; tke_lhs_arr(i, j, k) = 0.5 * beta_star * rho_arr(i, j, k) * sdr_arr(i, j, k) * deltaT; @@ -207,6 +216,9 @@ void KOmegaSST::update_turbulent_viscosity( (1.0 - tmp_f1) * 2.0 * rho_arr(i, j, k) * sigma_omega2 * gko / (sdr_arr(i, j, k) + 1e-15); + const amrex::Real sdr_diss_amb = + beta * rho_arr(i, j, k) * sdr_amb * sdr_amb; + if (diff_type == DiffusionType::Crank_Nicolson) { sdr_src_arr(i, j, k) = production_omega; @@ -224,8 +236,9 @@ void KOmegaSST::update_turbulent_viscosity( production_omega + cross_diffusion; sdr_diss_arr(i, j, k) = -rho_arr(i, j, k) * beta * - sdr_arr(i, j, k) * - sdr_arr(i, j, k); + sdr_arr(i, j, k) * + sdr_arr(i, j, k) + + sdr_diss_amb; sdr_lhs_arr(i, j, k) = 0.5 * rho_arr(i, j, k) * beta * sdr_arr(i, j, k) * deltaT; diff --git a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H index c109ffc187..f7ab18105a 100644 --- a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H +++ b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H @@ -49,11 +49,15 @@ public: TurbulenceModel::CoeffsDictType model_coeffs() const override; protected: + Field& m_rans_ind; + //! Turbulence constants amrex::Real m_Cdes1{0.78}; amrex::Real m_Cdes2{0.61}; amrex::Real m_Cdt1{20.0}; amrex::Real m_Cdt2{3.0}; + amrex::Real m_Cl{5.0}; + amrex::Real m_Ct{1.87}; amrex::Real m_Cw{0.15}; amrex::Real m_kappa{0.41}; }; diff --git a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp index 9e6a9c235c..e7127b3c55 100644 --- a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp +++ b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp @@ -5,6 +5,7 @@ #include "amr-wind/fvm/gradient.H" #include "amr-wind/fvm/strainrate.H" #include "amr-wind/fvm/vorticity.H" +#include "amr-wind/fvm/vorticity_mag.H" #include "amr-wind/turbulence/turb_utils.H" #include "amr-wind/equation_systems/tke/TKE.H" #include "amr-wind/equation_systems/sdr/SDR.H" @@ -20,6 +21,7 @@ KOmegaSSTIDDES::~KOmegaSSTIDDES() = default; template KOmegaSSTIDDES::KOmegaSSTIDDES(CFDSim& sim) : KOmegaSST(sim) + , m_rans_ind(sim.repo().declare_field("rans_indicator", 1, 1, 1)) {} template @@ -32,6 +34,8 @@ void KOmegaSSTIDDES::parse_model_coeffs() pp.query("Cdes2", this->m_Cdes2); pp.query("Cdt1", this->m_Cdt1); pp.query("Cdt2", this->m_Cdt2); + pp.query("Cl", this->m_Cl); + pp.query("Ct", this->m_Ct); pp.query("Cw", this->m_Cw); pp.query("kappa", this->m_kappa); } @@ -49,11 +53,15 @@ TurbulenceModel::CoeffsDictType KOmegaSSTIDDES::model_coeffs() const {"sigma_k2", this->m_sigma_k2}, {"sigma_omega1", this->m_sigma_omega1}, {"sigma_omega2", this->m_sigma_omega2}, + {"tke_amb", this->m_tke_amb}, + {"sdr_amb", this->m_sdr_amb}, {"a1", this->m_a1}, {"Cdes1", this->m_Cdes1}, {"Cdes2", this->m_Cdes2}, {"Cdt1", this->m_Cdt1}, {"Cdt2", this->m_Cdt2}, + {"Cl", this->m_Cl}, + {"Ct", this->m_Ct}, {"Cw", this->m_Cw}, {"kappa", this->m_kappa}}; } @@ -72,9 +80,16 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( const amrex::Real beta2 = this->m_beta2; const amrex::Real sigma_omega2 = this->m_sigma_omega2; const amrex::Real a1 = this->m_a1; + const amrex::Real tke_amb = this->m_tke_amb; + const amrex::Real sdr_amb = this->m_sdr_amb; const amrex::Real Cdes1 = this->m_Cdes1; const amrex::Real Cdes2 = this->m_Cdes2; + const amrex::Real Cdt1 = this->m_Cdt1; + const amrex::Real Cdt2 = this->m_Cdt2; + const amrex::Real Cl = this->m_Cl; + const amrex::Real Ct = this->m_Ct; const amrex::Real Cw = this->m_Cw; + const amrex::Real kappa = this->m_kappa; auto& mu_turb = this->mu_turb(); auto lam_mu = (this->m_transport).mu(); @@ -99,6 +114,9 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( // Compute strain rate into shear production term fvm::strainrate(this->m_shear_prod, vel); + auto vortmag = (this->m_sim.repo()).create_scratch_field(1, 0); + fvm::vorticity_mag(*vortmag, vel); + const amrex::Real deltaT = (this->m_sim).time().deltaT(); const int nlevels = repo.num_active_levels(); @@ -107,7 +125,8 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( const amrex::Real dx = geom.CellSize()[0]; const amrex::Real dy = geom.CellSize()[1]; const amrex::Real dz = geom.CellSize()[2]; - const amrex::Real hmax = amrex::max(amrex::max(dx, dy), dz); + const amrex::Real hmax = + amrex::max(amrex::max(dx, dy), dz); for (amrex::MFIter mfi(mu_turb(lev)); mfi.isValid(); ++mfi) { const auto& bx = mfi.tilebox(); @@ -116,10 +135,12 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( const auto& rho_arr = den(lev).const_array(mfi); const auto& gradK_arr = (*gradK)(lev).array(mfi); const auto& gradOmega_arr = (*gradOmega)(lev).array(mfi); - const auto& tke_arr = (*this->m_tke)(lev).array(mfi); - const auto& sdr_arr = (*this->m_sdr)(lev).array(mfi); + const auto& tke_arr = tke(lev).array(mfi); + const auto& sdr_arr = sdr(lev).array(mfi); const auto& wd_arr = (this->m_walldist)(lev).array(mfi); const auto& shear_prod_arr = (this->m_shear_prod)(lev).array(mfi); + const auto& vortmag_arr = (*vortmag)(lev).const_array(mfi); + const auto& rans_ind_arr = (this->m_rans_ind)(lev).array(mfi); const auto& diss_arr = (this->m_diss)(lev).array(mfi); const auto& sdr_src_arr = (this->m_sdr_src)(lev).array(mfi); const auto& sdr_diss_arr = (this->m_sdr_diss)(lev).array(mfi); @@ -134,7 +155,7 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( gradK_arr(i, j, k, 1) * gradOmega_arr(i, j, k, 1) + gradK_arr(i, j, k, 2) * gradOmega_arr(i, j, k, 2)); - amrex::Real cdkomega = amrex::max( + amrex::Real cdkomega = amrex::max( 1e-10, 2.0 * rho_arr(i, j, k) * sigma_omega2 * gko / (sdr_arr(i, j, k) + 1e-15)); @@ -152,8 +173,10 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( rho_arr(i, j, k) + 1e-15); amrex::Real tmp4 = shear_prod_arr(i, j, k); + amrex::Real tmp5 = vortmag_arr(i, j, k); - amrex::Real arg1 = amrex::min(amrex::max(tmp2, tmp3), tmp1); + amrex::Real arg1 = amrex::min( + amrex::max(tmp2, tmp3), tmp1); amrex::Real tmp_f1 = std::tanh(arg1 * arg1 * arg1 * arg1); amrex::Real alpha = tmp_f1 * (alpha1 - alpha2) + alpha2; @@ -163,36 +186,58 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( amrex::max(2.0 * tmp2, tmp3); amrex::Real f2 = std::tanh(arg2 * arg2); - mu_arr(i, j, k) = - rho_arr(i, j, k) * a1 * tke_arr(i, j, k) / - amrex::max(a1 * sdr_arr(i, j, k), tmp4 * f2); + mu_arr(i, j, k) = rho_arr(i, j, k) * a1 * tke_arr(i, j, k) / + amrex::max( + a1 * sdr_arr(i, j, k), tmp4 * f2); f1_arr(i, j, k) = tmp_f1; - // const amrex::Real alpha_des = 0.25 - wd_arr(i, j, k) / - // hmax; const amrex::Real fb = amrex::min( - // 2.0 * std::exp(-9.0 * alpha_des * alpha_des), 1.0); - // const amrex::Real rdt = - // (mu_arr(i, j, k) / - // (rho_arr(i, j, k) * kappa * kappa * wd_arr(i, j, k) - // * - // wd_arr(i, j, k) * - // std::sqrt(0.5 * (tmp4 * tmp4 + tmp5 * tmp5)))); - // const amrex::Real fdt = - // 1.0 - std::tanh(std::pow(Cdt1 * rdt, Cdt2)); - // const amrex::Real fdtilde = amrex::max((1.0 - fdt), fb); - // const amrex::Real fdtilde = 0.0; + const amrex::Real alpha_des = 0.25 - wd_arr(i, j, k) / hmax; + const amrex::Real denom = + (rho_arr(i, j, k) * kappa * kappa * wd_arr(i, j, k) * + wd_arr(i, j, k) * + std::sqrt(0.5 * (tmp4 * tmp4 + tmp5 * tmp5))); + const amrex::Real rdl = lam_mu_arr(i, j, k) / denom; + const amrex::Real rdt = mu_arr(i, j, k) / denom; + const amrex::Real fl = + std::tanh(std::pow(Cl * Cl * rdl, 10)); + const amrex::Real ft = + std::tanh(std::pow(Ct * Ct * rdt, 3)); + const amrex::Real fe1 = + (alpha < 0) ? 2.0 * std::exp(-9.0 * alpha * alpha) + : 2.0 * std::exp(-11.09 * alpha * alpha); + const amrex::Real fe2 = + 1.0 - amrex::max(ft, fl); + const amrex::Real fe = + fe2 * amrex::max((fe1 - 1.0), 0.0); + const amrex::Real fb = amrex::min( + 2.0 * std::exp(-9.0 * alpha_des * alpha_des), 1.0); + const amrex::Real fdt = + 1.0 - std::tanh(std::pow(Cdt1 * rdt, Cdt2)); + const amrex::Real fdtilde = + amrex::max((1.0 - fdt), fb); const amrex::Real cdes = tmp_f1 * (Cdes1 - Cdes2) + Cdes2; const amrex::Real l_les = cdes * - amrex::min( - Cw * amrex::max(wd_arr(i, j, k), hmax), hmax); - // const amrex::Real l_rans = std::sqrt(tke_arr(i, j, k)) / - // (beta_star * sdr_arr(i, j, - // k)); - // const amrex::Real l_iddes = - // fdtilde * (l_rans - l_les) + l_les; - const amrex::Real l_iddes = l_les; + amrex::min( + Cw * amrex::max(wd_arr(i, j, k), hmax), + hmax); + const amrex::Real l_rans = std::sqrt(tke_arr(i, j, k)) / + (beta_star * sdr_arr(i, j, k)); + const amrex::Real rans_ind = fdtilde * (1.0 + fe); + rans_ind_arr(i, j, k) = rans_ind; + const amrex::Real l_iddes = amrex::max( + 1.0e-16, rans_ind * l_rans + (1.0 - fdtilde) * l_les); + + const amrex::Real sqrt_tke_amb = std::sqrt(tke_amb); + const amrex::Real l_sst_amb = + sqrt_tke_amb / + (beta_star * amrex::max(1.0e-16, sdr_amb)); + const amrex::Real l_iddes_amb = amrex::max( + 1.0e-16, + rans_ind * l_sst_amb + (1.0 - fdtilde) * l_les); + const amrex::Real diss_amb = + rho_arr(i, j, k) * tke_amb * sqrt_tke_amb / l_iddes_amb; // For TKE equation: shear_prod_arr(i, j, k) = amrex::min( @@ -202,8 +247,9 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( std::sqrt(tke_arr(i, j, k)) / l_iddes); diss_arr(i, j, k) = -rho_arr(i, j, k) * - std::sqrt(tke_arr(i, j, k)) * - tke_arr(i, j, k) / l_iddes; + std::sqrt(tke_arr(i, j, k)) * + tke_arr(i, j, k) / l_iddes + + diss_amb; tke_lhs_arr(i, j, k) = 0.5 * rho_arr(i, j, k) * std::sqrt(tke_arr(i, j, k)) / @@ -222,6 +268,9 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( (1.0 - tmp_f1) * 2.0 * rho_arr(i, j, k) * sigma_omega2 * gko / (sdr_arr(i, j, k) + 1e-15); + const amrex::Real sdr_diss_amb = + beta * rho_arr(i, j, k) * sdr_amb * sdr_amb; + if (diff_type == DiffusionType::Crank_Nicolson) { sdr_src_arr(i, j, k) = production_omega; @@ -239,8 +288,9 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( production_omega + cross_diffusion; sdr_diss_arr(i, j, k) = -rho_arr(i, j, k) * beta * - sdr_arr(i, j, k) * - sdr_arr(i, j, k); + sdr_arr(i, j, k) * + sdr_arr(i, j, k) + + sdr_diss_amb; sdr_lhs_arr(i, j, k) = 0.5 * rho_arr(i, j, k) * beta * sdr_arr(i, j, k) * deltaT; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7a7edfbac4..2b6e52933b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -189,6 +189,7 @@ add_test_re(uniform_ct_disk) add_test_re(uniform_ct_disk_gaussian) add_test_re(joukowsky_disk) add_test_re(channel_kwsst) +add_test_re(channel_kwsst_sust) add_test_re(channel_kwsstiddes) add_test_re(channel_godunov_laminar) add_test_re(channel_smagorinsky_analytical) diff --git a/test/test_files/channel_kwsst_sust/channel_kwsst_sust.inp b/test/test_files/channel_kwsst_sust/channel_kwsst_sust.inp new file mode 100644 index 0000000000..94de2ab5d3 --- /dev/null +++ b/test/test_files/channel_kwsst_sust/channel_kwsst_sust.inp @@ -0,0 +1,66 @@ +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# SIMULATION STOP # +#.......................................# +time.stop_time = 1.00 # Max (simulated) time to evolve +time.max_step = 10 # Max number of time steps +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# TIME STEP COMPUTATION # +#.......................................# +time.fixed_dt = -0.0005 # Use this constant dt if > 0 +time.cfl = 0.25 # CFL factor +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# INPUT AND OUTPUT # +#.......................................# +time.plot_interval = 10 # Steps between plot files +time.checkpoint_interval = -100 # Steps between checkpoint files +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# PHYSICS # +#.......................................# +incflo.density = 1.0 # Reference density +incflo.use_godunov = 1 +incflo.diffusion_type = 2 +transport.viscosity = 1.0e-3 +turbulence.model = KOmegaSST + +ICNS.source_terms = BodyForce +BodyForce.magnitude = 1.0 0 0 +TKE.source_terms = KwSSTSrc +SDR.source_terms = SDRSrc +KOmegaSST_coeffs.tke_amb = 0.05 +KOmegaSST_coeffs.sdr_amb = 3528.0 +incflo.physics = ChannelFlow +ChannelFlow.re_tau = 1000.0 +ChannelFlow.density = 1.0 +ChannelFlow.tke0 = 0.05 +ChannelFlow.sdr0 = 3528.0 + +io.output_default_variables = 0 +io.outputs = density velocity_mueff sdr tke mu_turb +io.derived_outputs = "components(velocity,0)" + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# ADAPTIVE MESH REFINEMENT # +#.......................................# +amr.n_cell = 16 512 8 # Grid cells at coarsest AMRlevel +amr.max_level = 0 # Max AMR level in hierarchy +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# GEOMETRY # +#.......................................# +geometry.prob_lo = 0. 0. 0. # Lo corner coordinates +geometry.prob_hi = 6.283185307179586 2. 3.141592653589793 # Hi corner coordinates +geometry.is_periodic = 1 0 1 # Periodicity x y z (0/1) + +# Boundary conditions +ylo.type = "no_slip_wall" +yhi.type = "no_slip_wall" +ylo.tke = 0.0 +yhi.tke = 0.0 +ylo.sdr = 3198361.6 +yhi.sdr = 3198361.6 + +incflo.verbose = 0 + +mac_proj.mg_rtol = 1.0e-11 +mac_proj.mg_atol = 1.0e-11 +mac_proj.do_semicoarsening = true +nodal_proj.mg_atol = 1.0e-11 From 5e7d611d233514c590e4aed039ee01c416f59bc5 Mon Sep 17 00:00:00 2001 From: "Marc T. Henry de Frahan" Date: Wed, 21 Jun 2023 09:39:57 -0600 Subject: [PATCH 21/38] Fix box type in abl boundary plane input for MAC vel (#860) --- amr-wind/wind_energy/ABLBoundaryPlane.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/amr-wind/wind_energy/ABLBoundaryPlane.cpp b/amr-wind/wind_energy/ABLBoundaryPlane.cpp index af48493482..b8beb70398 100644 --- a/amr-wind/wind_energy/ABLBoundaryPlane.cpp +++ b/amr-wind/wind_energy/ABLBoundaryPlane.cpp @@ -894,6 +894,11 @@ void ABLBoundaryPlane::populate_data( amrex::Abort("No inflow data at this level."); } + if (ori.isHigh()) { + amrex::Warning( + "We typically don't inflow boundary planes on the high side."); + } + const size_t nc = mfab.nComp(); #ifdef AMREX_USE_OMP @@ -902,7 +907,10 @@ void ABLBoundaryPlane::populate_data( for (amrex::MFIter mfi(mfab, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi) { - const auto& sbx = mfi.growntilebox(1); + auto sbx = mfi.growntilebox(1); + if (!sbx.cellCentered()) { + sbx.enclosedCells(); + } const auto& src = m_in_data.interpolate_data(ori, lev); const auto& bx = sbx & src.box(); if (bx.isEmpty()) { From b41395c2f906b294eb3d73119e6f8f44212ba0cf Mon Sep 17 00:00:00 2001 From: "Georgios (Yorgos) Deskos" Date: Wed, 21 Jun 2023 13:51:32 -0600 Subject: [PATCH 22/38] init tke profile (#864) --- amr-wind/wind_energy/ABLFieldInit.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/amr-wind/wind_energy/ABLFieldInit.cpp b/amr-wind/wind_energy/ABLFieldInit.cpp index b74b981278..cd696d9d61 100644 --- a/amr-wind/wind_energy/ABLFieldInit.cpp +++ b/amr-wind/wind_energy/ABLFieldInit.cpp @@ -203,7 +203,7 @@ void ABLFieldInit::perturb_temperature( void ABLFieldInit::init_tke( const amrex::Geometry& geom, amrex::MultiFab& tke_fab) const { - tke_fab.setVal(m_tke_init, 1); + tke_fab.setVal(m_tke_init); if (!m_tke_init_profile) { return; @@ -219,18 +219,19 @@ void ABLFieldInit::init_tke( #endif for (amrex::MFIter mfi(tke_fab, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi) { - const auto& bx = mfi.growntilebox(1); + const auto& bx = mfi.tilebox(); const auto& tke = tke_fab.array(mfi); // Profile definition from Beare et al. (2006) amrex::ParallelFor( bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { const amrex::Real z = problo[2] + (k + 0.5) * dx[2]; + if (z < tke_cutoff_height) { tke(i, j, k) = tke_init_factor * - std::pow(1. - z / tke_cutoff_height, 3.0); + std::pow(1. - z / tke_cutoff_height, 3); } else { - tke(i, j, k) = 0.; + tke(i, j, k) = 1.e-20; } }); } From 5ae35331c43715818366a0cf2c89424095774b81 Mon Sep 17 00:00:00 2001 From: "Marc T. Henry de Frahan" Date: Thu, 22 Jun 2023 13:38:29 -0600 Subject: [PATCH 23/38] Fix for fill abl inflow (#862) --- amr-wind/wind_energy/ABLFillInflow.H | 2 +- amr-wind/wind_energy/ABLFillInflow.cpp | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/amr-wind/wind_energy/ABLFillInflow.H b/amr-wind/wind_energy/ABLFillInflow.H index 8edfe3951f..6172e8a9cd 100644 --- a/amr-wind/wind_energy/ABLFillInflow.H +++ b/amr-wind/wind_energy/ABLFillInflow.H @@ -15,7 +15,7 @@ namespace amr_wind { * * \sa ABLBoundaryPlane */ -class ABLFillInflow : public FieldFillPatchOps +class ABLFillInflow : public FieldFillPatchOps { public: ABLFillInflow( diff --git a/amr-wind/wind_energy/ABLFillInflow.cpp b/amr-wind/wind_energy/ABLFillInflow.cpp index 588367fe74..bb5c85e4a8 100644 --- a/amr-wind/wind_energy/ABLFillInflow.cpp +++ b/amr-wind/wind_energy/ABLFillInflow.cpp @@ -7,7 +7,7 @@ ABLFillInflow::ABLFillInflow( const amrex::AmrCore& mesh, const SimTime& time, const ABLBoundaryPlane& bndry_plane) - : FieldFillPatchOps( + : FieldFillPatchOps( field, mesh, time, FieldInterpolator::CellConsLinear) , m_bndry_plane(bndry_plane) {} @@ -21,7 +21,8 @@ void ABLFillInflow::fillpatch( const amrex::IntVect& nghost, const FieldState fstate) { - FieldFillPatchOps::fillpatch(lev, time, mfab, nghost, fstate); + FieldFillPatchOps::fillpatch( + lev, time, mfab, nghost, fstate); m_bndry_plane.populate_data(lev, time, m_field, mfab); } @@ -33,7 +34,7 @@ void ABLFillInflow::fillpatch_from_coarse( const amrex::IntVect& nghost, const FieldState fstate) { - FieldFillPatchOps::fillpatch_from_coarse( + FieldFillPatchOps::fillpatch_from_coarse( lev, time, mfab, nghost, fstate); m_bndry_plane.populate_data(lev, time, m_field, mfab); @@ -46,7 +47,8 @@ void ABLFillInflow::fillphysbc( const amrex::IntVect& nghost, const FieldState fstate) { - FieldFillPatchOps::fillphysbc(lev, time, mfab, nghost, fstate); + FieldFillPatchOps::fillphysbc( + lev, time, mfab, nghost, fstate); m_bndry_plane.populate_data(lev, time, m_field, mfab); } @@ -87,7 +89,7 @@ void ABLFillInflow::fillpatch_sibling_fields( } } - FieldFillPatchOps::fillpatch_sibling_fields( + FieldFillPatchOps::fillpatch_sibling_fields( lev, time, mfabs, ffabs, cfabs, nghost, lbcrec, fstate, itype); for (int i = 0; i < static_cast(mfabs.size()); i++) { From ffd72051dc1a9bf5eaa618435da2f31fcba621cc Mon Sep 17 00:00:00 2001 From: "Marc T. Henry de Frahan" Date: Thu, 22 Jun 2023 14:53:05 -0600 Subject: [PATCH 24/38] Fix MPL inflow (#865) --- amr-wind/wind_energy/ABLFillMPL.H | 2 +- amr-wind/wind_energy/ABLFillMPL.cpp | 12 +++++++----- amr-wind/wind_energy/ABLModulatedPowerLaw.cpp | 10 ++++++++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/amr-wind/wind_energy/ABLFillMPL.H b/amr-wind/wind_energy/ABLFillMPL.H index 9da6783823..6c9eea8aa2 100644 --- a/amr-wind/wind_energy/ABLFillMPL.H +++ b/amr-wind/wind_energy/ABLFillMPL.H @@ -15,7 +15,7 @@ namespace amr_wind { * * \sa ABLBoundaryPlane */ -class ABLFillMPL : public FieldFillPatchOps +class ABLFillMPL : public FieldFillPatchOps { public: ABLFillMPL( diff --git a/amr-wind/wind_energy/ABLFillMPL.cpp b/amr-wind/wind_energy/ABLFillMPL.cpp index fe6fd18a55..661c9b4e65 100644 --- a/amr-wind/wind_energy/ABLFillMPL.cpp +++ b/amr-wind/wind_energy/ABLFillMPL.cpp @@ -7,7 +7,7 @@ ABLFillMPL::ABLFillMPL( const amrex::AmrCore& mesh, const SimTime& time, const ABLModulatedPowerLaw& abl_mpl) - : FieldFillPatchOps( + : FieldFillPatchOps( field, mesh, time, FieldInterpolator::CellConsLinear) , m_abl_mpl(abl_mpl) {} @@ -21,7 +21,8 @@ void ABLFillMPL::fillpatch( const amrex::IntVect& nghost, const FieldState fstate) { - FieldFillPatchOps::fillpatch(lev, time, mfab, nghost, fstate); + FieldFillPatchOps::fillpatch( + lev, time, mfab, nghost, fstate); if (m_field.base_name() == "velocity") { m_abl_mpl.set_velocity(lev, time, m_field, mfab); @@ -37,7 +38,7 @@ void ABLFillMPL::fillpatch_from_coarse( const amrex::IntVect& nghost, const FieldState fstate) { - FieldFillPatchOps::fillpatch_from_coarse( + FieldFillPatchOps::fillpatch_from_coarse( lev, time, mfab, nghost, fstate); if (m_field.base_name() == "velocity") { @@ -54,7 +55,8 @@ void ABLFillMPL::fillphysbc( const amrex::IntVect& nghost, const FieldState fstate) { - FieldFillPatchOps::fillphysbc(lev, time, mfab, nghost, fstate); + FieldFillPatchOps::fillphysbc( + lev, time, mfab, nghost, fstate); if (m_field.base_name() == "velocity") { m_abl_mpl.set_velocity(lev, time, m_field, mfab); @@ -100,7 +102,7 @@ void ABLFillMPL::fillpatch_sibling_fields( } } - FieldFillPatchOps::fillpatch_sibling_fields( + FieldFillPatchOps::fillpatch_sibling_fields( lev, time, mfabs, ffabs, cfabs, nghost, lbcrec, fstate, itype); for (int i = 0; i < static_cast(mfabs.size()); i++) { diff --git a/amr-wind/wind_energy/ABLModulatedPowerLaw.cpp b/amr-wind/wind_energy/ABLModulatedPowerLaw.cpp index a83c893831..a5b0a21dba 100644 --- a/amr-wind/wind_energy/ABLModulatedPowerLaw.cpp +++ b/amr-wind/wind_energy/ABLModulatedPowerLaw.cpp @@ -173,7 +173,10 @@ void ABLModulatedPowerLaw::set_velocity( : amrex::adjCellHi(domain, idir, nghost); for (amrex::MFIter mfi(mfab); mfi.isValid(); ++mfi) { - const auto& gbx = amrex::grow(mfi.validbox(), nghost); + auto gbx = amrex::grow(mfi.validbox(), nghost); + if (!gbx.cellCentered()) { + gbx.enclosedCells(); + } const auto& bx = gbx & dbx; if (!bx.ok()) { continue; @@ -247,7 +250,10 @@ void ABLModulatedPowerLaw::set_temperature( : amrex::adjCellHi(domain, idir, nghost); for (amrex::MFIter mfi(mfab); mfi.isValid(); ++mfi) { - const auto& gbx = amrex::grow(mfi.validbox(), nghost); + auto gbx = amrex::grow(mfi.validbox(), nghost); + if (!gbx.cellCentered()) { + gbx.enclosedCells(); + } const auto& bx = gbx & dbx; if (!bx.ok()) { continue; From 4b71037218723e0c63d54c140423ef503ac3c912 Mon Sep 17 00:00:00 2001 From: prakash <120606615+moprak-nrel@users.noreply.github.com> Date: Mon, 26 Jun 2023 13:26:58 -0600 Subject: [PATCH 25/38] add implementaiton for a log-law based wall model (#847) * add implementaiton for a log-law based wall model * make clang-format happy * make LogLaw header only and add gpu fix * linter fix * Add unit test and remove print for gpu * remove .h from cmakelist and fix gpu * Add a Schumann type model too * change default constructor for loglaw, and fix bug in utau_mean initialization * add a base amd model that can work without ABL physics to test the log-law wall-model for channel flow * Remove default constructors * Add the newly added wall-models to the doc, along with documentation for the base AMD model and Smagorisnky model * allow the specification of a reference index for the log-law instead of just the first cell * Change to a while loop, and abort if u_tau hasn't converged * make clang happy * formatting change * fix breaking changes from pr #842 * gpu fix for for the notherm model, needed to use CellSizeArray, and a GPUArray for grid spacing --------- Co-authored-by: Marc T. Henry de Frahan --- .../boundary_conditions/wall_models/LogLaw.H | 59 ++++++++ .../wall_models/ShearStressSimple.H | 40 +++++ .../wall_models/WallFunction.H | 26 +++- .../wall_models/WallFunction.cpp | 137 +++++++++++++++++- amr-wind/turbulence/LES/AMDNoTherm.H | 84 +++++++++++ amr-wind/turbulence/LES/AMDNoTherm.cpp | 80 ++++++++++ amr-wind/turbulence/LES/CMakeLists.txt | 1 + docs/sphinx/theory/theory.rst | 135 ++++++++++++++++- unit_tests/CMakeLists.txt | 1 + unit_tests/boundary_conditions/CMakeLists.txt | 4 + .../boundary_conditions/test_log_law.cpp | 47 ++++++ unit_tests/turbulence/test_turbulence_LES.cpp | 98 +++++++++++++ 12 files changed, 698 insertions(+), 14 deletions(-) create mode 100644 amr-wind/boundary_conditions/wall_models/LogLaw.H create mode 100644 amr-wind/boundary_conditions/wall_models/ShearStressSimple.H create mode 100644 amr-wind/turbulence/LES/AMDNoTherm.H create mode 100644 amr-wind/turbulence/LES/AMDNoTherm.cpp create mode 100644 unit_tests/boundary_conditions/CMakeLists.txt create mode 100644 unit_tests/boundary_conditions/test_log_law.cpp diff --git a/amr-wind/boundary_conditions/wall_models/LogLaw.H b/amr-wind/boundary_conditions/wall_models/LogLaw.H new file mode 100644 index 0000000000..dc02629da3 --- /dev/null +++ b/amr-wind/boundary_conditions/wall_models/LogLaw.H @@ -0,0 +1,59 @@ +#ifndef LogLaw_H +#define LogLaw_H + +#include "AMReX_AmrCore.H" +#include +namespace amr_wind { +struct LogLaw +{ + /* + * A simple wall model that sets the wall-shear stress + * based on computing u_tau given the horizontal velocity + * magnitude at a zref. This is akin to an explicit non-linear + * Robin boundary condition at the wall. + */ + + // Log law constants from Lee & Moser 2015 + // https://doi.org/10.1017/jfm.2015.268. + amrex::Real B{4.27}; + amrex::Real kappa{0.384}; + int max_iters = 25; // Max iterations for u_tau Newton-Raphson solve + // Reference height for log law + amrex::Real zref; + int ref_index{0}; + amrex::Real nu; // molecular viscosity + // u_tau state variable, gets updated in update_utau depending on + // the type of wall model used + amrex::Real utau_mean{1.0}; + amrex::Real wspd_mean; // mean horizontal velocity magnitude + + void update_utau_mean() { utau_mean = get_utau(wspd_mean); } + + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real + get_utau(amrex::Real wspd) const + { + amrex::Real utau_iter = -1; + amrex::Real wspd_pred; + amrex::Real wspd_deriv; + amrex::Real zplus; + amrex::Real utau = utau_mean; + int iter = 0; + while ((std::abs(utau_iter - utau) > 1e-5) && iter <= max_iters) { + utau_iter = utau; + zplus = zref * utau / nu; + // Get wspd for a given utau from log-law + wspd_pred = utau * (std::log(zplus) / kappa + B); + wspd_deriv = (1 + std::log(zplus)) / kappa + B; // d(wspd)/d(utau) + utau = + utau - (wspd_pred - wspd) / wspd_deriv; // Newton-Raphson update + ++iter; + } + if (iter == max_iters) { + amrex::Abort(); + } + return utau; + } +}; +} /* namespace amr_wind */ + +#endif /* LogLaw_H */ diff --git a/amr-wind/boundary_conditions/wall_models/ShearStressSimple.H b/amr-wind/boundary_conditions/wall_models/ShearStressSimple.H new file mode 100644 index 0000000000..679efdc26e --- /dev/null +++ b/amr-wind/boundary_conditions/wall_models/ShearStressSimple.H @@ -0,0 +1,40 @@ +#ifndef SHEARSTRESSSIMPLE_H +#define SHEARSTRESSSIMPLE_H + +#include "amr-wind/boundary_conditions/wall_models/LogLaw.H" +#include "amr-wind/wind_energy/ShearStress.H" + +namespace amr_wind { + +struct SimpleShearSchumann +{ + explicit SimpleShearSchumann(const amr_wind::LogLaw& ll) + : utau2(ll.utau_mean * ll.utau_mean), wspd_mean(ll.wspd_mean), m_ll(ll) + {} + + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real + get_shear(amrex::Real u, amrex::Real /* wspd */) const + { + return u / wspd_mean * utau2; + }; + + amrex::Real utau2; + amrex::Real wspd_mean; + const amr_wind::LogLaw m_ll; +}; +struct SimpleShearLogLaw +{ + explicit SimpleShearLogLaw(const amr_wind::LogLaw& ll) : m_ll(ll) {} + + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real + get_shear(amrex::Real u, amrex::Real wspd) const + { + amrex::Real utau = m_ll.get_utau(wspd); + return utau * utau * u / wspd; + }; + + const amr_wind::LogLaw m_ll; +}; +} // namespace amr_wind + +#endif /* SHEARSTRESSSIMPLE_H */ diff --git a/amr-wind/boundary_conditions/wall_models/WallFunction.H b/amr-wind/boundary_conditions/wall_models/WallFunction.H index 5247c285a2..fff1dd1082 100644 --- a/amr-wind/boundary_conditions/wall_models/WallFunction.H +++ b/amr-wind/boundary_conditions/wall_models/WallFunction.H @@ -3,6 +3,8 @@ #include "amr-wind/CFDSim.H" #include "amr-wind/core/FieldBCOps.H" +#include "amr-wind/utilities/FieldPlaneAveragingFine.H" +#include "amr-wind/boundary_conditions/wall_models/LogLaw.H" namespace amr_wind { @@ -16,9 +18,14 @@ namespace amr_wind { class WallFunction { public: - explicit WallFunction(const CFDSim& sim); + explicit WallFunction(CFDSim& sim); - amrex::Real utau() const { return m_utau; } + amrex::Real utau() const { return m_log_law.utau_mean; } + LogLaw log_law() const { return m_log_law; } + + //! Update the mean velocity at a given timestep + void update_umean(); + void update_utau_mean(); ~WallFunction() = default; @@ -27,7 +34,10 @@ private: const amrex::AmrCore& m_mesh; - amrex::Real m_utau{0.0}; + //! LogLaw instance + LogLaw m_log_law; + int m_direction{2}; ///< Direction normal to wall, hardcoded to z + VelPlaneAveragingFine m_pa_vel; }; /** Applies a shear-stress value at the domain boundary @@ -38,15 +48,21 @@ private: class VelWallFunc : public FieldBCIface { public: - VelWallFunc(Field& velocity, const WallFunction& wall_func); + VelWallFunc(Field& velocity, WallFunction& wall_func); void operator()(Field& velocity, const FieldState rho_state) override; static void wall_model( Field& velocity, const FieldState rho_state, const amrex::Real utau); + template + static void wall_model( + Field& velocity, + const FieldState rho_state, + const ShearStressSimple& tau); + private: - const WallFunction& m_wall_func; + WallFunction& m_wall_func; std::string m_wall_shear_stress_type{"constant"}; }; } // namespace amr_wind diff --git a/amr-wind/boundary_conditions/wall_models/WallFunction.cpp b/amr-wind/boundary_conditions/wall_models/WallFunction.cpp index d33a2298a1..342e49b314 100644 --- a/amr-wind/boundary_conditions/wall_models/WallFunction.cpp +++ b/amr-wind/boundary_conditions/wall_models/WallFunction.cpp @@ -1,4 +1,5 @@ #include "amr-wind/boundary_conditions/wall_models/WallFunction.H" +#include "amr-wind/boundary_conditions/wall_models/ShearStressSimple.H" #include "amr-wind/utilities/tensor_ops.H" #include "amr-wind/utilities/trig_ops.H" #include "amr-wind/diffusion/diffusion.H" @@ -11,30 +12,46 @@ namespace amr_wind { -WallFunction::WallFunction(const CFDSim& sim) : m_sim(sim), m_mesh(m_sim.mesh()) +WallFunction::WallFunction(CFDSim& sim) + : m_sim(sim), m_mesh(m_sim.mesh()), m_pa_vel(sim, m_direction) { { amrex::ParmParse pp("BodyForce"); amrex::Vector body_force{{0.0, 0.0, 0.0}}; pp.getarr("magnitude", body_force); - m_utau = std::sqrt(body_force[0]); - AMREX_ALWAYS_ASSERT_WITH_MESSAGE( - std::abs(body_force[1]) < 1e-16, - "body force in y should be zero for this wall function"); + m_log_law.utau_mean = std::sqrt(std::sqrt( + body_force[0] * body_force[0] + body_force[1] * body_force[1])); AMREX_ALWAYS_ASSERT_WITH_MESSAGE( std::abs(body_force[2]) < 1e-16, "body force in z should be zero for this wall function"); } + { + amrex::ParmParse pp("transport"); + pp.query("viscosity", m_log_law.nu); + } + { + amrex::ParmParse pp("WallFunction"); + // Reference height for log-law + if (pp.contains("log_law_ref_index")) { + pp.get("log_law_ref_index", m_log_law.ref_index); + } + const auto& geom = m_mesh.Geom(0); + m_log_law.zref = + (geom.ProbLo(m_direction) + + (m_log_law.ref_index + 0.5) * geom.CellSize(m_direction)); + } } -VelWallFunc::VelWallFunc(Field& /*unused*/, const WallFunction& wall_func) +VelWallFunc::VelWallFunc(Field& /*unused*/, WallFunction& wall_func) : m_wall_func(wall_func) { amrex::ParmParse pp("WallFunction"); pp.query("wall_shear_stress_type", m_wall_shear_stress_type); m_wall_shear_stress_type = amrex::toLower(m_wall_shear_stress_type); - if (m_wall_shear_stress_type == "constant") { + if (m_wall_shear_stress_type == "constant" || + m_wall_shear_stress_type == "log_law" || + m_wall_shear_stress_type == "schumann") { amrex::Print() << "Shear Stress model: " << m_wall_shear_stress_type << std::endl; } else { @@ -42,6 +59,95 @@ VelWallFunc::VelWallFunc(Field& /*unused*/, const WallFunction& wall_func) } } +template +void VelWallFunc::wall_model( + Field& velocity, const FieldState rho_state, const ShearStressSimple& tau) +{ + BL_PROFILE("amr-wind::VelWallFunc"); + constexpr int idim = 2; + const auto& repo = velocity.repo(); + const auto& density = repo.get_field("density", rho_state); + const auto& viscosity = repo.get_field("velocity_mueff"); + const int nlevels = repo.num_active_levels(); + const auto idx_offset = tau.m_ll.ref_index; + + amrex::Orientation zlo(amrex::Direction::z, amrex::Orientation::low); + amrex::Orientation zhi(amrex::Direction::z, amrex::Orientation::high); + if ((velocity.bc_type()[zlo] != BC::wall_model) && + (velocity.bc_type()[zhi] != BC::wall_model)) { + return; + } + + for (int lev = 0; lev < nlevels; ++lev) { + const auto& geom = repo.mesh().Geom(lev); + const auto& domain = geom.Domain(); + amrex::MFItInfo mfi_info{}; + + const auto& rho_lev = density(lev); + auto& vel_lev = velocity(lev); + auto& vold_lev = velocity.state(FieldState::Old)(lev); + const auto& eta_lev = viscosity(lev); + + if (amrex::Gpu::notInLaunchRegion()) { + mfi_info.SetDynamic(true); + } +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (amrex::MFIter mfi(vel_lev, mfi_info); mfi.isValid(); ++mfi) { + const auto& bx = mfi.validbox(); + auto varr = vel_lev.array(mfi); + auto vold_arr = vold_lev.array(mfi); + auto den = rho_lev.array(mfi); + auto eta = eta_lev.array(mfi); + + if (bx.smallEnd(idim) == domain.smallEnd(idim) && + velocity.bc_type()[zlo] == BC::wall_model) { + amrex::ParallelFor( + amrex::bdryLo(bx, idim), + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real mu = eta(i, j, k); + const amrex::Real uu = + vold_arr(i, j, k + idx_offset, 0); + const amrex::Real vv = + vold_arr(i, j, k + idx_offset, 1); + const amrex::Real wspd = std::sqrt(uu * uu + vv * vv); + // Dirichlet BC + varr(i, j, k - 1, 2) = 0.0; + + // Shear stress BC + varr(i, j, k - 1, 0) = + tau.get_shear(uu, wspd) / mu * den(i, j, k); + varr(i, j, k - 1, 1) = + tau.get_shear(vv, wspd) / mu * den(i, j, k); + }); + } + + if (bx.bigEnd(idim) == domain.bigEnd(idim) && + velocity.bc_type()[zhi] == BC::wall_model) { + amrex::ParallelFor( + amrex::bdryHi(bx, idim), + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real mu = eta(i, j, k - 1); + const amrex::Real uu = + vold_arr(i, j, k - 1 - idx_offset, 0); + const amrex::Real vv = + vold_arr(i, j, k - 1 - idx_offset, 1); + const amrex::Real wspd = std::sqrt(uu * uu + vv * vv); + // Dirichlet BC + varr(i, j, k, 2) = 0.0; + + // Shear stress BC + varr(i, j, k, 0) = + -tau.get_shear(uu, wspd) / mu * den(i, j, k); + varr(i, j, k, 1) = + -tau.get_shear(vv, wspd) / mu * den(i, j, k); + }); + } + } + } +} + void VelWallFunc::wall_model( Field& velocity, const FieldState rho_state, const amrex::Real utau) { @@ -119,7 +225,24 @@ void VelWallFunc::operator()(Field& velocity, const FieldState rho_state) { if (m_wall_shear_stress_type == "constant") { wall_model(velocity, rho_state, m_wall_func.utau()); + } else if (m_wall_shear_stress_type == "log_law") { + m_wall_func.update_umean(); + m_wall_func.update_utau_mean(); + auto tau = SimpleShearLogLaw(m_wall_func.log_law()); + wall_model(velocity, rho_state, tau); + } else if (m_wall_shear_stress_type == "schumann") { + m_wall_func.update_umean(); + auto tau = SimpleShearSchumann(m_wall_func.log_law()); + wall_model(velocity, rho_state, tau); } } +void WallFunction::update_umean() +{ + m_pa_vel(); + m_log_law.wspd_mean = + m_pa_vel.line_hvelmag_average_interpolated(m_log_law.zref); +} + +void WallFunction::update_utau_mean() { m_log_law.update_utau_mean(); } } // namespace amr_wind diff --git a/amr-wind/turbulence/LES/AMDNoTherm.H b/amr-wind/turbulence/LES/AMDNoTherm.H new file mode 100644 index 0000000000..e937bfdbbf --- /dev/null +++ b/amr-wind/turbulence/LES/AMDNoTherm.H @@ -0,0 +1,84 @@ +#ifndef AMDNOTHERM_H +#define AMDNOTHERM_H + +#include +#include +#include +#include "amr-wind/incflo_enums.H" +#include "amr-wind/turbulence/TurbModelBase.H" +#include "amr-wind/core/FieldRepo.H" +#include "amr-wind/fvm/stencils.H" + +namespace amr_wind::turbulence { +/** AMD LES Model + * \ingroup turb_model + */ +template +class AMDNoTherm : public TurbModelBase +{ +public: + static std::string identifier() + { + return "AMDNoTherm-" + Transport::identifier(); + } + + explicit AMDNoTherm(CFDSim& sim); + + //! Model name for debugging purposes + std::string model_name() const override { return "AMDNoTherm"; } + + //! Update the turbulent viscosity field + void update_turbulent_viscosity( + const FieldState fstate, const DiffusionType /*unused*/) override; + + //! Return model coefficients dictionary + TurbulenceModel::CoeffsDictType model_coeffs() const override; + + //! Parse turbulence model coefficients for this model + void parse_model_coeffs() override; + + //! No post advance work for this model + void post_advance_work() override{}; + +private: + //! Poincare coefficient (default value set for 2nd order AMR-wind + //! discretization) + amrex::Real m_C{0.333333333333333}; + const Field& m_vel; + const Field& m_rho; +}; + +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real amd_base_muvel( + int i, + int j, + int k, + const amrex::GpuArray dx, // Grid spacing + amrex::Real C, // Poincare const + amrex::Array4 const& gradVel) noexcept +{ + + amrex::Real num_shear = 0; + amrex::Real denom = 0; + for (int ii = 0; ii < AMREX_SPACEDIM; ++ii) { + for (int jj = 0; jj < AMREX_SPACEDIM; ++jj) { + denom = denom + gradVel(i, j, k, ii * AMREX_SPACEDIM + jj) * + gradVel(i, j, k, ii * AMREX_SPACEDIM + jj); + amrex::Real sij = + 0.5 * (gradVel(i, j, k, ii * AMREX_SPACEDIM + jj) + + gradVel(i, j, k, jj * AMREX_SPACEDIM + ii)); + for (int kk = 0; kk < AMREX_SPACEDIM; ++kk) { + amrex::Real dkui = gradVel(i, j, k, ii * AMREX_SPACEDIM + kk); + amrex::Real dkuj = gradVel(i, j, k, jj * AMREX_SPACEDIM + kk); + num_shear = num_shear + dkui * dkuj * dx[kk] * dx[kk] * sij; + } + } + } + denom = std::max(1e-15, denom); + num_shear = -C * num_shear; + + return std::max(1e-15, num_shear / denom); +} + +} // namespace amr_wind::turbulence + +#endif /* AMDNoTherm_H */ diff --git a/amr-wind/turbulence/LES/AMDNoTherm.cpp b/amr-wind/turbulence/LES/AMDNoTherm.cpp new file mode 100644 index 0000000000..1358c16073 --- /dev/null +++ b/amr-wind/turbulence/LES/AMDNoTherm.cpp @@ -0,0 +1,80 @@ +#include +#include + +#include "amr-wind/fvm/gradient.H" +#include "amr-wind/turbulence/LES/AMDNoTherm.H" +#include "amr-wind/turbulence/TurbModelDefs.H" + +#include "AMReX_REAL.H" +#include "AMReX_MultiFab.H" +#include "AMReX_ParmParse.H" + +namespace amr_wind { +namespace turbulence { + +template +AMDNoTherm::AMDNoTherm(CFDSim& sim) + : TurbModelBase(sim) + , m_vel(sim.repo().get_field("velocity")) + , m_rho(sim.repo().get_field("density")) +{} + +template +void AMDNoTherm::parse_model_coeffs() +{ + const std::string coeffs_dict = this->model_name() + "_coeffs"; + amrex::ParmParse pp(coeffs_dict); + pp.query("C_poincare", m_C); +} + +template +void AMDNoTherm::update_turbulent_viscosity( + const FieldState fstate, const DiffusionType /*unused*/) +{ + BL_PROFILE( + "amr-wind::" + this->identifier() + "::update_turbulent_viscosity"); + + auto& mu_turb = this->mu_turb(); + auto& repo = mu_turb.repo(); + const auto& vel = m_vel.state(fstate); + const auto& den = m_rho.state(fstate); + auto gradVel = repo.create_scratch_field(AMREX_SPACEDIM * AMREX_SPACEDIM); + fvm::gradient(*gradVel, vel); + const auto& geom_vec = repo.mesh().Geom(); + + const amrex::Real C_poincare = this->m_C; + + const int nlevels = repo.num_active_levels(); + for (int lev = 0; lev < nlevels; ++lev) { + const auto& geom = geom_vec[lev]; + + const auto& dx = geom.CellSizeArray(); + for (amrex::MFIter mfi(mu_turb(lev)); mfi.isValid(); ++mfi) { + const auto& bx = mfi.tilebox(); + const auto& gradVel_arr = (*gradVel)(lev).array(mfi); + const auto& mu_arr = mu_turb(lev).array(mfi); + const auto& rho_arr = den(lev).const_array(mfi); + amrex::ParallelFor( + bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { + const amrex::Real rho = rho_arr(i, j, k); + mu_arr(i, j, k) = + rho * + amd_base_muvel(i, j, k, dx, C_poincare, gradVel_arr); + }); + } + } + + mu_turb.fillpatch(this->m_sim.time().current_time()); +} + +template +TurbulenceModel::CoeffsDictType AMDNoTherm::model_coeffs() const +{ + return TurbulenceModel::CoeffsDictType{{"C_poincare", this->m_C}}; +} + +} // namespace turbulence + +INSTANTIATE_TURBULENCE_MODEL(AMDNoTherm); + +} // namespace amr_wind diff --git a/amr-wind/turbulence/LES/CMakeLists.txt b/amr-wind/turbulence/LES/CMakeLists.txt index 1baced8e00..bae52243a3 100644 --- a/amr-wind/turbulence/LES/CMakeLists.txt +++ b/amr-wind/turbulence/LES/CMakeLists.txt @@ -2,4 +2,5 @@ target_sources(${amr_wind_lib_name} PRIVATE Smagorinsky.cpp OneEqKsgs.cpp AMD.cpp + AMDNoTherm.cpp ) diff --git a/docs/sphinx/theory/theory.rst b/docs/sphinx/theory/theory.rst index 47a87083a3..4ebb455dbe 100644 --- a/docs/sphinx/theory/theory.rst +++ b/docs/sphinx/theory/theory.rst @@ -192,9 +192,47 @@ Turbulence Models LES models for subgrid scales ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Smagorinsky model +^^^^^^^^^^^^^^^^^ -AMD model -^^^^^^^^^^^ +Simple eddy viscosity model, the dissipation is calculated using the +resolved strain rate tensor and the grid resolution as + +.. math:: + + \begin{aligned} + \tau_{ij} &= -2 \nu_t \widetilde{S}_{ij} \\ + \nu_t &= C_s^2 \Delta^2 (2 \langle S_{ij} S_{ij} \rangle)^{\frac{1}{2}} + \end{aligned} + + +AMDNoTherm model +^^^^^^^^^^^^^^^^^ +This is the implementation of the base AMD model, useful for flows without a temperature field. + +The eddy viscosity is calculated using an anisotropic derivative with a +different filter width in each direction + +.. math:: + + \begin{aligned} + \hat{\partial}_i &= \sqrt{C} \delta_i \partial_i \textrm{ for } i=1,2,3 \\ + C &= 1/3, \textrm{ Poincare coefficient for } 2^{nd} \textrm{ order gradient} \\ + \delta_i &= \textrm{Filter width along dimension } i \textrm{ for anisotropic grids} + \end{aligned} + +The anisotropic derivative is used to define the eddy viscosity as + +.. math:: + + \begin{aligned} + \tau_{ij} &= -2 \nu_t \widetilde{S}_{ij} \\ + \nu_t &= \frac{- (\hat{\partial}_k \widetilde{u}_i) (\hat{\partial}_k \widetilde{u}_j) \widetilde{S}_{ij}}{ (\partial_l \widetilde{u}_m) (\partial_l \widetilde{u}_m) } + \end{aligned} + + +AMD model (for ABL) +^^^^^^^^^^^^^^^^^^^ The eddy viscosity is calculated using an anisotropic derivative with a different filter width in each direction @@ -316,6 +354,99 @@ There is a simple unit test for both :math:`\nu_t` and :math:`D_e` in ``unit_tests/turbulence/test_turbulence_LES.cpp`` under ``test_AMD_setup_calc``. +Wall models +----------- +The wall models descibed in this section are implemented in ``AMR-wind`` for +running wall-bounded flows (non-ABL cases). + +Log-law wall model +~~~~~~~~~~~~~~~~~~ + +This wall model computes the local :math:`u_\tau` from the velocity at +the first grid cell, and uses this to compute the shear stress, which is +then used as a boundary condition. + +The log law: + +.. math:: u_{\mathrm{mag}} = u_\tau \left(\frac{1}{\kappa}\log\left(\frac{u_\tau z}{\nu}\right) + B\right). \label{eq:loglaw} + +Given a horizontal velocity magnitude +:math:`u_{\mathrm{mag}} = \sqrt{u^2 + v^2}` at +:math:`z = z_{\mathrm{ref}}`, :math:`u_\tau` can be computed using a +non-linear solve to satisfy `[eq:loglaw] <#eq:loglaw>`__. + +In ``AMR-wind`` Newton-Raphson iterations are used with a convergence +criterion of :math:`\lvert u_\tau^{n+1} - u_\tau^n \rvert < 10^{-5}`. +For this, derivative of +:math:`\frac{\partial u_{\mathrm{mag}}}{\partial {u_\tau}}` is used, + +.. math:: \frac{\partial u_{\mathrm{mag}}}{\partial {u_\tau}} = \left(\frac{1}{\kappa}\left(1+\log\left(\frac{u_\tau z_{\mathrm{ref}}}{\nu}\right)\right) + B\right) + +.. math:: u_\tau^{n+1} = u_\tau^{n} - \left(u_\tau^n \left(\frac{1}{\kappa}\log\left(\frac{u_\tau^n z_{\mathrm{ref}}}{\nu}\right) + B\right) - u_{\mathrm{mag}}\right)/\frac{\partial u_{\mathrm{mag}}}{\partial {u_\tau}}. + +Finally, the shear stress is calculated as, + +.. math:: + + \begin{aligned} + \tau_{xz} &= u_\tau^2 \frac{u}{u_\mathrm{mag}} \\ + \tau_{yz} &= u_\tau^2 \frac{v}{u_\mathrm{mag}} + \end{aligned} + +Constant stress model +~~~~~~~~~~~~~~~~~~~~~ + +NOTE: This wall model will be ill-posed unless combined with a Dirichlet +boundary condition on the other wall, :math:`\langle u \rangle` can +drift by a constant otherwise. + +This is a trivial wall model, where the shear stresses are specified as +constants. For a pressure gradient driven channel, + +.. math:: + + \begin{aligned} + u_\tau^2 &= -\frac{\mathrm{d} P}{\mathrm{d} x} \\ + \tau_{xz} &= u_\tau^2 \\ + \tau_{yz} &= 0 + \end{aligned} + +Schumann model +~~~~~~~~~~~~~~ + +NOTE: This wall model will be ill-posed unless combined with a Dirichlet +boundary condition on the other wall, :math:`\langle u \rangle` can +drift by a constant otherwise. + +This model is a modified version of the constant stress model, where the +fluctuations from a reference height :math:`z_\mathrm{ref}` are used to +add fluctuations in the shear stress. + +.. math:: + + \begin{aligned} + u_\tau^2 &= -\frac{\mathrm{d} P}{\mathrm{d} x} \\ + \tau_{xz} &= u_\tau^2 \frac{u}{\langle u_\mathrm{mag} \rangle} \\ + \tau_{yz} &= u_\tau^2 \frac{v}{\langle u_\mathrm{mag} \rangle} + \end{aligned} + +where, :math:`\langle u_\mathrm{mag} \rangle` is the planar average of +:math:`u_{\mathrm{mag}} = \sqrt{u^2 + v^2}` at :math:`z_\mathrm{ref}`. + +Symmetric wall boundary +~~~~~~~~~~~~~~~~~~~~~~~ + +This is a boundary condition to for flows with a symmetry across the +z direction (example: *half-channel* simulations) at the centerline. + +.. math:: + + \begin{aligned} + \tau_{xz} &= 0 \\ + \tau_{yz} &= 0 \\ + w &= 0 + \end{aligned} + Navigating source code ------------------------ diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index 5fab38a2e7..065884d88c 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(fvm) add_subdirectory(multiphase) add_subdirectory(ocean_waves) add_subdirectory(projection) +add_subdirectory(boundary_conditions) if(AMR_WIND_ENABLE_MASA) add_subdirectory(mms) diff --git a/unit_tests/boundary_conditions/CMakeLists.txt b/unit_tests/boundary_conditions/CMakeLists.txt new file mode 100644 index 0000000000..7d6460d6f2 --- /dev/null +++ b/unit_tests/boundary_conditions/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${amr_wind_unit_test_exe_name} PRIVATE + # test cases + test_log_law.cpp + ) diff --git a/unit_tests/boundary_conditions/test_log_law.cpp b/unit_tests/boundary_conditions/test_log_law.cpp new file mode 100644 index 0000000000..dd3c159357 --- /dev/null +++ b/unit_tests/boundary_conditions/test_log_law.cpp @@ -0,0 +1,47 @@ +#include "gtest/gtest.h" +#include "aw_test_utils/MeshTest.H" +#include "aw_test_utils/test_utils.H" +#include "amr-wind/boundary_conditions/wall_models/LogLaw.H" + +namespace amr_wind_tests { + +class LogLawTest : public MeshTest +{ +protected: + void populate_parameters() override { MeshTest::populate_parameters(); } + amrex::Real log_law_actual(const amrex::Real utau) const + { + return utau * (std::log(zref * utau / nu) / 0.384 + 4.27); + } + + const amrex::Real zref = 1.0 / 32.0; + // Molecular viscosity + const amrex::Real nu = 1e-4; + // Set test tolerance to convergence test in LogLaw + const amrex::Real tol = 1.0e-5; +}; + +TEST_F(LogLawTest, test_log_law) +{ + populate_parameters(); + amr_wind::LogLaw ll; + ll.zref = zref; + ll.nu = nu; + + amrex::Real utau_expected = 0.1; + amrex::Real wspd = log_law_actual(utau_expected); + auto utau = ll.get_utau(wspd); + EXPECT_NEAR(utau, 0.1, tol); + + utau_expected = 0.05; + EXPECT_NEAR(ll.get_utau(log_law_actual(utau_expected)), utau_expected, tol); + + utau_expected = 2.0; + EXPECT_NEAR(ll.get_utau(log_law_actual(utau_expected)), utau_expected, tol); + + utau_expected = 0.5; + ll.wspd_mean = log_law_actual(utau_expected); + ll.update_utau_mean(); + EXPECT_NEAR(ll.utau_mean, utau_expected, tol); +} +} // namespace amr_wind_tests diff --git a/unit_tests/turbulence/test_turbulence_LES.cpp b/unit_tests/turbulence/test_turbulence_LES.cpp index 4fd1ec9076..03d028665b 100644 --- a/unit_tests/turbulence/test_turbulence_LES.cpp +++ b/unit_tests/turbulence/test_turbulence_LES.cpp @@ -69,6 +69,37 @@ void init_field_amd(amr_wind::Field& fld, amrex::Real scale) } } +void init_field_incomp(amr_wind::Field& fld, amrex::Real scale) +{ + const auto& mesh = fld.repo().mesh(); + const int nlevels = fld.repo().num_active_levels(); + + amrex::Real offset = 0.0; + if (fld.field_location() == amr_wind::FieldLoc::CELL) { + offset = 0.5; + } + + for (int lev = 0; lev < nlevels; ++lev) { + const auto& dx = mesh.Geom(lev).CellSizeArray(); + const auto& problo = mesh.Geom(lev).ProbLoArray(); + + for (amrex::MFIter mfi(fld(lev)); mfi.isValid(); ++mfi) { + auto bx = mfi.growntilebox(); + const auto& farr = fld(lev).array(mfi); + + amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) { + const amrex::Real x = problo[0] + (i + offset) * dx[0]; + const amrex::Real y = problo[1] + (j + offset) * dx[1]; + const amrex::Real z = problo[2] + (k + offset) * dx[2]; + + farr(i, j, k, 0) = 1 * x * scale; + farr(i, j, k, 1) = -2 * y * scale; + farr(i, j, k, 2) = 1 * z * scale; + }); + } + } +} + void init_field1(amr_wind::Field& fld, amrex::Real tgrad) { const auto& mesh = fld.repo().mesh(); @@ -402,4 +433,71 @@ TEST_F(TurbLESTest, test_AMD_setup_calc) EXPECT_NEAR(ae_min_val, amd_ae_answer, tol); EXPECT_NEAR(ae_max_val, amd_ae_answer, tol); } + +TEST_F(TurbLESTest, test_AMDNoTherm_setup_calc) +{ + // Parser inputs for turbulence model + const amrex::Real C = 0.3; + const amrex::Real rho0 = 1.0; + { + amrex::ParmParse pp("turbulence"); + pp.add("model", (std::string) "AMDNoTherm"); + } + { + amrex::ParmParse pp("AMDNoTherm_coeffs"); + pp.add("C_poincare", C); + } + { + amrex::ParmParse pp("incflo"); + pp.add("density", rho0); + amrex::Vector vvec{8.0, 0.0, 0.0}; + pp.addarr("velocity", vvec); + } + + // Initialize necessary parts of solver + populate_parameters(); + initialize_mesh(); + auto& pde_mgr = sim().pde_manager(); + pde_mgr.register_icns(); + sim().init_physics(); + + // Create turbulence model + sim().create_turbulence_model(); + // Get turbulence model + auto& tmodel = sim().turbulence_model(); + + // Get coefficients + auto model_dict = tmodel.model_coeffs(); + + for (const std::pair n : model_dict) { + // Only a single model parameter, Cs + EXPECT_EQ(n.first, "C_poincare"); + EXPECT_EQ(n.second, C); + } + + // Constants for fields + const amrex::Real scale = 2.0; + // Set up velocity field with constant strainrate + auto& vel = sim().repo().get_field("velocity"); + init_field_incomp(vel, scale); + // Set up uniform unity density field + auto& dens = sim().repo().get_field("density"); + dens.setVal(rho0); + + // Update turbulent viscosity directly + tmodel.update_turbulent_viscosity( + amr_wind::FieldState::New, DiffusionType::Crank_Nicolson); + auto& muturb = sim().repo().get_field("mu_turb"); + + // Check values of turbulent viscosity + const auto min_val = utils::field_min(muturb); + const auto max_val = utils::field_max(muturb); + const amrex::Real tol = 1e-12; + + const amrex::Real amd_answer = -C * std::pow(scale, 3) * + (dx * dx - 8 * dy * dy + dz * dz) / + (6 * scale * scale); + EXPECT_NEAR(min_val, amd_answer, tol); + EXPECT_NEAR(max_val, amd_answer, tol); +} } // namespace amr_wind_tests From ef0a19870b15b6025a3e70a62ed4fb896a9e23b5 Mon Sep 17 00:00:00 2001 From: sbidadi9 <96149491+sbidadi9@users.noreply.github.com> Date: Thu, 29 Jun 2023 16:29:24 -0600 Subject: [PATCH 26/38] Implicit treatment of source terms for SST and IDDES (#866) * Implicit treatment of source terms for SST and IDDES * Reference to Menter's paper --- amr-wind/turbulence/RANS/KOmegaSST.cpp | 21 +++++++++++++++++++-- amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp | 18 +++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/amr-wind/turbulence/RANS/KOmegaSST.cpp b/amr-wind/turbulence/RANS/KOmegaSST.cpp index 5ebbc21fdd..d54e3208c2 100644 --- a/amr-wind/turbulence/RANS/KOmegaSST.cpp +++ b/amr-wind/turbulence/RANS/KOmegaSST.cpp @@ -197,12 +197,13 @@ void KOmegaSST::update_turbulent_viscosity( const amrex::Real diss_amb = beta_star * rho_arr(i, j, k) * sdr_amb * tke_amb; + diss_arr(i, j, k) = -beta_star * rho_arr(i, j, k) * tke_arr(i, j, k) * sdr_arr(i, j, k) + diss_amb; - tke_lhs_arr(i, j, k) = 0.5 * beta_star * rho_arr(i, j, k) * + tke_lhs_arr(i, j, k) = beta_star * rho_arr(i, j, k) * sdr_arr(i, j, k) * deltaT; // For SDR equation: @@ -221,6 +222,8 @@ void KOmegaSST::update_turbulent_viscosity( if (diff_type == DiffusionType::Crank_Nicolson) { + tke_lhs_arr(i, j, k) = 0.5 * tke_lhs_arr(i, j, k); + sdr_src_arr(i, j, k) = production_omega; sdr_diss_arr(i, j, k) = cross_diffusion; @@ -230,8 +233,22 @@ void KOmegaSST::update_turbulent_viscosity( 0.5 * std::abs(cross_diffusion) / (sdr_arr(i, j, k) + 1e-15)) * deltaT; - } else { + } else if (diff_type == DiffusionType::Implicit) { + /* Source term linearization is based on Florian + Menter's (1993) AIAA paper */ + diss_arr(i, j, k) = 0.0; + + sdr_src_arr(i, j, k) = production_omega; + + sdr_diss_arr(i, j, k) = 0.0; + sdr_lhs_arr(i, j, k) = + (2.0 * rho_arr(i, j, k) * beta * sdr_arr(i, j, k) + + std::abs(cross_diffusion) / + (sdr_arr(i, j, k) + 1e-15)) * + deltaT; + + } else { sdr_src_arr(i, j, k) = production_omega + cross_diffusion; diff --git a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp index e7127b3c55..3d863cc4ee 100644 --- a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp +++ b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp @@ -251,7 +251,7 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( tke_arr(i, j, k) / l_iddes + diss_amb; - tke_lhs_arr(i, j, k) = 0.5 * rho_arr(i, j, k) * + tke_lhs_arr(i, j, k) = rho_arr(i, j, k) * std::sqrt(tke_arr(i, j, k)) / l_iddes * deltaT; @@ -273,6 +273,8 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( if (diff_type == DiffusionType::Crank_Nicolson) { + tke_lhs_arr(i, j, k) = 0.5 * tke_lhs_arr(i, j, k); + sdr_src_arr(i, j, k) = production_omega; sdr_diss_arr(i, j, k) = cross_diffusion; @@ -283,6 +285,20 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( (sdr_arr(i, j, k) + 1e-15)) * deltaT; + } else if (diff_type == DiffusionType::Implicit) { + /* Source term linearization is based on Florian + Menter's (1993) AIAA paper */ + diss_arr(i, j, k) = 0.0; + + sdr_src_arr(i, j, k) = production_omega; + + sdr_diss_arr(i, j, k) = 0.0; + + sdr_lhs_arr(i, j, k) = + (2.0 * rho_arr(i, j, k) * beta * sdr_arr(i, j, k) + + std::abs(cross_diffusion) / + (sdr_arr(i, j, k) + 1e-15)) * + deltaT; } else { sdr_src_arr(i, j, k) = production_omega + cross_diffusion; From 05367b31460c0d9af4d36ac5c6582c56f07d25e2 Mon Sep 17 00:00:00 2001 From: Matt Churchfield Date: Wed, 12 Jul 2023 11:53:30 -0400 Subject: [PATCH 27/38] Remove 90 deg Latitude Constraint from Hurricane Forcing (#872) * Removing the constraint that latitude must be 90 degrees for the hurricane forcing. * Small update for formatting. * Ran clang-format... * Putting back what I deleted with clang-format... Nice job, Matt... --------- Co-authored-by: Matt Churchfield --- .../icns/source_terms/HurricaneForcing.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/amr-wind/equation_systems/icns/source_terms/HurricaneForcing.cpp b/amr-wind/equation_systems/icns/source_terms/HurricaneForcing.cpp index 72cc204108..0df4a05378 100644 --- a/amr-wind/equation_systems/icns/source_terms/HurricaneForcing.cpp +++ b/amr-wind/equation_systems/icns/source_terms/HurricaneForcing.cpp @@ -21,14 +21,12 @@ HurricaneForcing::HurricaneForcing(const CFDSim& sim) : m_mesh(sim.mesh()) amrex::ParmParse pp("CoriolisForcing"); amrex::Real rot_time_period = 86400.0; pp.query("rotational_time_period", rot_time_period); - m_coriolis_factor = 2.0 * utils::two_pi() / rot_time_period; - amrex::Print() << "Geostrophic forcing: Coriolis factor = " - << m_coriolis_factor << std::endl; amrex::Real latitude = 90.0; pp.query("latitude", latitude); - AMREX_ALWAYS_ASSERT( - amrex::Math::abs(latitude - 90.0) < - static_cast(vs::DTraits::eps())); + m_coriolis_factor = (2.0 * utils::two_pi() / rot_time_period) * + std::sin(utils::radians(latitude)); + amrex::Print() << "Geostrophic forcing: Coriolis factor = " + << m_coriolis_factor << std::endl; } { From 7439a2bf587008e04c47cd2659fac03c2cfd1833 Mon Sep 17 00:00:00 2001 From: Michael B Kuhn <31661049+mbkuhn@users.noreply.github.com> Date: Wed, 12 Jul 2023 11:42:12 -0600 Subject: [PATCH 28/38] Actuators: sample mac velocity instead of cell-centered velocity (#868) * new sampling is default for TurbineFastLine and TurbineFastDisk * big improvement shown for ALM * new input parameter available for Actuator, added to documentation * unit test confirms implementation of face velocity sampling --- amr-wind/wind_energy/actuator/Actuator.H | 2 + amr-wind/wind_energy/actuator/Actuator.cpp | 23 ++- .../wind_energy/actuator/ActuatorContainer.H | 12 ++ .../actuator/ActuatorContainer.cpp | 145 ++++++++++++++++++ docs/sphinx/user/inputs_Actuator.rst | 15 ++ .../wind_energy/actuator/test_act_utils.H | 26 +++- .../actuator/test_actuator_sampling.cpp | 125 +++++++++++++++ 7 files changed, 340 insertions(+), 8 deletions(-) diff --git a/amr-wind/wind_energy/actuator/Actuator.H b/amr-wind/wind_energy/actuator/Actuator.H index 7d6c2bda36..1486c55083 100644 --- a/amr-wind/wind_energy/actuator/Actuator.H +++ b/amr-wind/wind_energy/actuator/Actuator.H @@ -75,6 +75,8 @@ private: std::vector> m_actuators; std::unique_ptr m_container; + + bool m_sample_nmhalf{false}; }; } // namespace actuator diff --git a/amr-wind/wind_energy/actuator/Actuator.cpp b/amr-wind/wind_energy/actuator/Actuator.cpp index d8cb7e5698..0ecc66ae2e 100644 --- a/amr-wind/wind_energy/actuator/Actuator.cpp +++ b/amr-wind/wind_energy/actuator/Actuator.cpp @@ -34,6 +34,12 @@ void Actuator::pre_init_actions() std::string type; pp.query("type", type); pp1.query("type", type); + if (type == "TurbineFastLine" || type == "TurbineFastDisk") { + // Only one kind of sampling can be chosen. If OpenFAST is involved + // in the Actuator type, the default behavior is to sample velocity + // at n-1/2 so that forcing is at n+1/2 + m_sample_nmhalf = true; + } AMREX_ALWAYS_ASSERT(!type.empty()); auto obj = ActuatorModel::create(type, m_sim, tname, i); @@ -44,6 +50,9 @@ void Actuator::pre_init_actions() obj->read_inputs(inp); m_actuators.emplace_back(std::move(obj)); } + + // Check if sampling should be modified aside from default behavior + pp.query("sample_vel_nmhalf", m_sample_nmhalf); } void Actuator::post_init_actions() @@ -179,9 +188,17 @@ void Actuator::update_positions() m_container->update_positions(); // Sample velocities at the new locations - const auto& vel = m_sim.repo().get_field("velocity"); - const auto& density = m_sim.repo().get_field("density"); - m_container->sample_fields(vel, density); + if (m_sample_nmhalf) { + const auto& umac = m_sim.repo().get_field("u_mac"); + const auto& vmac = m_sim.repo().get_field("v_mac"); + const auto& wmac = m_sim.repo().get_field("w_mac"); + const auto& density = m_sim.repo().get_field("density"); + m_container->sample_fields(umac, vmac, wmac, density); + } else { + const auto& vel = m_sim.repo().get_field("velocity"); + const auto& density = m_sim.repo().get_field("density"); + m_container->sample_fields(vel, density); + } } /** Provide updated velocities from container to actuator instances diff --git a/amr-wind/wind_energy/actuator/ActuatorContainer.H b/amr-wind/wind_energy/actuator/ActuatorContainer.H index 7cf57cf345..030862b2b2 100644 --- a/amr-wind/wind_energy/actuator/ActuatorContainer.H +++ b/amr-wind/wind_energy/actuator/ActuatorContainer.H @@ -78,6 +78,12 @@ public: void sample_fields(const Field& vel, const Field& density); + void sample_fields( + const Field& umac, + const Field& vmac, + const Field& wmac, + const Field& density); + int num_actuator_points() const { return static_cast(m_data.position.size()); @@ -87,6 +93,12 @@ public: void interpolate_fields(const Field& vel, const Field& density); + void interpolate_fields( + const Field& umac, + const Field& vmac, + const Field& wmac, + const Field& density); + void populate_field_buffers(); void initialize_particles(const int total_pts); diff --git a/amr-wind/wind_energy/actuator/ActuatorContainer.cpp b/amr-wind/wind_energy/actuator/ActuatorContainer.cpp index 8a448d493f..0b4db03e40 100644 --- a/amr-wind/wind_energy/actuator/ActuatorContainer.cpp +++ b/amr-wind/wind_energy/actuator/ActuatorContainer.cpp @@ -213,6 +213,29 @@ void ActuatorContainer::sample_fields(const Field& vel, const Field& density) m_is_scattered = false; } +void ActuatorContainer::sample_fields( + const Field& umac, + const Field& vmac, + const Field& wmac, + const Field& density) +{ + BL_PROFILE("amr-wind::actuator::ActuatorContainer::sample_velocities"); + AMREX_ALWAYS_ASSERT(m_container_initialized && m_is_scattered); + + // Sample velocity field + interpolate_fields(umac, vmac, wmac, density); + + // Recall particles to the MPI ranks that contains their corresponding + // turbines + // Redistribute(); + + // Populate the velocity buffer that all actuator instances can access + populate_field_buffers(); + + // Indicate that the particles have been restored to their original MPI rank + m_is_scattered = false; +} + /** Helper method for ActuatorContainer::sample_fields * * Loops over the particle tiles and copies velocity data from particles to the @@ -356,6 +379,128 @@ void ActuatorContainer::interpolate_fields( } } +void ActuatorContainer::interpolate_fields( + const Field& umac, + const Field& vmac, + const Field& wmac, + const Field& density) +{ + BL_PROFILE("amr-wind::actuator::ActuatorContainer::interpolate_velocities"); + auto* dptr = m_pos_device.data(); + const int nlevels = m_mesh.finestLevel() + 1; + for (int lev = 0; lev < nlevels; ++lev) { + const auto& geom = m_mesh.Geom(lev); + const auto dx = geom.CellSizeArray(); + const auto dxi = geom.InvCellSizeArray(); + const auto plo = geom.ProbLoArray(); + + for (ParIterType pti(*this, lev); pti.isValid(); ++pti) { + const int np = pti.numParticles(); + auto* pstruct = pti.GetArrayOfStructs()().data(); + const auto uarr = umac(lev).const_array(pti); + const auto varr = vmac(lev).const_array(pti); + const auto warr = wmac(lev).const_array(pti); + const auto darr = density(lev).const_array(pti); + + amrex::ParallelFor(np, [=] AMREX_GPU_DEVICE(const int ip) noexcept { + auto& pp = pstruct[ip]; + // Determine offsets within the containing cell + const amrex::Real x = + (pp.pos(0) - plo[0] - 0.5 * dx[0]) * dxi[0]; + const amrex::Real y = + (pp.pos(1) - plo[1] - 0.5 * dx[1]) * dxi[1]; + const amrex::Real z = + (pp.pos(2) - plo[2] - 0.5 * dx[2]) * dxi[2]; + + const amrex::Real xf = (pp.pos(0) - plo[0]) * dxi[0]; + const amrex::Real yf = (pp.pos(1) - plo[1]) * dxi[1]; + const amrex::Real zf = (pp.pos(2) - plo[2]) * dxi[2]; + + // Index of the low corner + const int i = static_cast(std::floor(x)); + const int j = static_cast(std::floor(y)); + const int k = static_cast(std::floor(z)); + + const int i_f = static_cast(std::floor(xf)); + const int j_f = static_cast(std::floor(yf)); + const int k_f = static_cast(std::floor(zf)); + + // Interpolation weights in each direction (linear basis) + const amrex::Real wx_hi = (x - i); + const amrex::Real wy_hi = (y - j); + const amrex::Real wz_hi = (z - k); + + const amrex::Real wx_lo = 1.0 - wx_hi; + const amrex::Real wy_lo = 1.0 - wy_hi; + const amrex::Real wz_lo = 1.0 - wz_hi; + + const amrex::Real wxf_hi = (xf - i_f); + const amrex::Real wyf_hi = (yf - j_f); + const amrex::Real wzf_hi = (zf - k_f); + + const amrex::Real wxf_lo = 1.0 - wxf_hi; + const amrex::Real wyf_lo = 1.0 - wyf_hi; + const amrex::Real wzf_lo = 1.0 - wzf_hi; + + const int iproc = pp.cpu(); + + // u + int ic = 0; + pp.rdata(ic) = + wxf_lo * wy_lo * wz_lo * uarr(i_f, j, k) + + wxf_lo * wy_lo * wz_hi * uarr(i_f, j, k + 1) + + wxf_lo * wy_hi * wz_lo * uarr(i_f, j + 1, k) + + wxf_lo * wy_hi * wz_hi * uarr(i_f, j + 1, k + 1) + + wxf_hi * wy_lo * wz_lo * uarr(i_f + 1, j, k) + + wxf_hi * wy_lo * wz_hi * uarr(i_f + 1, j, k + 1) + + wxf_hi * wy_hi * wz_lo * uarr(i_f + 1, j + 1, k) + + wxf_hi * wy_hi * wz_hi * uarr(i_f + 1, j + 1, k + 1); + + // Reset position vectors so that the particles return back + // to the MPI ranks with the turbines upon redistribution + pp.pos(ic) = dptr[iproc][ic]; + + // v + ic = 1; + pp.rdata(ic) = + wx_lo * wyf_lo * wz_lo * varr(i, j_f, k) + + wx_lo * wyf_lo * wz_hi * varr(i, j_f, k + 1) + + wx_lo * wyf_hi * wz_lo * varr(i, j_f + 1, k) + + wx_lo * wyf_hi * wz_hi * varr(i, j_f + 1, k + 1) + + wx_hi * wyf_lo * wz_lo * varr(i + 1, j_f, k) + + wx_hi * wyf_lo * wz_hi * varr(i + 1, j_f, k + 1) + + wx_hi * wyf_hi * wz_lo * varr(i + 1, j_f + 1, k) + + wx_hi * wyf_hi * wz_hi * varr(i + 1, j_f + 1, k + 1); + pp.pos(ic) = dptr[iproc][ic]; + + // w + ic = 2; + pp.rdata(ic) = + wx_lo * wy_lo * wzf_lo * warr(i, j, k_f) + + wx_lo * wy_lo * wzf_hi * warr(i, j, k_f + 1) + + wx_lo * wy_hi * wzf_lo * warr(i, j + 1, k_f) + + wx_lo * wy_hi * wzf_hi * warr(i, j + 1, k_f + 1) + + wx_hi * wy_lo * wzf_lo * warr(i + 1, j, k_f) + + wx_hi * wy_lo * wzf_hi * warr(i + 1, j, k_f + 1) + + wx_hi * wy_hi * wzf_lo * warr(i + 1, j + 1, k_f) + + wx_hi * wy_hi * wzf_hi * warr(i + 1, j + 1, k_f + 1); + pp.pos(ic) = dptr[iproc][ic]; + + // density + pp.rdata(AMREX_SPACEDIM) = + wx_lo * wy_lo * wz_lo * darr(i, j, k) + + wx_lo * wy_lo * wz_hi * darr(i, j, k + 1) + + wx_lo * wy_hi * wz_lo * darr(i, j + 1, k) + + wx_lo * wy_hi * wz_hi * darr(i, j + 1, k + 1) + + wx_hi * wy_lo * wz_lo * darr(i + 1, j, k) + + wx_hi * wy_lo * wz_hi * darr(i + 1, j, k + 1) + + wx_hi * wy_hi * wz_lo * darr(i + 1, j + 1, k) + + wx_hi * wy_hi * wz_hi * darr(i + 1, j + 1, k + 1); + }); + } + } +} + /** Determine position vector of a point within each MPI rank * * Loops over the boxArray and determines the first patch that belongs to the diff --git a/docs/sphinx/user/inputs_Actuator.rst b/docs/sphinx/user/inputs_Actuator.rst index 6c0d43d140..a14203639b 100644 --- a/docs/sphinx/user/inputs_Actuator.rst +++ b/docs/sphinx/user/inputs_Actuator.rst @@ -29,6 +29,21 @@ turbines as actuator disks and actuator line models. supported are: ``TurbineFastLine``, ``TurbineFastDisk``, and ``FixedWingLine``. +.. input_param:: Actuator.sample_vel_nmhalf + + **type:** Bool, optional + + This option sets the type of velocity sampling used to inform the actuator model. + Setting this variable to true (or `1`) makes the Actuator velocity sampling use + the face-centered velocity field at the time instant of `n-1/2`. For simulations + coupled to OpenFAST, this enables the actuator forces to be implemented at the + time instant of `n+1/2`, which fits best with the underlying numerical model of AMR-Wind + and has been shown to dramatically improve the accuracy of the Actuator Line Model. + This option defaults to true (`1`) when the Actuator type is ``TurbineFastLine`` or + ``TurbineFastDisk``, and it defaults to false (`0`) in all other cases. When the + option is false, the actuator routine samples the cell-centered velocity + at the time instant `n`. + FixedWingLine """"""""""""" diff --git a/unit_tests/wind_energy/actuator/test_act_utils.H b/unit_tests/wind_energy/actuator/test_act_utils.H index 9bfc058908..9a3e41b0a6 100644 --- a/unit_tests/wind_energy/actuator/test_act_utils.H +++ b/unit_tests/wind_energy/actuator/test_act_utils.H @@ -13,9 +13,25 @@ inline void init_field(amr_wind::Field& fld) const int nlevels = fld.repo().num_active_levels(); const int ncomp = fld.num_comp(); - amrex::Real offset = 0.0; + amrex::Real offsetx = 0.0; + amrex::Real offsety = 0.0; + amrex::Real offsetz = 0.0; if (fld.field_location() == amr_wind::FieldLoc::CELL) { - offset = 0.5; + offsetx = 0.5; + offsety = 0.5; + offsetz = 0.5; + } + if (fld.field_location() == amr_wind::FieldLoc::XFACE) { + offsety = 0.5; + offsetz = 0.5; + } + if (fld.field_location() == amr_wind::FieldLoc::YFACE) { + offsetx = 0.5; + offsetz = 0.5; + } + if (fld.field_location() == amr_wind::FieldLoc::ZFACE) { + offsetx = 0.5; + offsety = 0.5; } for (int lev = 0; lev < nlevels; ++lev) { @@ -27,9 +43,9 @@ inline void init_field(amr_wind::Field& fld) const auto& farr = fld(lev).array(mfi); amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) { - const amrex::Real x = problo[0] + (i + offset) * dx[0]; - const amrex::Real y = problo[1] + (j + offset) * dx[1]; - const amrex::Real z = problo[2] + (k + offset) * dx[2]; + const amrex::Real x = problo[0] + (i + offsetx) * dx[0]; + const amrex::Real y = problo[1] + (j + offsety) * dx[1]; + const amrex::Real z = problo[2] + (k + offsetz) * dx[2]; for (int d = 0; d < ncomp; d++) { farr(i, j, k, d) = x + y + z; diff --git a/unit_tests/wind_energy/actuator/test_actuator_sampling.cpp b/unit_tests/wind_energy/actuator/test_actuator_sampling.cpp index cad8ef04c8..c45b709ff9 100644 --- a/unit_tests/wind_energy/actuator/test_actuator_sampling.cpp +++ b/unit_tests/wind_energy/actuator/test_actuator_sampling.cpp @@ -168,4 +168,129 @@ TEST_F(ActuatorTest, act_container) } } +TEST_F(ActuatorTest, act_container_macvels) +{ + const int nprocs = amrex::ParallelDescriptor::NProcs(); + if (nprocs > 2) { + GTEST_SKIP(); + } + + const int iproc = amrex::ParallelDescriptor::MyProc(); + initialize_mesh(); + auto& umac = + sim().repo().declare_field("u_mac", 1, 3, 1, amr_wind::FieldLoc::XFACE); + auto& vmac = + sim().repo().declare_field("v_mac", 1, 3, 1, amr_wind::FieldLoc::YFACE); + auto& wmac = + sim().repo().declare_field("w_mac", 1, 3, 1, amr_wind::FieldLoc::ZFACE); + auto& density = sim().repo().declare_field("density", 1, 3); + init_field(umac); + init_field(vmac); + init_field(wmac); + density.setVal(1.0); + + // Number of turbines in an MPI rank + const int num_turbines = 2; + // Number of points per turbine + const int num_nodes = 16; + + TestActContainer ac(mesh(), num_turbines); + auto& data = ac.get_data_obj(); + + for (int it = 0; it < num_turbines; ++it) { + data.num_pts[it] = num_nodes; + } + + ac.initialize_container(); + + { + const int lev = 0; + int idx = 0; + const amrex::Real dz = mesh().Geom(lev).CellSize(2); + const amrex::Real ypos = 32.0 * (iproc + 1); + auto& pvec = data.position; + for (int it = 0; it < num_turbines; ++it) { + const amrex::Real xpos = 32.0 * (it + 1); + for (int ni = 0; ni < num_nodes; ++ni) { + const amrex::Real zpos = (ni + 0.5) * dz; + + pvec[idx].x() = xpos; + pvec[idx].y() = ypos; + pvec[idx].z() = zpos; + ++idx; + } + } + ASSERT_EQ(idx, ac.num_actuator_points()); + } + + ac.update_positions(); + + // Check that the update positions scattered the particles to the MPI rank + // that contains the cell enclosing the particle location +#ifndef AMREX_USE_GPU + ASSERT_EQ( + ac.num_actuator_points() * nprocs, ac.NumberOfParticlesAtLevel(0)); +#endif + { + using ParIter = amr_wind::actuator::ActuatorContainer::ParIterType; + int total_particles = 0; + const int lev = 0; + for (ParIter pti(ac, lev); pti.isValid(); ++pti) { + total_particles += pti.numParticles(); + } + + if (iproc == 0) { + ASSERT_EQ(num_turbines * num_nodes * nprocs, total_particles); + } else { + ASSERT_EQ(total_particles, 0); + } + } + + ac.sample_fields(umac, vmac, wmac, density); + ac.Redistribute(); + + // Check to make sure that the velocity sampling gathered the particles back + // to their original MPI ranks +#ifndef AMREX_USE_GPU + ASSERT_EQ( + ac.num_actuator_points() * nprocs, ac.NumberOfParticlesAtLevel(0)); +#endif + { + using ParIter = amr_wind::actuator::ActuatorContainer::ParIterType; + int counter = 0; + int total_particles = 0; + const int lev = 0; + for (ParIter pti(ac, lev); pti.isValid(); ++pti) { + ++counter; + total_particles += pti.numParticles(); + } + + // All particles should have been recalled back to the same patch within + // each MPI rank + ASSERT_EQ(counter, 1); + // Total number of particles should be the same as what this MPI rank + // created + ASSERT_EQ(num_turbines * num_nodes, total_particles); + } + + // Check the interpolated velocity field + { + namespace vs = amr_wind::vs; + constexpr amrex::Real rtol = 1.0e-12; + amrex::Real rerr = 0.0; + const int npts = ac.num_actuator_points(); + const auto& pvec = data.position; + const auto& vvec = data.velocity; + for (int ip = 0; ip < npts; ++ip) { + const auto& pos = pvec[ip]; + const auto& pvel = vvec[ip]; + + const amrex::Real vval = pos.x() + pos.y() + pos.z(); + const vs::Vector vgold{vval, vval, vval}; + rerr += vs::mag_sqr(pvel - vgold); + } + EXPECT_NEAR(rerr, 0.0, rtol); + } +} + } // namespace amr_wind_tests From 9dd9e0e6503e32b8271538186b9dd5c8c5212c5f Mon Sep 17 00:00:00 2001 From: Bruce Perry <53018946+baperry2@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:35:32 -0600 Subject: [PATCH 29/38] update amrex submod (#876) * update amrex submod * clang-tidy fixes for AMREX_SPACEDIM --- amr-wind/core/Field.cpp | 4 +++- amr-wind/overset/TiogaInterface.cpp | 10 +++++----- amr-wind/utilities/sampling/SamplingContainer.H | 2 +- cmake/set_compile_flags.cmake | 4 ++-- submods/amrex | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/amr-wind/core/Field.cpp b/amr-wind/core/Field.cpp index 17dcc7cc78..0ccf55f492 100644 --- a/amr-wind/core/Field.cpp +++ b/amr-wind/core/Field.cpp @@ -20,7 +20,9 @@ FieldInfo::FieldInfo( , m_ngrow(ngrow) , m_nstates(nstates) , m_floc(floc) - , m_bc_values(AMREX_SPACEDIM * 2, amrex::Vector(ncomp, 0.0)) + , m_bc_values( + static_cast(AMREX_SPACEDIM) * 2, + amrex::Vector(ncomp, 0.0)) , m_bc_values_dview(static_cast(ncomp) * AMREX_SPACEDIM * 2) , m_bcrec(ncomp) , m_bcrec_d(ncomp) diff --git a/amr-wind/overset/TiogaInterface.cpp b/amr-wind/overset/TiogaInterface.cpp index 347bc6af96..486c36d148 100644 --- a/amr-wind/overset/TiogaInterface.cpp +++ b/amr-wind/overset/TiogaInterface.cpp @@ -48,11 +48,11 @@ AMROversetInfo::AMROversetInfo(const int nglobal, const int nlocal) : level(nglobal) , mpi_rank(nglobal) , local_id(nglobal) - , ilow(AMREX_SPACEDIM * nglobal) - , ihigh(AMREX_SPACEDIM * nglobal) - , dims(AMREX_SPACEDIM * nglobal) - , xlo(AMREX_SPACEDIM * nglobal) - , dx(AMREX_SPACEDIM * nglobal) + , ilow(static_cast(AMREX_SPACEDIM) * nglobal) + , ihigh(static_cast(AMREX_SPACEDIM) * nglobal) + , dims(static_cast(AMREX_SPACEDIM) * nglobal) + , xlo(static_cast(AMREX_SPACEDIM) * nglobal) + , dx(static_cast(AMREX_SPACEDIM) * nglobal) , global_idmap(nlocal) , iblank_node(nlocal) , iblank_cell(nlocal) diff --git a/amr-wind/utilities/sampling/SamplingContainer.H b/amr-wind/utilities/sampling/SamplingContainer.H index d7133ac934..d29a500375 100644 --- a/amr-wind/utilities/sampling/SamplingContainer.H +++ b/amr-wind/utilities/sampling/SamplingContainer.H @@ -64,7 +64,7 @@ class SamplingContainer { public: explicit SamplingContainer(amrex::AmrCore& mesh) - : AmrParticleContainer< + : amrex::AmrParticleContainer< SNStructReal, SNStructInt, SNArrayReal, diff --git a/cmake/set_compile_flags.cmake b/cmake/set_compile_flags.cmake index 239c3f3fd9..3f4140f686 100644 --- a/cmake/set_compile_flags.cmake +++ b/cmake/set_compile_flags.cmake @@ -35,10 +35,10 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR if (${AMR_WIND_USE_INTERNAL_AMREX}) if ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND AMR_WIND_ENABLE_FPE_TRAP_FOR_TESTS) target_compile_options( - amrex PUBLIC $<$:-ffp-exception-behavior=maytrap>) + amrex_3d PUBLIC $<$:-ffp-exception-behavior=maytrap>) endif() target_compile_options( - amrex PUBLIC $<$:-Wno-pass-failed>) + amrex_3d PUBLIC $<$:-Wno-pass-failed>) else() if ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND AMR_WIND_ENABLE_FPE_TRAP_FOR_TESTS) target_compile_options( diff --git a/submods/amrex b/submods/amrex index 2f47d132d7..59a3106293 160000 --- a/submods/amrex +++ b/submods/amrex @@ -1 +1 @@ -Subproject commit 2f47d132d71c49abbeac5e081fe9ce886c621d54 +Subproject commit 59a310629397b69d4d5242ee0eba11ef8718f2aa From c4be30826216bfdd65060420af7ac7185ca9ca97 Mon Sep 17 00:00:00 2001 From: Jon Rood Date: Fri, 14 Jul 2023 13:53:07 -0600 Subject: [PATCH 30/38] Suppress some cppcheck warnings. (#878) --- cmake/amr-wind-utils.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/amr-wind-utils.cmake b/cmake/amr-wind-utils.cmake index 5b2c159b39..cfd55b4cea 100644 --- a/cmake/amr-wind-utils.cmake +++ b/cmake/amr-wind-utils.cmake @@ -108,7 +108,7 @@ macro(init_code_checks) COMMAND ${CMAKE_COMMAND} -E make_directory cppcheck # cppcheck ignores -isystem directories, so we change them to regular -I include directories (with no spaces either) COMMAND sed "s/isystem /I/g" ${CMAKE_BINARY_DIR}/compile_commands.json > cppcheck_compile_commands.json - COMMAND ${CPPCHECK_EXE} --template=gcc --inline-suppr --suppress=unusedFunction --suppress=useStlAlgorithm --std=c++17 --language=c++ --enable=all --project=cppcheck_compile_commands.json -i ${CMAKE_SOURCE_DIR}/submods/amrex/Src -i ${CMAKE_SOURCE_DIR}/submods/AMReX-Hydro -i ${CMAKE_SOURCE_DIR}/submods/googletest --output-file=cppcheck-full-report.txt -j ${NP} + COMMAND ${CPPCHECK_EXE} --template=gcc --inline-suppr --suppress=unusedFunction --suppress=useStlAlgorithm --suppress=missingIncludeSystem --std=c++17 --language=c++ --enable=all --project=cppcheck_compile_commands.json -i ${CMAKE_SOURCE_DIR}/submods/amrex/Src -i ${CMAKE_SOURCE_DIR}/submods/AMReX-Hydro -i ${CMAKE_SOURCE_DIR}/submods/googletest --output-file=cppcheck-full-report.txt -j ${NP} COMMENT "Run cppcheck on project compile_commands.json" BYPRODUCTS cppcheck-full-report.txt WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/cppcheck From 00895fbbd6ab4c22cdcfa05cb99334fe5d0871ca Mon Sep 17 00:00:00 2001 From: Jon Rood Date: Fri, 14 Jul 2023 16:53:38 -0600 Subject: [PATCH 31/38] Fix Intel OneAPI CI. (#877) --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d92e90714c..ec308c570f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -178,11 +178,11 @@ jobs: - name: Prepare SyCL environment run: | export DEBIAN_FRONTEND=noninteractive - sudo wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB - sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB + sudo wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB + sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list sudo apt-get update - sudo apt-get install -y --no-install-recommends ninja-build intel-oneapi-dpcpp-cpp-compiler intel-oneapi-mkl-devel + sudo apt-get install -y --no-install-recommends ninja-build intel-oneapi-compiler-dpcpp-cpp intel-oneapi-mkl-devel - name: Configure and build run: | set +e From 1af032a6e42b37c4726d7006e958ab93637e2f13 Mon Sep 17 00:00:00 2001 From: sbidadi9 <96149491+sbidadi9@users.noreply.github.com> Date: Fri, 14 Jul 2023 17:46:29 -0600 Subject: [PATCH 32/38] Adding clipping of SDR production term for IDDES model (#873) Co-authored-by: Jon Rood --- amr-wind/turbulence/RANS/KOmegaSST.H | 2 -- amr-wind/turbulence/RANS/KOmegaSSTIDDES.H | 1 + amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp | 7 +++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/amr-wind/turbulence/RANS/KOmegaSST.H b/amr-wind/turbulence/RANS/KOmegaSST.H index fba60bbca2..e89929fc8c 100644 --- a/amr-wind/turbulence/RANS/KOmegaSST.H +++ b/amr-wind/turbulence/RANS/KOmegaSST.H @@ -94,8 +94,6 @@ protected: amrex::Real m_buoyancy_factor = 0.0; amrex::Real m_sigma_t{0.85}; amrex::Vector m_gravity{{0.0, 0.0, -9.81}}; - - DiffusionType m_diff_type = DiffusionType::Crank_Nicolson; }; } // namespace amr_wind::turbulence diff --git a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H index f7ab18105a..2628c68773 100644 --- a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H +++ b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.H @@ -60,6 +60,7 @@ protected: amrex::Real m_Ct{1.87}; amrex::Real m_Cw{0.15}; amrex::Real m_kappa{0.41}; + amrex::Real m_sdr_prod_clip_factor{10.0}; }; } // namespace amr_wind::turbulence diff --git a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp index 3d863cc4ee..9178369c6e 100644 --- a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp +++ b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp @@ -38,6 +38,7 @@ void KOmegaSSTIDDES::parse_model_coeffs() pp.query("Ct", this->m_Ct); pp.query("Cw", this->m_Cw); pp.query("kappa", this->m_kappa); + pp.query("sdr_prod_clip_factor", this->m_sdr_prod_clip_factor); } template @@ -63,7 +64,8 @@ TurbulenceModel::CoeffsDictType KOmegaSSTIDDES::model_coeffs() const {"Cl", this->m_Cl}, {"Ct", this->m_Ct}, {"Cw", this->m_Cw}, - {"kappa", this->m_kappa}}; + {"kappa", this->m_kappa}, + {"sdr_prod_clip_factor", this->m_sdr_prod_clip_factor}}; } template @@ -90,6 +92,7 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( const amrex::Real Ct = this->m_Ct; const amrex::Real Cw = this->m_Cw; const amrex::Real kappa = this->m_kappa; + const amrex::Real sdr_prod_clip_factor = this->m_sdr_prod_clip_factor; auto& mu_turb = this->mu_turb(); auto lam_mu = (this->m_transport).mu(); @@ -260,7 +263,7 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( rho_arr(i, j, k) * alpha * amrex::min( tmp4 * tmp4, - 10.0 * + sdr_prod_clip_factor * amrex::max(sdr_arr(i, j, k), 0.0) * std::sqrt(tke_arr(i, j, k)) / l_iddes); From caebcd1f2841cf780ea3dcd566f05dbc21bc476f Mon Sep 17 00:00:00 2001 From: Tony Martinez Date: Mon, 17 Jul 2023 15:50:40 -0500 Subject: [PATCH 33/38] Update turbine_fast_ops.H (#879) fix bug with number of points. --- amr-wind/wind_energy/actuator/turbine/fast/turbine_fast_ops.H | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amr-wind/wind_energy/actuator/turbine/fast/turbine_fast_ops.H b/amr-wind/wind_energy/actuator/turbine/fast/turbine_fast_ops.H index 56afb90c94..e32e37b60f 100644 --- a/amr-wind/wind_energy/actuator/turbine/fast/turbine_fast_ops.H +++ b/amr-wind/wind_energy/actuator/turbine/fast/turbine_fast_ops.H @@ -165,7 +165,7 @@ struct InitDataOp tdata.num_blades = sz_info[0]; // back calculate what the value per blade for number of points in // the openfast data structure - tdata.num_vel_pts_blade = sz_info[2] / tdata.num_blades - 1; + tdata.num_vel_pts_blade = sz_info[2] / tdata.num_blades; data.grid().resize(sz_info[1], sz_info[2]); tdata.chord.resize(sz_info[1]); tdata.num_pts_tower = sz_info[3]; From 487b948016840069454d9b90c212a84da05c64d2 Mon Sep 17 00:00:00 2001 From: "Marc T. Henry de Frahan" Date: Thu, 20 Jul 2023 15:47:05 -0600 Subject: [PATCH 34/38] Fix some warnings (#883) --- amr-wind/CFDSim.cpp | 13 +++++----- amr-wind/boundary_conditions/BCInterface.cpp | 2 ++ amr-wind/core/Field.cpp | 6 +++-- amr-wind/equation_systems/AdvOp_Godunov.H | 2 +- amr-wind/equation_systems/AdvOp_MOL.H | 2 +- .../equation_systems/icns/icns_advection.H | 9 ++++--- .../equation_systems/icns/icns_diffusion.H | 3 +-- .../icns/source_terms/ABLMeanBoussinesq.H | 2 +- .../icns/source_terms/ABLMeanBoussinesq.cpp | 3 ++- .../tke/source_terms/KsgsM84Src.cpp | 2 +- amr-wind/equation_systems/tke/tke_ops.H | 1 + .../bluff_body/bluff_body_ops.cpp | 18 +++++++------- .../immersed_boundary/bluff_body/box_ops.H | 8 +++---- .../bluff_body/cylinder_ops.H | 16 ++++++------- .../immersed_boundary/bluff_body/sphere_ops.H | 10 ++++---- .../relaxation_zones/hos_waves_ops.H | 2 +- .../relaxation_zones/linear_waves_ops.H | 15 ++++++------ .../relaxation_zones/stokes_waves_ops.H | 16 ++++++------- amr-wind/overset/TiogaInterface.cpp | 6 ++--- amr-wind/physics/multiphase/MultiPhase.cpp | 12 +++++----- .../incflo_apply_nodal_projection.cpp | 2 +- amr-wind/setup/init.cpp | 7 +++--- amr-wind/transport_models/TwoPhaseTransport.H | 18 +++++--------- amr-wind/turbulence/LES/AMD.cpp | 5 ++-- amr-wind/turbulence/LES/AMDNoTherm.cpp | 3 ++- amr-wind/turbulence/LES/OneEqKsgs.cpp | 2 +- amr-wind/turbulence/RANS/KOmegaSST.cpp | 5 ++-- amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp | 6 ++--- amr-wind/utilities/SecondMomentAveraging.cpp | 4 ++-- amr-wind/utilities/ThirdMomentAveraging.cpp | 6 ++--- .../utilities/averaging/TimeAveraging.cpp | 2 +- amr-wind/utilities/diagnostics.H | 18 +++++++------- amr-wind/utilities/diagnostics.cpp | 24 +++++++++---------- amr-wind/utilities/sampling/FieldNorms.cpp | 2 +- amr-wind/utilities/sampling/Sampling.cpp | 2 +- amr-wind/wind_energy/ABLFieldInit.cpp | 5 +--- amr-wind/wind_energy/ABLStats.cpp | 2 +- .../wind_energy/actuator/disk/Joukowsky_ops.H | 5 ++-- .../actuator/disk/uniform_ct_ops.H | 5 ++-- .../actuator/turbine/fast/FastIface.cpp | 3 +-- unit_tests/aw_test_utils/MeshTest.cpp | 2 +- unit_tests/core/test_field.cpp | 8 +++---- unit_tests/multiphase/test_momflux.cpp | 14 +++++------ unit_tests/turbulence/test_turbulence_LES.cpp | 8 +++---- unit_tests/utilities/test_free_surface.cpp | 7 +----- .../actuator/test_actuator_joukowsky_disk.cpp | 4 ++-- .../actuator/test_disk_functions.cpp | 2 -- unit_tests/wind_energy/test_abl_bc.cpp | 2 +- unit_tests/wind_energy/test_abl_src.cpp | 4 ++-- 49 files changed, 158 insertions(+), 167 deletions(-) diff --git a/amr-wind/CFDSim.cpp b/amr-wind/CFDSim.cpp index f85ff79e1d..b02e05f78a 100644 --- a/amr-wind/CFDSim.cpp +++ b/amr-wind/CFDSim.cpp @@ -23,18 +23,19 @@ CFDSim::~CFDSim() = default; void CFDSim::create_turbulence_model() { - std::string transport_model = "ConstTransport"; - std::string turbulence_model = "Laminar"; + std::string transport_model_name = "ConstTransport"; + std::string turbulence_model_name = "Laminar"; { amrex::ParmParse pp("transport"); - pp.query("model", transport_model); + pp.query("model", transport_model_name); } { amrex::ParmParse pp("turbulence"); - pp.query("model", turbulence_model); + pp.query("model", turbulence_model_name); } - const std::string identifier = turbulence_model + "-" + transport_model; + const std::string identifier = + turbulence_model_name + "-" + transport_model_name; m_turbulence = turbulence::TurbulenceModel::create(identifier, *this); m_turbulence->parse_model_coeffs(); } @@ -45,7 +46,7 @@ void CFDSim::init_physics() amrex::Vector phys_names; pp.queryarr("physics", phys_names); - for (auto& phy : phys_names) { + for (const auto& phy : phys_names) { m_physics_mgr.create(phy, *this); } } diff --git a/amr-wind/boundary_conditions/BCInterface.cpp b/amr-wind/boundary_conditions/BCInterface.cpp index d0c4acb72a..b17013afce 100644 --- a/amr-wind/boundary_conditions/BCInterface.cpp +++ b/amr-wind/boundary_conditions/BCInterface.cpp @@ -131,6 +131,7 @@ std::pair BCIface::get_dirichlet_udfs() "faces"); } else { inflow_udf = val; + has_inflow_udf = true; } } } @@ -146,6 +147,7 @@ std::pair BCIface::get_dirichlet_udfs() "faces"); } else { wall_udf = val; + has_wall_udf = true; } } } diff --git a/amr-wind/core/Field.cpp b/amr-wind/core/Field.cpp index 0ccf55f492..dd44acad04 100644 --- a/amr-wind/core/Field.cpp +++ b/amr-wind/core/Field.cpp @@ -268,7 +268,7 @@ void Field::fillphysbc(amrex::Real time) noexcept void Field::apply_bc_funcs(const FieldState rho_state) noexcept { BL_ASSERT(m_info->bc_initialized() && m_info->m_bc_copied_to_device); - for (auto& func : m_info->m_bc_func) { + for (const auto& func : m_info->m_bc_func) { (*func)(*this, rho_state); } } @@ -296,8 +296,9 @@ void Field::advance_states() noexcept for (int i = num_time_states() - 1; i > 0; --i) { const auto sold = static_cast(i); const auto snew = static_cast(i - 1); + // cppcheck-suppress constVariableReference auto& old_field = state(sold); - auto& new_field = state(snew); + const auto& new_field = state(snew); for (int lev = 0; lev < m_repo.num_active_levels(); ++lev) { amrex::MultiFab::Copy( old_field(lev), new_field(lev), 0, 0, num_comp(), num_grow()); @@ -308,6 +309,7 @@ void Field::advance_states() noexcept void Field::copy_state(FieldState to_state, FieldState from_state) noexcept { BL_PROFILE("amr-wind::Field::copy_state"); + // cppcheck-suppress constVariableReference auto& to_field = state(to_state); const auto& from_field = state(from_state); diff --git a/amr-wind/equation_systems/AdvOp_Godunov.H b/amr-wind/equation_systems/AdvOp_Godunov.H index 178c221812..2a93b8e6ea 100644 --- a/amr-wind/equation_systems/AdvOp_Godunov.H +++ b/amr-wind/equation_systems/AdvOp_Godunov.H @@ -84,7 +84,7 @@ struct AdvectionOp< const auto& geom = repo.mesh().Geom(); const auto& src_term = fields.src_term; - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& conv_term = fields.conv_term; const auto& dof_field = fields.field.state(fstate); diff --git a/amr-wind/equation_systems/AdvOp_MOL.H b/amr-wind/equation_systems/AdvOp_MOL.H index 1b61ba8b04..256876ef14 100644 --- a/amr-wind/equation_systems/AdvOp_MOL.H +++ b/amr-wind/equation_systems/AdvOp_MOL.H @@ -49,7 +49,7 @@ struct AdvectionOp< const auto& repo = fields.repo; const auto& geom = repo.mesh().Geom(); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& conv_term = fields.conv_term.state(fstate); const auto& dof_field = fields.field.state(fstate); const auto& den = density.state(fstate); diff --git a/amr-wind/equation_systems/icns/icns_advection.H b/amr-wind/equation_systems/icns/icns_advection.H index bf7e434380..d89e9c4dcf 100644 --- a/amr-wind/equation_systems/icns/icns_advection.H +++ b/amr-wind/equation_systems/icns/icns_advection.H @@ -314,11 +314,11 @@ struct AdvectionOp void operator()(const FieldState fstate, const amrex::Real dt) { - auto& repo = fields.repo; + const auto& repo = fields.repo; const auto& geom = repo.mesh().Geom(); const auto& src_term = fields.src_term; - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& conv_term = fields.conv_term; const auto& dof_field = fields.field.state(fstate); @@ -550,8 +550,7 @@ struct AdvectionOp const amrex::Real /*time*/) { - // cppcheck-suppress constVariable - auto& repo = fields.repo; + const auto& repo = fields.repo; auto& dof_field = fields.field.state(fstate); // computation of velocity on faces requires @@ -579,7 +578,7 @@ struct AdvectionOp const auto& repo = fields.repo; const auto& geom = repo.mesh().Geom(); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& conv_term = fields.conv_term.state(fstate); const auto& dof_field = fields.field.state(fstate); const auto& rho = repo.get_field("density").state(fstate); diff --git a/amr-wind/equation_systems/icns/icns_diffusion.H b/amr-wind/equation_systems/icns/icns_diffusion.H index c4872ad310..eb9f2daa72 100644 --- a/amr-wind/equation_systems/icns/icns_diffusion.H +++ b/amr-wind/equation_systems/icns/icns_diffusion.H @@ -443,8 +443,7 @@ public: { const FieldState fstate = FieldState::New; auto& repo = m_pdefields.repo; - // cppcheck-suppress constVariable - auto& field = m_pdefields.field; + const auto& field = m_pdefields.field; const auto& density = m_density.state(fstate); const int nlevels = repo.num_active_levels(); const int ndim = field.num_comp(); diff --git a/amr-wind/equation_systems/icns/source_terms/ABLMeanBoussinesq.H b/amr-wind/equation_systems/icns/source_terms/ABLMeanBoussinesq.H index 328b3f83f5..d479cd9965 100644 --- a/amr-wind/equation_systems/icns/source_terms/ABLMeanBoussinesq.H +++ b/amr-wind/equation_systems/icns/source_terms/ABLMeanBoussinesq.H @@ -53,7 +53,7 @@ private: bool m_const_profile{false}; //! Read a temperature profile from a text file - void read_temperature_profile(std::string profile_file_name); + void read_temperature_profile(const std::string& profile_file_name); }; } // namespace amr_wind::pde::icns diff --git a/amr-wind/equation_systems/icns/source_terms/ABLMeanBoussinesq.cpp b/amr-wind/equation_systems/icns/source_terms/ABLMeanBoussinesq.cpp index b15ad07aa7..390e37b705 100644 --- a/amr-wind/equation_systems/icns/source_terms/ABLMeanBoussinesq.cpp +++ b/amr-wind/equation_systems/icns/source_terms/ABLMeanBoussinesq.cpp @@ -126,7 +126,8 @@ void ABLMeanBoussinesq::mean_temperature_update(const FieldPlaneAveraging& tavg) tavg.line_average().end(), m_theta_vals.begin()); } -void ABLMeanBoussinesq::read_temperature_profile(std::string profile_file_name) +void ABLMeanBoussinesq::read_temperature_profile( + const std::string& profile_file_name) { amrex::Vector theta_ht, theta_vals; diff --git a/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.cpp b/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.cpp index 5e27b189c8..5855218366 100644 --- a/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.cpp +++ b/amr-wind/equation_systems/tke/source_terms/KsgsM84Src.cpp @@ -36,7 +36,7 @@ void KsgsM84Src::operator()( const amrex::Real Ceps = this->m_Ceps; const amrex::Real CepsGround = this->m_CepsGround; - auto& repo = (this->m_tke).repo(); + const auto& repo = (this->m_tke).repo(); const auto geom = repo.mesh().Geom(lev); const amrex::Real dx = geom.CellSize()[0]; diff --git a/amr-wind/equation_systems/tke/tke_ops.H b/amr-wind/equation_systems/tke/tke_ops.H index 35b10bf318..11e32aef0c 100644 --- a/amr-wind/equation_systems/tke/tke_ops.H +++ b/amr-wind/equation_systems/tke/tke_ops.H @@ -36,6 +36,7 @@ struct PostSolveOp void operator()(const amrex::Real time) { + // cppcheck-suppress constVariableReference auto& field = m_fields.field; const auto& repo = field.repo(); const int nlevels = repo.num_active_levels(); diff --git a/amr-wind/immersed_boundary/bluff_body/bluff_body_ops.cpp b/amr-wind/immersed_boundary/bluff_body/bluff_body_ops.cpp index b2ef98916a..9988921584 100644 --- a/amr-wind/immersed_boundary/bluff_body/bluff_body_ops.cpp +++ b/amr-wind/immersed_boundary/bluff_body/bluff_body_ops.cpp @@ -31,7 +31,7 @@ void apply_mms_vel(CFDSim& sim) const int nlevels = sim.repo().num_active_levels(); const auto& levelset = sim.repo().get_field("ib_levelset"); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& velocity = sim.repo().get_field("velocity"); auto& m_conv_taylor_green = sim.physics_manager().get(); @@ -50,8 +50,8 @@ void apply_mms_vel(CFDSim& sim) for (amrex::MFIter mfi(levelset(lev)); mfi.isValid(); ++mfi) { const auto& bx = mfi.growntilebox(); - auto phi = levelset(lev).array(mfi); - auto varr = velocity(lev).array(mfi); + const auto& phi = levelset(lev).const_array(mfi); + const auto& varr = velocity(lev).array(mfi); amrex::ParallelFor( bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { const amrex::Real x = problo[0] + (i + 0.5) * dx[0]; @@ -77,7 +77,7 @@ void apply_dirichlet_vel(CFDSim& sim, const amrex::Vector& vel_bc) { const int nlevels = sim.repo().num_active_levels(); auto& geom = sim.mesh().Geom(); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& velocity = sim.repo().get_field("velocity"); auto& levelset = sim.repo().get_field("ib_levelset"); levelset.fillpatch(sim.time().current_time()); @@ -94,13 +94,13 @@ void apply_dirichlet_vel(CFDSim& sim, const amrex::Vector& vel_bc) for (amrex::MFIter mfi(levelset(lev)); mfi.isValid(); ++mfi) { const auto& bx = mfi.tilebox(); - auto varr = velocity(lev).array(mfi); - auto phi_arr = levelset(lev).array(mfi); + const auto& varr = velocity(lev).array(mfi); + const auto& phi_arr = levelset(lev).const_array(mfi); auto norm_arr = normal(lev).array(mfi); - amrex::Real velx = vel_bc[0]; - amrex::Real vely = vel_bc[1]; - amrex::Real velz = vel_bc[2]; + const amrex::Real velx = vel_bc[0]; + const amrex::Real vely = vel_bc[1]; + const amrex::Real velz = vel_bc[2]; amrex::ParallelFor( bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { diff --git a/amr-wind/immersed_boundary/bluff_body/box_ops.H b/amr-wind/immersed_boundary/bluff_body/box_ops.H index 11bbaa1095..4c2175f7a0 100644 --- a/amr-wind/immersed_boundary/bluff_body/box_ops.H +++ b/amr-wind/immersed_boundary/bluff_body/box_ops.H @@ -48,9 +48,9 @@ struct InitDataOp const auto& wdata = data.meta(); auto& sim = data.sim(); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& mask_node = sim.repo().get_int_field("mask_node"); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& levelset = sim.repo().get_field("ib_levelset"); auto nlevels = sim.repo().num_active_levels(); @@ -61,8 +61,8 @@ struct InitDataOp const auto& dx = geom[lev].CellSizeArray(); for (amrex::MFIter mfi(levelset(lev)); mfi.isValid(); ++mfi) { const auto& bx = mfi.growntilebox(); - auto epsilon_node = mask_node(lev).array(mfi); - auto phi = levelset(lev).array(mfi); + const auto& epsilon_node = mask_node(lev).array(mfi); + const auto& phi = levelset(lev).array(mfi); const amrex::Real x0 = wdata.center_loc[0]; const amrex::Real y0 = wdata.center_loc[1]; diff --git a/amr-wind/immersed_boundary/bluff_body/cylinder_ops.H b/amr-wind/immersed_boundary/bluff_body/cylinder_ops.H index 82139ce601..2002f661b6 100644 --- a/amr-wind/immersed_boundary/bluff_body/cylinder_ops.H +++ b/amr-wind/immersed_boundary/bluff_body/cylinder_ops.H @@ -46,9 +46,9 @@ struct InitDataOp const auto& wdata = data.meta(); auto& sim = data.sim(); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& mask_node = sim.repo().get_int_field("mask_node"); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& levelset = sim.repo().get_field("ib_levelset"); auto nlevels = sim.repo().num_active_levels(); @@ -59,7 +59,7 @@ struct InitDataOp const auto& dx = geom[lev].CellSizeArray(); for (amrex::MFIter mfi(levelset(lev)); mfi.isValid(); ++mfi) { - auto phi = levelset(lev).array(mfi); + const auto& phi = levelset(lev).array(mfi); const amrex::Real x0 = wdata.center_loc[0]; const amrex::Real y0 = wdata.center_loc[1]; @@ -69,23 +69,23 @@ struct InitDataOp bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { const amrex::Real x = problo[0] + (i + 0.5) * dx[0]; const amrex::Real y = problo[1] + (j + 0.5) * dx[1]; - amrex::Real phi_glob = phi(i, j, k); - amrex::Real r = std::sqrt( + const amrex::Real phi_glob = phi(i, j, k); + const amrex::Real r = std::sqrt( (x - x0) * (x - x0) + (y - y0) * (y - y0)); - amrex::Real phi_loc = r - R; + const amrex::Real phi_loc = r - R; phi(i, j, k) = std::min(phi_loc, phi_glob); }); const auto& nbx = mfi.nodaltilebox(); - auto epsilon_node = mask_node(lev).array(mfi); + const auto& epsilon_node = mask_node(lev).array(mfi); amrex::ParallelFor( nbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { const amrex::Real x = problo[0] + i * dx[0]; const amrex::Real y = problo[1] + j * dx[1]; - amrex::Real r = std::sqrt( + const amrex::Real r = std::sqrt( (x - x0) * (x - x0) + (y - y0) * (y - y0)); if (r <= R) { diff --git a/amr-wind/immersed_boundary/bluff_body/sphere_ops.H b/amr-wind/immersed_boundary/bluff_body/sphere_ops.H index 630a5704cd..ec8fc94709 100644 --- a/amr-wind/immersed_boundary/bluff_body/sphere_ops.H +++ b/amr-wind/immersed_boundary/bluff_body/sphere_ops.H @@ -44,9 +44,9 @@ struct InitDataOp const auto& wdata = data.meta(); auto& sim = data.sim(); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& mask_node = sim.repo().get_int_field("mask_node"); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& levelset = sim.repo().get_field("ib_levelset"); auto nlevels = sim.repo().num_active_levels(); @@ -58,7 +58,7 @@ struct InitDataOp for (amrex::MFIter mfi(levelset(lev)); mfi.isValid(); ++mfi) { const auto& bx = mfi.growntilebox(); - auto phi = levelset(lev).array(mfi); + const auto& phi = levelset(lev).array(mfi); const amrex::Real x0 = wdata.center_loc[0]; const amrex::Real y0 = wdata.center_loc[1]; @@ -74,12 +74,12 @@ struct InitDataOp (x - x0) * (x - x0) + (y - y0) * (y - y0) + (z - z0) * (z - z0)); - amrex::Real phi_loc = r - R; + const amrex::Real phi_loc = r - R; phi(i, j, k) = std::min(phi_loc, phi_glob); }); const auto& nbx = mfi.nodaltilebox(); - auto epsilon_node = mask_node(lev).array(mfi); + const auto& epsilon_node = mask_node(lev).array(mfi); amrex::ParallelFor( nbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { const amrex::Real x = problo[0] + i * dx[0]; diff --git a/amr-wind/ocean_waves/relaxation_zones/hos_waves_ops.H b/amr-wind/ocean_waves/relaxation_zones/hos_waves_ops.H index 105a12787e..408333129c 100644 --- a/amr-wind/ocean_waves/relaxation_zones/hos_waves_ops.H +++ b/amr-wind/ocean_waves/relaxation_zones/hos_waves_ops.H @@ -118,7 +118,7 @@ void ReadHOSFileLev( } void StoreHOSDataLoop( - HOSWaves::MetaType& wdata, + const HOSWaves::MetaType& wdata, amrex::Array4 const& phi, amrex::Array4 const& vel, const amrex::Real* dev_eta_data, diff --git a/amr-wind/ocean_waves/relaxation_zones/linear_waves_ops.H b/amr-wind/ocean_waves/relaxation_zones/linear_waves_ops.H index adb2eb23ec..eb79ebe0b5 100644 --- a/amr-wind/ocean_waves/relaxation_zones/linear_waves_ops.H +++ b/amr-wind/ocean_waves/relaxation_zones/linear_waves_ops.H @@ -35,16 +35,17 @@ struct InitDataOp auto& sim = data.sim(); + // cppcheck-suppress constVariableReference auto& m_levelset = sim.repo().get_field("levelset"); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& m_velocity = sim.repo().get_field("velocity"); const auto& problo = geom.ProbLoArray(); const auto& dx = geom.CellSizeArray(); for (amrex::MFIter mfi(m_levelset(level)); mfi.isValid(); ++mfi) { - auto phi = m_levelset(level).array(mfi); - auto vel = m_velocity(level).array(mfi); + const auto& phi = m_levelset(level).array(mfi); + const auto& vel = m_velocity(level).array(mfi); const amrex::Real zsl = wdata.zsl; const auto& gbx3 = mfi.growntilebox(3); @@ -108,9 +109,9 @@ struct UpdateRelaxZonesOp auto& sim = data.sim(); const auto& time = sim.time().new_time(); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& m_ow_levelset = sim.repo().get_field("ow_levelset"); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& m_ow_velocity = sim.repo().get_field("ow_velocity"); auto nlevels = sim.repo().num_active_levels(); @@ -121,8 +122,8 @@ struct UpdateRelaxZonesOp const auto& dx = geom[lev].CellSizeArray(); for (amrex::MFIter mfi(m_ow_levelset(lev)); mfi.isValid(); ++mfi) { - auto phi = m_ow_levelset(lev).array(mfi); - auto vel = m_ow_velocity(lev).array(mfi); + const auto& phi = m_ow_levelset(lev).array(mfi); + const auto& vel = m_ow_velocity(lev).array(mfi); const amrex::Real waveheight = wdata.wave_height; const amrex::Real wavelength = wdata.wave_length; diff --git a/amr-wind/ocean_waves/relaxation_zones/stokes_waves_ops.H b/amr-wind/ocean_waves/relaxation_zones/stokes_waves_ops.H index dbe5bea220..3fd83b9e5b 100644 --- a/amr-wind/ocean_waves/relaxation_zones/stokes_waves_ops.H +++ b/amr-wind/ocean_waves/relaxation_zones/stokes_waves_ops.H @@ -36,16 +36,16 @@ struct InitDataOp const auto& wdata = data.meta(); auto& sim = data.sim(); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& m_levelset = sim.repo().get_field("levelset"); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& velocity = sim.repo().get_field("velocity"); const auto& problo = geom.ProbLoArray(); const auto& dx = geom.CellSizeArray(); for (amrex::MFIter mfi(m_levelset(level)); mfi.isValid(); ++mfi) { - auto phi = m_levelset(level).array(mfi); - auto vel = velocity(level).array(mfi); + const auto& phi = m_levelset(level).array(mfi); + const auto& vel = velocity(level).array(mfi); const amrex::Real zsl = wdata.zsl; const auto& gbx3 = mfi.growntilebox(3); @@ -71,9 +71,9 @@ struct UpdateRelaxZonesOp auto& sim = data.sim(); const auto& time = sim.time().new_time(); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& m_ow_levelset = sim.repo().get_field("ow_levelset"); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& m_ow_velocity = sim.repo().get_field("ow_velocity"); auto nlevels = sim.repo().num_active_levels(); @@ -84,8 +84,8 @@ struct UpdateRelaxZonesOp const auto& dx = geom[lev].CellSizeArray(); for (amrex::MFIter mfi(m_ow_levelset(lev)); mfi.isValid(); ++mfi) { - auto phi = m_ow_levelset(lev).array(mfi); - auto vel = m_ow_velocity(lev).array(mfi); + const auto& phi = m_ow_levelset(lev).array(mfi); + const auto& vel = m_ow_velocity(lev).array(mfi); const amrex::Real waveheight = wdata.wave_height; const amrex::Real wavelength = wdata.wave_length; diff --git a/amr-wind/overset/TiogaInterface.cpp b/amr-wind/overset/TiogaInterface.cpp index 486c36d148..59e42d414d 100644 --- a/amr-wind/overset/TiogaInterface.cpp +++ b/amr-wind/overset/TiogaInterface.cpp @@ -103,7 +103,7 @@ void TiogaInterface::post_regrid_actions() void TiogaInterface::pre_overset_conn_work() { - auto& repo = m_sim.repo(); + const auto& repo = m_sim.repo(); const int num_ghost = m_sim.pde_manager().num_ghost_state(); m_iblank_cell_host = repo.create_int_scratch_field_on_host( "iblank_cell_host", 1, num_ghost, FieldLoc::CELL); @@ -122,7 +122,7 @@ void TiogaInterface::pre_overset_conn_work() void TiogaInterface::post_overset_conn_work() { - auto& repo = m_sim.repo(); + const auto& repo = m_sim.repo(); const int nlevels = repo.num_active_levels(); for (int lev = 0; lev < nlevels; ++lev) { htod_memcpy(m_iblank_cell(lev), (*m_iblank_cell_host)(lev), 0, 0, 1); @@ -389,7 +389,7 @@ void TiogaInterface::amr_to_tioga_mesh() void TiogaInterface::amr_to_tioga_iblank() { BL_PROFILE("amr-wind::TiogaInterface::amr_to_tioga_iblank"); - auto& mesh = m_sim.mesh(); + const auto& mesh = m_sim.mesh(); const int nlevels = mesh.finestLevel() + 1; // Reset local patch counter diff --git a/amr-wind/physics/multiphase/MultiPhase.cpp b/amr-wind/physics/multiphase/MultiPhase.cpp index 5dbc09aeb0..1dc99e2a77 100644 --- a/amr-wind/physics/multiphase/MultiPhase.cpp +++ b/amr-wind/physics/multiphase/MultiPhase.cpp @@ -366,11 +366,11 @@ void MultiPhase::calculate_advected_facedensity() amrex::Real c_r2 = m_rho2; // Get advected vof terms at each face - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& advalpha_x = m_sim.repo().get_field("advalpha_x"); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& advalpha_y = m_sim.repo().get_field("advalpha_y"); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& advalpha_z = m_sim.repo().get_field("advalpha_z"); for (int lev = 0; lev < nlevels; ++lev) { @@ -382,9 +382,9 @@ void MultiPhase::calculate_advected_facedensity() const auto& ybx = amrex::surroundingNodes(bx, 1); const auto& zbx = amrex::surroundingNodes(bx, 2); - auto aa_x = advalpha_x(lev).array(mfi); - auto aa_y = advalpha_y(lev).array(mfi); - auto aa_z = advalpha_z(lev).array(mfi); + const auto& aa_x = advalpha_x(lev).array(mfi); + const auto& aa_y = advalpha_y(lev).array(mfi); + const auto& aa_z = advalpha_z(lev).array(mfi); amrex::ParallelFor( bxg1, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { diff --git a/amr-wind/projection/incflo_apply_nodal_projection.cpp b/amr-wind/projection/incflo_apply_nodal_projection.cpp index 586ae26863..b7c1a9c5a2 100644 --- a/amr-wind/projection/incflo_apply_nodal_projection.cpp +++ b/amr-wind/projection/incflo_apply_nodal_projection.cpp @@ -414,7 +414,7 @@ void incflo::ApplyProjection( // Determine if reference pressure should be added back if (m_repo.field_exists("reference_pressure") && time != 0.0) { - auto& p0 = m_repo.get_field("reference_pressure"); + const auto& p0 = m_repo.get_field("reference_pressure"); for (int lev = 0; lev <= finest_level; lev++) { amrex::MultiFab::Add( pressure(lev), p0(lev), 0, 0, 1, pressure.num_grow()[0]); diff --git a/amr-wind/setup/init.cpp b/amr-wind/setup/init.cpp index 1181470147..2482a98baa 100644 --- a/amr-wind/setup/init.cpp +++ b/amr-wind/setup/init.cpp @@ -128,11 +128,12 @@ void incflo::InitialIterations() // Add mean pressure back if available if (m_repo.field_exists("reference_pressure")) { - auto& pressure = m_repo.get_field("p"); - auto& p0 = m_repo.get_field("reference_pressure"); + // cppcheck-suppress constVariableReference + auto& press = m_repo.get_field("p"); + const auto& p0 = m_repo.get_field("reference_pressure"); for (int lev = 0; lev <= finest_level; lev++) { amrex::MultiFab::Add( - pressure(lev), p0(lev), 0, 0, 1, pressure.num_grow()[0]); + press(lev), p0(lev), 0, 0, 1, press.num_grow()[0]); } } amrex::Print() << "Completed initial pressure iterations" << std::endl diff --git a/amr-wind/transport_models/TwoPhaseTransport.H b/amr-wind/transport_models/TwoPhaseTransport.H index 9aed3ea906..52b580fa30 100644 --- a/amr-wind/transport_models/TwoPhaseTransport.H +++ b/amr-wind/transport_models/TwoPhaseTransport.H @@ -65,16 +65,13 @@ public: if (m_ifacetype == InterfaceCapturingMethod::VOF) { - // cppcheck-suppress constVariable - auto& vof = m_repo.get_field("vof"); + const auto& vof = m_repo.get_field("vof"); for (int lev = 0; lev < m_repo.num_active_levels(); ++lev) { for (amrex::MFIter mfi((*mu)(lev)); mfi.isValid(); ++mfi) { const auto& vbx = mfi.growntilebox(); - const amrex::Array4& volfrac = - vof(lev).array(mfi); - const amrex::Array4& visc = - (*mu)(lev).array(mfi); + const auto& volfrac = vof(lev).const_array(mfi); + const auto& visc = (*mu)(lev).array(mfi); const amrex::Real mu1 = m_mu1; const amrex::Real mu2 = m_mu2; amrex::ParallelFor( @@ -88,18 +85,15 @@ public: } else if (m_ifacetype == InterfaceCapturingMethod::LS) { - // cppcheck-suppress constVariable - auto& levelset = m_repo.get_field("levelset"); + const auto& levelset = m_repo.get_field("levelset"); const auto& geom = m_repo.mesh().Geom(); for (int lev = 0; lev < m_repo.num_active_levels(); ++lev) { for (amrex::MFIter mfi((*mu)(lev)); mfi.isValid(); ++mfi) { const auto& vbx = mfi.growntilebox(); const auto& dx = geom[lev].CellSizeArray(); - const amrex::Array4& visc = - (*mu)(lev).array(mfi); - const amrex::Array4& phi = - levelset(lev).array(mfi); + const auto& visc = (*mu)(lev).array(mfi); + const auto& phi = levelset(lev).const_array(mfi); const amrex::Real eps = std::cbrt(2. * dx[0] * dx[1] * dx[2]); const amrex::Real mu1 = m_mu1; diff --git a/amr-wind/turbulence/LES/AMD.cpp b/amr-wind/turbulence/LES/AMD.cpp index 7629252fb6..18f8edd39d 100644 --- a/amr-wind/turbulence/LES/AMD.cpp +++ b/amr-wind/turbulence/LES/AMD.cpp @@ -11,6 +11,7 @@ namespace amr_wind { namespace turbulence { template +// cppcheck-suppress uninitMemberVar AMD::AMD(CFDSim& sim) : TurbModelBase(sim) , m_vel(sim.repo().get_field("velocity")) @@ -46,7 +47,7 @@ void AMD::update_turbulent_viscosity( "amr-wind::" + this->identifier() + "::update_turbulent_viscosity"); auto& mu_turb = this->mu_turb(); - auto& repo = mu_turb.repo(); + const auto& repo = mu_turb.repo(); const auto& vel = m_vel.state(fstate); const auto& temp = m_temperature.state(fstate); const auto& den = m_rho.state(fstate); @@ -227,7 +228,7 @@ void AMD::update_alphaeff(Field& alphaeff) BL_PROFILE("amr-wind::" + this->identifier() + "::update_alphaeff"); - auto& repo = alphaeff.repo(); + const auto& repo = alphaeff.repo(); const auto& geom_vec = repo.mesh().Geom(); const amrex::Real C_poincare = this->m_C; namespace stencil = amr_wind::fvm::stencil; diff --git a/amr-wind/turbulence/LES/AMDNoTherm.cpp b/amr-wind/turbulence/LES/AMDNoTherm.cpp index 1358c16073..310aac57f3 100644 --- a/amr-wind/turbulence/LES/AMDNoTherm.cpp +++ b/amr-wind/turbulence/LES/AMDNoTherm.cpp @@ -13,6 +13,7 @@ namespace amr_wind { namespace turbulence { template +// cppcheck-suppress uninitMemberVar AMDNoTherm::AMDNoTherm(CFDSim& sim) : TurbModelBase(sim) , m_vel(sim.repo().get_field("velocity")) @@ -35,7 +36,7 @@ void AMDNoTherm::update_turbulent_viscosity( "amr-wind::" + this->identifier() + "::update_turbulent_viscosity"); auto& mu_turb = this->mu_turb(); - auto& repo = mu_turb.repo(); + const auto& repo = mu_turb.repo(); const auto& vel = m_vel.state(fstate); const auto& den = m_rho.state(fstate); auto gradVel = repo.create_scratch_field(AMREX_SPACEDIM * AMREX_SPACEDIM); diff --git a/amr-wind/turbulence/LES/OneEqKsgs.cpp b/amr-wind/turbulence/LES/OneEqKsgs.cpp index 4660c5ff34..864f02eb34 100644 --- a/amr-wind/turbulence/LES/OneEqKsgs.cpp +++ b/amr-wind/turbulence/LES/OneEqKsgs.cpp @@ -233,7 +233,7 @@ void OneEqKsgsM84::post_advance_work() // Update sdr field based on sfs ke auto& tke = *(this->m_tke); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& sdr = *(this->m_sdr); const amrex::Real Ce = this->m_Ce; diff --git a/amr-wind/turbulence/RANS/KOmegaSST.cpp b/amr-wind/turbulence/RANS/KOmegaSST.cpp index d54e3208c2..bd107705d0 100644 --- a/amr-wind/turbulence/RANS/KOmegaSST.cpp +++ b/amr-wind/turbulence/RANS/KOmegaSST.cpp @@ -86,11 +86,10 @@ void KOmegaSST::update_turbulent_viscosity( const auto& den = this->m_rho.state(fstate); const auto& tke = (*this->m_tke).state(fstate); const auto& sdr = (*this->m_sdr).state(fstate); - // cppcheck-suppress constVariable - auto& repo = mu_turb.repo(); + const auto& repo = mu_turb.repo(); auto& tke_lhs = (this->m_sim).repo().get_field("tke_lhs_src_term"); tke_lhs.setVal(0.0); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& sdr_lhs = (this->m_sim).repo().get_field("sdr_lhs_src_term"); auto gradK = (this->m_sim.repo()).create_scratch_field(3, 0); diff --git a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp index 9178369c6e..1d3f2cd6e3 100644 --- a/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp +++ b/amr-wind/turbulence/RANS/KOmegaSSTIDDES.cpp @@ -19,6 +19,7 @@ template KOmegaSSTIDDES::~KOmegaSSTIDDES() = default; template +// cppcheck-suppress uninitMemberVar KOmegaSSTIDDES::KOmegaSSTIDDES(CFDSim& sim) : KOmegaSST(sim) , m_rans_ind(sim.repo().declare_field("rans_indicator", 1, 1, 1)) @@ -99,12 +100,11 @@ void KOmegaSSTIDDES::update_turbulent_viscosity( const auto& den = this->m_rho.state(fstate); const auto& tke = (*this->m_tke).state(fstate); const auto& sdr = (*this->m_sdr).state(fstate); - // cppcheck-suppress constVariable - auto& repo = mu_turb.repo(); + const auto& repo = mu_turb.repo(); const auto& geom_vec = repo.mesh().Geom(); auto& tke_lhs = (this->m_sim).repo().get_field("tke_lhs_src_term"); tke_lhs.setVal(0.0); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& sdr_lhs = (this->m_sim).repo().get_field("sdr_lhs_src_term"); auto gradK = (this->m_sim.repo()).create_scratch_field(3, 0); diff --git a/amr-wind/utilities/SecondMomentAveraging.cpp b/amr-wind/utilities/SecondMomentAveraging.cpp index 334674d86e..53d6a9ea08 100644 --- a/amr-wind/utilities/SecondMomentAveraging.cpp +++ b/amr-wind/utilities/SecondMomentAveraging.cpp @@ -137,8 +137,8 @@ void SecondMomentAveraging::compute_average( m_plane_average2.line_average().data(), m_plane_average2.line_average().size()); - amrex::Real* line_avg1 = lavg1.data(); - amrex::Real* line_avg2 = lavg2.data(); + const auto* line_avg1 = lavg1.data(); + const auto* line_avg2 = lavg2.data(); amrex::Real denom = 1.0 / (amrex::Real)m_plane_average1.ncell_plane(); diff --git a/amr-wind/utilities/ThirdMomentAveraging.cpp b/amr-wind/utilities/ThirdMomentAveraging.cpp index e31b11d489..e7cc421bb0 100644 --- a/amr-wind/utilities/ThirdMomentAveraging.cpp +++ b/amr-wind/utilities/ThirdMomentAveraging.cpp @@ -158,9 +158,9 @@ void ThirdMomentAveraging::compute_average( m_plane_average3.line_average().data(), m_plane_average3.line_average().size()); - amrex::Real* line_avg1 = lavg1.data(); - amrex::Real* line_avg2 = lavg2.data(); - amrex::Real* line_avg3 = lavg3.data(); + const auto* line_avg1 = lavg1.data(); + const auto* line_avg2 = lavg2.data(); + const auto* line_avg3 = lavg3.data(); amrex::Real denom = 1.0 / (amrex::Real)m_plane_average1.ncell_plane(); diff --git a/amr-wind/utilities/averaging/TimeAveraging.cpp b/amr-wind/utilities/averaging/TimeAveraging.cpp index 55e2e723e1..4c1d35c753 100644 --- a/amr-wind/utilities/averaging/TimeAveraging.cpp +++ b/amr-wind/utilities/averaging/TimeAveraging.cpp @@ -87,7 +87,7 @@ void TimeAveraging::post_advance_work() } const amrex::Real elapsed_time = (cur_time - m_start_time); - for (auto& avg : m_averages) { + for (const auto& avg : m_averages) { (*avg)(time, m_filter, elapsed_time); } } diff --git a/amr-wind/utilities/diagnostics.H b/amr-wind/utilities/diagnostics.H index 91f4b6d1e6..7605db0121 100644 --- a/amr-wind/utilities/diagnostics.H +++ b/amr-wind/utilities/diagnostics.H @@ -22,10 +22,10 @@ amrex::Real get_vel_min( const int vdir); amrex::Real get_vel_loc( - amrex::MultiFab& vel, - amrex::iMultiFab& level_mask, - int vdir, - int ldir, + const amrex::MultiFab& vel, + const amrex::iMultiFab& level_mask, + const int vdir, + const int ldir, amrex::Real vel_max, const amrex::GpuArray problo, const amrex::GpuArray dx); @@ -47,10 +47,10 @@ amrex::Real get_macvel_min( const int vdir); amrex::Real get_macvel_loc( - amrex::MultiFab& macvel, - amrex::iMultiFab& level_mask, - int vdir, - int ldir, + const amrex::MultiFab& macvel, + const amrex::iMultiFab& level_mask, + const int vdir, + const int ldir, amrex::Real vel_max, const amrex::GpuArray problo, const amrex::GpuArray dx); @@ -63,4 +63,4 @@ amrex::Array PrintMaxMACVelLocations( } // namespace amr_wind::diagnostics -#endif \ No newline at end of file +#endif diff --git a/amr-wind/utilities/diagnostics.cpp b/amr-wind/utilities/diagnostics.cpp index 5f2cc159e6..5cc4271602 100644 --- a/amr-wind/utilities/diagnostics.cpp +++ b/amr-wind/utilities/diagnostics.cpp @@ -41,10 +41,10 @@ amrex::Real amr_wind::diagnostics::get_vel_min( } amrex::Real amr_wind::diagnostics::get_vel_loc( - amrex::MultiFab& vel, - amrex::iMultiFab& level_mask, - int vdir, - int ldir, + const amrex::MultiFab& vel, + const amrex::iMultiFab& level_mask, + const int vdir, + const int ldir, amrex::Real vel_max, const amrex::GpuArray problo, const amrex::GpuArray dx) @@ -114,10 +114,10 @@ amrex::Real amr_wind::diagnostics::get_macvel_min( } amrex::Real amr_wind::diagnostics::get_macvel_loc( - amrex::MultiFab& macvel, - amrex::iMultiFab& level_mask, - int vdir, - int ldir, + const amrex::MultiFab& macvel, + const amrex::iMultiFab& level_mask, + const int vdir, + const int ldir, amrex::Real mvel_max, const amrex::GpuArray problo, const amrex::GpuArray dx) @@ -154,7 +154,7 @@ amrex::Array amr_wind::diagnostics::PrintMaxVelLocations( BL_PROFILE("amr-wind::diagnostics::PrintMaxVelLocations"); // Get fields - auto& vel = repo.get_field("velocity"); + const auto& vel = repo.get_field("velocity"); const int finest_level = repo.num_active_levels() - 1; // Get infinity norm of velocities @@ -335,9 +335,9 @@ amrex::Array amr_wind::diagnostics::PrintMaxMACVelLocations( BL_PROFILE("amr-wind::diagnostics::PrintMaxMACVelLocations"); // Get fields - auto& u_mac = repo.get_field("u_mac"); - auto& v_mac = repo.get_field("v_mac"); - auto& w_mac = repo.get_field("w_mac"); + const auto& u_mac = repo.get_field("u_mac"); + const auto& v_mac = repo.get_field("v_mac"); + const auto& w_mac = repo.get_field("w_mac"); const int finest_level = repo.num_active_levels() - 1; // Get infinity norm of mac velocities diff --git a/amr-wind/utilities/sampling/FieldNorms.cpp b/amr-wind/utilities/sampling/FieldNorms.cpp index 8798c28576..bd58dc540d 100644 --- a/amr-wind/utilities/sampling/FieldNorms.cpp +++ b/amr-wind/utilities/sampling/FieldNorms.cpp @@ -23,7 +23,7 @@ void FieldNorms::initialize() pp.query("output_frequency", m_out_freq); } - auto& io_mng = m_sim.io_manager(); + const auto& io_mng = m_sim.io_manager(); for (const auto& fld : io_mng.plot_fields()) { ioutils::add_var_names(m_var_names, fld->name(), fld->num_comp()); } diff --git a/amr-wind/utilities/sampling/Sampling.cpp b/amr-wind/utilities/sampling/Sampling.cpp index 36281e9133..40b745fd8d 100644 --- a/amr-wind/utilities/sampling/Sampling.cpp +++ b/amr-wind/utilities/sampling/Sampling.cpp @@ -52,7 +52,7 @@ void Sampling::initialize() // Load different probe types, default probe type is line int idx = 0; m_total_particles = 0; - for (auto& lbl : labels) { + for (const auto& lbl : labels) { const std::string key = m_label + "." + lbl; amrex::ParmParse pp1(key); std::string stype = "LineSampler"; diff --git a/amr-wind/wind_energy/ABLFieldInit.cpp b/amr-wind/wind_energy/ABLFieldInit.cpp index cd696d9d61..47d82173d8 100644 --- a/amr-wind/wind_energy/ABLFieldInit.cpp +++ b/amr-wind/wind_energy/ABLFieldInit.cpp @@ -160,10 +160,7 @@ void ABLFieldInit::operator()( } void ABLFieldInit::perturb_temperature( - const int lev, - const amrex::Geometry& geom, - // cppcheck-suppress constParameter - Field& temperature) const + const int lev, const amrex::Geometry& geom, Field& temperature) const { /** Perturbations for the temperature field * diff --git a/amr-wind/wind_energy/ABLStats.cpp b/amr-wind/wind_energy/ABLStats.cpp index 3640cf2a89..b75d5bf68f 100644 --- a/amr-wind/wind_energy/ABLStats.cpp +++ b/amr-wind/wind_energy/ABLStats.cpp @@ -102,7 +102,7 @@ void ABLStats::calc_sfs_stress_avgs( BL_PROFILE("amr-wind::ABLStats::calc_sfs_stress_avgs"); - auto& repo = m_sim.repo(); + const auto& repo = m_sim.repo(); const auto& m_vel = repo.get_field("velocity"); auto gradVel = repo.create_scratch_field(9); diff --git a/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H b/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H index a3bc010396..81a03c0f19 100644 --- a/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H +++ b/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H @@ -328,7 +328,7 @@ struct ProcessOutputsOp { private: // cppcheck-suppress uninitMemberVarPrivate - Joukowsky::DataType& m_data; + const Joukowsky::DataType& m_data; //! Path to the output directory (specified by Actuator physics class) std::string m_out_dir; @@ -340,7 +340,8 @@ private: public: // cppcheck-suppress constParameter - explicit ProcessOutputsOp(Joukowsky::DataType& data) + explicit ProcessOutputsOp( + const Joukowsky::DataType& data) : m_data(data) {} void operator()(Joukowsky::DataType& /*data*/) {} diff --git a/amr-wind/wind_energy/actuator/disk/uniform_ct_ops.H b/amr-wind/wind_energy/actuator/disk/uniform_ct_ops.H index d6616e0718..72ac4d7bdf 100644 --- a/amr-wind/wind_energy/actuator/disk/uniform_ct_ops.H +++ b/amr-wind/wind_energy/actuator/disk/uniform_ct_ops.H @@ -127,7 +127,7 @@ struct ProcessOutputsOp { private: // cppcheck-suppress uninitMemberVarPrivate - UniformCt::DataType& m_data; + const UniformCt::DataType& m_data; //! Path to the output directory (specified by Actuator physics class) std::string m_out_dir; @@ -139,7 +139,8 @@ private: public: // cppcheck-suppress constParameter - explicit ProcessOutputsOp(UniformCt::DataType& data) + explicit ProcessOutputsOp( + const UniformCt::DataType& data) : m_data(data) {} void operator()(UniformCt::DataType& /*unused*/) {} diff --git a/amr-wind/wind_energy/actuator/turbine/fast/FastIface.cpp b/amr-wind/wind_energy/actuator/turbine/fast/FastIface.cpp index 353078b0ca..4c4df585b9 100644 --- a/amr-wind/wind_energy/actuator/turbine/fast/FastIface.cpp +++ b/amr-wind/wind_energy/actuator/turbine/fast/FastIface.cpp @@ -253,7 +253,7 @@ void FastIface::fast_init_turbine(FastTurbine& fi) } } -// cppcheck-suppress constParameter +// cppcheck-suppress constParameterReference // NOLINTNEXTLINE(readability-convert-member-functions-to-static) void FastIface::fast_replay_turbine(FastTurbine& fi) { @@ -301,7 +301,6 @@ void FastIface::fast_replay_turbine(FastTurbine& fi) #endif } -// cppcheck-suppress constParameter // NOLINTNEXTLINE(readability-convert-member-functions-to-static) void FastIface::fast_restart_turbine(FastTurbine& fi) { diff --git a/unit_tests/aw_test_utils/MeshTest.cpp b/unit_tests/aw_test_utils/MeshTest.cpp index be13909f79..66555afdae 100644 --- a/unit_tests/aw_test_utils/MeshTest.cpp +++ b/unit_tests/aw_test_utils/MeshTest.cpp @@ -34,7 +34,7 @@ void MeshTest::reset_prob_domain() pp.getarr("is_periodic", periodic, 0, AMREX_SPACEDIM); amrex::RealBox rb(problo.data(), probhi.data()); - amrex::Geometry* gg = amrex::AMReX::top()->getDefaultGeometry(); + const amrex::Geometry* gg = amrex::AMReX::top()->getDefaultGeometry(); if (gg != nullptr) { amrex::Geometry::ResetDefaultProbDomain(rb); diff --git a/unit_tests/core/test_field.cpp b/unit_tests/core/test_field.cpp index 2eef72185c..7beaf67f2d 100644 --- a/unit_tests/core/test_field.cpp +++ b/unit_tests/core/test_field.cpp @@ -318,7 +318,7 @@ TEST_F(FieldRepoTest, int_scratch_fields) populate_parameters(); create_mesh_instance(); - auto& frepo = mesh().field_repo(); + const auto& frepo = mesh().field_repo(); // Check that int scratch field creation is disallowed before mesh is created #if !(defined(AMREX_USE_MPI) && defined(__APPLE__)) EXPECT_THROW( @@ -326,10 +326,8 @@ TEST_F(FieldRepoTest, int_scratch_fields) #endif initialize_mesh(); - // cppcheck-suppress constVariable auto ibcell_host = frepo.create_int_scratch_field_on_host( "iblank_cell_host", 1, 0, amr_wind::FieldLoc::CELL); - // cppcheck-suppress constVariable auto ibnode_host = frepo.create_int_scratch_field_on_host( "iblank_node_host", 1, 0, amr_wind::FieldLoc::NODE); @@ -351,9 +349,9 @@ TEST_F(FieldRepoTest, int_fields) initialize_mesh(); auto& frepo = mesh().field_repo(); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& ibcell = frepo.declare_int_field("iblank_cell", 1, 0, 1); - // cppcheck-suppress constVariable + // cppcheck-suppress constVariableReference auto& ibnode = frepo.declare_int_field( "iblank_node", 1, 0, 1, amr_wind::FieldLoc::NODE); diff --git a/unit_tests/multiphase/test_momflux.cpp b/unit_tests/multiphase/test_momflux.cpp index a098a1a2d0..5c1f3320a4 100644 --- a/unit_tests/multiphase/test_momflux.cpp +++ b/unit_tests/multiphase/test_momflux.cpp @@ -204,7 +204,7 @@ class MassMomFluxOpTest : public MeshTest const auto& advrho_f = repo.get_field("advalpha_" + sdir); // Get convective term - auto& conv_term = + const auto& conv_term = mom_eqn.fields().conv_term.state(amr_wind::FieldState::New); // Base level @@ -214,12 +214,12 @@ class MassMomFluxOpTest : public MeshTest const auto& problo = geom[lev].ProbLoArray(); for (amrex::MFIter mfi(vof(lev)); mfi.isValid(); ++mfi) { - const auto& um = umac(lev).array(mfi); - const auto& vm = vmac(lev).array(mfi); - const auto& wm = wmac(lev).array(mfi); - const auto& rf = advrho_f(lev).array(mfi); - const auto& vel = velocity(lev).array(mfi); - const auto& dqdt = conv_term(lev).array(mfi); + const auto& um = umac(lev).const_array(mfi); + const auto& vm = vmac(lev).const_array(mfi); + const auto& wm = wmac(lev).const_array(mfi); + const auto& rf = advrho_f(lev).const_array(mfi); + const auto& vel = velocity(lev).const_array(mfi); + const auto& dqdt = conv_term(lev).const_array(mfi); // Small mesh, loop in serial for check for (int i = 0; i < 2; ++i) { diff --git a/unit_tests/turbulence/test_turbulence_LES.cpp b/unit_tests/turbulence/test_turbulence_LES.cpp index 03d028665b..ef1cee932f 100644 --- a/unit_tests/turbulence/test_turbulence_LES.cpp +++ b/unit_tests/turbulence/test_turbulence_LES.cpp @@ -209,7 +209,7 @@ TEST_F(TurbLESTest, test_smag_setup_calc) // Update turbulent viscosity directly tmodel.update_turbulent_viscosity( amr_wind::FieldState::New, DiffusionType::Crank_Nicolson); - auto& muturb = sim().repo().get_field("mu_turb"); + const auto& muturb = sim().repo().get_field("mu_turb"); // Check values of turbulent viscosity auto min_val = utils::field_min(muturb); @@ -321,7 +321,7 @@ TEST_F(TurbLESTest, test_1eqKsgs_setup_calc) // Update turbulent viscosity directly tmodel.update_turbulent_viscosity( amr_wind::FieldState::New, DiffusionType::Crank_Nicolson); - auto& muturb = sim().repo().get_field("mu_turb"); + const auto& muturb = sim().repo().get_field("mu_turb"); // Check values of turbulent viscosity const auto min_val = utils::field_min(muturb); @@ -408,7 +408,7 @@ TEST_F(TurbLESTest, test_AMD_setup_calc) // Update turbulent viscosity directly tmodel.update_turbulent_viscosity( amr_wind::FieldState::New, DiffusionType::Crank_Nicolson); - auto& muturb = sim().repo().get_field("mu_turb"); + const auto& muturb = sim().repo().get_field("mu_turb"); // Check values of turbulent viscosity const auto min_val = utils::field_min(muturb); @@ -487,7 +487,7 @@ TEST_F(TurbLESTest, test_AMDNoTherm_setup_calc) // Update turbulent viscosity directly tmodel.update_turbulent_viscosity( amr_wind::FieldState::New, DiffusionType::Crank_Nicolson); - auto& muturb = sim().repo().get_field("mu_turb"); + const auto& muturb = sim().repo().get_field("mu_turb"); // Check values of turbulent viscosity const auto min_val = utils::field_min(muturb); diff --git a/unit_tests/utilities/test_free_surface.cpp b/unit_tests/utilities/test_free_surface.cpp index d85c560414..aef9912540 100644 --- a/unit_tests/utilities/test_free_surface.cpp +++ b/unit_tests/utilities/test_free_surface.cpp @@ -137,10 +137,7 @@ void init_vof_slope( class FSRefineMesh : public AmrTestMesh { public: - FSRefineMesh() - : m_sim(*this) - , m_repo(m_sim.repo()) - , m_mesh_refiner(new amr_wind::RefineCriteriaManager(m_sim)) + FSRefineMesh() : m_mesh_refiner(new amr_wind::RefineCriteriaManager(m_sim)) {} amr_wind::FieldRepo& field_repo() { return m_repo; } amr_wind::CFDSim& sim() { return m_sim; } @@ -193,8 +190,6 @@ class FSRefineMesh : public AmrTestMesh } private: - amr_wind::CFDSim m_sim; - amr_wind::FieldRepo& m_repo; std::unique_ptr m_mesh_refiner; }; diff --git a/unit_tests/wind_energy/actuator/test_actuator_joukowsky_disk.cpp b/unit_tests/wind_energy/actuator/test_actuator_joukowsky_disk.cpp index 23f9f064e0..d12293c861 100644 --- a/unit_tests/wind_energy/actuator/test_actuator_joukowsky_disk.cpp +++ b/unit_tests/wind_energy/actuator/test_actuator_joukowsky_disk.cpp @@ -163,7 +163,7 @@ struct ProcessOutputsOp<::amr_wind_tests::Joukowsky, ActSrcDisk> { ProcessOutputsOp<::amr_wind_tests::Joukowsky, ActSrcDisk>( ::amr_wind_tests::Joukowsky::DataType& /**/) - {} // cppcheck-suppress missingReturn + {} void operator()(::amr_wind_tests::Joukowsky::DataType& /*data*/) {} void read_io_options(const utils::ActParser& /**/) {} void prepare_outputs(const std::string& /**/) {} @@ -205,4 +205,4 @@ TEST_F(ActJoukowskyTest, execution) act.pre_init_actions(); act.post_init_actions(); } -} // namespace amr_wind_tests \ No newline at end of file +} // namespace amr_wind_tests diff --git a/unit_tests/wind_energy/actuator/test_disk_functions.cpp b/unit_tests/wind_energy/actuator/test_disk_functions.cpp index cd75fcbfa3..dca60c2dd4 100644 --- a/unit_tests/wind_energy/actuator/test_disk_functions.cpp +++ b/unit_tests/wind_energy/actuator/test_disk_functions.cpp @@ -39,7 +39,6 @@ class TestAreaComputer : public ::testing::TestWithParam amrex::Real area; }; -// cppcheck-suppress uninitDerivedMemberVar TEST_P(TestAreaComputer, area_matches) { const auto params = GetParam(); @@ -53,7 +52,6 @@ TEST_P(TestAreaComputer, area_matches) EXPECT_NEAR(area, area_computed, 1.e-12 * area); } -// cppcheck-suppress uninitDerivedMemberVar TEST_P(TestAreaComputer, weight_sums_to_one) { const auto params = GetParam(); diff --git a/unit_tests/wind_energy/test_abl_bc.cpp b/unit_tests/wind_energy/test_abl_bc.cpp index 811d1a4c8a..a702065612 100644 --- a/unit_tests/wind_energy/test_abl_bc.cpp +++ b/unit_tests/wind_energy/test_abl_bc.cpp @@ -143,7 +143,7 @@ TEST_F(ABLMeshTest, abl_wall_model) icns_eq.compute_mueff(amr_wind::FieldState::Old); // Check test setup by verifying mu - auto& viscosity = sim().repo().get_field("velocity_mueff"); + const auto& viscosity = sim().repo().get_field("velocity_mueff"); EXPECT_NEAR(mu, utils::field_max(viscosity), tol); EXPECT_NEAR(mu, utils::field_min(viscosity), tol); diff --git a/unit_tests/wind_energy/test_abl_src.cpp b/unit_tests/wind_energy/test_abl_src.cpp index 9a4a4377b4..ba4ccbaec6 100644 --- a/unit_tests/wind_energy/test_abl_src.cpp +++ b/unit_tests/wind_energy/test_abl_src.cpp @@ -364,7 +364,7 @@ TEST_F(ABLMeshTest, coriolis_const_vel) // Velocity in x-direction test { - amrex::Real golds[AMREX_SPACEDIM] = { + const amrex::Real golds[AMREX_SPACEDIM] = { 0.0, -corfac * latfac * vel_comp, corfac * latfac * vel_comp}; vel.setVal(0.0); src_term.setVal(0.0); @@ -388,7 +388,7 @@ TEST_F(ABLMeshTest, coriolis_const_vel) // Velocity in y-direction test { - amrex::Real golds[AMREX_SPACEDIM] = { + const amrex::Real golds[AMREX_SPACEDIM] = { corfac * latfac * vel_comp, 0.0, 0.0}; vel.setVal(0.0); src_term.setVal(0.0); From f701e87727b191f14ce167c89f5d0ec7fcc0dcfe Mon Sep 17 00:00:00 2001 From: "Marc T. Henry de Frahan" Date: Fri, 21 Jul 2023 10:14:17 -0600 Subject: [PATCH 35/38] Remove cppcheck suppressions (#884) --- amr-wind/equation_systems/PDEOps.H | 1 - amr-wind/main.cpp | 17 +++++++---------- .../wind_energy/actuator/disk/uniform_ct_ops.H | 1 - 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/amr-wind/equation_systems/PDEOps.H b/amr-wind/equation_systems/PDEOps.H index 005f9f9a13..f845bb14e3 100644 --- a/amr-wind/equation_systems/PDEOps.H +++ b/amr-wind/equation_systems/PDEOps.H @@ -83,7 +83,6 @@ struct SrcTermOpBase for (auto& src_name : src_terms) { // Prefer to use emplace_back here - // cppcheck-suppress useStlAlgorithm sources.emplace_back(PDE::SrcTerm::create(src_name, sim)); } } diff --git a/amr-wind/main.cpp b/amr-wind/main.cpp index c4f1855e55..6e70fafdea 100644 --- a/amr-wind/main.cpp +++ b/amr-wind/main.cpp @@ -17,16 +17,13 @@ int main(int argc, char* argv[]) return 1; } - // cppcheck-suppress knownConditionTrueFalse - if (argc >= 2) { - // Look for "-h" or "--help" flag and print usage - for (auto i = 1; i < argc; i++) { - const std::string param(argv[i]); - if ((param == "--help") || (param == "-h")) { - amr_wind::io::print_banner(MPI_COMM_WORLD, std::cout); - amr_wind::io::print_usage(MPI_COMM_WORLD, std::cout); - return 0; - } + // Look for "-h" or "--help" flag and print usage + for (auto i = 1; i < argc; i++) { + const std::string param(argv[i]); + if ((param == "--help") || (param == "-h")) { + amr_wind::io::print_banner(MPI_COMM_WORLD, std::cout); + amr_wind::io::print_usage(MPI_COMM_WORLD, std::cout); + return 0; } } diff --git a/amr-wind/wind_energy/actuator/disk/uniform_ct_ops.H b/amr-wind/wind_energy/actuator/disk/uniform_ct_ops.H index 72ac4d7bdf..5de30a0760 100644 --- a/amr-wind/wind_energy/actuator/disk/uniform_ct_ops.H +++ b/amr-wind/wind_energy/actuator/disk/uniform_ct_ops.H @@ -138,7 +138,6 @@ private: int m_out_freq{10}; public: - // cppcheck-suppress constParameter explicit ProcessOutputsOp( const UniformCt::DataType& data) : m_data(data) From 761b26dc2a542b4c7c8ba47d6ba8482c3dfdfe8b Mon Sep 17 00:00:00 2001 From: Michael B Kuhn <31661049+mbkuhn@users.noreply.github.com> Date: Mon, 24 Jul 2023 10:16:28 -0600 Subject: [PATCH 36/38] Time intervals for plt and chkpt output (#881) * ability to output plt and chkpt files based on time intervals (not just based on timestep intervals) * option to enforce dt (to line up with output intervals) is available as input bool --------- Co-authored-by: Jon Rood --- amr-wind/core/SimTime.H | 46 +++++++- amr-wind/core/SimTime.cpp | 112 ++++++++++++++++++- unit_tests/core/test_simtime.cpp | 186 +++++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+), 8 deletions(-) diff --git a/amr-wind/core/SimTime.H b/amr-wind/core/SimTime.H index 0674ed3ac0..8a1a7604d4 100644 --- a/amr-wind/core/SimTime.H +++ b/amr-wind/core/SimTime.H @@ -4,6 +4,21 @@ #include "AMReX_REAL.H" #include "AMReX_GpuQualifiers.H" #include "AMReX_Extension.H" +#include +#include + +namespace { +AMREX_FORCE_INLINE amrex::Real get_enforced_dt_for_output( + const amrex::Real dt, + const amrex::Real cur_time, + const amrex::Real interval, + const amrex::Real tol) +{ + // Tolerance for being slightly under is relative to requested interval + return std::min( + dt, (std::floor(cur_time / interval + tol) + 1) * interval - cur_time); +} +} // namespace namespace amr_wind { @@ -158,6 +173,9 @@ private: //! Initial reduction in timestep size for startup amrex::Real m_init_shrink{0.1}; + //! Maximum growth of dt between timesteps + amrex::Real m_dt_growth{0.1}; + //! Counter for the number of timesteps since start of simulation int m_time_index{0}; @@ -176,12 +194,36 @@ private: //! Maximum timesteps for simulation int m_stop_time_index{-1}; - //! Time interval for plot file output + //! Time step interval for plot file output int m_plt_interval{-1}; - //! Time interval for writing checkpoint/restart files + //! Time interval for plot file output + amrex::Real m_plt_t_interval{-1.0}; + + //! Bool for if plt time interval should be forced + bool m_force_plt_dt{false}; + + //! Relative (to dt) tolerance for plot time interval output + amrex::Real m_plt_t_tol{1e-8}; + + //! Relative (to plt_t_interval) tolerance for enforcing dt + amrex::Real m_force_plt_tol{1e-3}; + + //! Time step interval for writing checkpoint/restart files int m_chkpt_interval{-1}; + //! Time interval for writing checkpoint/restart files + amrex::Real m_chkpt_t_interval{-1.0}; + + //! Bool for if checkpoint time interval should be forced + bool m_force_chkpt_dt{false}; + + //! Relative (to dt) tolerance for checkpoint time interval output + amrex::Real m_chkpt_t_tol{1e-8}; + + //! Relative (to chkpt_t_interval) tolerance for enforcing dt + amrex::Real m_force_chkpt_tol{1e-3}; + //! Time interval for regridding int m_regrid_interval{-1}; diff --git a/amr-wind/core/SimTime.cpp b/amr-wind/core/SimTime.cpp index d4fbf1f01b..3e0947313f 100644 --- a/amr-wind/core/SimTime.cpp +++ b/amr-wind/core/SimTime.cpp @@ -19,21 +19,75 @@ void SimTime::parse_parameters() pp.query("fixed_dt", m_fixed_dt); pp.query("initial_dt", m_initial_dt); pp.query("init_shrink", m_init_shrink); + pp.query("max_dt_growth", m_dt_growth); pp.query("cfl", m_max_cfl); pp.query("verbose", m_verbose); pp.query("regrid_interval", m_regrid_interval); pp.query("plot_interval", m_plt_interval); + pp.query("plot_time_interval", m_plt_t_interval); + pp.query("enforce_plot_time_dt", m_force_plt_dt); pp.query("checkpoint_interval", m_chkpt_interval); + pp.query("checkpoint_time_interval", m_chkpt_t_interval); + pp.query("enforce_checkpoint_time_dt", m_force_chkpt_dt); pp.query("regrid_start", m_regrid_start_index); pp.query("plot_start", m_plt_start_index); pp.query("checkpoint_start", m_chkpt_start_index); pp.query("use_force_cfl", m_use_force_cfl); + // Tolerances + pp.query("plot_time_interval_reltol", m_plt_t_tol); + pp.query("enforce_plot_dt_reltol", m_force_plt_tol); + pp.query("checkpoint_time_interval_reltol", m_chkpt_t_tol); + pp.query("enforce_checkpoint_dt_reltol", m_force_chkpt_tol); + if (m_fixed_dt > 0.0) { m_dt[0] = m_fixed_dt; } else { m_adaptive = true; } + + if (m_plt_interval > 0 && m_plt_t_interval > 0.0) { + amrex::Abort( + "plot_interval and plot_time_interval are both specified. " + "timestep- and time-based plotting should not be used together; " + "please only specify one."); + } + + if (m_chkpt_interval > 0 && m_chkpt_t_interval > 0.0) { + amrex::Abort( + "checkpoint_interval and checkpoint_time_interval are both " + "specified. timestep- and time-based checkpointing should not be " + "used together; please only specify one."); + } + + if (m_plt_t_interval <= 0.0 && m_force_plt_dt) { + amrex::Abort( + "enforce_plot_time_dt is true, but no plot time interval has been " + "provided."); + } + + if (m_chkpt_t_interval <= 0.0 && m_force_chkpt_dt) { + amrex::Abort( + "enforce_checkpoint_time_dt is true, but no checkpoint time " + "interval has been provided."); + } + + if (!m_adaptive && (m_force_plt_dt || m_force_chkpt_dt)) { + amrex::Abort( + "an output time interval has been specified to be enforced upon " + "dt, but dt is not adaptive."); + } + + if (m_force_plt_dt && m_force_chkpt_dt) { + amrex::Print() + << "WARNING: Time intervals will be enforced upon dt for both " + "plotfiles and checkpoint files."; + amrex::Print() + << " -- If these time intervals are different and not factors of " + "each other, inefficient behavior may result, depending on " + "tolerances: dt may be shortened often and outputs may occur in " + "consecutive timesteps."; + } } bool SimTime::new_timestep() @@ -110,9 +164,9 @@ void SimTime::set_current_cfl( dt_new *= m_init_shrink; } - // Limit timestep growth to 10% per timestep + // Limit timestep growth to % per timestep if (m_dt[0] > 0.0) { - dt_new = amrex::min(dt_new, 1.1 * m_dt[0]); + dt_new = amrex::min(dt_new, (1.0 + m_dt_growth) * m_dt[0]); } // Don't overshoot stop time @@ -123,6 +177,25 @@ void SimTime::set_current_cfl( if (m_adaptive) { m_dt[0] = dt_new; + // Shorten timestep to hit output frequency exactly + if (m_chkpt_t_interval > 0.0 && m_force_chkpt_dt) { + // Shorten dt if going to overshoot next output time + m_dt[0] = get_enforced_dt_for_output( + m_dt[0], m_cur_time, m_chkpt_interval, m_force_chkpt_tol); + // how it works: the floor operator gets the index of the last + // output, with a tolerance proportional to the current dt. + // adding 1 and multiplying by the time interval finds the next + // expected output time. if the time increment between the current + // time and the next expected output is less than the current dt, + // then the dt becomes this increment to get to the next output + // time. + } + if (m_plt_t_interval > 0.0 && m_force_plt_dt) { + // Shorten dt if going to overshoot next output time + m_dt[0] = get_enforced_dt_for_output( + m_dt[0], m_cur_time, m_plt_t_interval, m_force_plt_tol); + } + if (m_is_init && m_initial_dt > 0.0) { m_dt[0] = amrex::min(dt_new, m_initial_dt); } @@ -200,16 +273,43 @@ bool SimTime::do_regrid() const bool SimTime::write_plot_file() const { + // If dt is enforced, allow smallest tolerance to be in effect. This avoids + // unintentionally plotting in consecutive timesteps because of shortened dt + amrex::Real tol = m_plt_t_tol * m_dt[0]; + tol = + (m_force_chkpt_dt + ? std::min(tol, m_force_chkpt_tol * m_chkpt_t_interval) + : tol); + tol = + (m_force_plt_dt ? std::min(tol, m_force_plt_tol * m_plt_t_interval) + : tol); return ( - (m_plt_interval > 0) && - ((m_time_index - m_plt_start_index) % m_plt_interval == 0)); + ((m_plt_interval > 0) && + ((m_time_index - m_plt_start_index) % m_plt_interval == 0)) || + (m_plt_t_interval > 0.0 && + ((m_new_time + tol) / m_plt_t_interval - + std::floor((m_new_time + tol) / m_plt_t_interval) < + m_dt[0] / m_plt_t_interval))); } bool SimTime::write_checkpoint() const { + // If dt is enforced, use smallest tolerance + amrex::Real tol = m_plt_t_tol * m_dt[0]; + tol = + (m_force_chkpt_dt + ? std::min(tol, m_force_chkpt_tol * m_chkpt_t_interval) + : tol); + tol = + (m_force_plt_dt ? std::min(tol, m_force_plt_tol * m_plt_t_interval) + : tol); return ( - (m_chkpt_interval > 0) && - ((m_time_index - m_chkpt_start_index) % m_chkpt_interval == 0)); + ((m_chkpt_interval > 0) && + ((m_time_index - m_chkpt_start_index) % m_chkpt_interval == 0)) || + (m_chkpt_t_interval > 0.0 && + ((m_new_time + tol) / m_chkpt_t_interval - + std::floor((m_new_time + tol) / m_chkpt_t_interval) < + m_dt[0] / m_chkpt_t_interval))); } bool SimTime::write_last_plot_file() const diff --git a/unit_tests/core/test_simtime.cpp b/unit_tests/core/test_simtime.cpp index f70e25d165..ba18d8165c 100644 --- a/unit_tests/core/test_simtime.cpp +++ b/unit_tests/core/test_simtime.cpp @@ -83,6 +83,7 @@ TEST_F(SimTimeTest, time_loop) if (time.do_regrid()) { ++regrid_counter; } + std::cout << time.new_time() << std::endl; } EXPECT_EQ(counter, 5); EXPECT_EQ(plot_counter, 5); @@ -130,4 +131,189 @@ TEST_F(SimTimeTest, fixed_dt_loop) EXPECT_FALSE(time.write_last_plot_file()); } +TEST_F(SimTimeTest, plt_chk_timeinterval_loop) +{ + build_simtime_params(); + { + amrex::ParmParse pp("time"); + pp.add("regrid_interval", -1); + pp.add("checkpoint_interval", -1); + pp.add("plot_interval", -1); + pp.add("checkpoint_time_interval", 4.0); + pp.add("plot_time_interval", 1.0); + pp.add("fixed_dt", 0.3); + pp.add("stop_time", 5.0); + pp.add("max_step", 100); + } + amr_wind::SimTime time; + time.parse_parameters(); + + int counter = 0; + int plot_counter = 0; + int plot_step_sum = 0; + int chkpt_counter = 0; + int chkpt_step_sum = 0; + while (time.new_timestep()) { + time.set_current_cfl(0.45 / 0.3, 0.0, 0.0); + ++counter; + + if (time.write_plot_file()) { + ++plot_counter; + plot_step_sum += counter; + } + if (time.write_checkpoint()) { + ++chkpt_counter; + chkpt_step_sum += counter; + } + } + EXPECT_EQ(plot_counter, 5); + EXPECT_EQ(plot_step_sum, 4 + 7 + 10 + 14 + 17); + EXPECT_EQ(chkpt_counter, 1); + EXPECT_EQ(chkpt_step_sum, 14); +} + +TEST_F(SimTimeTest, enforce_dt_out) +{ + // Should not change if already correct + amrex::Real result = get_enforced_dt_for_output(0.1, 3.9, 2.0, 1e-3); + EXPECT_NEAR(result, 0.1, 1e-12); + + // Should not change if short of interval + result = get_enforced_dt_for_output(0.1, 3.9 - 2.0 * 5e-4, 2.0, 1e-3); + EXPECT_NEAR(result, 0.1, 1e-12); + + // Should not change if starting near interval + result = get_enforced_dt_for_output(0.1, 4.0, 2.0, 1e-3); + EXPECT_NEAR(result, 0.1, 1e-12); + result = get_enforced_dt_for_output(0.1, 4.0 - 2.0 * 0.99e-3, 2.0, 1e-3); + EXPECT_NEAR(result, 0.1, 1e-12); + // Past the tolerance, will change + result = get_enforced_dt_for_output(0.1, 4.0 - 2.0 * 1.01e-3, 2.0, 1e-3); + EXPECT_LT(result, 0.1); + + // Shortens dt if overlapping with intervals + result = get_enforced_dt_for_output(0.1, 3.95, 2.0, 1e-3); + EXPECT_NEAR(result, 0.05, 1e-12); +} + +TEST_F(SimTimeTest, enforce_timeinterval) +{ + build_simtime_params(); + { + amrex::ParmParse pp("time"); + pp.add("regrid_interval", -1); + pp.add("checkpoint_interval", -1); + pp.add("plot_interval", -1); + + pp.add("plot_time_interval", 0.5); + pp.add("enforce_plot_time_dt", true); + + // Default values for tolerances + pp.add("stop_time", 1.0); + pp.add("max_step", 10); + } + amr_wind::SimTime time; + time.parse_parameters(); + + int counter = 0; + int plot_counter = 0; + amrex::Real plot_time_sum = 0.0; + int plot_step_sum = 0; + while (time.new_timestep()) { + time.set_current_cfl(0.45 / 0.4, 0.0, 0.0); + ++counter; + if (time.write_plot_file()) { + ++plot_counter; + plot_time_sum += time.new_time(); + plot_step_sum += counter; + } + } + EXPECT_EQ(plot_counter, 2); + EXPECT_NEAR(plot_time_sum, 1.5, 1e-8); + EXPECT_EQ(plot_step_sum, 2 + 6); +} + +TEST_F(SimTimeTest, enforce_timeinterval_bigtimetol) +{ + build_simtime_params(); + { + amrex::ParmParse pp("time"); + pp.add("regrid_interval", -1); + pp.add("checkpoint_interval", -1); + pp.add("plot_interval", -1); + + pp.add("plot_time_interval", 0.5); + pp.add("enforce_plot_time_dt", true); + + // Choose values that could give issues + pp.add("enforce_plot_dt_reltol", 1e-8); + pp.add("plot_time_interval_reltol", 1e0); + + pp.add("stop_time", 1.0); + pp.add("max_step", 10); + } + amr_wind::SimTime time; + time.parse_parameters(); + + int counter = 0; + int plot_counter = 0; + amrex::Real plot_time_sum = 0.0; + int plot_step_sum = 0; + while (time.new_timestep()) { + time.set_current_cfl(0.45 / 0.4, 0.0, 0.0); + ++counter; + if (time.write_plot_file()) { + ++plot_counter; + plot_time_sum += time.new_time(); + plot_step_sum += counter; + } + } + EXPECT_EQ(plot_counter, 2); + EXPECT_NEAR(plot_time_sum, 1.5, 1e-8); + EXPECT_EQ(plot_step_sum, 2 + 6); +} + +TEST_F(SimTimeTest, enforce_timeinterval_bigdttol) +{ + build_simtime_params(); + { + amrex::ParmParse pp("time"); + pp.add("regrid_interval", -1); + pp.add("checkpoint_interval", -1); + pp.add("plot_interval", -1); + + pp.add("plot_time_interval", 0.5); + pp.add("enforce_plot_time_dt", true); + + // Weak enforcement of plot time interval on dt + pp.add("enforce_plot_dt_reltol", 1e0); + pp.add("plot_time_interval_reltol", 1e-8); + + pp.add("stop_time", 1.0); + pp.add("max_step", 10); + } + amr_wind::SimTime time; + time.parse_parameters(); + + int counter = 0; + int plot_counter = 0; + amrex::Real plot_time_sum = 0.0; + int plot_step_sum = 0; + while (time.new_timestep()) { + time.set_current_cfl(0.45 / 0.4, 0.0, 0.0); + ++counter; + if (time.write_plot_file()) { + ++plot_counter; + plot_time_sum += time.new_time(); + plot_step_sum += counter; + } + } + // With big dt tolerance, dt never gets shortened except at the end + // (reaching t = 1.0). Ordinary plot time interval tolerance ensures that + // plot files are still written at the first step after interval is passed. + EXPECT_EQ(plot_counter, 2); + EXPECT_NEAR(plot_time_sum, 0.8 + 1.0, 1e-8); + EXPECT_EQ(plot_step_sum, 2 + 3); +} + } // namespace amr_wind_tests From 9fda161c2d895a08323b099d3cc1a78d3ca98f0c Mon Sep 17 00:00:00 2001 From: Tony Martinez Date: Thu, 27 Jul 2023 09:49:49 -0600 Subject: [PATCH 37/38] Added flag to ensure that mac velocity not sampled for init or restart in Actuator (#889) Co-authored-by: Michael Kuhn --- amr-wind/wind_energy/actuator/Actuator.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/amr-wind/wind_energy/actuator/Actuator.cpp b/amr-wind/wind_energy/actuator/Actuator.cpp index 0ecc66ae2e..54f4485ae6 100644 --- a/amr-wind/wind_energy/actuator/Actuator.cpp +++ b/amr-wind/wind_energy/actuator/Actuator.cpp @@ -188,7 +188,9 @@ void Actuator::update_positions() m_container->update_positions(); // Sample velocities at the new locations - if (m_sample_nmhalf) { + if (m_sample_nmhalf && + (m_sim.time().current_time() > m_sim.time().start_time())) { + // Avoid using mac velocities if at init or restart const auto& umac = m_sim.repo().get_field("u_mac"); const auto& vmac = m_sim.repo().get_field("v_mac"); const auto& wmac = m_sim.repo().get_field("w_mac"); From 9cf678b2ce6771d7b425e9e69f64e39981f44194 Mon Sep 17 00:00:00 2001 From: "Marc T. Henry de Frahan" Date: Fri, 28 Jul 2023 09:39:01 -0600 Subject: [PATCH 38/38] Bump clang-tidy (#890) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec308c570f..3e5d2d1d63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,12 +13,12 @@ jobs: - name: Clone uses: actions/checkout@v3 - name: Check formatting - uses: DoozyX/clang-format-lint-action@v0.14 + uses: DoozyX/clang-format-lint-action@v0.16.2 with: source: './amr-wind ./unit_tests ./tools/utilities' exclude: '.' extensions: 'H,h,cpp' - clangFormatVersion: 14 + clangFormatVersion: 16 CPU: needs: Formatting runs-on: ${{matrix.os}}