diff --git a/CMakeLists.txt b/CMakeLists.txt index 349201e..37225d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,7 @@ message(STATUS "CMAKE_CXX_COMPILER: " ${CMAKE_CXX_COMPILER_ID}) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) # Because of GCC bug 48891 - message(FATAL_ERROR "GCC version 6.0 or higher required") + message(FATAL_ERROR "GCC version 6.0 or higher required") endif() endif() @@ -128,19 +128,14 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}\ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}\ -O0 -g") -set (CXX_FLAGS_WARNING "-Wall -Wextra -Werror -Wno-error=unused-parameter\ - -Wno-error=unused-function -Wno-error=unknown-pragmas") - -# Also warn about implicit conversions if the compiler supports it -if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") - set (CXX_FLAGS_WARNING "${CXX_FLAGS_WARNING} -Wdouble-promotion -Wfloat-conversion") +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set (CXX_FLAGS_WARNING "-Wall -Wextra -Werror")# -Wdouble-promotion -Wfloat-conversion") +elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") + set (CXX_FLAGS_WARNING "-Wall -Wextra -Werror-all -Wsign-conversion") +else() + message(WARNING "Using an unknown compiler. Compilation warning flags were not set.") endif() -# Other flags. -D_FORCE_INLINES is a workaround to some CUDA/C++ "feature" -# which botches the compilation ("memcpy was not declared in this scope") -# (Not required with cc >= 3.0) -#set(CXX_FLAGS_ETC "-D_FORCE_INLINES") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}\ ${CXX_FLAGS_WARNING}\ ${CXX_FLAGS_ETC}\ diff --git a/acc/mhd_solver/stencil_process.sps b/acc/mhd_solver/stencil_process.sps index c1a2214..eb0fc0c 100644 --- a/acc/mhd_solver/stencil_process.sps +++ b/acc/mhd_solver/stencil_process.sps @@ -12,6 +12,9 @@ uniform Scalar dsx; uniform Scalar dsy; uniform Scalar dsz; +uniform Scalar lnT0; +uniform Scalar lnrho0; + uniform int nx_min; uniform int ny_min; uniform int nz_min; @@ -58,9 +61,10 @@ continuity(in Vector uu, in Scalar lnrho) { Vector momentum(in Vector uu, in Scalar lnrho, in Scalar ss, in Vector aa) { const Matrix S = stress_tensor(uu); - const Scalar cs2 = cs2_sound * exp(gamma * value(ss) / cp_sound + (gamma - 1) * (value(lnrho) - LNRHO0)); + const Scalar cs2 = cs2_sound * exp(gamma * value(ss) / cp_sound + (gamma - 1) * (value(lnrho) - lnrho0)); const Vector j = (Scalar(1.) / mu0) * (gradient_of_divergence(aa) - laplace_vec(aa)); // Current density const Vector B = curl(aa); + //TODO: DOES INTHERMAL VERSTION INCLUDE THE MAGNETIC FIELD? const Scalar inv_rho = Scalar(1.) / exp(value(lnrho)); // Regex replace CPU constants with get\(AC_([a-zA-Z_0-9]*)\) @@ -100,21 +104,23 @@ momentum(in Vector uu, in Scalar lnrho, in Scalar tt) { #else Vector momentum(in Vector uu, in Scalar lnrho) { - // !!!!!!!!!!!!!!!!%JP: NOTE TODO IMPORTANT!!!!!!!!!!!!!!!!!!!!!!!! - // NOT CHECKED FOR CORRECTNESS: USE AT YOUR OWN RISK - const Matrix S = stress_tensor(uu); - const Scalar cs2 = cs2_sound * exp((gamma - 1) * (value(lnrho) - LNRHO0)); - // Regex replace CPU constants with get\(AC_([a-zA-Z_0-9]*)\) - // \1 - const Vector mom = - mul(gradients(uu), value(uu)) - - cs2 * gradient(lnrho) - + nu_visc * ( - laplace_vec(uu) - + Scalar(1. / 3.) * gradient_of_divergence(uu) - + Scalar(2.) * mul(S, gradient(lnrho)) - ) - + zeta * gradient_of_divergence(uu); - return mom; + Vector mom; + + const Matrix S = stress_tensor(uu); + + // Isothermal: we have constant speed of sound + + mom = -mul(gradients(uu), value(uu)) - + cs2_sound * gradient(lnrho) + + nu_visc * + (laplace_vec(uu) + Scalar(1. / 3.) * gradient_of_divergence(uu) + + Scalar(2.) * mul(S, gradient(lnrho))) + zeta * gradient_of_divergence(uu); + + #if LGRAVITY + mom = mom - (Vector){0, 0, -10.0}; + #endif + + return mom; } #endif @@ -139,8 +145,8 @@ induction(in Vector uu, in Vector aa) { #if LENTROPY Scalar lnT( in Scalar ss, in Scalar lnrho) { - const Scalar lnT = LNT0 + gamma * value(ss) / cp_sound + - (gamma - Scalar(1.)) * (value(lnrho) - LNRHO0); + const Scalar lnT = lnT0 + gamma * value(ss) / cp_sound + + (gamma - Scalar(1.)) * (value(lnrho) - lnrho0); return lnT; } @@ -193,8 +199,12 @@ heat_transfer(in Vector uu, in Scalar lnrho, in Scalar tt) } #endif + + #if LFORCING + + Vector simple_vortex_forcing(Vector a, Vector b, Scalar magnitude) { @@ -207,24 +217,68 @@ simple_outward_flow_forcing(Vector a, Vector b, Scalar magnitude) return magnitude * (1 / length(b - a)) * normalized(b - a); // Outward flow } + +// The Pencil Code forcing_hel_noshear(), manual Eq. 222, inspired forcing function with adjustable helicity Vector -forcing(int3 globalVertexIdx) +helical_forcing(Scalar magnitude, Vector k_force, Vector xx, Vector ff_re, Vector ff_im, Scalar phi) +{ + + xx.x = xx.x*(2.0*M_PI/(dsx*(DCONST_INT(AC_ny_max) - DCONST_INT(AC_ny_min)))); + xx.y = xx.y*(2.0*M_PI/(dsy*(DCONST_INT(AC_ny_max) - DCONST_INT(AC_ny_min)))); + xx.z = xx.z*(2.0*M_PI/(dsz*(DCONST_INT(AC_ny_max) - DCONST_INT(AC_ny_min)))); + + Scalar cos_phi = cos(phi); + Scalar sin_phi = sin(phi); + Scalar cos_k_dox_x = cos(dot(k_force, xx)); + Scalar sin_k_dox_x = sin(dot(k_force, xx)); + // Phase affect only the x-component + //Scalar real_comp = cos_k_dox_x; + //Scalar imag_comp = sin_k_dox_x; + Scalar real_comp_phase = cos_k_dox_x*cos_phi - sin_k_dox_x*sin_phi; + Scalar imag_comp_phase = cos_k_dox_x*sin_phi + sin_k_dox_x*cos_phi; + + + Vector force = (Vector){ ff_re.x*real_comp_phase - ff_im.x*imag_comp_phase, + ff_re.y*real_comp_phase - ff_im.y*imag_comp_phase, + ff_re.z*real_comp_phase - ff_im.z*imag_comp_phase}; + + return force; +} + +Vector +forcing(int3 globalVertexIdx, Scalar dt) { Vector a = Scalar(.5) * (Vector){globalGrid.n.x * dsx, globalGrid.n.y * dsy, globalGrid.n.z * dsz}; // source (origin) - Vector b = (Vector){(globalVertexIdx.x - nx_min) * dsx, + Vector xx = (Vector){(globalVertexIdx.x - nx_min) * dsx, (globalVertexIdx.y - ny_min) * dsy, (globalVertexIdx.z - nz_min) * dsz}; // sink (current index) + const Scalar cs2 = cs2_sound; + const Scalar cs = sqrt(cs2); + + //Placeholders until determined properly + Scalar magnitude = DCONST_REAL(AC_forcing_magnitude); + Scalar phase = DCONST_REAL(AC_forcing_phase); + Vector k_force = (Vector){ DCONST_REAL(AC_k_forcex), DCONST_REAL(AC_k_forcey), DCONST_REAL(AC_k_forcez)}; + Vector ff_re = (Vector){DCONST_REAL(AC_ff_hel_rex), DCONST_REAL(AC_ff_hel_rey), DCONST_REAL(AC_ff_hel_rez)}; + Vector ff_im = (Vector){DCONST_REAL(AC_ff_hel_imx), DCONST_REAL(AC_ff_hel_imy), DCONST_REAL(AC_ff_hel_imz)}; - Scalar magnitude = 0.05; //Determine that forcing funtion type at this point. - Vector c = simple_vortex_forcing(a, b, magnitude); - //Vector c = simple_outward_flow_forcing(a, b, magnitude); + //Vector force = simple_vortex_forcing(a, xx, magnitude); + //Vector force = simple_outward_flow_forcing(a, xx, magnitude); + Vector force = helical_forcing(magnitude, k_force, xx, ff_re,ff_im, phase); - if (is_valid(c)) { return c; } - else { return (Vector){0, 0, 0}; } + //Scaling N = magnitude*cs*sqrt(k*cs/dt) * dt + const Scalar NN = cs*sqrt(DCONST_REAL(AC_kaver)*cs); + //MV: Like in the Pencil Code. I don't understandf the logic here. + force.x = sqrt(dt)*NN*force.x; + force.y = sqrt(dt)*NN*force.y; + force.z = sqrt(dt)*NN*force.z; + + if (is_valid(force)) { return force; } + else { return (Vector){0, 0, 0}; } } #endif // LFORCING @@ -272,7 +326,7 @@ solve(Scalar dt) { #if LFORCING if (step_number == 2) { - out_uu = out_uu + dt * forcing(globalVertexIdx); + out_uu = out_uu + forcing(globalVertexIdx, dt); } #endif } diff --git a/analysis/python/jupyter/notebook_example.ipynb b/analysis/python/jupyter/notebook_example.ipynb index e85aef1..f7e4956 100644 --- a/analysis/python/jupyter/notebook_example.ipynb +++ b/analysis/python/jupyter/notebook_example.ipynb @@ -24,7 +24,8 @@ "metadata": {}, "outputs": [], "source": [ - "meshdir = \"/scratch/data/mvaisala/forcingtest/\"" + "meshdir = \"/scratch/data/mvaisala/forcingtest/\"\n", + "#meshdir = \"/scratch/data/mvaisala/normaltest/\"" ] }, { @@ -34,7 +35,7 @@ "outputs": [], "source": [ "#imesh = 30000\n", - "imesh = 30000\n", + "imesh = 100\n", "mesh = ad.read.Mesh(imesh, fdir=meshdir)" ] }, @@ -101,15 +102,8 @@ "metadata": {}, "outputs": [], "source": [ - "vis.lineplot.plot_ts(ts, lnrho=1, uutot=1, uux=1, uuy=1, ss=1)" + "vis.lineplot.plot_ts(ts, lnrho=1, uutot=1, uux=1, uuy=1, uuz=1, ss=1)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/config/astaroth.conf b/config/astaroth.conf index 6d83764..41b7e51 100644 --- a/config/astaroth.conf +++ b/config/astaroth.conf @@ -38,6 +38,10 @@ AC_chi = 0.0001 // Forcing AC_relhel = 0.0 +AC_forcing_magnitude = 1e-5 +AC_kmin = 0.8 +AC_kmax = 1.2 + // Entropy AC_cp_sound = 1.0 diff --git a/include/astaroth.h b/include/astaroth.h index dbe9b17..c354f1e 100644 --- a/include/astaroth.h +++ b/include/astaroth.h @@ -136,7 +136,6 @@ extern "C" { FUNC(AC_cs_sound), \ FUNC(AC_eta), \ FUNC(AC_mu0), \ - FUNC(AC_relhel), \ FUNC(AC_cp_sound), \ FUNC(AC_gamma), \ FUNC(AC_cv_sound), \ @@ -152,6 +151,23 @@ extern "C" { FUNC(AC_angl_uu), \ FUNC(AC_lnrho_edge),\ FUNC(AC_lnrho_out),\ + /* Forcing parameters. User configured. */\ + FUNC(AC_forcing_magnitude),\ + FUNC(AC_relhel), \ + FUNC(AC_kmin), \ + FUNC(AC_kmax), \ + /* Forcing parameters. Set by the generator. */\ + FUNC(AC_forcing_phase),\ + FUNC(AC_k_forcex),\ + FUNC(AC_k_forcey),\ + FUNC(AC_k_forcez),\ + FUNC(AC_kaver),\ + FUNC(AC_ff_hel_rex),\ + FUNC(AC_ff_hel_rey),\ + FUNC(AC_ff_hel_rez),\ + FUNC(AC_ff_hel_imx),\ + FUNC(AC_ff_hel_imy),\ + FUNC(AC_ff_hel_imz),\ /* Additional helper params */\ /* (deduced from other params do not set these directly!) */\ FUNC(AC_G_CONST),\ @@ -386,6 +402,15 @@ AcResult acQuit(void); unless otherwise stated. */ AcResult acSynchronize(void); +/** */ +AcResult acLoadDeviceConstant(const AcRealParam param, const AcReal value); + +/** Tool for loading forcing vector information into the device memory + */ +AcResult acForcingVec(const AcReal forcing_magnitude, const AcReal3 k_force, + const AcReal3 ff_hel_re, const AcReal3 ff_hel_im, const AcReal forcing_phase, + const AcReal kaver); + /* End extern "C" */ #ifdef __cplusplus } diff --git a/src/core/astaroth.cu b/src/core/astaroth.cu index 9c37117..0c49477 100644 --- a/src/core/astaroth.cu +++ b/src/core/astaroth.cu @@ -505,3 +505,44 @@ acSynchronize(void) return AC_SUCCESS; } + +AcResult +acLoadDeviceConstant(const AcRealParam param, const AcReal value) +{ + for (int i = 0; i < num_devices; ++i) { + loadDeviceConstant(devices[i], param, value); + } + return AC_SUCCESS; +} + +// Tool for loading forcing vector information into the device memory +// %JP: Added a generic function for loading device constants (acLoadDeviceConstant). +// This acForcingVec should go outside the core library since it references user-defined +// parameters such as AC_forcing_magnitude which may not be defined in all projects. +// host_forcing.cc is probably a good place for this. +AcResult +acForcingVec(const AcReal forcing_magnitude, const AcReal3 k_force, const AcReal3 ff_hel_re, + const AcReal3 ff_hel_im, const AcReal forcing_phase, const AcReal kaver) +{ + + for (int i = 0; i < num_devices; ++i) { + loadDeviceConstant(devices[i], AC_forcing_magnitude, forcing_magnitude); + loadDeviceConstant(devices[i], AC_forcing_phase, forcing_phase); + + loadDeviceConstant(devices[i], AC_k_forcex, k_force.x); + loadDeviceConstant(devices[i], AC_k_forcey, k_force.y); + loadDeviceConstant(devices[i], AC_k_forcez, k_force.z); + + loadDeviceConstant(devices[i], AC_ff_hel_rex, ff_hel_re.x); + loadDeviceConstant(devices[i], AC_ff_hel_rey, ff_hel_re.y); + loadDeviceConstant(devices[i], AC_ff_hel_rez, ff_hel_re.z); + + loadDeviceConstant(devices[i], AC_ff_hel_imx, ff_hel_im.x); + loadDeviceConstant(devices[i], AC_ff_hel_imy, ff_hel_im.y); + loadDeviceConstant(devices[i], AC_ff_hel_imz, ff_hel_im.z); + + loadDeviceConstant(devices[i], AC_kaver, kaver); + } + + return AC_SUCCESS; +} diff --git a/src/core/kernels/kernels.cuh b/src/core/kernels/kernels.cuh index 1ff9051..a8d4d50 100644 --- a/src/core/kernels/kernels.cuh +++ b/src/core/kernels/kernels.cuh @@ -543,39 +543,6 @@ normalized(const AcReal3& vec) return inv_len * vec; } -// Sinusoidal forcing -// https://arxiv.org/pdf/1704.04676.pdf -// NOTE: This method of forcing is depracated. However, it will remain in here -// until a corresponding scheme exists in the new code. -__constant__ AcReal3 forcing_vec; -__constant__ AcReal forcing_phi; -static __device__ __forceinline__ AcReal3 -DEPRECATED_forcing(const int i, const int j, const int k) -{ -#define DOMAIN_SIZE_X (DCONST_INT(AC_nx) * DCONST_REAL(AC_dsx)) -#define DOMAIN_SIZE_Y (DCONST_INT(AC_ny) * DCONST_REAL(AC_dsy)) -#define DOMAIN_SIZE_Z (DCONST_INT(AC_nz) * DCONST_REAL(AC_dsz)) - const AcReal3 k_vec = (AcReal3){ - (i - DCONST_INT(AC_nx_min)) * DCONST_REAL(AC_dsx) - AcReal(.5) * DOMAIN_SIZE_X, - (j - DCONST_INT(AC_ny_min)) * DCONST_REAL(AC_dsy) - AcReal(.5) * DOMAIN_SIZE_Y, - (k - DCONST_INT(AC_nz_min)) * DCONST_REAL(AC_dsz) - AcReal(.5) * DOMAIN_SIZE_Z}; - AcReal inv_len = reciprocal_len(k_vec); - if (isnan(inv_len) || isinf(inv_len)) - inv_len = 0; - if (inv_len > 2) // hack to make it cool - inv_len = 2; - const AcReal k_dot_x = dot(k_vec, forcing_vec); - - const AcReal waves = cos(k_dot_x) * cos(forcing_phi) - sin(k_dot_x) * sin(forcing_phi); - - return inv_len * inv_len * waves * forcing_vec; -} - -// Note: LNT0 and LNRHO0 must be set very carefully: if the magnitude is different that other values -// in the mesh, then we will inherently lose precision -#define LNT0 (AcReal(0.0)) -#define LNRHO0 (AcReal(0.0)) - #define H_CONST (AcReal(0.0)) #define C_CONST (AcReal(0.0)) @@ -739,31 +706,11 @@ read_out(const int idx, AcReal* __restrict__ field[], const int3 handle) * ============================================================================= */ -static AcReal -randf(void) -{ - return AcReal(rand()) / AcReal(RAND_MAX); -} - AcResult rk3_step_async(const cudaStream_t stream, const int& step_number, const int3& start, const int3& end, const AcReal dt, VertexBufferArray* buffer) { const dim3 tpb(32, 1, 4); -/////////////////// Forcing -#if LFORCING - const AcReal ff_scale = AcReal(.2); - static AcReal3 ff = ff_scale * (AcReal3){1, 0, 0}; - const AcReal radians = randf() * AcReal(2 * M_PI) / 360 / 8; - const AcMatrix rotz = create_rotz(radians); - ff = mul(rotz, ff); - cudaMemcpyToSymbolAsync(forcing_vec, &ff, sizeof(ff), 0, cudaMemcpyHostToDevice, stream); - - const AcReal ff_phi = AcReal(M_PI); // AcReal(2 * M_PI) * randf(); - cudaMemcpyToSymbolAsync(forcing_phi, &ff_phi, sizeof(ff_phi), 0, cudaMemcpyHostToDevice, - stream); -#endif // LFORCING - ////////////////////////// const int nx = end.x - start.x; const int ny = end.y - start.y; diff --git a/src/standalone/autotest.cc b/src/standalone/autotest.cc index 107b950..0f961fa 100644 --- a/src/standalone/autotest.cc +++ b/src/standalone/autotest.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2014-2018, Johannes Pekkilae, Miikka Vaeisalae. + Copyright (C) 2014-2019, Johannes Pekkilae, Miikka Vaeisalae. This file is part of Astaroth. @@ -32,6 +32,7 @@ #include "core/math_utils.h" #include "model/host_memory.h" #include "model/host_timestep.h" +#include "model/host_forcing.h" #include "model/model_boundconds.h" #include "model/model_reduce.h" #include "model/model_rk3.h" @@ -699,6 +700,13 @@ run_autotest(void) VTXBUF_UUX, VTXBUF_UUY, VTXBUF_UUZ); const AcReal dt = host_timestep(umax, config); +#if LFORCING + + //CURRENTLY AUTOTEST NOT SUPPORTED WITH FORCING!!! + +#endif + + // Host integration step model_rk3(dt, model_mesh); boundconds(config, model_mesh); diff --git a/src/standalone/model/host_forcing.cc b/src/standalone/model/host_forcing.cc new file mode 100644 index 0000000..4ce7892 --- /dev/null +++ b/src/standalone/model/host_forcing.cc @@ -0,0 +1,177 @@ +/* + Copyright (C) 2014-2019, Johannes Pekkilae, Miikka Vaeisalae. + + This file is part of Astaroth. + + Astaroth is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Astaroth is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Astaroth. If not, see . +*/ + +/** + * @file + * \brief Brief info. + * + * Detailed info. + * + */ +#include "host_forcing.h" + +#include "core/math_utils.h" + +// The is a wrapper for genering random numbers with a chosen system. +AcReal +get_random_number_01() +{ + // TODO: Implement better randon number generator http://www.cplusplus.com/reference/random/ + return AcReal(rand()) / AcReal(RAND_MAX); +} + +AcReal3 +cross(const AcReal3& a, const AcReal3& b) +{ + AcReal3 c; + + c.x = a.y * b.z - a.z * b.y; + c.y = a.z * b.x - a.x * b.z; + c.z = a.x * b.y - a.y * b.x; + + return c; +} + +AcReal +dot(const AcReal3& a, const AcReal3& b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +AcReal3 +vec_norm(const AcReal3& a) +{ + AcReal3 c; + AcReal norm = dot(a, a); + + c.x = a.x / sqrt(norm); + c.y = a.y / sqrt(norm); + c.z = a.z / sqrt(norm); + + return c; +} + +AcReal3 +vec_multi_scal(const AcReal scal, const AcReal3& a) +{ + AcReal3 c; + + c.x = a.x * scal; + c.y = a.y * scal; + c.z = a.z * scal; + + return c; +} + +// Generate forcing wave vector k_force +AcReal3 +helical_forcing_k_generator(const AcReal kmax, const AcReal kmin) +{ + AcReal phi, theta, kk; // Spherical direction coordinates + AcReal3 k_force; // forcing wave vector + + AcReal delta_k = kmax - kmin; + + // Generate vector in spherical coordinates + phi = AcReal(2.0) * AcReal(M_PI) * get_random_number_01(); + theta = AcReal(M_PI) * get_random_number_01(); + kk = delta_k * get_random_number_01() + kmin; + + // Cast into Cartesian form + k_force = (AcReal3){AcReal(kk * sin(theta) * cos(phi)), // + AcReal(kk * sin(theta) * sin(phi)), // + AcReal(kk * cos(theta))}; + + // printf("k_force.x %f, k_force.y %f, k_force.z %f \n", k_force.x, k_force.y, k_force.z); + + // Round the numbers. In that way k(x/y/z) will get complete waves. + k_force.x = round(k_force.x); + k_force.y = round(k_force.y); + k_force.z = round(k_force.z); + + // printf("After rounding --> k_force.x %f, k_force.y %f, k_force.z %f \n", k_force.x, + // k_force.y, k_force.z); + + return k_force; +} + +// Generate the unit perpendicular unit vector e required for helical forcing +// Addapted from Pencil code forcing.f90 hel_vec() subroutine. +void +helical_forcing_e_generator(AcReal3* e_force, const AcReal3 k_force) +{ + + AcReal3 k_cross_e = cross(k_force, *e_force); + k_cross_e = vec_norm(k_cross_e); + AcReal3 k_cross_k_cross_e = cross(k_force, k_cross_e); + k_cross_k_cross_e = vec_norm(k_cross_k_cross_e); + AcReal phi = AcReal(2.0) * AcReal(M_PI) * get_random_number_01(); + AcReal3 ee_tmp1 = vec_multi_scal(cos(phi), k_cross_e); + AcReal3 ee_tmp2 = vec_multi_scal(sin(phi), k_cross_k_cross_e); + + *e_force = (AcReal3){ee_tmp1.x + ee_tmp2.x, ee_tmp1.y + ee_tmp2.y, ee_tmp1.z + ee_tmp2.z}; +} + +// PC Manual Eq. 223 +void +helical_forcing_special_vector(AcReal3* ff_hel_re, AcReal3* ff_hel_im, const AcReal3 k_force, + const AcReal3 e_force, const AcReal relhel) +{ + + // k dot e + AcReal3 kdote; + kdote.x = k_force.x * e_force.x; + kdote.y = k_force.y * e_force.y; + kdote.z = k_force.z * e_force.z; + + // k cross e + AcReal3 k_cross_e; + k_cross_e.x = k_force.y * e_force.z - k_force.z * e_force.y; + k_cross_e.y = k_force.z * e_force.x - k_force.x * e_force.z; + k_cross_e.z = k_force.x * e_force.y - k_force.y * e_force.x; + + // k cross k cross e + AcReal3 k_cross_k_cross_e; + k_cross_k_cross_e.x = k_force.y * k_cross_e.z - k_force.z * k_cross_e.y; + k_cross_k_cross_e.y = k_force.z * k_cross_e.x - k_force.x * k_cross_e.z; + k_cross_k_cross_e.z = k_force.x * k_cross_e.y - k_force.y * k_cross_e.x; + + // abs(k) + AcReal kabs = sqrt(k_force.x * k_force.x + k_force.y * k_force.y + k_force.z * k_force.z); + + AcReal denominator = sqrt(AcReal(1.0) + relhel * relhel) * kabs * + sqrt(kabs * kabs - + (kdote.x * kdote.x + kdote.y * kdote.y + kdote.z * kdote.z)); + + // MV: I suspect there is a typo in the Pencil Code manual! + //*ff_hel_re = (AcReal3){-relhel*kabs*k_cross_e.x/denominator, + // -relhel*kabs*k_cross_e.y/denominator, + // -relhel*kabs*k_cross_e.z/denominator}; + + //*ff_hel_im = (AcReal3){k_cross_k_cross_e.x/denominator, + // k_cross_k_cross_e.y/denominator, + // k_cross_k_cross_e.z/denominator}; + + // See PC forcing.f90 forcing_hel_both() + *ff_hel_re = (AcReal3){kabs * k_cross_e.x / denominator, kabs * k_cross_e.y, + kabs * k_cross_e.z}; + + *ff_hel_im = (AcReal3){relhel * k_cross_k_cross_e.x / denominator, relhel * k_cross_k_cross_e.y, + relhel * k_cross_k_cross_e.z}; +} diff --git a/src/standalone/model/host_forcing.h b/src/standalone/model/host_forcing.h new file mode 100644 index 0000000..569485e --- /dev/null +++ b/src/standalone/model/host_forcing.h @@ -0,0 +1,45 @@ + +/* + Copyright (C) 2014-2019, Johannes Pekkilae, Miikka Vaeisalae. + + This file is part of Astaroth. + + Astaroth is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Astaroth is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Astaroth. If not, see . +*/ + +/** + * @file + * \brief Brief info. + * + * Detailed info. + * + */ +#pragma once +#include "astaroth.h" + +AcReal get_random_number_01(); + +AcReal3 cross(const AcReal3& a, const AcReal3& b); + +AcReal dot(const AcReal3& a, const AcReal3& b); + +AcReal3 vec_norm(const AcReal3& a); + +AcReal3 vec_multi_scal(const AcReal scal, const AcReal3& a); + +AcReal3 helical_forcing_k_generator(const AcReal kmax, const AcReal kmin); + +void helical_forcing_e_generator(AcReal3* e_force, const AcReal3 k_force); + +void helical_forcing_special_vector(AcReal3* ff_hel_re, AcReal3* ff_hel_im, const AcReal3 k_force, const AcReal3 e_force, const AcReal relhel); diff --git a/src/standalone/model/host_timestep.cc b/src/standalone/model/host_timestep.cc index 9c51d83..54f47cc 100644 --- a/src/standalone/model/host_timestep.cc +++ b/src/standalone/model/host_timestep.cc @@ -50,7 +50,8 @@ host_timestep(const AcReal& umax, const AcMeshInfo& mesh_info) // New, closer to the actual Courant timestep // See Pencil Code user manual p. 38 (timestep section) const long double uu_dt = cdt * dsmin / (fabsl(umax) + sqrtl(cs2_sound + 0.0l)); - const long double visc_dt = cdtv * dsmin * dsmin / max(max(nu_visc, eta), max(gamma, chi)) + 1; // TODO NOTE: comment the +1 out to get scientifically accurate results + const long double visc_dt = cdtv * dsmin * dsmin / max(max(nu_visc, eta), max(gamma, chi));// + 1; // TODO NOTE: comment the +1 out to get scientifically accurate results + //MV: White the +1? It was messing up my computations! const long double dt = min(uu_dt, visc_dt); return AcReal(timescale) * AcReal(dt); diff --git a/src/standalone/model/model_rk3.cc b/src/standalone/model/model_rk3.cc index a18f1b8..dbb4413 100644 --- a/src/standalone/model/model_rk3.cc +++ b/src/standalone/model/model_rk3.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2014-2018, Johannes Pekkilae, Miikka Vaeisalae. + Copyright (C) 2014-2019, Johannes Pekkilae, Miikka Vaeisalae. This file is part of Astaroth. @@ -553,11 +553,6 @@ normalized(const ModelVector& vec) return inv_len * vec; } -// Note: LNT0 and LNRHO0 must be set very carefully: if the magnitude is different that other values -// in the mesh, then we will inherently lose precision -#define LNT0 (ModelScalar(0.0)) -#define LNRHO0 (ModelScalar(0.0)) - #define H_CONST (ModelScalar(0.0)) #define C_CONST (ModelScalar(0.0)) @@ -571,9 +566,10 @@ momentum(const ModelVectorData& uu, const ModelScalarData& lnrho { #if LENTROPY const ModelMatrix S = stress_tensor(uu); - const ModelScalar cs2 = get(AC_cs2_sound) * expl(get(AC_gamma) * value(ss) / get(AC_cp_sound) + - (get(AC_gamma) - 1) * (value(lnrho) - LNRHO0)); - const ModelVector j = (ModelScalar(1.) / get(AC_mu0)) * + const ModelScalar cs2 = get(AC_cs2_sound) * + expl(get(AC_gamma) * value(ss) / get(AC_cp_sound) + + (get(AC_gamma) - 1) * (value(lnrho) - get(AC_lnrho0))); + const ModelVector j = (ModelScalar(1.) / get(AC_mu0)) * (gradient_of_divergence(aa) - laplace_vec(aa)); // Current density const ModelVector B = curl(aa); const ModelScalar inv_rho = ModelScalar(1.) / expl(value(lnrho)); @@ -591,7 +587,8 @@ momentum(const ModelVectorData& uu, const ModelScalarData& lnrho // !!!!!!!!!!!!!!!!%JP: NOTE TODO IMPORTANT!!!!!!!!!!!!!!!!!!!!!!!! // NOT CHECKED FOR CORRECTNESS: USE AT YOUR OWN RISK const ModelMatrix S = stress_tensor(uu); - const ModelScalar cs2 = get(AC_cs2_sound) * expl((get(AC_gamma) - 1) * (value(lnrho) - LNRHO0)); + const ModelScalar cs2 = get(AC_cs2_sound) * + expl((get(AC_gamma) - 1) * (value(lnrho) - get(AC_lnrho0))); const ModelVector mom = -mul(gradients(uu), value(uu)) - cs2 * gradient(lnrho) + get(AC_nu_visc) * (laplace_vec(uu) + @@ -623,8 +620,8 @@ induction(const ModelVectorData& uu, const ModelVectorData& aa) static inline ModelScalar lnT(const ModelScalarData& ss, const ModelScalarData& lnrho) { - const ModelScalar lnT = LNT0 + get(AC_gamma) * value(ss) / get(AC_cp_sound) + - (get(AC_gamma) - ModelScalar(1.)) * (value(lnrho) - LNRHO0); + const ModelScalar lnT = get(AC_lnT0) + get(AC_gamma) * value(ss) / get(AC_cp_sound) + + (get(AC_gamma) - ModelScalar(1.)) * (value(lnrho) - get(AC_lnrho0)); return lnT; } @@ -678,41 +675,96 @@ entropy(const ModelScalarData& ss, const ModelVectorData& uu, const ModelScalarD */ } -static bool +static inline bool is_valid(const ModelScalar a) { return !isnan(a) && !isinf(a); } -static bool +static inline bool is_valid(const ModelVector& a) { return is_valid(a.x) && is_valid(a.y) && is_valid(a.z); } -static inline ModelVector -forcing(int3 globalVertexIdx) -{ - // source (origin) - ModelVector a = ModelScalar(.5) * (ModelVector){get(AC_nx) * get(AC_dsx), - get(AC_ny) * get(AC_dsy), - get(AC_nz) * get(AC_dsz)}; - // sink (current index) - ModelVector b = (ModelVector){(globalVertexIdx.x - get(AC_nx_min)) * get(AC_dsx), - (globalVertexIdx.y - get(AC_ny_min)) * get(AC_dsy), - (globalVertexIdx.z - get(AC_nz_min)) * get(AC_dsz)}; +#if 0 +//FORCING NOT SUPPORTED FOR AUTOTEST - ModelScalar magnitude = 0.05; - // Vector c = magnitude * (1 / length(b - a)) * normalized(b - a); // Outward flow - ModelVector c = magnitude * cross(normalized(b - a), (ModelVector){0, 0, 1}); // Vortex - if (is_valid(c)) { - return c; - } - else { - return (ModelVector){0, 0, 0}; - } +static inline ModelVector +simple_vortex_forcing(ModelVector a, ModelVector b, ModelScalar magnitude) +{ + return magnitude * cross(normalized(b - a), (ModelVector){0, 0, 1}); // Vortex } +static inline ModelVector +simple_outward_flow_forcing(ModelVector a, ModelVector b, ModelScalar magnitude) +{ + return magnitude * (1 / length(b - a)) * normalized(b - a); // Outward flow +} + + +// The Pencil Code forcing_hel_noshear(), manual Eq. 222, inspired forcing function with adjustable helicity +static inline ModelVector +helical_forcing(ModelScalar magnitude, ModelVector k_force, ModelVector xx, ModelVector ff_re, ModelVector ff_im, ModelScalar phi) +{ + + xx.x = xx.x*(2.0*M_PI/(get(AC_dsx)*(get(AC_ny_max) - get(AC_ny_min)))); + xx.y = xx.y*(2.0*M_PI/(get(AC_dsy)*(get(AC_ny_max) - get(AC_ny_min)))); + xx.z = xx.z*(2.0*M_PI/(get(AC_dsz)*(get(AC_ny_max) - get(AC_ny_min)))); + + ModelScalar cos_phi = cos(phi); + ModelScalar sin_phi = sin(phi); + ModelScalar cos_k_dox_x = cos(dot(k_force, xx)); + ModelScalar sin_k_dox_x = sin(dot(k_force, xx)); + // Phase affect only the x-component + ModelScalar real_comp_phase = cos_k_dox_x*cos_phi - sin_k_dox_x*sin_phi; + ModelScalar imag_comp_phase = cos_k_dox_x*sin_phi + sin_k_dox_x*cos_phi; + + + ModelVector force = (ModelVector){ ff_re.x*real_comp_phase - ff_im.x*imag_comp_phase, + ff_re.y*real_comp_phase - ff_im.y*imag_comp_phase, + ff_re.z*real_comp_phase - ff_im.z*imag_comp_phase}; + + return force; +} + +static inline ModelVector +forcing(int3 globalVertexIdx, ModelScalar dt) +{ + ModelVector a = ModelScalar(.5) * (ModelVector){ get(AC_nx) * get(AC_dsx), + get(AC_ny) * get(AC_dsy), + get(AC_nz) * get(AC_dsz)}; // source (origin) + ModelVector xx = (ModelVector){(globalVertexIdx.x - get(AC_nx_min)) * get(AC_dsx), + (globalVertexIdx.y - get(AC_ny_min) * get(AC_dsy)), + (globalVertexIdx.z - get(AC_nz_min) * get(AC_dsz))}; // sink (current index) + const ModelScalar cs2 = get(AC_cs2_sound); + const ModelScalar cs = sqrt(cs2); + + //Placeholders until determined properly + ModelScalar magnitude = get(AC_forcing_magnitude); + ModelScalar phase = get(AC_forcing_phase); + ModelVector k_force = (ModelVector){ get(AC_k_forcex), get(AC_k_forcey), get(AC_k_forcez)}; + ModelVector ff_re = (ModelVector){get(AC_ff_hel_rex), get(AC_ff_hel_rey), get(AC_ff_hel_rez)}; + ModelVector ff_im = (ModelVector){get(AC_ff_hel_imx), get(AC_ff_hel_imy), get(AC_ff_hel_imz)}; + + + //Determine that forcing funtion type at this point. + //ModelVector force = simple_vortex_forcing(a, xx, magnitude); + //ModelVector force = simple_outward_flow_forcing(a, xx, magnitude); + ModelVector force = helical_forcing(magnitude, k_force, xx, ff_re,ff_im, phase); + + //Scaling N = magnitude*cs*sqrt(k*cs/dt) * dt + const ModelScalar NN = cs*sqrt(get(AC_kaver)*cs); + //MV: Like in the Pencil Code. I don't understandf the logic here. + force.x = sqrt(dt)*NN*force.x; + force.y = sqrt(dt)*NN*force.y; + force.z = sqrt(dt)*NN*force.z; + + if (is_valid(force)) { return force; } + else { return (ModelVector){0, 0, 0}; } +} +#endif + static void solve_alpha_step(const int step_number, const ModelScalar dt, const int i, const int j, const int k, ModelMesh& in, ModelMesh* out) @@ -774,12 +826,13 @@ solve_beta_step(const int step_number, const ModelScalar dt, const int i, const for (int w = 0; w < NUM_VTXBUF_HANDLES; ++w) out->vertex_buffer[w][idx] += beta[step_number] * in.vertex_buffer[w][idx]; + (void)dt; // Suppress unused variable warning if forcing not used #if LFORCING if (step_number == 2) { - ModelVector force = forcing((int3){i, j, k}); - out->vertex_buffer[VTXBUF_UUX][idx] += force.x * dt; - out->vertex_buffer[VTXBUF_UUY][idx] += force.y * dt; - out->vertex_buffer[VTXBUF_UUZ][idx] += force.z * dt; + ModelVector force = forcing((int3){i, j, k}, dt); + out->vertex_buffer[VTXBUF_UUX][idx] += force.x; + out->vertex_buffer[VTXBUF_UUY][idx] += force.y; + out->vertex_buffer[VTXBUF_UUZ][idx] += force.z; } #endif } diff --git a/src/standalone/simulation.cc b/src/standalone/simulation.cc index 57cd0fe..156e23a 100644 --- a/src/standalone/simulation.cc +++ b/src/standalone/simulation.cc @@ -29,6 +29,7 @@ #include "config_loader.h" #include "core/errchk.h" #include "core/math_utils.h" +#include "model/host_forcing.h" #include "model/host_memory.h" #include "model/host_timestep.h" #include "model/model_reduce.h" @@ -202,7 +203,7 @@ run_simulation(void) load_config(&mesh_info); AcMesh* mesh = acmesh_create(mesh_info); - //TODO: This need to be possible to define in astaroth.conf + // TODO: This need to be possible to define in astaroth.conf acmesh_init_to(INIT_TYPE_GAUSSIAN_RADIAL_EXPL, mesh); acInit(mesh_info); @@ -229,17 +230,54 @@ run_simulation(void) acStore(mesh); save_mesh(*mesh, 0, t_step); - const int max_steps = mesh_info.int_params[AC_max_steps]; - const int save_steps = mesh_info.int_params[AC_save_steps]; + const int max_steps = mesh_info.int_params[AC_max_steps]; + const int save_steps = mesh_info.int_params[AC_save_steps]; const int bin_save_steps = mesh_info.int_params[AC_bin_steps]; // TODO Get from mesh_info AcReal bin_save_t = mesh_info.real_params[AC_bin_save_t]; AcReal bin_crit_t = bin_save_t; + // Forcing properties + // AcReal relhel = mesh_info.real_params[AC_relhel]; // JP: gcc warning: unused + // AcReal magnitude = mesh_info.real_params[AC_forcing_magnitude]; // JP: gcc warning: unused + // AcReal kmin = mesh_info.real_params[AC_kmin]; // JP: gcc warning: unused + // AcReal kmax = mesh_info.real_params[AC_kmax]; // JP: gcc warning: unused + + // AcReal kaver = (kmax - kmin) / AcReal(2.0); // JP: gcc warning: unused + + /* initialize random seed: */ + srand(312256655); + /* Step the simulation */ for (int i = 1; i < max_steps; ++i) { const AcReal umax = acReduceVec(RTYPE_MAX, VTXBUF_UUX, VTXBUF_UUY, VTXBUF_UUZ); const AcReal dt = host_timestep(umax, mesh_info); + +#if LFORCING + // Generate a forcing vector before canculating an integration step. + + // Generate forcing wave vector k_force + AcReal3 k_force; + k_force = helical_forcing_k_generator(kmax, kmin); + + // Randomize the phase + AcReal phase = AcReal(2.0) * AcReal(M_PI) * get_random_number_01(); + + // Generate e for k. Needed for the sake of isotrophy. + AcReal3 e_force; + if ((k_force.y == 0.0) && (k_force.z == 0.0)) { + e_force = (AcReal3){0.0, 1.0, 0.0}; + } + else { + e_force = (AcReal3){1.0, 0.0, 0.0}; + } + helical_forcing_e_generator(&e_force, k_force); + + AcReal3 ff_hel_re, ff_hel_im; + helical_forcing_special_vector(&ff_hel_re, &ff_hel_im, k_force, e_force, relhel); + acForcingVec(magnitude, k_force, ff_hel_re, ff_hel_im, phase, kaver); +#endif + acIntegrate(dt); t_step += dt;