diff --git a/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/config.cfg b/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/config.cfg new file mode 100755 index 0000000000000000000000000000000000000000..067b942df5494321fd7d5440df1b7dff32b4f11a --- /dev/null +++ b/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/config.cfg @@ -0,0 +1,2 @@ +[pack] +files = main.cu Makefile \ No newline at end of file diff --git a/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/include/DiffusionSpace_sparseGrid.hpp b/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/include/DiffusionSpace_sparseGrid.hpp new file mode 100755 index 0000000000000000000000000000000000000000..d613e0a43d3d205f0cf966f20052d1dd8d3b10d9 --- /dev/null +++ b/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/include/DiffusionSpace_sparseGrid.hpp @@ -0,0 +1,295 @@ +// +// Created by jstark on 28.09.21. +// + +#ifndef SPARSEGRID_DIFFUSIONSPACE_SPARSEGRID_HPP +#define SPARSEGRID_DIFFUSIONSPACE_SPARSEGRID_HPP + +#include "math.h" + +template <typename T> +static bool is_diffusionSpace(const T & phi_sdf, const T & lower_bound, const T & upper_bound) +{ + const T EPSILON = std::numeric_limits<T>::epsilon(); + const T _b_low = lower_bound + EPSILON; + const T _b_up = upper_bound - EPSILON; + return (phi_sdf > _b_low && phi_sdf < _b_up); +} + +template <size_t PHI_SDF_ECS_FULL, size_t PHI_SDF_ECS_SPARSE, typename grid_type, typename sparse_in_type, typename T> +void get_diffusion_domain_sparse_grid(grid_type & grid, + sparse_in_type & sparse_grid, + const T b_low, + const T b_up) +{ + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + if(is_diffusionSpace(grid.template get<PHI_SDF_ECS_FULL>(key), b_low, b_up)) + { + sparse_grid.template insertFlush<PHI_SDF_ECS_SPARSE>(key) = grid.template get<PHI_SDF_ECS_FULL>(key); + } + ++dom; + } + // Mapping? + // Load Balancing? +} + +#if 0 +template <size_t PHI_SDF_ECS_FULL, size_t PHI_SDF_ECS_SPARSE, typename grid_type, typename grid_type_upscale, typename sparse_in_type, typename T> +void get_diffusion_domain_sparse_grid_upscale(grid_type & grid, + grid_type_upscale & grid_upscale, + sparse_in_type & sparse_grid, + const int upscale_factor, // total upscale factor that is product all dimensions, e.g. 2^3 = 8 + const T b_low, + const T b_up) +{ + auto dom = grid.getDomainIterator(); + auto dom_upscale = grid_upscale.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + auto key_upscale = dom_upscale.get(); + + for(int i = 0, i < upscale_factor, ++i) // Place upscale number of points for each key + { + if(is_diffusionSpace(grid.template get<PHI_SDF_ECS_FULL>(key), b_low, b_up)) + { + sparse_grid.template insertFlush<PHI_SDF_ECS_SPARSE>(key_upscale) = grid.template get<PHI_SDF_ECS_FULL>(key); + } + ++dom_upscale + } + + + ++dom; + } + +} +#endif + +template < +size_t PHI_SDF_ECS_FULL, +size_t PHI_SDF_SHELL_FULL, +size_t PHI_SDF_ECS_SPARSE, +size_t PHI_SDF_SHELL_SPARSE, +typename grid_type, +typename sparse_in_type, +typename T> +void get_diffusion_domain_sparse_grid_with_shell(grid_type & grid_ecs, + grid_type & grid_shell, + sparse_in_type & sparse_grid, + const T b_low, + const T b_up) +{ + auto dom = grid_ecs.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + if(is_diffusionSpace(grid_ecs.template get<PHI_SDF_ECS_FULL>(key), b_low, b_up)) + { + sparse_grid.template insertFlush<PHI_SDF_ECS_SPARSE>(key) = grid_ecs.template get<PHI_SDF_ECS_FULL>(key); + sparse_grid.template insertFlush<PHI_SDF_SHELL_SPARSE>(key) = grid_shell.template get<PHI_SDF_SHELL_FULL>(key); + } + ++dom; + } +} + + +#if 0 +// Init c0 + auto dom = g_sparse.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + if (g_sparse.template getProp<PHI_SDF>(key) < 4 * g_sparse.spacing(x)) + { + g_sparse.template getProp<CONC_N>(key) = + (1 - 0.25 * g_sparse.template getProp<PHI_SDF>(key)) / g_sparse.spacing(x); + } + ++dom; + } + if(v_cl.rank() == 0) std::cout << "Initialized grid with smoothed c0 at the boundary." << std::endl; + g_sparse.write(path_output + "g_init", FORMAT_BINARY); +#endif + + + + +template <typename point_type, typename y_margin_type> +auto distance_from_margin(point_type & coord, y_margin_type y_margin) +{ + return(coord.get(1) - y_margin); +} + +template <typename point_type, typename y_margin_type, typename width_type> +bool is_source(point_type & coord, y_margin_type y_margin, width_type width_source) +{ + return (coord.get(1) >= y_margin + && distance_from_margin(coord, y_margin) <= width_source); +} + +template <typename T> +static bool is_inner_surface(const T & phi_sdf, const T & lower_bound) +{ + const T EPSILON = std::numeric_limits<T>::epsilon(); + const T _b_low = lower_bound + EPSILON; + return (phi_sdf > _b_low); +} + +template <size_t PHI_SDF, size_t K_SOURCE, size_t K_SINK, typename grid_type, typename y_margin_type, typename width_type, typename k_type> +void init_reactionTerms(grid_type & grid, + width_type width_source, + y_margin_type y_margin, + k_type k_source, + k_type k_sink, + int no_membrane_points=4) +{ + /* + * Setting the reaction terms according to their distance from the margin of the blastoderm along the dorsal-ventral axis + * + * */ + + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + Point<grid_type::dims, y_margin_type> coord = grid.getPos(key); + + if(grid.template get<PHI_SDF>(key) < no_membrane_points * grid.spacing(0)) // If point lies close to the cell surface + { + if(is_source(coord, y_margin, width_source)) // If point lies within width_source distance from the margin, set k_source + { + grid.template insertFlush<K_SOURCE>(key) = k_source; + } + else + { + grid.template insertFlush<K_SOURCE>(key) = 0.0; // If membrane point not in source, emission is zero + } + grid.template insertFlush<K_SINK>(key) = k_sink; // Have sink at all membranes + + } + + else // For the nodes that are further away from the membrane, set both reaction terms to zero + { + grid.template insertFlush<K_SOURCE>(key) = 0.0; + grid.template insertFlush<K_SINK>(key) = 0.0; + } + ++dom; + } +} + +template < +size_t PHI_SDF_ECS_SPARSE, +size_t PHI_SDF_SHELL_SPARSE, +size_t K_SOURCE, +size_t K_SINK, +typename grid_type, +typename y_margin_type, +typename width_type, +typename k_type, +typename boundary_type> +void init_reactionTerms_with_shell(grid_type & grid, + width_type width_source, + y_margin_type y_margin, + k_type k_source, + k_type k_sink, + boundary_type b_low_shell, + int no_membrane_points=4) +{ + /* + * Setting the reaction terms according to their distance from the margin of the blastoderm along the dorsal-ventral axis + * + * */ + + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + Point<grid_type::dims, y_margin_type> coord = grid.getPos(key); + + if( + grid.template get<PHI_SDF_ECS_SPARSE>(key) < no_membrane_points * grid.spacing(0) // If point is a surface point + && is_inner_surface(grid.template get<PHI_SDF_SHELL_SPARSE>(key), b_low_shell) // and this surface is not towards the yolk or EVL + ) + { + if(is_source(coord, y_margin, width_source)) // If point lies within width_source distance from the margin, set k_source + { + grid.template insertFlush<K_SOURCE>(key) = k_source; + } + else + { + grid.template insertFlush<K_SOURCE>(key) = 0.0; // If membrane point not in source, emission is zero + } + grid.template insertFlush<K_SINK>(key) = k_sink; // Have sink at all membranes + + } + + else // For the nodes that are further away from the membrane or belong to the outer surface, set both reaction terms to zero + { + grid.template insertFlush<K_SOURCE>(key) = 0.0; + grid.template insertFlush<K_SINK>(key) = 0.0; + } + ++dom; + } +} + + +template <size_t PHI_SDF, size_t K_SOURCE, size_t K_SINK, typename grid_type, typename coord_type, typename k_type> +void init_reactionTerms_smoothed(grid_type & grid, + coord_type y_start, + coord_type y_peak, + coord_type y_stop, + k_type k_source, + k_type k_sink, + coord_type no_membrane_points=4, + coord_type smoothing=0.25) +{ + /* + * Setting the reaction terms according to the position along the animal-vegetal axis = y-axis + * + * */ + const int x = 0; + const int y = 1; + const int z = 2; + + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + Point<grid_type::dims, coord_type> coord = grid.getPos(key); +// k_type sdf_based_smoothing = (1 - smoothing * grid.template getProp<PHI_SDF>(key)) / grid.spacing(x); + k_type sdf_based_smoothing = 1.0; + if(grid.template get<PHI_SDF>(key) < no_membrane_points * grid.spacing(0)) // If grid node lies close to the + // membrane + { + // If membrane on animal side of the peak, emission strength increases with y + if (coord[y] > y_start + && coord[y] < y_peak + std::numeric_limits<coord_type>::epsilon()) + { + grid.template insertFlush<K_SOURCE>(key) = k_source * (coord[y] - y_start) * sdf_based_smoothing; + } + // Else if membrane on vegetal side of the peak, emission strength decreases with y as moving towards the + // boundary to the yolk + else if (coord[y] >= y_peak - std::numeric_limits<coord_type>::epsilon() + && coord[y] < y_stop - std::numeric_limits<coord_type>::epsilon()) + { + grid.template insertFlush<K_SOURCE>(key) = k_source * (y_stop - coord[y]) * sdf_based_smoothing; + } + // Else source is zero + else grid.template insertFlush<K_SOURCE>(key) = 0.0; + grid.template insertFlush<K_SINK>(key) = k_sink; // Have sink at all membranes + } + + else // For the nodes that are further away from the membrane, set the reaction terms to zero + { + grid.template insertFlush<K_SOURCE>(key) = 0.0; + grid.template insertFlush<K_SINK>(key) = 0.0; + } + ++dom; + } +} + + + +#endif //SPARSEGRID_DIFFUSIONSPACE_SPARSEGRID_HPP diff --git a/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/include/HelpFunctions_diffusion.hpp b/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/include/HelpFunctions_diffusion.hpp new file mode 100755 index 0000000000000000000000000000000000000000..7e3069c8549f142ae2f3b31a730533d196530365 --- /dev/null +++ b/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/include/HelpFunctions_diffusion.hpp @@ -0,0 +1,195 @@ +// +// Created by jstark on 08.10.21 +// + +#ifndef DIFFUSION_HELPFUNCTIONSDIFFUSION_HPP +#define DIFFUSION_HELPFUNCTIONSDIFFUSION_HPP +#include "util/PathsAndFiles.hpp" + +#include "level_set/redistancing_Sussman/HelpFunctionsForGrid.hpp" + +/**@brief Get timestep that fulfills stability criterion nD-diffusion using a FTCS scheme + * + * @tparam grid_type Template type of input g_sparse. + * @tparam T Template type of diffusion coefficient and timestep. + * @param grid Input grid of type grid_type. + * @param k_max Max. diffusion coefficient in the domain. + * @return T type timestep. + */ +template <typename grid_type, typename T> +T diffusion_time_step(grid_type & grid, T k_max) +{ + T sum = 0; + for(int d = 0; d < grid_type::dims; ++d) + { + sum += (T)1.0 / (T)(grid.spacing(d) * grid.spacing(d)); + } + + return 1.0 / ((T)4.0 * k_max * sum); +} + +/**@brief Sigmoidal which can be shifted and stretched in order to get desired smooth inhomog. diffusion-coeff scalar + * field + * + * @tparam T Template type of coordinates. + * @param x T type abscissa axis of sigmoidal function. + * @param x_shift T type shift of sigmoidal along abscissa. + * @param x_stretch T type stretching of sigmoidal along acscissa - the smaller, the steeper! + * @param y_min T type asymptotic lowest y-value. + * @param y_max T type asymptotic maximal y-value. + * @return Function value of sigmoidal for input value x. + */ +template <typename T> +T get_smooth_sigmoidal(T x, T x_shift, T x_stretch, T y_min, T y_max) +{ + return y_min + y_max / (1.0 + exp(- (x_shift + x_stretch * x))); +} + +/**@brief Sums up property values over all particles in grid. + * + * @tparam Prop Index of property whose values will be summed up over all particles. + * @tparam grid_type Template type of input particle vector. + * @param grid Input particle vector. Can be a subset. + * @return Sum of values stored in Prop. + */ +template <size_t Prop, typename grid_type> +auto sum_prop_over_grid(grid_type & grid) +{ + auto sum = 0.0; + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + sum += grid.template getProp<Prop>(key); + ++dom; + } + auto &v_cl = create_vcluster(); + v_cl.sum(sum); + v_cl.execute(); + return sum; +} + +/**@brief Writing out the total mass to a csv file. + * + * @tparam Conc Index of property containing the concentration. + * @tparam T_mass Template type of initial mass. + * @param grid Input particle vector_dist of type grid_type. + * @param m_initial Initial total mass. + * @param t Current time of the diffusion. + * @param i Current iteration of the diffusion. + * @param path_output std::string Path where csv file will be saved. + * @param filename std::string Name of csv file. Default: "/total_mass.csv" + * @Outfile 4 columns: Time, Total mass, Mass difference, Max mass + */ +template <size_t Conc, typename grid_type, typename T, typename T_mass> +void monitor_total_mass(grid_type & grid, const T_mass m_initial, const T p_volume, const T t, const size_t i, + const std::string & path_output, const std::string & filename="total_mass.csv") +{ + auto &v_cl = create_vcluster(); + +// if (m_initial == 0) +// { +// if (v_cl.rank() == 0) std::cout << "m_initial is zero! Normalizing the total mass with the initial mass will " +// "not work. Aborting..." << std::endl; +// abort(); +// } + + T m_total = sum_prop_over_grid<Conc>(grid) * p_volume; + T m_diff = m_total - m_initial; + T m_max = get_max_val<Conc>(grid) * p_volume; + + if (v_cl.rank() == 0) + { + std::string outpath = path_output + "/" + filename; + create_file_if_not_exist(outpath); + std::ofstream outfile; + outfile.open(outpath, std::ios_base::app); // append instead of overwrite + + if (i == 0) + { + outfile << "Time, Total mass, Mass difference, Max mass" << std::endl; +// std::cout << "Time, Total mass, Total mass in-/decrease, Max mass" << std::endl; + } + + outfile + << to_string_with_precision(t, 6) << "," + << to_string_with_precision(m_total, 6) << "," + << to_string_with_precision(m_diff, 6) << "," + << to_string_with_precision(m_max, 6) + << std::endl; + outfile.close(); +#if 0 + std::cout + << to_string_with_precision(t, 6) << "," + << to_string_with_precision(m_total, 6) << "," + << to_string_with_precision(m_diff, 6) << "," + << to_string_with_precision(m_max, 6) + << std::endl; +#endif +// if (m_domain_normalized > 100 || m_domain_normalized < 0) +// { +// std::cout << "Mass increases or is negative, something went wrong. Aborting..." << std::endl; +// abort(); +// } + } +} + +/**@brief Writing out the total concentration over time to a csv file. + * + * @tparam Conc Index of property containing the concentration. + * @param grid Input particle vector_dist of type grid_type. + * @param t Current time of the diffusion. + * @param i Current iteration of the diffusion. + * @param path_output std::string Path where csv file will be saved. + * @param filename std::string Name of csv file. Default: "/normalized_total_mass.csv" + * @Outfile 3 columns: timestep of writeout i, total concentration + */ +template <size_t Conc, typename grid_type, typename T> +void monitor_total_concentration(grid_type & grid, const T t, const size_t i, + const std::string & path_output, const std::string & filename="total_conc.csv") +{ + auto &v_cl = create_vcluster(); + T c_total = sum_prop_over_grid<Conc>(grid); + + if (v_cl.rank() == 0) + { + std::string outpath = path_output + "/" + filename; + create_file_if_not_exist(outpath); + std::ofstream outfile; + outfile.open(outpath, std::ios_base::app); // append instead of overwrite + + if (i == 0) + { + outfile << "Time, Total concentration" << std::endl; + } + + outfile + << to_string_with_precision(t, 6) << "," + << to_string_with_precision(c_total, 6) + << std::endl; + outfile.close(); + } +} + +/**@brief Sets the emission term to 0 if concentration surpasses a threshold. This is to avoid accumulation of mass + * at unconnected islands. + * + * @tparam Conc Index of property containing the concentration. + * @tparam Emission Index of property containing the emission constant. + * @tparam grid_type Template type of input particle vector. + * @tparam key_vector_type Template type of particle keys. + * @param grid Input particle vector. Can be a subset. + * @param keys Keys for which necessity for adaptation is checked. + * @param threshold Maximal concentration that is tolerated for keeping emission. + */ +template <size_t Conc, size_t Emission, typename grid_type, typename key_vector_type, typename T> +void adapt_emission(grid_type & grid, key_vector_type & keys, T threshold) +{ + for (int i = 0; i < keys.size(); ++i) + { + auto key = keys.get(i); + if (grid.template getProp<Conc>(key) > threshold) grid.template getProp<Emission>(key) = 0.0; + } +} + +#endif //DIFFUSION_HELPFUNCTIONSDIFFUSION_HPP diff --git a/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/main.cu b/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/main.cu new file mode 100755 index 0000000000000000000000000000000000000000..b607f52d5cbd5e0f7022882eead120535a5746bc --- /dev/null +++ b/example/SparseGrid/9_inhomogeneous_diffusion_porous_catalyst_CaCO3/main.cu @@ -0,0 +1,498 @@ +// +// Created by jstark on 2023-04-24. +// +/** + * @file 9_inhomogeneous_diffusion_porous_catalyst/main.cu + * @page 9_inhomogeneous_diffusion_porous_catalyst diffusion_porous_media 3D + * + * # Simulate inhomogeneous diffusion in a CaCO\f$_3\f$ particle packed bed # + * + * In this example, we simulate inhomogeneous diffusion in the fluid phase of a CaCO\f$_3\f$ particle packed bed. For the complete image-based simulation pipeline and performance of this simulation, we refer to + * <a href="https://arxiv.org/abs/2304.11165">J. Stark, I. F. Sbalzarini "An open-source pipeline for solving continuous reaction-diffusion models in image-based geometries of porous media." arXiv preprint arXiv:2304.11165 (2023).</a> + + * The geometry of the fluid phase is reconstructed based on $\upmu$CT images provided by kindly provided by Prof. Jörg Petrasch (Michigan State University, College of Engineering) (see: <a href="https://asmedigitalcollection.asme.org/heattransfer/article/131/7/072701/444766/Tomographic-Characterization-of-a-Semitransparent">S. Haussener et al. “Tomographic characterization of a semitransparent-particle packed bed and determination of its thermal radiative propertiesâ€. In: Journal of Heat Transfer 131.7 (2009)</a>). + + * For image based reconstruction and redistancing see @ref example_sussman_images_3D. + * + */ + +/** + * @page 9_inhomogeneous_diffusion_porous_catalyst diffusion_porous_media 3D + * + * ## Include ## {#e_CaC03_include} + * + * We start with inluding header files, setting some paths and indices: + * + * \snippet SparseGrid/9_inhomogeneous_diffusion_porous_catalyst/main.cu Include + * + */ +//! \cond [Include] \endcond + + +#include <iostream> +#include <typeinfo> + +#include "CSVReader/CSVReader.hpp" + +// Include redistancing files +#include "util/PathsAndFiles.hpp" +#include "level_set/redistancing_Sussman/RedistancingSussman.hpp" +#include "RawReader/InitGridWithPixel.hpp" +#include "RemoveLines.hpp" // For removing thin (diagonal or straight) lines + +#include "level_set/redistancing_Sussman/HelpFunctionsForGrid.hpp" +#include "level_set/redistancing_Sussman/AnalyticalSDF.hpp" +#include "FiniteDifference/FD_simple.hpp" +#include "Decomposition/Distribution/BoxDistribution.hpp" +#include "timer.hpp" + +// Help functions for simulating diffusion in the image-based geometry +#include "include/DiffusionSpace_sparseGrid.hpp" +#include "include/HelpFunctions_diffusion.hpp" + + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// input +const std::string path_to_redistancing_result = +"/INPUT_PATH/benchmarks/CaCO3/sussman_redistancing/build/output_sussman_maxIter6e3_CaCO3_fluidPhase_531x531x531/"; + + +const std::string redistancing_filename = "grid_CaCO3_post_redistancing.hdf5"; +const std::string path_to_size = path_to_redistancing_result; + +// output +const std::string output_name = "output_inhomogDiffusion_CaCO3_fluid_phase"; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Property indices full grid +const size_t PHI_FULL = 0; + +typedef aggregate<float> props_full; +const openfpm::vector<std::string> prop_names_full = {"Phi_Sussman_Out"}; + + +// Property indices sparse grid +const size_t PHI_PHASE = 0; +const size_t CONC_N = 1; +const size_t CONC_NPLUS1 = 2; +const size_t DIFFUSION_COEFFICIENT = 3; + + +typedef aggregate<float, float, float, float> props_sparse; +const openfpm::vector<std::string> prop_names_sparse = {"PHI_PHASE", + "CONC_N", + "CONC_NPLUS1", + "DIFFUSION_COEFFICIENT"}; + +// Space indices +constexpr size_t x = 0, y = 1, z = 2; + +// Parameters for grid size +const size_t dims = 3; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//! \cond [Include] \endcond + +/** + * @page 9_inhomogeneous_diffusion_porous_catalyst diffusion_porous_media 3D + * + * ## Initialization and output folder ## {#e_CaC03_init} + * + * We start with + * * Initializing OpenFPM + * * Setting the output path and creating an output folder + * + * \snippet SparseGrid/9_inhomogeneous_diffusion_porous_catalyst/main.cu Initialization and output folder + * + */ +//! \cond [Initialization and output folder] \endcond +int main(int argc, char* argv[]) +{ + // Initialize library. + openfpm_init(&argc, &argv); + auto & v_cl = create_vcluster(); + + timer t_total; + timer t_iterative_diffusion_total; + timer t_iteration_wct; + timer t_GPU; + + t_total.start(); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Set current working directory, define output paths and create folders where output will be saved + std::string cwd = get_cwd(); + const std::string path_output = cwd + "/" + output_name + "/"; + create_directory_if_not_exist(path_output); + + if(v_cl.rank()==0) std::cout << "Redistancing result will be loaded from " << path_to_redistancing_result << redistancing_filename << std::endl; + + if(v_cl.rank()==0) std::cout << "Outputs will be saved to " << path_output << std::endl; + + //! \cond [Initialization and output folder] \endcond + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * @page 9_inhomogeneous_diffusion_porous_catalyst diffusion_porous_media 3D + * + * ## Create a dense grid and load redistancing result ## {#e_CaC03_grid} + * + * \snippet SparseGrid/9_inhomogeneous_diffusion_porous_catalyst/main.cu Grid creation + * + */ + //! \cond [Grid creation] \endcond + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Create full grid and load redistancing result + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Read size of sussman redistancing output grid from csv files + openfpm::vector<size_t> v_sz; + openfpm::vector<float> v_L; + + size_t m, n; + read_csv_to_vector(path_to_size + "/N.csv", v_sz, m, n); + read_csv_to_vector(path_to_size + "/L.csv", v_L, m, n); + + const float Lx_low = 0.0; + const float Lx_up = v_L.get(x); + const float Ly_low = 0.0; + const float Ly_up = v_L.get(y); + const float Lz_low = 0.0; + const float Lz_up = v_L.get(z); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + size_t sz[dims] = {v_sz.get(x), v_sz.get(y), v_sz.get(z)}; + Box<dims, float> box({Lx_low, Ly_low, Lz_low}, {Lx_up, Ly_up, Lz_up}); + Ghost<dims, long int> ghost(1); + typedef grid_dist_id<dims, float, props_full> grid_in_type; + grid_in_type g_dist(sz, box, ghost); + + g_dist.load(path_to_redistancing_result + "/" + redistancing_filename); // Load SDF + //! \cond [Grid creation] \endcond + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * @page 9_inhomogeneous_diffusion_porous_catalyst diffusion_porous_media 3D + * + * ## Create a sparse grid of fluid phase ## {#e_CaC03_sparse_grid} + * + * Obtain sparse grid by placing grid points inside the fluid phase only using the signed distance function + * + * \snippet SparseGrid/9_inhomogeneous_diffusion_porous_catalyst/main.cu Sparse grid creation + * + */ + //! \cond [Sparse grid creation] \endcond + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Get sparse grid of diffusion domain + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Create sparse grid + std::cout << "Rank " << v_cl.rank() << " starts creating sparse grid." << std::endl; + typedef CartDecomposition<dims,float, CudaMemory, memory_traits_inte, BoxDistribution<dims,float> > Dec; + typedef sgrid_dist_id_gpu<dims, float, props_sparse, CudaMemory, Dec> sparse_grid_type; + sparse_grid_type g_sparse(sz, box, ghost); + g_sparse.setPropNames(prop_names_sparse); + + // Lower and upper bound for SDF indicating fluid phase + const float d_low = 0.0, d_up = Lx_up; + + // Alternatively: load sparse grid of diffusion domain from HDF5 file +// g_sparse.load(path_to_redistancing_result + "/" + redistancing_filename); + + // Obtain sparse grid + get_diffusion_domain_sparse_grid<PHI_FULL, PHI_PHASE>(g_dist, g_sparse, d_low, d_up); + std::cout << "Rank " << v_cl.rank() << " finished creating sparse grid." << std::endl; + + //! \cond [Sparse grid creation] \endcond + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * @page 9_inhomogeneous_diffusion_porous_catalyst diffusion_porous_media 3D + * + * ## Defining the inhomogeneous diffusion coefficient ## {#e_CaC03_diff_coeff} + * + * The inhomogeneous diffusion coefficient is smoothed around the interface by a sigmoidal function that depends on + * the signed distance function + * + * \snippet SparseGrid/9_inhomogeneous_diffusion_porous_catalyst/main.cu Diffusion coefficient + * + */ + //! \cond [Diffusion coefficient] \endcond + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Initialize properties on sparse grid + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Diffusion coefficient + const float min_porosity = 0.0; + const float max_porosity = 1.0; + + float D_molecular = 1.0; + float D_min = D_molecular * min_porosity; + float D_max = D_molecular * max_porosity; + + float min_sdf_calcite = get_min_val<PHI_PHASE>(g_sparse); + + float x_stretch = 4.0 * g_sparse.spacing(x); + float x_shift = min_sdf_calcite * x_stretch; + float y_min = D_min; + float y_max = D_max - D_min; + + const float c0 = 10.0; + + auto dom = g_sparse.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + + Point<dims, float> coords = g_sparse.getPos(key); + if(coords.get(x) <= 0.05 * Lx_up) + { + g_sparse.template insertFlush<CONC_N>(key) = c0; + } + else g_sparse.template insertFlush<CONC_N>(key) = 0.0; + + // Compute diffusion coefficient and store on grid + g_sparse.template insertFlush<DIFFUSION_COEFFICIENT>(key) = + get_smooth_sigmoidal( + g_sparse.template getProp<PHI_PHASE>(key) - min_sdf_calcite, + x_shift, + x_stretch, + y_min, + y_max); + ++dom; + } + + //! \cond [Diffusion coefficient] \endcond + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * @page 9_inhomogeneous_diffusion_porous_catalyst diffusion_porous_media 3D + * + * ## Defining the functor for the inhomogeneous diffusion ## {#e_CaC03_functor} + * + * The functor contains the forward time central space discretization of the inhomogeneous diffusion equation. + * The stencil size is 1. + * It will be passed to the GPU and convolved with the sparse grid. + * + * \snippet SparseGrid/9_inhomogeneous_diffusion_porous_catalyst/main.cu Functor + * + */ + //! \cond [Functor] \endcond + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Defining the functor that will be passed to the GPU to simulate reaction-diffusion + // GetCpBlockType<typename SGridGpu, unsigned int prp, unsigned int stencil_size> + typedef typename GetCpBlockType<decltype(g_sparse),0,1>::type CpBlockType; + + const float dx = g_sparse.spacing(x), dy = g_sparse.spacing(y), dz = g_sparse.spacing(z); + // Stability criterion for FTCS + const float dt = diffusion_time_step(g_sparse, D_max); + + std::cout << "dx = " << dx << "dy = " << dy << "dz = " << dz << std::endl; + std::cout << "dt = " << dt << std::endl; + + + auto func_inhomogDiffusion = [dx, dy, dz, dt, d_low] __device__ ( + float & u_out, // concentration output + float & D_out, // diffusion coefficient output (dummy) + float & phi_out, // sdf of domain output (dummy) + CpBlockType & u, // concentration input + CpBlockType & D, // diffusion coefficient input (to get also the half step diffusion coefficients) + CpBlockType & phi, // sdf of domain (for boundary conditions) + auto & block, + int offset, + int i, + int j, + int k + ) + { + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Stencil + // Concentration + float u_c = u(i, j, k); + float u_px = u(i+1, j, k); + float u_mx = u(i-1, j, k); + float u_py = u(i, j+1, k); + float u_my = u(i, j-1, k); + float u_pz = u(i, j, k+1); + float u_mz = u(i, j, k-1); + + // Signed distance function + float phi_c = phi(i, j, k); + float phi_px = phi(i+1, j, k); + float phi_mx = phi(i-1, j, k); + float phi_py = phi(i, j+1, k); + float phi_my = phi(i, j-1, k); + float phi_pz = phi(i, j, k+1); + float phi_mz = phi(i, j, k-1); + + // Diffusion coefficient + float D_c = D(i, j, k); + float D_px = D(i+1, j, k); + float D_mx = D(i-1, j, k); + float D_py = D(i, j+1, k); + float D_my = D(i, j-1, k); + float D_pz = D(i, j, k+1); + float D_mz = D(i, j, k-1); + + // Impose no-flux boundary conditions + if (phi_px <= d_low + std::numeric_limits<float>::epsilon()) {u_px = u_c; D_px = D_c;} + if (phi_mx <= d_low + std::numeric_limits<float>::epsilon()) {u_mx = u_c; D_mx = D_c;} + if (phi_py <= d_low + std::numeric_limits<float>::epsilon()) {u_py = u_c; D_py = D_c;} + if (phi_my <= d_low + std::numeric_limits<float>::epsilon()) {u_my = u_c; D_my = D_c;} + if (phi_pz <= d_low + std::numeric_limits<float>::epsilon()) {u_pz = u_c; D_pz = D_c;} + if (phi_mz <= d_low + std::numeric_limits<float>::epsilon()) {u_mz = u_c; D_mz = D_c;} + + // Interpolate diffusion constant of half-step neighboring points + float D_half_px = (D_c + D_px)/2.0; + float D_half_mx = (D_c + D_mx)/2.0; + float D_half_py = (D_c + D_py)/2.0; + float D_half_my = (D_c + D_my)/2.0; + float D_half_pz = (D_c + D_pz)/2.0; + float D_half_mz = (D_c + D_mz)/2.0; + + // Compute concentration of next time point + u_out = u_c + dt * + (1/(dx*dx) * (D_half_px * (u_px - u_c) - D_half_mx * (u_c - u_mx)) + + 1/(dy*dy) * (D_half_py * (u_py - u_c) - D_half_my * (u_c - u_my)) + + 1/(dz*dz) * (D_half_pz * (u_pz - u_c) - D_half_mz * (u_c - u_mz))); + + // dummy outs + D_out = D_c; + phi_out = phi_c; + }; + + + //! \cond [Functor] \endcond + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * @page 9_inhomogeneous_diffusion_porous_catalyst diffusion_porous_media 3D + * + * ## Run inhomogeneous diffusion ## {#e_CaC03_run} + * + * + * \snippet SparseGrid/9_inhomogeneous_diffusion_porous_catalyst/main.cu Run + * + */ + //! \cond [Run] \endcond + // Create text file to which wall clock time for iteration incl. ghost communication will be saved + std::string path_time_filewct = path_output + "/time_wct_incl_ghostCommunication" + std::to_string(v_cl.rank()) + ".txt"; + std::ofstream out_wct_file(path_time_filewct, std::ios_base::app); + + // Create text file to which GPU time will be saved + std::string path_time_filegpu = path_output + "/time_GPU" + std::to_string(v_cl.rank()) + ".txt"; + std::ofstream out_GPUtime_file(path_time_filegpu, std::ios_base::app); + + // Iterative diffusion + const size_t iterations = 103; + const size_t number_write_outs = 10; + const size_t interval_write = iterations / number_write_outs; // Find interval for writing to reach + size_t iter = 0; + float t = 0; + + // Copy from host to GPU for simulation + g_sparse.template hostToDevice<CONC_N, CONC_NPLUS1, DIFFUSION_COEFFICIENT, PHI_PHASE>(); + t_iterative_diffusion_total.start(); + while(iter <= iterations) + { + if (iter % 2 == 0) + { + t_iteration_wct.start(); + g_sparse.template ghost_get<PHI_PHASE, CONC_N, DIFFUSION_COEFFICIENT>(RUN_ON_DEVICE); + t_GPU.startGPU(); + g_sparse.template conv3_b< + CONC_N, + DIFFUSION_COEFFICIENT, + PHI_PHASE, + CONC_NPLUS1, + DIFFUSION_COEFFICIENT, + PHI_PHASE, 1> + ({0, 0, 0}, + {(long int) sz[x]-1, (long int) sz[y]-1, (long int) sz[z]-1}, + func_inhomogDiffusion); + t_GPU.stopGPU(); + t_iteration_wct.stop(); + + // Write out time to text-file + out_wct_file << t_iteration_wct.getwct() << ","; + out_GPUtime_file << t_GPU.getwctGPU() << ","; + } + else + { + t_iteration_wct.start(); + g_sparse.template ghost_get<PHI_PHASE, CONC_NPLUS1, DIFFUSION_COEFFICIENT>(RUN_ON_DEVICE); + t_GPU.startGPU(); + g_sparse.template conv3_b< + CONC_NPLUS1, + DIFFUSION_COEFFICIENT, + PHI_PHASE, + CONC_N, + DIFFUSION_COEFFICIENT, + PHI_PHASE, 1> + ({0, 0, 0}, + {(long int) sz[x]-1, (long int) sz[y]-1, (long int) sz[z]-1}, + func_inhomogDiffusion); + t_GPU.stopGPU(); + t_iteration_wct.stop(); + // Write out time to text-file + out_wct_file << t_iteration_wct.getwct() << ","; + out_GPUtime_file << t_GPU.getwctGPU() << ","; + } + + + if (iter % interval_write == 0) + { + // Copy from GPU to host for writing + g_sparse.template deviceToHost<CONC_N, CONC_NPLUS1, DIFFUSION_COEFFICIENT, PHI_PHASE>(); + + // Write g_sparse to vtk + g_sparse.write_frame(path_output + "g_sparse_diffuse", iter, FORMAT_BINARY); + + // Write g_sparse to hdf5 + g_sparse.save(path_output + "g_sparse_diffuse_" + std::to_string(iter) + ".hdf5"); + + if (iter % 2 == 0) + { + monitor_total_concentration<CONC_N>(g_sparse, t, iter, path_output,"total_conc.csv"); + } + else + { + monitor_total_concentration<CONC_NPLUS1>(g_sparse, t, iter, path_output,"total_conc.csv"); + } + } + + + t += dt; + iter += 1; + } + t_iterative_diffusion_total.stop(); + out_GPUtime_file.close(); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + t_total.stop(); + std::cout << "Rank " << v_cl.rank() << " total time for the whole programm : " << t_total.getwct() << std::endl; + + //! \cond [Run] \endcond + + /** + * @page 9_inhomogeneous_diffusion_porous_catalyst diffusion_porous_media 3D + * + * ## Terminate ## {#e_CaC03_terminate} + * + * We end with terminating OpenFPM. + * + * \snippet SparseGrid/9_inhomogeneous_diffusion_porous_catalyst/main.cu Terminate + */ + //! \cond [Terminate] \endcond + openfpm_finalize(); // Finalize openFPM library + return 0; +} +//! \cond [Terminate] \endcond + +/** + * @page 9_inhomogeneous_diffusion_porous_catalyst diffusion_porous_media 3D + * + * ## Full code ## {#e_CaC03_full} + * + * @include SparseGrid/9_inhomogeneous_diffusion_porous_catalyst/main.cu + */