From 37cb3491bd14709b175c026e98a3ce23395b353f Mon Sep 17 00:00:00 2001 From: github-action Date: Tue, 17 Sep 2024 08:22:23 +0000 Subject: [PATCH] Update API docs --- api/.nojekyll | 0 api/css/font-awesome.css | 1672 ++++++++++ api/css/font-awesome.min.css | 4 + api/css/local.css | 298 ++ api/css/pygments.css | 75 + api/favicon.png | Bin 0 -> 803 bytes api/fonts/FontAwesome.otf | Bin 0 -> 85908 bytes api/fonts/fontawesome-webfont.eot | Bin 0 -> 56006 bytes api/fonts/fontawesome-webfont.svg | 520 +++ api/fonts/fontawesome-webfont.ttf | Bin 0 -> 112160 bytes api/fonts/fontawesome-webfont.woff | Bin 0 -> 65452 bytes api/fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20335 bytes api/fonts/glyphicons-halflings-regular.svg | 229 ++ api/fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 41280 bytes api/fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23320 bytes api/index.html | 179 + api/interface/alloc_tdsops.html | 394 +++ api/interface/allocator_t.html | 267 ++ api/interface/copy_data_to_f.html | 262 ++ api/interface/copy_f_to_data.html | 262 ++ api/interface/cuda_allocator_t.html | 267 ++ api/interface/cuda_backend_t.html | 267 ++ api/interface/cuda_field_t.html | 282 ++ api/interface/cuda_poisson_fft_t.html | 296 ++ api/interface/cuda_tdsops_t.html | 403 +++ api/interface/fft_backward.html | 244 ++ api/interface/fft_forward.html | 244 ++ api/interface/fft_postprocess.html | 229 ++ api/interface/field_t.html | 282 ++ api/interface/get_index_reordering.html | 514 +++ api/interface/init_poisson_fft.html | 289 ++ api/interface/mesh_t.html | 329 ++ api/interface/omp_backend_t.html | 267 ++ api/interface/omp_poisson_fft_t.html | 296 ++ api/interface/poisson_solver.html | 259 ++ api/interface/reorder.html | 279 ++ api/interface/scalar_product.html | 263 ++ api/interface/solver_t.html | 282 ++ api/interface/stepper_func.html | 244 ++ api/interface/sum_intox.html | 262 ++ api/interface/tds_solve.html | 280 ++ api/interface/tdsops_t.html | 402 +++ api/interface/time_intg_t.html | 297 ++ api/interface/transeq_ders.html | 340 ++ api/interface/vecadd.html | 291 ++ api/interface/vector_calculus_t.html | 252 ++ api/js/MathJax-config/.gitignore | 0 api/js/svg-pan-zoom.min.js | 3 + api/lists/absint.html | 217 ++ api/lists/files.html | 248 ++ api/lists/modules.html | 274 ++ api/lists/namelists.html | 138 + api/lists/procedures.html | 1112 +++++++ api/lists/types.html | 257 ++ api/module/m_allocator.html | 962 ++++++ api/module/m_base_backend.html | 1754 ++++++++++ api/module/m_common.html | 986 ++++++ api/module/m_cuda_allocator.html | 1024 ++++++ api/module/m_cuda_backend.html | 3125 ++++++++++++++++++ api/module/m_cuda_common.html | 250 ++ api/module/m_cuda_exec_dist.html | 949 ++++++ api/module/m_cuda_exec_thom.html | 335 ++ api/module/m_cuda_kernels_dist.html | 1296 ++++++++ api/module/m_cuda_kernels_reorder.html | 1261 +++++++ api/module/m_cuda_kernels_thom.html | 554 ++++ api/module/m_cuda_poisson_fft.html | 1113 +++++++ api/module/m_cuda_sendrecv.html | 645 ++++ api/module/m_cuda_spectral.html | 469 +++ api/module/m_cuda_tdsops.html | 1321 ++++++++ api/module/m_field.html | 717 ++++ api/module/m_mesh.html | 2075 ++++++++++++ api/module/m_omp_backend.html | 2843 ++++++++++++++++ api/module/m_omp_common.html | 250 ++ api/module/m_omp_exec_dist.html | 933 ++++++ api/module/m_omp_kernels_dist.html | 570 ++++ api/module/m_omp_poisson_fft.html | 987 ++++++ api/module/m_omp_sendrecv.html | 378 +++ api/module/m_ordering.html | 1252 +++++++ api/module/m_poisson_fft.html | 1038 ++++++ api/module/m_solver.html | 1729 ++++++++++ api/module/m_tdsops.html | 2026 ++++++++++++ api/module/m_time_integrator.html | 1166 +++++++ api/module/m_vector_calculus.html | 1144 +++++++ api/namelist/domain_params.html | 259 ++ api/namelist/solver_params.html | 292 ++ api/proc/alloc_cuda_tdsops.html | 406 +++ api/proc/alloc_omp_tdsops.html | 406 +++ api/proc/allocate_tdsops.html | 314 ++ api/proc/allocator_init.html | 261 ++ api/proc/axpby.html | 299 ++ api/proc/base_init.html | 241 ++ api/proc/base_init~2.html | 301 ++ api/proc/buffer_copy.html | 299 ++ api/proc/compute_padded_dims.html | 256 ++ api/proc/copy_data_to_f_cuda.html | 271 ++ api/proc/copy_data_to_f_omp.html | 271 ++ api/proc/copy_f_to_data_cuda.html | 271 ++ api/proc/copy_f_to_data_omp.html | 271 ++ api/proc/copy_into_buffers.html | 284 ++ api/proc/copy_into_buffers~2.html | 299 ++ api/proc/create_block.html | 265 ++ api/proc/create_cuda_block.html | 263 ++ api/proc/cuda_allocator_init.html | 261 ++ api/proc/cuda_field_init.html | 276 ++ api/proc/cuda_tdsops_init.html | 398 +++ api/proc/curl.html | 332 ++ api/proc/curl~2.html | 379 +++ api/proc/der_univ_dist.html | 419 +++ api/proc/der_univ_dist~2.html | 419 +++ api/proc/der_univ_subs.html | 314 ++ api/proc/der_univ_subs~2.html | 314 ++ api/proc/der_univ_thom.html | 359 ++ api/proc/der_univ_thom_per.html | 359 ++ api/proc/deriv_1st.html | 316 ++ api/proc/deriv_2nd.html | 346 ++ api/proc/destroy.html | 246 ++ api/proc/divergence_v2c.html | 396 +++ api/proc/divergence_v2p.html | 302 ++ api/proc/domain_decomposition.html | 242 ++ api/proc/exec_dist_tds_compact.html | 434 +++ api/proc/exec_dist_tds_compact~2.html | 419 +++ api/proc/exec_dist_transeq_3fused.html | 674 ++++ api/proc/exec_dist_transeq_compact.html | 674 ++++ api/proc/exec_thom_tds_compact.html | 299 ++ api/proc/fft_backward_cuda.html | 256 ++ api/proc/fft_backward_omp.html | 256 ++ api/proc/fft_forward_cuda.html | 256 ++ api/proc/fft_forward_omp.html | 256 ++ api/proc/fft_postprocess_cuda.html | 241 ++ api/proc/fft_postprocess_omp.html | 241 ++ api/proc/field_init.html | 276 ++ api/proc/finalize.html | 241 ++ api/proc/get_block.html | 285 ++ api/proc/get_block_ids.html | 251 ++ api/proc/get_coordinates.html | 294 ++ api/proc/get_dims.html | 264 ++ api/proc/get_dims_dataloc.html | 277 ++ api/proc/get_dirs_from_rdr.html | 269 ++ api/proc/get_field_data.html | 288 ++ api/proc/get_field_dims_dir.html | 279 ++ api/proc/get_field_dims_phi.html | 264 ++ api/proc/get_field_dims_phi_dataloc.html | 279 ++ api/proc/get_global_dims.html | 264 ++ api/proc/get_index_dir.html | 390 +++ api/proc/get_index_ijk.html | 390 +++ api/proc/get_index_reordering_dirs.html | 361 ++ api/proc/get_index_reordering_rdr.html | 344 ++ api/proc/get_n_dir.html | 279 ++ api/proc/get_n_groups_dir.html | 264 ++ api/proc/get_n_groups_phi.html | 264 ++ api/proc/get_n_phi.html | 264 ++ api/proc/get_padded_dims_dir.html | 264 ++ api/proc/get_padded_dims_phi.html | 265 ++ api/proc/get_rdr_from_dirs.html | 262 ++ api/proc/get_sz.html | 249 ++ api/proc/get_tds_n.html | 277 ++ api/proc/gradient_c2v.html | 396 +++ api/proc/gradient_p2v.html | 302 ++ api/proc/init.html | 291 ++ api/proc/init_cuda_poisson_fft.html | 301 ++ api/proc/init_omp_poisson_fft.html | 301 ++ api/proc/init~4.html | 385 +++ api/proc/init~5.html | 261 ++ api/proc/init~6.html | 246 ++ api/proc/init~7.html | 261 ++ api/proc/interpl_mid.html | 316 ++ api/proc/is_root.html | 249 ++ api/proc/laplacian.html | 319 ++ api/proc/mesh_init.html | 324 ++ api/proc/output.html | 256 ++ api/proc/poisson_cg.html | 269 ++ api/proc/poisson_fft.html | 269 ++ api/proc/preprocess_dist.html | 256 ++ api/proc/preprocess_thom.html | 256 ++ api/proc/process_spectral_div_u.html | 437 +++ api/proc/release_block.html | 259 ++ api/proc/reorder_c2x.html | 269 ++ api/proc/reorder_cuda.html | 286 ++ api/proc/reorder_omp.html | 286 ++ api/proc/reorder_x2c.html | 269 ++ api/proc/reorder_x2y.html | 269 ++ api/proc/reorder_x2z.html | 269 ++ api/proc/reorder_y2x.html | 269 ++ api/proc/reorder_y2z.html | 284 ++ api/proc/reorder_z2x.html | 269 ++ api/proc/reorder_z2y.html | 284 ++ api/proc/resolve_field_t.html | 254 ++ api/proc/rotate.html | 254 ++ api/proc/run.html | 241 ++ api/proc/scalar_product.html | 284 ++ api/proc/scalar_product_cuda.html | 278 ++ api/proc/scalar_product_omp.html | 291 ++ api/proc/sendrecv_3fields.html | 464 +++ api/proc/sendrecv_fields.html | 344 ++ api/proc/sendrecv_fields~2.html | 344 ++ api/proc/set_data_loc.html | 256 ++ api/proc/set_field_data.html | 286 ++ api/proc/set_padded_dims.html | 256 ++ api/proc/set_shape.html | 256 ++ api/proc/set_shape_cuda.html | 256 ++ api/proc/set_sz.html | 256 ++ api/proc/stagder_1st.html | 331 ++ api/proc/step.html | 346 ++ api/proc/sum_intox_omp.html | 284 ++ api/proc/sum_yintox.html | 269 ++ api/proc/sum_yintox_cuda.html | 271 ++ api/proc/sum_yintox_omp.html | 271 ++ api/proc/sum_zintox.html | 269 ++ api/proc/sum_zintox_cuda.html | 271 ++ api/proc/sum_zintox_omp.html | 271 ++ api/proc/tds_solve_cuda.html | 286 ++ api/proc/tds_solve_dist.html | 316 ++ api/proc/tds_solve_dist~2.html | 284 ++ api/proc/tds_solve_omp.html | 286 ++ api/proc/tdsops_init.html | 412 +++ api/proc/transeq.html | 335 ++ api/proc/transeq_3fused_dist.html | 644 ++++ api/proc/transeq_3fused_subs.html | 479 +++ api/proc/transeq_cuda_dist.html | 376 +++ api/proc/transeq_cuda_thom.html | 349 ++ api/proc/transeq_omp_dist.html | 346 ++ api/proc/transeq_x_cuda.html | 346 ++ api/proc/transeq_x_omp.html | 346 ++ api/proc/transeq_y_cuda.html | 346 ++ api/proc/transeq_y_omp.html | 346 ++ api/proc/transeq_z_cuda.html | 346 ++ api/proc/transeq_z_omp.html | 346 ++ api/proc/vecadd_cuda.html | 301 ++ api/proc/vecadd_omp.html | 301 ++ api/proc/waves_set.html | 303 ++ api/program/xcompact.html | 726 +++++ api/search.html | 139 + api/sourcefile/allocator.f90.html | 283 ++ api/sourcefile/allocator.f90~2.html | 419 +++ api/sourcefile/backend.f90.html | 495 +++ api/sourcefile/backend.f90~2.html | 912 ++++++ api/sourcefile/backend.f90~3.html | 785 +++++ api/sourcefile/common.f90.html | 218 ++ api/sourcefile/common.f90~2.html | 261 ++ api/sourcefile/common.f90~3.html | 218 ++ api/sourcefile/distributed.f90.html | 865 +++++ api/sourcefile/distributed.f90~2.html | 442 +++ api/sourcefile/exec_dist.f90.html | 337 ++ api/sourcefile/exec_dist.f90~2.html | 403 +++ api/sourcefile/exec_thom.f90.html | 249 ++ api/sourcefile/field.f90.html | 270 ++ api/sourcefile/mesh.f90.html | 693 ++++ api/sourcefile/ordering.f90.html | 323 ++ api/sourcefile/poisson_fft.f90.html | 272 ++ api/sourcefile/poisson_fft.f90~2.html | 427 +++ api/sourcefile/poisson_fft.f90~3.html | 449 +++ api/sourcefile/reorder.f90.html | 500 +++ api/sourcefile/sendrecv.f90.html | 306 ++ api/sourcefile/sendrecv.f90~2.html | 250 ++ api/sourcefile/solver.f90.html | 761 +++++ api/sourcefile/spectral_processing.f90.html | 321 ++ api/sourcefile/tdsops.f90.html | 293 ++ api/sourcefile/tdsops.f90~2.html | 1076 ++++++ api/sourcefile/thomas.f90.html | 389 +++ api/sourcefile/time_integrator.f90.html | 528 +++ api/sourcefile/vector_calculus.f90.html | 606 ++++ api/sourcefile/xcompact.f90.html | 327 ++ api/src/allocator.f90 | 207 ++ api/src/backend.f90 | 574 ++++ api/src/common.f90 | 6 + api/src/distributed.f90 | 230 ++ api/src/exec_dist.f90 | 192 ++ api/src/exec_thom.f90 | 37 + api/src/field.f90 | 58 + api/src/mesh.f90 | 481 +++ api/src/ordering.f90 | 111 + api/src/poisson_fft.f90 | 237 ++ api/src/reorder.f90 | 288 ++ api/src/sendrecv.f90 | 38 + api/src/solver.f90 | 549 ++++ api/src/spectral_processing.f90 | 109 + api/src/tdsops.f90 | 865 +++++ api/src/thomas.f90 | 177 + api/src/time_integrator.f90 | 316 ++ api/src/vector_calculus.f90 | 394 +++ api/src/xcompact.f90 | 115 + api/tipuesearch/.DS_Store | Bin 0 -> 12292 bytes api/tipuesearch/img/.DS_Store | Bin 0 -> 6148 bytes api/tipuesearch/img/loader.gif | Bin 0 -> 4178 bytes api/tipuesearch/img/search.png | Bin 0 -> 368 bytes api/tipuesearch/tipuesearch.css | 158 + api/tipuesearch/tipuesearch.js | 452 +++ api/tipuesearch/tipuesearch.min.js | 14 + api/tipuesearch/tipuesearch_content.js | 1 + api/tipuesearch/tipuesearch_set.js | 23 + api/type/allocator_t.html | 852 +++++ api/type/base_backend_t.html | 2149 ++++++++++++ api/type/cuda_allocator_t.html | 830 +++++ api/type/cuda_backend_t.html | 3253 +++++++++++++++++++ api/type/cuda_field_t.html | 606 ++++ api/type/cuda_poisson_fft_t.html | 1319 ++++++++ api/type/cuda_tdsops_t.html | 1772 ++++++++++ api/type/dirps_t.html | 392 +++ api/type/field_t.html | 592 ++++ api/type/flist_t.html | 246 ++ api/type/geo_t.html | 264 ++ api/type/mesh_t.html | 2240 +++++++++++++ api/type/omp_backend_t.html | 2823 ++++++++++++++++ api/type/omp_poisson_fft_t.html | 1194 +++++++ api/type/parallel_t.html | 427 +++ api/type/poisson_fft_t.html | 1023 ++++++ api/type/solver_t.html | 1315 ++++++++ api/type/tdsops_t.html | 1582 +++++++++ api/type/time_intg_t.html | 1032 ++++++ api/type/vector_calculus_t.html | 1058 ++++++ 310 files changed, 137689 insertions(+) create mode 100644 api/.nojekyll create mode 100644 api/css/font-awesome.css create mode 100644 api/css/font-awesome.min.css create mode 100644 api/css/local.css create mode 100644 api/css/pygments.css create mode 100644 api/favicon.png create mode 100644 api/fonts/FontAwesome.otf create mode 100644 api/fonts/fontawesome-webfont.eot create mode 100644 api/fonts/fontawesome-webfont.svg create mode 100644 api/fonts/fontawesome-webfont.ttf create mode 100644 api/fonts/fontawesome-webfont.woff create mode 100644 api/fonts/glyphicons-halflings-regular.eot create mode 100644 api/fonts/glyphicons-halflings-regular.svg create mode 100644 api/fonts/glyphicons-halflings-regular.ttf create mode 100644 api/fonts/glyphicons-halflings-regular.woff create mode 100644 api/index.html create mode 100644 api/interface/alloc_tdsops.html create mode 100644 api/interface/allocator_t.html create mode 100644 api/interface/copy_data_to_f.html create mode 100644 api/interface/copy_f_to_data.html create mode 100644 api/interface/cuda_allocator_t.html create mode 100644 api/interface/cuda_backend_t.html create mode 100644 api/interface/cuda_field_t.html create mode 100644 api/interface/cuda_poisson_fft_t.html create mode 100644 api/interface/cuda_tdsops_t.html create mode 100644 api/interface/fft_backward.html create mode 100644 api/interface/fft_forward.html create mode 100644 api/interface/fft_postprocess.html create mode 100644 api/interface/field_t.html create mode 100644 api/interface/get_index_reordering.html create mode 100644 api/interface/init_poisson_fft.html create mode 100644 api/interface/mesh_t.html create mode 100644 api/interface/omp_backend_t.html create mode 100644 api/interface/omp_poisson_fft_t.html create mode 100644 api/interface/poisson_solver.html create mode 100644 api/interface/reorder.html create mode 100644 api/interface/scalar_product.html create mode 100644 api/interface/solver_t.html create mode 100644 api/interface/stepper_func.html create mode 100644 api/interface/sum_intox.html create mode 100644 api/interface/tds_solve.html create mode 100644 api/interface/tdsops_t.html create mode 100644 api/interface/time_intg_t.html create mode 100644 api/interface/transeq_ders.html create mode 100644 api/interface/vecadd.html create mode 100644 api/interface/vector_calculus_t.html create mode 100644 api/js/MathJax-config/.gitignore create mode 100644 api/js/svg-pan-zoom.min.js create mode 100644 api/lists/absint.html create mode 100644 api/lists/files.html create mode 100644 api/lists/modules.html create mode 100644 api/lists/namelists.html create mode 100644 api/lists/procedures.html create mode 100644 api/lists/types.html create mode 100644 api/module/m_allocator.html create mode 100644 api/module/m_base_backend.html create mode 100644 api/module/m_common.html create mode 100644 api/module/m_cuda_allocator.html create mode 100644 api/module/m_cuda_backend.html create mode 100644 api/module/m_cuda_common.html create mode 100644 api/module/m_cuda_exec_dist.html create mode 100644 api/module/m_cuda_exec_thom.html create mode 100644 api/module/m_cuda_kernels_dist.html create mode 100644 api/module/m_cuda_kernels_reorder.html create mode 100644 api/module/m_cuda_kernels_thom.html create mode 100644 api/module/m_cuda_poisson_fft.html create mode 100644 api/module/m_cuda_sendrecv.html create mode 100644 api/module/m_cuda_spectral.html create mode 100644 api/module/m_cuda_tdsops.html create mode 100644 api/module/m_field.html create mode 100644 api/module/m_mesh.html create mode 100644 api/module/m_omp_backend.html create mode 100644 api/module/m_omp_common.html create mode 100644 api/module/m_omp_exec_dist.html create mode 100644 api/module/m_omp_kernels_dist.html create mode 100644 api/module/m_omp_poisson_fft.html create mode 100644 api/module/m_omp_sendrecv.html create mode 100644 api/module/m_ordering.html create mode 100644 api/module/m_poisson_fft.html create mode 100644 api/module/m_solver.html create mode 100644 api/module/m_tdsops.html create mode 100644 api/module/m_time_integrator.html create mode 100644 api/module/m_vector_calculus.html create mode 100644 api/namelist/domain_params.html create mode 100644 api/namelist/solver_params.html create mode 100644 api/proc/alloc_cuda_tdsops.html create mode 100644 api/proc/alloc_omp_tdsops.html create mode 100644 api/proc/allocate_tdsops.html create mode 100644 api/proc/allocator_init.html create mode 100644 api/proc/axpby.html create mode 100644 api/proc/base_init.html create mode 100644 api/proc/base_init~2.html create mode 100644 api/proc/buffer_copy.html create mode 100644 api/proc/compute_padded_dims.html create mode 100644 api/proc/copy_data_to_f_cuda.html create mode 100644 api/proc/copy_data_to_f_omp.html create mode 100644 api/proc/copy_f_to_data_cuda.html create mode 100644 api/proc/copy_f_to_data_omp.html create mode 100644 api/proc/copy_into_buffers.html create mode 100644 api/proc/copy_into_buffers~2.html create mode 100644 api/proc/create_block.html create mode 100644 api/proc/create_cuda_block.html create mode 100644 api/proc/cuda_allocator_init.html create mode 100644 api/proc/cuda_field_init.html create mode 100644 api/proc/cuda_tdsops_init.html create mode 100644 api/proc/curl.html create mode 100644 api/proc/curl~2.html create mode 100644 api/proc/der_univ_dist.html create mode 100644 api/proc/der_univ_dist~2.html create mode 100644 api/proc/der_univ_subs.html create mode 100644 api/proc/der_univ_subs~2.html create mode 100644 api/proc/der_univ_thom.html create mode 100644 api/proc/der_univ_thom_per.html create mode 100644 api/proc/deriv_1st.html create mode 100644 api/proc/deriv_2nd.html create mode 100644 api/proc/destroy.html create mode 100644 api/proc/divergence_v2c.html create mode 100644 api/proc/divergence_v2p.html create mode 100644 api/proc/domain_decomposition.html create mode 100644 api/proc/exec_dist_tds_compact.html create mode 100644 api/proc/exec_dist_tds_compact~2.html create mode 100644 api/proc/exec_dist_transeq_3fused.html create mode 100644 api/proc/exec_dist_transeq_compact.html create mode 100644 api/proc/exec_thom_tds_compact.html create mode 100644 api/proc/fft_backward_cuda.html create mode 100644 api/proc/fft_backward_omp.html create mode 100644 api/proc/fft_forward_cuda.html create mode 100644 api/proc/fft_forward_omp.html create mode 100644 api/proc/fft_postprocess_cuda.html create mode 100644 api/proc/fft_postprocess_omp.html create mode 100644 api/proc/field_init.html create mode 100644 api/proc/finalize.html create mode 100644 api/proc/get_block.html create mode 100644 api/proc/get_block_ids.html create mode 100644 api/proc/get_coordinates.html create mode 100644 api/proc/get_dims.html create mode 100644 api/proc/get_dims_dataloc.html create mode 100644 api/proc/get_dirs_from_rdr.html create mode 100644 api/proc/get_field_data.html create mode 100644 api/proc/get_field_dims_dir.html create mode 100644 api/proc/get_field_dims_phi.html create mode 100644 api/proc/get_field_dims_phi_dataloc.html create mode 100644 api/proc/get_global_dims.html create mode 100644 api/proc/get_index_dir.html create mode 100644 api/proc/get_index_ijk.html create mode 100644 api/proc/get_index_reordering_dirs.html create mode 100644 api/proc/get_index_reordering_rdr.html create mode 100644 api/proc/get_n_dir.html create mode 100644 api/proc/get_n_groups_dir.html create mode 100644 api/proc/get_n_groups_phi.html create mode 100644 api/proc/get_n_phi.html create mode 100644 api/proc/get_padded_dims_dir.html create mode 100644 api/proc/get_padded_dims_phi.html create mode 100644 api/proc/get_rdr_from_dirs.html create mode 100644 api/proc/get_sz.html create mode 100644 api/proc/get_tds_n.html create mode 100644 api/proc/gradient_c2v.html create mode 100644 api/proc/gradient_p2v.html create mode 100644 api/proc/init.html create mode 100644 api/proc/init_cuda_poisson_fft.html create mode 100644 api/proc/init_omp_poisson_fft.html create mode 100644 api/proc/init~4.html create mode 100644 api/proc/init~5.html create mode 100644 api/proc/init~6.html create mode 100644 api/proc/init~7.html create mode 100644 api/proc/interpl_mid.html create mode 100644 api/proc/is_root.html create mode 100644 api/proc/laplacian.html create mode 100644 api/proc/mesh_init.html create mode 100644 api/proc/output.html create mode 100644 api/proc/poisson_cg.html create mode 100644 api/proc/poisson_fft.html create mode 100644 api/proc/preprocess_dist.html create mode 100644 api/proc/preprocess_thom.html create mode 100644 api/proc/process_spectral_div_u.html create mode 100644 api/proc/release_block.html create mode 100644 api/proc/reorder_c2x.html create mode 100644 api/proc/reorder_cuda.html create mode 100644 api/proc/reorder_omp.html create mode 100644 api/proc/reorder_x2c.html create mode 100644 api/proc/reorder_x2y.html create mode 100644 api/proc/reorder_x2z.html create mode 100644 api/proc/reorder_y2x.html create mode 100644 api/proc/reorder_y2z.html create mode 100644 api/proc/reorder_z2x.html create mode 100644 api/proc/reorder_z2y.html create mode 100644 api/proc/resolve_field_t.html create mode 100644 api/proc/rotate.html create mode 100644 api/proc/run.html create mode 100644 api/proc/scalar_product.html create mode 100644 api/proc/scalar_product_cuda.html create mode 100644 api/proc/scalar_product_omp.html create mode 100644 api/proc/sendrecv_3fields.html create mode 100644 api/proc/sendrecv_fields.html create mode 100644 api/proc/sendrecv_fields~2.html create mode 100644 api/proc/set_data_loc.html create mode 100644 api/proc/set_field_data.html create mode 100644 api/proc/set_padded_dims.html create mode 100644 api/proc/set_shape.html create mode 100644 api/proc/set_shape_cuda.html create mode 100644 api/proc/set_sz.html create mode 100644 api/proc/stagder_1st.html create mode 100644 api/proc/step.html create mode 100644 api/proc/sum_intox_omp.html create mode 100644 api/proc/sum_yintox.html create mode 100644 api/proc/sum_yintox_cuda.html create mode 100644 api/proc/sum_yintox_omp.html create mode 100644 api/proc/sum_zintox.html create mode 100644 api/proc/sum_zintox_cuda.html create mode 100644 api/proc/sum_zintox_omp.html create mode 100644 api/proc/tds_solve_cuda.html create mode 100644 api/proc/tds_solve_dist.html create mode 100644 api/proc/tds_solve_dist~2.html create mode 100644 api/proc/tds_solve_omp.html create mode 100644 api/proc/tdsops_init.html create mode 100644 api/proc/transeq.html create mode 100644 api/proc/transeq_3fused_dist.html create mode 100644 api/proc/transeq_3fused_subs.html create mode 100644 api/proc/transeq_cuda_dist.html create mode 100644 api/proc/transeq_cuda_thom.html create mode 100644 api/proc/transeq_omp_dist.html create mode 100644 api/proc/transeq_x_cuda.html create mode 100644 api/proc/transeq_x_omp.html create mode 100644 api/proc/transeq_y_cuda.html create mode 100644 api/proc/transeq_y_omp.html create mode 100644 api/proc/transeq_z_cuda.html create mode 100644 api/proc/transeq_z_omp.html create mode 100644 api/proc/vecadd_cuda.html create mode 100644 api/proc/vecadd_omp.html create mode 100644 api/proc/waves_set.html create mode 100644 api/program/xcompact.html create mode 100644 api/search.html create mode 100644 api/sourcefile/allocator.f90.html create mode 100644 api/sourcefile/allocator.f90~2.html create mode 100644 api/sourcefile/backend.f90.html create mode 100644 api/sourcefile/backend.f90~2.html create mode 100644 api/sourcefile/backend.f90~3.html create mode 100644 api/sourcefile/common.f90.html create mode 100644 api/sourcefile/common.f90~2.html create mode 100644 api/sourcefile/common.f90~3.html create mode 100644 api/sourcefile/distributed.f90.html create mode 100644 api/sourcefile/distributed.f90~2.html create mode 100644 api/sourcefile/exec_dist.f90.html create mode 100644 api/sourcefile/exec_dist.f90~2.html create mode 100644 api/sourcefile/exec_thom.f90.html create mode 100644 api/sourcefile/field.f90.html create mode 100644 api/sourcefile/mesh.f90.html create mode 100644 api/sourcefile/ordering.f90.html create mode 100644 api/sourcefile/poisson_fft.f90.html create mode 100644 api/sourcefile/poisson_fft.f90~2.html create mode 100644 api/sourcefile/poisson_fft.f90~3.html create mode 100644 api/sourcefile/reorder.f90.html create mode 100644 api/sourcefile/sendrecv.f90.html create mode 100644 api/sourcefile/sendrecv.f90~2.html create mode 100644 api/sourcefile/solver.f90.html create mode 100644 api/sourcefile/spectral_processing.f90.html create mode 100644 api/sourcefile/tdsops.f90.html create mode 100644 api/sourcefile/tdsops.f90~2.html create mode 100644 api/sourcefile/thomas.f90.html create mode 100644 api/sourcefile/time_integrator.f90.html create mode 100644 api/sourcefile/vector_calculus.f90.html create mode 100644 api/sourcefile/xcompact.f90.html create mode 100644 api/src/allocator.f90 create mode 100644 api/src/backend.f90 create mode 100644 api/src/common.f90 create mode 100644 api/src/distributed.f90 create mode 100644 api/src/exec_dist.f90 create mode 100644 api/src/exec_thom.f90 create mode 100644 api/src/field.f90 create mode 100644 api/src/mesh.f90 create mode 100644 api/src/ordering.f90 create mode 100644 api/src/poisson_fft.f90 create mode 100644 api/src/reorder.f90 create mode 100644 api/src/sendrecv.f90 create mode 100644 api/src/solver.f90 create mode 100644 api/src/spectral_processing.f90 create mode 100644 api/src/tdsops.f90 create mode 100644 api/src/thomas.f90 create mode 100644 api/src/time_integrator.f90 create mode 100644 api/src/vector_calculus.f90 create mode 100644 api/src/xcompact.f90 create mode 100644 api/tipuesearch/.DS_Store create mode 100644 api/tipuesearch/img/.DS_Store create mode 100644 api/tipuesearch/img/loader.gif create mode 100644 api/tipuesearch/img/search.png create mode 100644 api/tipuesearch/tipuesearch.css create mode 100644 api/tipuesearch/tipuesearch.js create mode 100644 api/tipuesearch/tipuesearch.min.js create mode 100644 api/tipuesearch/tipuesearch_content.js create mode 100644 api/tipuesearch/tipuesearch_set.js create mode 100644 api/type/allocator_t.html create mode 100644 api/type/base_backend_t.html create mode 100644 api/type/cuda_allocator_t.html create mode 100644 api/type/cuda_backend_t.html create mode 100644 api/type/cuda_field_t.html create mode 100644 api/type/cuda_poisson_fft_t.html create mode 100644 api/type/cuda_tdsops_t.html create mode 100644 api/type/dirps_t.html create mode 100644 api/type/field_t.html create mode 100644 api/type/flist_t.html create mode 100644 api/type/geo_t.html create mode 100644 api/type/mesh_t.html create mode 100644 api/type/omp_backend_t.html create mode 100644 api/type/omp_poisson_fft_t.html create mode 100644 api/type/parallel_t.html create mode 100644 api/type/poisson_fft_t.html create mode 100644 api/type/solver_t.html create mode 100644 api/type/tdsops_t.html create mode 100644 api/type/time_intg_t.html create mode 100644 api/type/vector_calculus_t.html diff --git a/api/.nojekyll b/api/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/api/css/font-awesome.css b/api/css/font-awesome.css new file mode 100644 index 000000000..4040b3cf8 --- /dev/null +++ b/api/css/font-awesome.css @@ -0,0 +1,1672 @@ +/*! + * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('../fonts/fontawesome-webfont.eot?v=4.2.0'); + src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.28571429em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.85714286em; +} +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +.fa-rotate-90 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #ffffff; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-desc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-asc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} +.fa-space-shuttle:before { + content: "\f197"; +} +.fa-slack:before { + content: "\f198"; +} +.fa-envelope-square:before { + content: "\f199"; +} +.fa-wordpress:before { + content: "\f19a"; +} +.fa-openid:before { + content: "\f19b"; +} +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: "\f19c"; +} +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: "\f19d"; +} +.fa-yahoo:before { + content: "\f19e"; +} +.fa-google:before { + content: "\f1a0"; +} +.fa-reddit:before { + content: "\f1a1"; +} +.fa-reddit-square:before { + content: "\f1a2"; +} +.fa-stumbleupon-circle:before { + content: "\f1a3"; +} +.fa-stumbleupon:before { + content: "\f1a4"; +} +.fa-delicious:before { + content: "\f1a5"; +} +.fa-digg:before { + content: "\f1a6"; +} +.fa-pied-piper:before { + content: "\f1a7"; +} +.fa-pied-piper-alt:before { + content: "\f1a8"; +} +.fa-drupal:before { + content: "\f1a9"; +} +.fa-joomla:before { + content: "\f1aa"; +} +.fa-language:before { + content: "\f1ab"; +} +.fa-fax:before { + content: "\f1ac"; +} +.fa-building:before { + content: "\f1ad"; +} +.fa-child:before { + content: "\f1ae"; +} +.fa-paw:before { + content: "\f1b0"; +} +.fa-spoon:before { + content: "\f1b1"; +} +.fa-cube:before { + content: "\f1b2"; +} +.fa-cubes:before { + content: "\f1b3"; +} +.fa-behance:before { + content: "\f1b4"; +} +.fa-behance-square:before { + content: "\f1b5"; +} +.fa-steam:before { + content: "\f1b6"; +} +.fa-steam-square:before { + content: "\f1b7"; +} +.fa-recycle:before { + content: "\f1b8"; +} +.fa-automobile:before, +.fa-car:before { + content: "\f1b9"; +} +.fa-cab:before, +.fa-taxi:before { + content: "\f1ba"; +} +.fa-tree:before { + content: "\f1bb"; +} +.fa-spotify:before { + content: "\f1bc"; +} +.fa-deviantart:before { + content: "\f1bd"; +} +.fa-soundcloud:before { + content: "\f1be"; +} +.fa-database:before { + content: "\f1c0"; +} +.fa-file-pdf-o:before { + content: "\f1c1"; +} +.fa-file-word-o:before { + content: "\f1c2"; +} +.fa-file-excel-o:before { + content: "\f1c3"; +} +.fa-file-powerpoint-o:before { + content: "\f1c4"; +} +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: "\f1c5"; +} +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: "\f1c6"; +} +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: "\f1c7"; +} +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "\f1c8"; +} +.fa-file-code-o:before { + content: "\f1c9"; +} +.fa-vine:before { + content: "\f1ca"; +} +.fa-codepen:before { + content: "\f1cb"; +} +.fa-jsfiddle:before { + content: "\f1cc"; +} +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: "\f1cd"; +} +.fa-circle-o-notch:before { + content: "\f1ce"; +} +.fa-ra:before, +.fa-rebel:before { + content: "\f1d0"; +} +.fa-ge:before, +.fa-empire:before { + content: "\f1d1"; +} +.fa-git-square:before { + content: "\f1d2"; +} +.fa-git:before { + content: "\f1d3"; +} +.fa-hacker-news:before { + content: "\f1d4"; +} +.fa-tencent-weibo:before { + content: "\f1d5"; +} +.fa-qq:before { + content: "\f1d6"; +} +.fa-wechat:before, +.fa-weixin:before { + content: "\f1d7"; +} +.fa-send:before, +.fa-paper-plane:before { + content: "\f1d8"; +} +.fa-send-o:before, +.fa-paper-plane-o:before { + content: "\f1d9"; +} +.fa-history:before { + content: "\f1da"; +} +.fa-circle-thin:before { + content: "\f1db"; +} +.fa-header:before { + content: "\f1dc"; +} +.fa-paragraph:before { + content: "\f1dd"; +} +.fa-sliders:before { + content: "\f1de"; +} +.fa-share-alt:before { + content: "\f1e0"; +} +.fa-share-alt-square:before { + content: "\f1e1"; +} +.fa-bomb:before { + content: "\f1e2"; +} +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: "\f1e3"; +} +.fa-tty:before { + content: "\f1e4"; +} +.fa-binoculars:before { + content: "\f1e5"; +} +.fa-plug:before { + content: "\f1e6"; +} +.fa-slideshare:before { + content: "\f1e7"; +} +.fa-twitch:before { + content: "\f1e8"; +} +.fa-yelp:before { + content: "\f1e9"; +} +.fa-newspaper-o:before { + content: "\f1ea"; +} +.fa-wifi:before { + content: "\f1eb"; +} +.fa-calculator:before { + content: "\f1ec"; +} +.fa-paypal:before { + content: "\f1ed"; +} +.fa-google-wallet:before { + content: "\f1ee"; +} +.fa-cc-visa:before { + content: "\f1f0"; +} +.fa-cc-mastercard:before { + content: "\f1f1"; +} +.fa-cc-discover:before { + content: "\f1f2"; +} +.fa-cc-amex:before { + content: "\f1f3"; +} +.fa-cc-paypal:before { + content: "\f1f4"; +} +.fa-cc-stripe:before { + content: "\f1f5"; +} +.fa-bell-slash:before { + content: "\f1f6"; +} +.fa-bell-slash-o:before { + content: "\f1f7"; +} +.fa-trash:before { + content: "\f1f8"; +} +.fa-copyright:before { + content: "\f1f9"; +} +.fa-at:before { + content: "\f1fa"; +} +.fa-eyedropper:before { + content: "\f1fb"; +} +.fa-paint-brush:before { + content: "\f1fc"; +} +.fa-birthday-cake:before { + content: "\f1fd"; +} +.fa-area-chart:before { + content: "\f1fe"; +} +.fa-pie-chart:before { + content: "\f200"; +} +.fa-line-chart:before { + content: "\f201"; +} +.fa-lastfm:before { + content: "\f202"; +} +.fa-lastfm-square:before { + content: "\f203"; +} +.fa-toggle-off:before { + content: "\f204"; +} +.fa-toggle-on:before { + content: "\f205"; +} +.fa-bicycle:before { + content: "\f206"; +} +.fa-bus:before { + content: "\f207"; +} +.fa-ioxhost:before { + content: "\f208"; +} +.fa-angellist:before { + content: "\f209"; +} +.fa-cc:before { + content: "\f20a"; +} +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: "\f20b"; +} +.fa-meanpath:before { + content: "\f20c"; +} diff --git a/api/css/font-awesome.min.css b/api/css/font-awesome.min.css new file mode 100644 index 000000000..ec53d4d6d --- /dev/null +++ b/api/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.2.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"} \ No newline at end of file diff --git a/api/css/local.css b/api/css/local.css new file mode 100644 index 000000000..f1a326226 --- /dev/null +++ b/api/css/local.css @@ -0,0 +1,298 @@ +body { + padding-top: 70px; +} +table.nostretch { + width=100% +} +.nostretch td { + class='block' +} +.nostretch tr td{ + width:1%; + white-space:nowrap; +} + +html { + scroll-padding-top: 70px; +} + +ol.hierarchy { + min-height: 40px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.smallcaps { + font-variant: small-caps; +} +.well .sidebar { + padding: 8px 0 +} +.sidebar a { + padding: 0px,0px,0px,0px +} +.varlist>tbody>tr>td { + padding-left: 3px; + padding-right: 3px; +} +.varlist>tbody>tr>td:first-child, .varlist>thead>tr>td:first-child { + padding-left: 8px; +} +.varlist>tbody>td>td:last-child, .varlist>thead>tr>td:last-child { + padding-right: 8px; +} + +.highlight pre { + overflow-x: auto; + overflow-wrap: normal; + white-space: pre +} + +/* .hl is for when line numbers are included, .highlight is for all + other cases. */ +.hl pre { + counter-reset: line-numbering; + overflow-x: auto; + overflow-wrap: normal; + white-space: pre; + padding: 0; + padding-right: 9.5px; + overflow-y: hidden; + padding-bottom: 9.5px; +} + +.hl pre a::before { + content: counter(line-numbering); + counter-increment: line-numbering; + padding-right: 0.7em; /* space after numbers */ + margin-top: 4.5em; + width: 60px; + text-align: right; + opacity: 0.7; + display: inline-block; + color: #aaa; + background: #eee; + margin-right: 10px; + border-right: 1px solid #ccc; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.hl pre a:first-of-type::before { + padding-top: 9.5px; +} + +.hl pre a:last-of-type::before { + padding-bottom: 9.5px; +} + +.hl pre a:only-of-type::before { + padding: 9.5px; +} + +.hl pre a { + display: inline-block; + height: 4.5em; + margin: -4.5em 0 0; +} +.codesum h3 { + margin-top: 2px; + margin-bottom: 2px; +} + +h1.inline, h2.inline, h3.inline { + display: inline; +} + +.depwarn { + float: right; +} + +.anchor { + position: absolute; + margin: -4.5em; + visibility:hidden; +} + +.alert { + margin-left: 5px; + margin-right: 5px; + margin-top: 5px; +} + +.alert-title { + margin-top: 0; + color: inherit; +} + +div.toc { + font-size: 14.73px; + padding-left: 0px; + padding-right: 0px; +} + +div.toc a { + padding-left: 20px; + padding-right: 20px; + margin-right: 15px; + padding-top: 5px; + padding-bottom: 5px; +} + +div.toc li { + font-size: 0.95em; + padding-left: 15px; +} + +div.toc li.title { + font-size: 1em; +} + +div.toc hr { + margin-top: 12px; + margin-bottom: 10px; +} + +.in-well { + padding: 0px 0px; + margin-bottom: 0px; + float:right; +} + +table tr.submod>td { + border-top: none; + font-size: 13.5px; +} + +.graph-help { + font-size: 10px; +} + +.depgraph { + width: 100%; + max-width: 1140px; +} + +#sidebar a { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.highlighttable { + width: auto; + table-layout: fixed; +} + +ul.checklist { + list-style-type: none; +} + +ul.checklist input[type="checkbox"] { + margin-left: -20.8px; + margin-right: 4.55px; +} + +.gitter-chat-embed { + z-index: 100000; +} + +table.graph { + text-align: center; +} + + +.graph td.root { + border:2px solid black; + padding:10px; +} + +.graph td.triangle-right:after { + content: ""; + display: block; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + border-left: 7px solid black; +} + +.graph td.triangle-left:after { + content: ""; + display: block; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + border-right: 7px solid black; +} + +.graph td.node { + color: white; + padding:10px; + border-style: solid; + border-width: 3px 0px 3px 0px; + border-color: white; +} + +.graph td.node a{ + color: white; +} + +.graph td.dashedText, +.graph td.solidText { + padding: 0px 10px 0px 10px; + min-width: 40px; + color: black; + border-color: black; +} + +.graph td.dashedText { + border-bottom-style: dashed; +} + +.graph td.solidText { + border-bottom-style: solid; +} + +.graph td.dashedBottom, +.graph td.dashedTop, +.graph td.solidTop, +.graph td.solidBottom { + min-width: 40px; + color: transparent; + border-color: black; +} + +.graph td.dashedBottom { + border-bottom-style: dashed; +} + +.graph td.dashedTop { + border-top-style: dashed; +} + +.graph td.solidBottom { + border-bottom-style: solid; +} + +.graph td.solidTop { + border-top-style: solid; +} + +/* Ensure tables in Pages don't collapse horizontally */ +td, th { + padding-right: 10px; +} + +.nav>li>a { + padding-left: 10px; + padding-right: 10px; +} + +.nav > .nav { + margin-left: 16px; +} diff --git a/api/css/pygments.css b/api/css/pygments.css new file mode 100644 index 000000000..c4b2fd9c9 --- /dev/null +++ b/api/css/pygments.css @@ -0,0 +1,75 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.codehilite .hll { background-color: #ffffcc } +.codehilite { background: #f8f8f8; } +.codehilite .c { color: #3D7B7B; font-style: italic } /* Comment */ +.codehilite .err { border: 1px solid #FF0000 } /* Error */ +.codehilite .k { color: #008000; font-weight: bold } /* Keyword */ +.codehilite .o { color: #666666 } /* Operator */ +.codehilite .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.codehilite .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.codehilite .cp { color: #9C6500 } /* Comment.Preproc */ +.codehilite .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.codehilite .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.codehilite .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.codehilite .gd { color: #A00000 } /* Generic.Deleted */ +.codehilite .ge { font-style: italic } /* Generic.Emph */ +.codehilite .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.codehilite .gr { color: #E40000 } /* Generic.Error */ +.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.codehilite .gi { color: #008400 } /* Generic.Inserted */ +.codehilite .go { color: #717171 } /* Generic.Output */ +.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.codehilite .gs { font-weight: bold } /* Generic.Strong */ +.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.codehilite .gt { color: #0044DD } /* Generic.Traceback */ +.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.codehilite .kp { color: #008000 } /* Keyword.Pseudo */ +.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.codehilite .kt { color: #B00040 } /* Keyword.Type */ +.codehilite .m { color: #666666 } /* Literal.Number */ +.codehilite .s { color: #BA2121 } /* Literal.String */ +.codehilite .na { color: #687822 } /* Name.Attribute */ +.codehilite .nb { color: #008000 } /* Name.Builtin */ +.codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.codehilite .no { color: #880000 } /* Name.Constant */ +.codehilite .nd { color: #AA22FF } /* Name.Decorator */ +.codehilite .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.codehilite .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.codehilite .nf { color: #0000FF } /* Name.Function */ +.codehilite .nl { color: #767600 } /* Name.Label */ +.codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.codehilite .nv { color: #19177C } /* Name.Variable */ +.codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.codehilite .w { color: #bbbbbb } /* Text.Whitespace */ +.codehilite .mb { color: #666666 } /* Literal.Number.Bin */ +.codehilite .mf { color: #666666 } /* Literal.Number.Float */ +.codehilite .mh { color: #666666 } /* Literal.Number.Hex */ +.codehilite .mi { color: #666666 } /* Literal.Number.Integer */ +.codehilite .mo { color: #666666 } /* Literal.Number.Oct */ +.codehilite .sa { color: #BA2121 } /* Literal.String.Affix */ +.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */ +.codehilite .sc { color: #BA2121 } /* Literal.String.Char */ +.codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */ +.codehilite .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.codehilite .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.codehilite .sx { color: #008000 } /* Literal.String.Other */ +.codehilite .sr { color: #A45A77 } /* Literal.String.Regex */ +.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */ +.codehilite .ss { color: #19177C } /* Literal.String.Symbol */ +.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.codehilite .fm { color: #0000FF } /* Name.Function.Magic */ +.codehilite .vc { color: #19177C } /* Name.Variable.Class */ +.codehilite .vg { color: #19177C } /* Name.Variable.Global */ +.codehilite .vi { color: #19177C } /* Name.Variable.Instance */ +.codehilite .vm { color: #19177C } /* Name.Variable.Magic */ +.codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */ diff --git a/api/favicon.png b/api/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6ad16ba65b1beac980db083f951c124ed54d7e GIT binary patch literal 803 zcmV+;1Kj+HP)I0lE3INd7Tp1k&#_l#EBR!4jk3OBm zxMLco>QdAh_c3@V6e52KPaY{|1qXm*#x4MWPFu*rEuHtt5|D^RXgaT9VV5r3LDK;M zfYa$=jkVd>1O)%<12fY$4wjK`!h(}x`nS3aSfm5c89K@ShO6a?9L&E+vw{PFR&V|5 z8eu7rnj*vGxH)X1shdudF`~eaAZBJ=OJ^?;L7v1_V0O-h-!HmMw(_5AMtldFFI3<} zWpP-$CH58wg#zB-*6X9Od5k1t{*!CJ_8>L{8fy0Q;_mO>*bq2#yo7^Y?Rkh`Fcf_P zQVGBOS2{*wlaNTIVqVi@-El^|$6cuD*YN8ddo|)0na+JXnq+%%Z(phu2?x-hQ7bLG) zh5l|MVScb_cOq zf@4JY`s|;gfx+kS h`e=}Df_RtbKLOh6;d+20-|GMX002ovPDHLkV1f_|W~%@I literal 0 HcmV?d00001 diff --git a/api/fonts/FontAwesome.otf b/api/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..81c9ad949b47f64afeca5642ee2494b6e3147f44 GIT binary patch literal 85908 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 uQr82 literal 0 HcmV?d00001 diff --git a/api/fonts/fontawesome-webfont.eot b/api/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..84677bc0c5f37f1fac9d87548c4554b5c91717cf GIT binary patch literal 56006 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�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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/fonts/fontawesome-webfont.ttf b/api/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..96a3639cdde5e8ab459c6380e3b9524ee81641dc GIT binary patch literal 112160 zcmd4434B%6xi`Gm+S8fmAvrlo&PmRY0RtpCNq`UzVTOQAPJkFt6hRae1aUelRlymQ zQd>1@rP6DAZLNJ>jTzMP+(K$0`&E{uGiX<@$^0Bj* zjc>h+@9aaq0r~!mH?7(H>b_@IA%CYN@h@Js=9BfD_WmjBx>B6P4J;=|L z*gaogzi!PXmP@^_OKdN0OC9TR!Og9|M7|68#QIHJcSI9`oyen3edvm-E?&cKe&o2s z9zGv+@J(xWZ06_ksKg${eJOV3noaBa>b7N(zd@4ZuFY3nvvrH}S6d|Z_?ILpuy*^p zwU<8k`DH^A`*H=!Yxt+$N|`HdFIzhD?}cbPXDv{x~s2|vQq5-paCaQM3Y!OPNF5nCt@Opaig)5 z&_BA)o4HFf>Tp`)&&HAj1n zE;_pU=#@urI(qNXM~{B~=ogP3Ir^)k?;bUdxsKHwYdO|)Y|*jR$F4kf)3JMxJ$mf( z$6h>kj(U#9k7kc9KH7hD^U>VV`;QJBefDVn z=qpDDj~+cH9rGNE9h-10du;Ks{$rbu<&NEdY~a|l$MVNsIW~Cg=z9{q;pA^lUUKrn zlNX#^esadi)Z$TndMZ3&PskJW1U!C^&*Swd9@)b^ z%p1J>)*&KJNa&{Wtet-S4~qkNYp~KfB*^A9Ejd(476h{=)!ErPnZm4*DWq8ivN!G>WO*aInGbAM zW5+jZ(sA*Q(y)olL>k5mPfFU8YEG&~CZIEKyfqZi>f?2(_Kvo=m!&f8J*+L>TEny_ zn+tccY$TP64CUy^vV}XF6AfWC7j8(Xv+HrYAf?(<_>(2Rqq#m@WwBI=slq!XyrUTz zZ@|UtT6lX8Z)**E)zR7Zj!xFm)*8~Jnd>iGaoPHrIGuI*d4|O7qHh3RB82$ls}LvjK^85rm)(IkZ8S;^@3biqStqSL@OYheV2dd>x6H z67mHx3?U_Fd|=#be86;ewXFBGcO;BM&%JSQ(-7IY6 z+WS)M+#5zpTy@wuao-!y8HbVrBv0maAQ34dO_df(QqrsGitggg7!a0DB~xi{AcV2* z@OJYS8FQco1L07(Mw!A}d*sfJ&K}n3H76(IrRl*yM-Y+`j!K}loSkUi;_VLTWff@N5+KGn92{g`wI8l>ifFK8-qQ!T(vlnSbWtjJ%h$u zg$HszzQU5Y=#qP9yz#f@dD%oFJFod~Z~Vtwg{RHBKZm&+l z2~0ba{*KnLU&WY2jEBx;!GJ$#Of#loLWBHV$N@+k< z5klH~R2u(QT4*(@Ix~bOQWgol!W6OH2Q`gPzhy`^c z|EBTHH{WDEx9zy=t{s_m+b+3iMniL^8Gj8kF1lpfI{EkJ{Wm4aPHRf1_qy@s@zONu zZ0REDD(PnFKIt*(UnNP+w5OU`omR~Pp(zYt{SkTQZBGfPFD?T%ru-@Sk0}39?;E?A zSS}S2nC%P)MM^~q5}`gB$06iO1=X@A4Wvg(eN>%Th98K9q+uatOZBDL!>3CYA{;MH zMGQJBBSlV(B<1oV#>n;4SNOtl@orTtVzChk99f!A!q#FhD50B5LYUYaO8JkvFH3#x zhSc8I*UrUpBrWI8bcaiXM*G?s9r+K+GDGE=QFkPZ!~`n%*(_ zvG@O{^JCw~rLG1e-_X_7z_N54N%LHJt}rS$`rhc=hm|a^k;TMo>A-$IoGgqa<&k9B z)w1O23zSu6Qu^3t$KZwk@mcu$M^(jm4~dbM(dQGRMt}6Z@^b&=SdAJAiAmQcP4N+)S%WTX7hVsynTt>kkEVD^q=mBAHyLZ;cOFw6P>;Di1AzFe;dC&vh(r1&6n54+)ZmYF4=SVmBV|MY+T#q zj@52x+WUAR*SEe8e?0doD!KCri+<|Mtanq))!cM>Z2oK4tw(V@wf?%-=Ep8?YIemo z887nr1%byo9f_6#;VbCha(Y2Z3YaNDN^2;I)`4aaI}8EM*gUnq{QfC<$>++ueB!`z z|5&=e^q}u*LnK)iHN965X-;W&^$?w0GF@Wt9TypuGDTVu^8vi4OIIS_o~qLVp;lTD zSf4s(B!C&I#~Rgi{8BHlT+=!&gjAX+SkU*l)WQhZfFL?cSKELkIza!6WmL;T;ZBg& z;0%bYb}>Cv3wA`2_P@G+|Eqkz$MIEvpnk5+T6KTO;o389yvM0m|H>6)(TR=s*xWAr zO=;cYp6jb}{V%7-V}HR_*)YRqjXV%?I!712*XnjUZb^v35jP6+5WQhP+w?0(h(|k; zt>-%;w&cCmE5hzOTccj*S3JRuR{PZ*HmAcLTv^#Vv5E(sqHIgcq$LiA&6&8*wz0gh zZF`%=Wfq z)lU$@GPB)_Xn$Yip3O2YpByU#Bi9+yg&O%wLw$gGZ&I1R&C0p;Av9#DZ`pO*mdRfc zP5Vr;y*>FE0ypp`5e(R+sx0}%`WIb8$BXn?#>zsS05m`sc7`;;8gbVEr6N8Kdc)vi zL9H6Olc2dGDaNPqY3x6HEKb>JDfAWk91f?Y$HHy=hq3cxe-Vr6mp0C0Mht~>MCh_X zrZD!pk>b$Irc3;ZE$!# zOwuf@d*i7zOF<4nI3Vs-zaDMqYB(-v6*9Ujm|Xgtah+Tj^jQBJ3Si^f)9GPxi$mXf5w>*Rl@62z<7wIC3#v{%*8x4EY=}; zIIt;%0+0#FKqMwc7!;Gh2KF8|etvxK-s7y{IJ^3Y@tCpNcOR4sQ00&GoruIj7O#am5JJ~A@UB=hEwMN$0;WM(eUT+hV0GZ&CnACJo$fHcD z6pM{e+IMz!-Py&xjnzih?`Qey#x%?o zcK8&~IZa!E7cscz7HLXHh|*+dZtLo@7TVY}G@E7JKmO3BJ{T|tsDZ5C=W;mMG^^Ff zd)Nmb(p1PO2)P5sonqz3A@GvpGB&SxI8J-KiIgGAF|l#jACgb9ZYHx=3*E2c#JVqH zS>B(D90#JReAkwV$k|B7_HHH5$~KuDH9XwG^G_HxG>PojJyUr@WnEom;pbD!#>g#I zk%WZkaIxuvjqU8f*qmY6D+95@pxf*5#A5MU9{bQm&!3v_GxAo8Kgn}Rzt3;vzyD#Y zo(k=SXMg#!hJh07*#tIBtTG-%k(3N32XDaha zanbhHkotR;HP##N?lt~<<1KzH&j_tN|L!?oT66m!X4{(pj!u6i^$%Ckz2e31IQ`Sv z!_2>z1vcJ_$Jn6CjlUSrU3uv(ezS^HyMK4@+*_~qUJ~}petH~N_Utwjtoqr*Q*T^#*Sx%O)a!|)YJ-#C{_4gTZc4Rw+4p z9hr6x3WEm&wX~fNlV&CgpGrIeN3V*i2`$$h_-bhP`6E>7oNMc5RzC}I@fVGsJzG7q z?%Fvc_s-uP`f8y2_CeOp`dItm?R?L{2PejtZHy7_7W|AWHmBQh(b@-@_Nh-9#~)mK zk)wN#xN8!qv5m{(6CXVIaaQs2&YdqCe=z$MlO<&kG@QU&*shE8W?LK^O-ROG?Khq? zjte}jv4vQw%D@R);cOw+X%4&cLURogyu_58sOzlL*9Iv8O(X`OM{aMCF*?NeobDYg zcg}2^JCdrXtE-^@RK#tYeVP{=z5};K)nrw$I#}5q>8fN5H<)mswR@7Z&Gq6JBD^Cy4*D0CV}jKUN(6-fuG-5pPU<;f0r zbs!DspYmm+-MD!r?j*vBQ>l!sWFFSaJS!uW$c7UrvQl!;APPMM=^^c){rr%jR6#dT z5A8skSgXPMj357T{4;PW^h;-k1S?(#@0O|e)_dc@whUdTUzWp zsgP50xR66eoC~=ER$W0{k|kWr4Ka2z6VEVQFXVX65Z6i0jHft?$P!(qf9isV4nlr; zYCqDDbeVmb0)2y0-Qa{PpzQR9ibu{5>*l8vbq)f2*fWJG^=| z6`M9q%^kl*z4@Q|CtPIi=?|%YLRu${@34%bND+a9C~ZR^i&!4Walr=V+N2Row`Y=t zOezDp{6Hp`;@?jycDlL1$Yzp8AerPpNaiwZpuI1XDs&K$B@xf{kiN0_E=Z_8{B5e) z25^7CiBKT2dcxNq)e4pqjZ3uDu-B5*!dzzX?`R)-gGNVd@ep3dzn99G&6Xt__{8hb z=H=2Q(pF#q@Fc+9z;WqRC)Cp&sm>lwf*MMYL~V2ex3sVh_NBG-oUUQd0s98lI~`Jq zb!#QrP6|~PS-G;jc3DHnc*lRu^r3YN?~7K1G=@EqJAztxoJCf-9F>Dj3ey!Oq4>uu z%)+@Vq*=U9e;}TQ)Y!>Cn7=q=yqlPF;m{|m>~>ql4*8SS9TqlD=cyC#C=M6zcUCGv zBnksatUu+7Qa5St(6!m~HZGdct+co-Rhm6eWlL>L*%~bNIxVre&f20n>($7%l%?Kk z2}CT8WISCNVw!B-Jb&og?X%pTs@b&>`In)3cMa{Af?6<$S}>CsQozN>RbUFz6|+_d zAxH`!#9$CqKwM!0A@*zK?r<=kPRIR~6Y7mQ#+<}>GarP_fz{bncl@t)T~14kJ#CyH zr@U%KUZ{cym*>R(D+4bDq;3dFO=KeEKJgMLk_u3WtWAoIwi>ZL7r9TOzXhkqfPIGW zKLC+KPRW^!C_05@ZzMjMXZ&ao)bKC9P(UAA~OsaVKC^<(MD>X*|K4Am1N4%J@UMF4;^~< zkUU5v)A1Y~2iyGXGF-~6^S2c)8w}00>CTKwoicw(jW3+=Eyt&2aq8Zb=PP zO^w_}QcAk1)oc8xpN;=;l0S9c(D!(_cS2jr@eZq4kg>=w$M-h6&#ex){d?RRn`UJD zj6bH8+gR8Vv^v$ErOfDwtcy-b^~sD+{;$cFq`X-Ekvo$zUCY<=S6#Xh zTV#CVqPqW>e3rvqt)={mPw}`|bA43B{%mttJdb}<=97(gDnqqCaBFF+FJN(*xC$5& zFc}1fUjr?As4eDgPq%>g($TqqR>NdLJEChKEA@crb3kB#9;KUQJSaP!btHhapyrT+ z0hg=;cyIzxVPtso{9d-Bv1(TDMe`=li!#nETGNcBJJ+^NzGQ1}>tYKl{Fb}#PUv<` zg#ag!X=ziHwd}XIg;$1Vf9!@;UGcM)_hcS^dG@x)o?bQX*>M|;E8Q`6_SL=Py5nBO zmU*?^vVH!A{53r?ZR_&cmrsd0Tff&zQh{-uX5dF;|zQ7t6aXHKE@IZ2X&0>yQ9L|8i0!qc6^ngZ#OZb3&6 zHI5@mq%|G$i;mJfd$o@zqE5DR1FM+2$nTGT{>I4@*4-0TT{ZV5Ee_4ftFH6%5X1+} z`?Tz|H`}YXM)%BY`^rt{@U*YKSLf~AUSH|7tMX;ss;X9=ZnY)d{_*k2&Ib!`F1M~- zdXC$tRE_JD100f26IPF-y;ahUn7P&vsl!Oz326=5M5;D4kpv?ERWPeGML^I!5OyL( z;Hl{#$9TF$ralnc8VPry(LJI`s-{EcNB%vo5r|!an2akKTSK_|FO@Yby z_r(`4F3)`MqYlS+FlUMT5-h3J*n=)hlM+z4ny#*_mOW0UIsAGx_g>t(C}w4fs@fW! zPN;HSpYhx2m_^xp!4(yLjd4Y`e>}b;;ID~Cnq0YL!MlAVwE{#in640b>T~od#;)r4>o%mY%VwB0bd)lR>dN&CU(v`_Taj0 zyeb?GD2@u3bNgjH;$vWnX^dr|+gKw#1OaYw91}`7G-ePp*eHvG2uU-9@Mj#y9^MZ6 zmuP!z_T?kV$ZUv|C0IHw80btq5DH)u21A#IdXo%_YG8;EjJK!o>=JWqXG8cZZI6e` z2i9fts#9xjT6{&5m0`i1c3gF<42vF&m}38U<6k`H*s3*-?#`?di7465ZimyY%0rT@ zLLD;ZszO)Qn=$4ba`0H$kT0CgoEqnfx}@_!d*@3}%su^(d$#`T9nZ*mwMCylcS(op zsIoh@uNPx}{A7AuhaBt*${pjLT;At-k-ertDLul5_UCk7&kCjt=R9=US z=>xE9sR#_JQY7p@AyH1nkp!&AMNY#}+{@8D1;@Nd(Scq15y}6L+HIOE%4m#ew`i1# zqp;KwIgaE1bi2peCwx?X^mvz#cKKN2x@hq~Jko#HSbtO-$KD^?<`H-)hn@2DKQzi8 zDyJK(Ii|Le*xR%@Xbp|cpAO#3%a6T3wy$IJOoHNr$l5a;G~7Qf?x|U)|9DyH(Ra#A zm8S=X>t)xRE;;n);j79>fwHToe@y7%$KZ;yLE#aRNxB!Pm1u+fM@Qq7(aHIpE~_yJ zg+|N@!I_Hu2N(yxQxnZTA&!c;Ql1_uBM*`p1w9_6ga0FYR@Pq$iiT7BSd{w;H8h`>BIMD(FHJ)kFVi7x|GW)nJ;6AZ1v^sL-LTGpA2t%8GrIAYq~T6C6~jPbD_K zn$dKIL%NiP+{kBaI<&oz-G1oMcAnpUi0$)LIh<({5H)#KKihY(bm!3ar`TS<3N3&s z7Xxns`bvkdN{!TlYl1iFXa!4^VHim8vfxq#Z;KbF!etx_QCd8=d0_MA0cG>?9Lo-H zP!k`Bj%r!-bYHmzq~f81n+q^q&x@ig=69Z;Von8*#7>Z5(9@GM}v(LOI^unfF9SyF`9#+83snd8@nYI*z{DwX;pBprhO6!fwV zdDkc@hYR=!Yf1>cWz#@|?T;G|dZx{t<~H`l**Nwz8z&d-Dx^)bhmOZnskp4o-t;OP zXS{0GU9>5I#5L)y6YA+v%4z9A(k{ynj!{GRD_K(^$B&(=H$+HSC?p8F1Rvk zZEbI}M6bMHi?)R25^>fX?+kl9;m&w7izgs8fBsbi{d)C*Tdhyt^@|H@;5T#OFYbEM zdb7D+wZ8$zG{D#-sYjZNR++OYr7)MFPUZ)KFY&>EDzbk8VGhEv4ElilLGFiSG37cY zoaQ?q@7Q`^Yd@D_UgHUG%*$3UIkbHU@PBB#oSoJIV-CkemoFS5KY4jGS2g1IFQNwx1=3EsDox z3r%XO*Ms#_7G1UH`3(a=84*9r`FXujDD~6ttWqO&N~xEx`EAY$kHyN~Fmk{bP5Ik) z8_$OA-07;jtbbS6#O3{qmrb9X4haNhxraC(1pZFsYe_^s!8L@{~tm-v>N91@m z;_&mAthT}m!8r)ZwXni&G3ysHc6e2cuKx_L5rsNBwc)p&`cD3mKXS^OC!e7SDC~$7 zCX2T0EXoSuq;*PLXmUh9wPj{M;m(EL`q3|cM750Rr};L_#z^&|uQ#YStGmc!0uoL^ ze~2}@{`f25cs#652=g_C8fPG)<|6?oQVD`7v9Ac+PquKh!OJ)<`-NdmhP46Mt1t!9Jbf5YbvNRYeKdPRQXEi*Fu?r7(Ee!c7^$>^~ zz18%yXz2J$G;|mk8a@miK?pkRK-OaCFNp+34mTYU{*ui)Tz?5pPN|<>L#kAgkeU`R z+G*ctf#OQ^90%2M=C`962Wgnh4)cRHYk6bDIF;7K=(db)#BhJh-#fa$V_t;LlGm%G z!D|a}0)?dCL<(ZgSyB8;#1wVbg;6ZR7_Bk&rI9I0@v}-p94Y(`8dr&WbP`8%JRd&! zuyRoS9VjNr%0s5*xJmVkty0-nc!&G_{)03V5kUFxkT~d9eo}a+@Qz5DmvEiRn02l| zotGBtG(~S^M(6+oWf`iXYW&=fT14fjfbXL>(3?1Z%>qM|!C=`jgc8r@NHSm!)97bd^BB^pd`)7G z%yyMpb7~vP{D4mTRueoJhLx(~TZwr$*8dvEl`yH^KyBo;zM(NKlIx;AG~KxT*XWHe3Pxr>fT`9ue@q)l z=UBpJlcm|9m;pHiG$kK22B|HW0}W&$T4Nf8U{8iPyHo=EFSHzqvR0D$XI_{%l2!0k z2haO+&K=&RJ3Q7*ysmx1f`$pxE*B-5TG&jJ!Dc&&ZO`90lYl||tKU@~ifl4yvI?z1~m&J3aL;2h$TDqHJk6$5{(-n`$ z#$I68q$2kv|Ma-H|M;Jh_t67mE^re=oaX7_>ex6SiZeW3tdH>F$b1p*nt~A!PCw#6 zjz5rLn<|MScjCs%4RoBz265hATg0||Hx7GkbjE2^{^c^O%TtU>*>_L>&~PP{A7-RD zsxL*mX>u|mV%F?|saXk}(SUNFv4WQO>wf>GIKvJR$4mV?Kdj08CwK-9y`rRegq|fs z>kl!Z9v<_L!4uFY{DfgbfEC`uRbf*JpaNbr{bP!L-fHZ;f@}A{Ro~rv?ocKF^Bqrt zjaFkYbNUVZVSYmfPe2J>tomhs+vB$v+!vg;_xoSx@2%WB^xzXvP`+gRS~$Ygu*s~N zQkZ7grDZ@zEs$c!0D9}=*!zI{gj|j6wL66P0aOvTaZQ@uUdXa!Dz$)25DMF1LU9-A zLl&e`#xHrkeL5^tG7F5?6IUeqaPMwmsIVuMnxEQ$0%TSOT$fSv#rF}dMZP7(O@LaU z)dGtwF;RjeRP)Kgwsd=28uhbeA=^HEdOOb>zr_1f?U@w6E6KARD3VMrzzbM%K?ZMU zDZCvI6t>mV`!c|-3)C!m(33nxbZnUPGB^HWH-YT61*nPqv|blgiH@Kueph{G2fCW% znGb0TwUyQqz4LjzGgtEcE)6E&kGeHX02apR%IJTiV`f<*A5RPmZI@nkmPyX z+e+g}GM)v=r13h&8t$f;ixm2fx6-)gKy&8FPoT)lWq@E^@E{2by)W4)@H8B)I(_jr zG{NN83}VOz*M9O7Th{i}tE$)Sap(@Wd~@ar{@p=vWn6*>ydR~A9C6fkoU?6UUFS@# z-s%o`tr6^$)d#lX?sePEoqCFY`uUL=6z&gA_ zh5-m8rovvs=b<=7q+ZSBHokuC-UH{f%An6h7-fhR5jCW=PYPQr-5_|tHbS0cEDu`K7OkDy_Tv- zHgZ{u@xFj`xDvNNVZ1E7t=m3q^i67wJ zEc^>X;FjkTmE?t;A@mX-Rk0y++Z`~AW#!T{`cQrIeZv18gdlm#$SHlTRY`>tUzH;Ghw_Uh#YA!c* zBc<3^T)r=Lu~+kXV_a8dRh7K%@!GD%UHGeg9JPX?>Ng<`<`7wz@3t3iTlmyd3vu!h z|6kN$1QA(*-f=cFU3jUxp z=kTP7JY&4^o1Iwn6~U_2f!$31a)hS>EykaI`P$%vd)#}&p7G5+)iq54FSp2Y&-|V! zx1RU$7dLf&>A5dHl(wY{x(7p)yMzPag&@#_3+ zUp5q}R$Q7>uV2_P*{{sBwPmjP@nhQ)KDTU5Cv9nO*t%-hRw3iSx`Eux4GU3;eDr8K z%-suGsDMDa>97!Rs=(mkbd5r~q!G>9NonHQ{rzW8oT0E4ckf=&Y36!mGdCb~2Xs*U zi*{YOZ0_8ZZT&gM8kcXq<(ajmE30oUUZEie{YK-iUvE8=^bU4aipn z?l#he_l)%2fxzAD7qAci#oavn_O|uceU*aFeD%8Z+unZp&wu8V8lunL7>Gs#=k7Fq zJhT3H#-CW|t@@euZ?TZ^$G1psesTb99R%G|2~VpT(m8j!$!w9ww+08r@3*1 z)Ic$_#So?ww3CeA4_*l7M<_>rCjc=xp>~4M=FN-FTZ_JYhVLHf1-pY?Zmilc(dKjP z^o+aj*!h9LC)i8OdBMsKn@^1-YT~jd`RJ{z!ou=_^z8k{wqMPEm0f<_HJ_Pw(Z5dm z?mg4;8>yd$!LJjlT*3p}$??Skn)-(A~R`zPk{uJJhFSHo?_guC8qW$&N0 zYj$0B$ulqR^1b`@=dRhD{UTTmnmZ5h=}`esae^r9`X7OlWSDpkTX+J;f}@Z|l)Au5 zPWu~nXAvtoWvM>toln@|y=5)%>9?wmi zR$W(DO{TlGi3IRHe$*?}D%%(UWP*VwoMl&Ome{u%Gl+-df^NVy?#gbS1 z$7TB-A5gtH-J!^C&G;{)kWroeRu^|$4-eTnvmveVZ!+0XTr#)kTps?3fxf)j-=6P# zyfD}A>era;WJ5;bn_gGHmD`67>mH|Ljg@8KWfiu-BRJ<&9~|RprRv~A!eWST7h`$zjH^7xVx+A!25}tvoG5~Z#!zDT^1>4mRjuOKPdb@?^Vlbu z`zzM7ItVVN6Lz5ze8pQ7?4d>WmoN>{-N-@{*rKI7I%||R8X2O7eZx27*b1V zA0^W@m?saH<_~u-4Ar!?Ef_aQJJ;ZGRf8WN>9b=Sx>mIJwf448u9{LTLf+6NS3fFp zQkt-+yQw19Qr$RX>UkILm}%BA=3?n7rFPZxXLZhPtQKODAs5u%d8obfjLEtyT-P!+ zec_kHeQbzuos_qi3e1uvlb@M{&z8ZpnnZTIM!fz_k6hzVpnwe=+9`D@Dyg^3^81 zc!L2!6_s`}NIGg{MDZ%+KU$jqZR2rcuJQP{L7qeGFur?fOH<3z?(t@pf)A0)wwa^A zL?bz#&wbZ;@%iUj?{`HBKy50dC?R5m@C3hfq-gnLG;kQl6;e<;sKiJGIJ1GB2$ehdM2gBMsjRe7_yqPK= zmIm{mqYkPo<45hLU>dcfPLnpuDLH8U!3vu(uUh18giauhn&3jQAjn9UbZR8prifia zb|KIR{L8^B)4D-yJ2?tgpLBI9F#k~2V%HU(kEGlzi+Ex1hD}BCJnOLz=sf2(@-Xp) zV=t~1@^sDbl=G!0u*MY|>|X`c135(7b2;Q@aquIERgetRFRZ- z>eUrC&jd1MkGR@qDsm^1PG4;(si$b|f%eV;_5m|v;TkGVic+_0)rst?UAtB>9QnYi zUGhLd@L3Cg>3Py;oi2C*OYK>=` zKiPXCUze$6i;+^Ybs6K(P=581sm8ymtoY&>UOue&+f*VO&+*tuCY~9 zyh>SPNR}h%j%MxH{V6?0D6xDbVq550js8*LFk1~Tj7Y-x9s&G^^1+ey8u)ta~26> zOnbT$6mF2_4E8bfAB4i%Od-c}7y(?|Su?U!PsQa(w2JdDS6jB)Dj_PCW~dj{aN}$%Mc5$t3u@A#?fLK5{8!h^UH!}N{Pf^pVNlo+pcw<(5ApuN z`#L7GA6g%O;NW0k00t+xerP+!9`6x)O^P#AgBgnAkJW{$xx^-X$M!QAJs-IL3m5D%zy6!Se- z+lToMl8-oAFJ_whU@}KExfC>xY`1mcD1r$W6bzhN$yowOjCGb=J8Kj<3-d33W7A?X z1EaJ2t+ifjx~^I7e{0M%+$vthhHMSu*Vbw z`~ZmoL;oY;eMD_$a38z_HB$W;$y6GMf!-rx27x;OO##Y|Ha&{<7zzVVz{L!vGANH$ zK?L&8KP=}26v_J${s~)xc{Fk^>nH8Ox-MN0Z};16*CZS44n6#W-N(Xpjo0c_D&A;o)RY}co7ef!KU%&R!sw(RzyZLpn*t?{gmM2@ZGKi!-#B50&F0W+w(BeW zjw{AjxNV=X1uxJoAFHz3T#G{EQWeZ=A1-RQIxIEU>MMM%D_TYs_4I`%)P=dXFnG7e zT~)cIQjzDZ4ssq`Jx5lMt#W&CqdH7C;QxIgZp~@rv*}*A+ASabXPzSX75G=s!AT)A z@=)-IG=U?*4csNbMJhr(K(TJIF!dTGT%!@(lEZRZtB=u&O#oJbkSRRS*Nw0J+qo-l zcsS82+x>7Mk+~|vNFm{=4%%+G_v>sHyNS)>-S^&L3s!p)DjWgfr-)(!M{DBY8&;fa z9Q*F%n#Wng)*EjR-?Cr6%lPBlyFKSOSiyC|eMnPu85>?Im~5z+`{V6*y}f&PVfT(7 z&8=ui22&ctO-0jm+2vunwc&ivE@j2?RYz}MxM0p}!!$RRtPcOaO(RieuuALWa2vsC zmPy5dG?by(8U5q7zGmmI?i92*is)7%{4WdYHUD!CR3V3n?sNM*teAT{*a@ z)fni{_D3p`jiF8@RXHxvm`0osXR>;Hc!K(q+pf#2HTAwsz#VJOO|+&!nLcw*;==x~ zUB5MC3=+a+zQnr86Dz{0=5*Wg+h#WMDUbZT6!Tfk);f!Et-NL&bKdZT6L5Alt3o33~kg2?G zS5tEOo^2Oid;oAkG$oK5@U#vo(dJPY4WmGtFNTB01XxRVse<0AQOUiJhe^nl%8(B$ zZHP2f0{f7~D1PH5!70fkNr|fmhevdHxSC_`K*m>Jqpm$KciT^3@HD5RoZ>Bhvk z%9PR>YD`u{FrKWxby4oX`e!H9*WbRpEnU}OukcTpvMyn~E5qJFNM#_-tS26F@%2}; zVy0${=iqteMg%D$d?=b!F-wvU76S_MYBoh4@D~Qj+%YTIkvyr(V*N@i7;&1W>ahQ& z%pHvQ{4j|T4I+yg0BbLWpG=L_|w5m2^r{yrW&la|t`bU2EvzS6MSmgaCgviBD^^Dy#2vRGJ2_&e&@nczDtWO&$muq6vy8Crruf+SEfkZ(&-phSRD;)dDx=AV=f zE8jXP&A;bxZrMFAZ)wV;s;ACau+8Th!jx=VFk@pm&iz}@Ry!K&7PfWFUpb4W!Iho0a(+kK!n(!|_3W+p&&fgS zB_xacqj9i;_=8Y9ojzV@rG>e zlUA;o-gtKMtmuYx>cW>U^klBC9+y13F}r5vqy}qnLhtmje@Y+_^k@!U4>j9t&Yrn5 zD0oFEG+5#WzhZURE%?tkbSiwTOy})fwpl7sA@>=($NXn0@D^B)|OJVvZB@c znWFRkOYq{UOqzOeko}7Y(APu;nPiQ5Qlh|RERS$~EMIGG;pP!ic<51!VX^1Vg_^a$ zp|m3)Y#GbL0x(+xP@{E^IH4zjLnk6m2li9)-^L;Ulo0O;Vi(F#*j>Rl8>H?Q53BV*n>cIw=Ptfn3p?u(Zk=|+5P*;{=UGH z`8KX7Rs@ygFO9paswR3?1m68gAG1yfSA;qy&ik+bzNKNHF?`;*>QHUste>&KT~8Tb zJJC6=y85bl73YT=9&fzrr$@d#eah5D6Kw02hgXDcUau{rH9SIN!ssAk7(iPL9EILv zAWSL^s!7Br0Eb8)ksvP$qU%V4NaI6E1`i)IG!`Y{ejSE6M8F0N$N_!0X z{0x*lg0Nr(e3>yyG-1mM;aF#w`9CyRNe-%@&s=Z;`;6m^QA?x~DYpNdbBqn@iVu%p zBH&xlFtbRbOa58Fa1?ohNN);NFrwwBqzYn2M0*C0BZX`5a$&;vT^i9w{ zZG5Mj`*f$O&TPrZlgg zJ0N51(3a1*i1mH)HRH$67{}hMZ+`RH%MaGZqs>j5_sv|?yJ*~XY~@Rq!?)kvzo|cY z`Gv~*wX8r2^D!Zsx(kGpr-`3oL;&X!8te)!Vhq-&IO#e>=)(KqHNI-GtDmM2dC2RQaKDaTOn>fRBT zR9qe$box&~iNyO6V9AfrVmXquQ$wf?^zEUk$dqKdpoWM*!8Bq$3n?BV>tF@@)Zsf^ zN{rldz(T;sOlMlYnfra!cT^^L$oSe@m9TV*r~@pqNuk((pw-|3cQ56W(SN@FM#;U*Q zWXa0=z-%~Q``QaeoW_y_q&N}nP>U!<;1)`KDe0!*k^{negj>KWX)(hVmtmu_D6fiV zeDC=2y$t{Od#v2q_e87msYjFw*U)>e3Pt&XInthQdslVJuFh57Z+qApdZzeyv=pcq zYIgPx`?b^SbrxX{b!IaSFv?@sZ~ zLG~PjX*dmgMfo;Gq7GA@dPX`c@d2Wf`p()Flhu=a7jpIh+OuO zL>LhnNwS4tHZ`(*zh}xhvCHNau2loZ`x91t;)PGFn4sj*kt`ONk%h*8>G@OBe|*sb z>om)Ye@st3f9bQabEbGa^Dbi(*f<_&yJGFMX=|@&E4*#I+TKU2uCKjm)xOWZch>=? zM*RVz-4GDkIC0>v_ddIC71|F^M9^u5dZXZP;D!zYo{r;*HUo7+X9`VDN3x7JkDU-- z6T?78c;+z-V@F~j=xIE!_V1~&IU2s6anx2fzA(Yo=+J8ecia(eYP3ywp|QHwk@E*L)*|{1mV7j+M3S4*NEOn^LcS(ZbHN+D0-B1!z89~c%ns}@?Y^y|#l9HF;J5Cf$7^FM#df5D7 zyFr@;1SLftMUe1_Gz_{nMJ^(=5y!<**s?*eO-!-cAB)vb?{28(5KYf*a8)qBFBG)Q zxd0Ab>K6|4x`SS+(3$8!~}O>tS)_>yc0RChcTo;ss>S!PmTA?#>}#gi4W zbCzbaCci^5Co>DC%=+ZrYTu=y;G~`dmtS_Ed*;sD>$5#egPrqb45HU>g@FT&9dNIZ zbqm;1N+Us`4j|dm!SHB0Az#A17*#Qrv{>jD#0r_dK)^_1oYF4aq87OVkT2v)DTEAA zA0gKPQwVbuMoo2l+rlx>zyS?8ns(~RX{P+E7=`j7>Ps5W(#84t?KC}y=9UqlBPL_*bCBqmMYG5$8?(Oj``Q!F=noXD0<2) zo&_Y%Eds7ZIRn_%lT2M%BTp4WTbOBrYK{KkpjrfM44cVE3wpFxP)0-q#XCESu6w!$ z4?{-L`RNLfQ@L*;*%BMJ!+!YfA@2Tuc<-%b8<0feFngaoDu>Oy5t<8T-<H{g-CZP!s{y^1=Mgc>R<6B!?G%*Cf!p?G!JyjKTn~gDSLZYMtHMgyVBUK&@Rz18mwWjRPkYhQSDMr?fLM_ zm}_jSE`@|-0}U+3>D0ayKB`@i%c5Dp2_Q1D?oCI`Kp0yn8p%e@CHyeOGz>R}d@;oo zu??rT>k_juG|Q)f0qNwJh85RmPQaO+{hU|eO1a+vBsCONkkoA*VSJ^e2L>HlDjk5G zk4Bz0g4rd`H-*)V!Vm=N9jSDixTQnv7Yxx3LAMaI51I)83GFB;o&KpbR9vW**N0Gd zX9t8@Aw**pCA4tL1qPa>>!`{Oq)-hBKq#!A7Sf6DB-tWrLgSFb-YhB!cZR|#;1v|% zco+%DO*%t*2O(TMhKDOankggwU?e z_Ecx6Q@k8lkJ{M-V`J8y!2>irXi;k?90=+==ux~)oH|H70u+G3>qyfW(K#h|5KE36 zO#UL=%Jf4SynX*J|L=LbCvC~+hfzLvaT|BK(@5wtTSg+kt4FI>zrvS!X)|? z-5S=^L}gslbO%JKR_4&Ni-hA$n<8-t*abHfR(C@o~br&x9AqcKV;0U!ynA$Rf6~`EyHkIA)!{SkXEa; zvd(2C#J#fYbJ{$z!zz2ZJLEll?3zwf#aYm;I;;p}%CVSK*==QVW%SN{wfaHI!p`3pgZH+%*$*Jrdu@4;^!d-um~}a6ClMg^wtVlwNn&V)n%{z7)^mquBKQmT(v5i)h}xo&W5PcD2q=wv;s>SL=)Ki8JH)&y-ShquQ zs}&ea8#yQV@B%AFC=9r(WNwR#IoudC-HJ%d%%&hVBuBVTwNgQ>NQLVb3@C=%9YGVU z%%!Uyt0HTfLz7(?$;J2TjCs%nJBxZ1%$W<*$YN=QInI*h2E=o=TQ#*_)1vrbl8c_< zfu>4D4JtC;rUyMCu2ltWmV~A|HGFN!D=X-0o#MAJr_U~HK21?A6*`3g5SNUWZpI~NHmko*o z?zQU{Xhviog086+#qY7=O?G_w8@{Rn@}m3N#dWE#`pRGL7I#gU|DfZ1r%3mSh;p?mGL2Q%!#elS?jHIhZMca0*Y3af+vI8O+r2rBu~N; zl`o<}V-o{;548^LK}q(B@a&*dDLkke3=4ZFW|CI?vxRfX$8!TroDZcx&ff@+|I zKYc(+m70`a;M+(D0U`p!N&X1?9eW4gkik$W=6HyiBilvH*yu4JB_?T&5TYuG_;3)Y z5nm>lv!cN+Yyu=hQXoB}Z%~sen?cOi54E`T0fh1l9(DB557ytiT9sg5YQ#*D$^dnG z07EcHUjcy3o+J(ftErzQ-6O0Jt=Pz5{ASJxNfgMl2D~CkM(9f*sn#H?C33|8c7jOt4haAS;3kmroNQ0J1 zE75gf+m-Qe%TXC)ZQ6Wb}Z0tFbxPf zpm50|wx+2$oUFd9;5x(SrPWqpcWTrYzcO8TY|)bI)opiGC&SH6Y=gK-;75L5_iLMB zrx}O0#pM_UVp+fn*MQ5z)V9cEYAk|$fO09`1XWnP)>$&Kk;5I5>B(;5nKYh7iozQR zUwz0~h##(H>a)>TU_x3W$LxN+tHE6van#E3=#i?%hUmU%VS4mPv>{!+FB*NNs&Q;7 z`Q~%>E!%P3vLnmRKmXjFJC?t)d`upn2}JENxz-V>bT@SAeml~zb^T#gWN(!J0f}hU z-e?+ys%l3UD!h4g+1_R6{BYTh>(4#^eAGNTOX~u-D+k#H{S9z%RTlc91?f^vLot7@V;m7?b*L!!L*tm zfp@$H`hF+s4r3M&F%PT_z-3!dbvkaDRkj@aSQlLXbjcFo#wBDY~y7yB#Lk7@S- z0l)FKag_gW<7gmv{slMRe1Tla?lW<;v1O*QjD4;)$?h|@Bt=&wCS+`ckQYg-qz%#z z>2~RE+@iO^QUp>1)}fh<(e zxhWFXVW)v^2edThT)-nRXGXLVR6;f54^O3`r6d9$)(5PU-YOpy{5ZRUorub6P0s1@ zx(bV~v?!p7*Dl-jz@6u=u3+ zxs-_9pDXs8pq2@CJZEMK(z`o4QJ%WIw1dGoB!+U1#h z`=(rxK6`oly$dHyWJ)i)&7x;L^@+fqrd@4Q5_Bj`Y1`G55C=Xm*`5ek#z$li$RhS% zF`msDOSbe|pz8K05hI^v2lmL=G_VN)e@Vb!wTR}Bgk=c6%D@D^E#hVqLE}>y&`}FS z+|h1zs%KBqw5`ZK$8#!p!@wpbkhopl>I^3>;2 zgZy(dso;X?lFwqr?>69J)M0$3;itw=`M(%HH9n2+&kc}!Hohh!HS`btP05)#KpR7( z^>J6j=A@3uAn<;oSosLA_6v0s#5<;@#gJ_Uv3a6w|<<%P=-FC+%Lx0`!#$%6O z!!NW=^*C*XC(gcf!`?pGGHq#g`Lx2jnz zLbUVuXCPsM{jV7AP8u zE=_$iwLfMw=?}|~j+0jkA*bdD%^ept6jUEW)~_K49%Dq#J+^#Hta(*G#*fhV&r=$%yy}6!s&3kOcYU7DR{_ zatN_eLArsDLXGJ>+?FzJ?L=*AdK#9VWAC3b2sdt8vY~g<#7Wi7mq#oU6MoNh&jz;e zqPA{s?AONk_KvTvY^gt|;-bm(E}6M>7Q0#fqd5*f7sVhxo-@9%k#S4YoI5wDZ9Wme^f8_}aQ-!p`8@kr!q>LEy?I=?vTE{_wn@w8v@UDutn4j4mi^iHJ*e0=uk;#u4E0^3s z+%O_3Zfw9r*xT?c$B6n=h;Ghwk|2zJL0Dp|1QttagJcKzfv^T---?DO z-2O49v~KIY%4T<|j^(b_%=tU7o;jnp_ouVgPfou5|M2!6fNhm$+pwN9wD-2;Az7B> zc*aAv;}s=whBKX=kdT;6XFxUqG7w2vDTNRqP)1`Y6ey%nHgD6`ZGqCVDRk1-w3Lt1 zGCC+Uu};40evV|zP6E8||NbAuXX%V*-p@U+o86`xev(bibGIce5== z>O?M5#A8su#Xv1GI_lbn(NVo<3AWZBC|)pUdtp-{6Izq4$OFWz+R8}VqQyN6o61K! zN*o@Y4KlZ@xO|mWnD^53iy-S)#yhn(QE%0Hklk+Tv<>GUzIVsY);6!*ktZ*3T8C1Q z%V9xS#1Kyb8Q+>T81k$aTH@M2EAQ=|*%GeKcZN&yo0>aspS9wK1uYXi5hwx{7@@_8 zS#*9gGihxBU8%{XT>0bkr&o<@9uo>zRZp9~v+E8v<9J@liGA6=fh#=u!)Ul4he|66 z1z@>`a%WzrISR@-qVA3n=Of$ZfBSso_lEm3A}SV<>}oP+?pd63Jp31B*nPu)8-DhA zcjkVJ#N9p;WaT78*FKs@v|-l{9x6kJ;vnRpGv{i~;hAs9c^R9To1K&BaPZV^89WCU zf9T3hia{yuXh{q@X&_+9?&n+^0V9&Mm!ozGp*pDSFU4Djb#pGhyvToDR0 z2N-rzCif@t|8|XEGh;|w#0X27L_8jZNWppl5|UyOS~B5LOG*mHTIPeIlkg76J4{QK zxYssqXmJ@T-Rs*f{(jHSKVG};iA$H1cg-l&1NT7dsC(`HoA1ARL)%oVK8pCk_62z> z9n#B6Hlz7$ZqW&yJGuBf@iA9_d}QnMdz-uWTrr{N>mhSUHyV2VwsUU&_1*iw_2I&{ z$d1KDwd1$W@2pXlP1>-8?fwh*0n4o$kS+%K{%q}>YGSQS<>)GG2%l3qZkk2iCGKFI zE}!o+RCw04KK|!PyPjCz^Z1@~%4f~6cqF5&b=1Cc?@jk!xxSSu=S|eK&G)bHJDw!| zkH;#26TD8fC?*TUG86y+m?Nircn)kZR^~TF7N>SmD9KASBaQs1vD!$Si~2D#XkJKnM5~ zT7#&w$Y???I^=>p zspDG`U6EvKVs>QxBIVQhx2(Nvnb%_}eP~Ygm}u+F8L`%j*N-o4ZZ0jVs3@weWf!JW zN&I7}T<(~)Pw#ZaIx4Cv+5MM2BeVhVFa@+X+mhPnP7ECL+0}jW0|YJLBh@*J_}kxZ{58pFTz8{E2E%;##*(zm zQ=>v9MFCAEaNfoc!wAEOVh9r=Dn}tgNQ~7ma@C^<{nXYQXOvk;_gXe%?~%PT%G8}u zw*JV;6wxLrb>w}hp+U=H0Ufq1)y?{@?uxpV{&%lAw0q{v-G|hjQij~kctGJ>F?ljY zk5En`5HZj&mPBT(6rx(-AE?H(skjtCR#KAi0Kg^|Ktd+*9DeMAXMa7BKmIH#E)tF# zp5;PL24#UjP6qG=els?V`;*WaUZ*~r)TD%z#J@|^g=BL6Fpw}1bcBzpACi)}@8QXa zQD!`wRG%G;BI1Y(LXwvm&Kr1|LVdD@2TEg7ga0@mJ{ZRXynNtNhv5Sd#THudkv)O= zkVdM6^O0`08!n=`Jb{!t*$ea?srzKgCA~D{Sh|e!uzkQDr*?rRZ+NRhDkRZ#u$_2$ zhl)9(*?yDL5@%>b$e*xIXui1bSni9c9nglz46T;&3;GWIuC`~k?>LVR8BwDN5W?{g zvGe*6pDeTp+&>`NK=5Q5xbh%U7b@Nu`Nk4Sh4MiMy8#&!D#oz&SB{x{VI5<27fv4Y zEjDFL`HD{Es-?zpatzGkFy1{4%I0qle+4H5~s7Ipjwywz+ZO5*qJ@cc%MHEn!gc8HtF+v0=#~`Oy zaLpr4703}$C`Z_7hx?2tLYeEl>|Esuww$ey#&FFBm)DV^W@kXv8{U z4V=7o>;tcg*A0ZlKd{=)6)QTYo_F5B@6yi;&UHH{))m&Jf61<6ACDe=C^WjM=uerp zÄ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 V*eEM;|ho{trTA6=?tf literal 0 HcmV?d00001 diff --git a/api/fonts/fontawesome-webfont.woff b/api/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..628b6a52a87e62c6f22426e17c01f6a303aa194e GIT binary patch literal 65452 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 q%u^)w#YX9ZIvhtu?9Cy6YRi6f6G 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 literal 0 HcmV?d00001 diff --git a/api/fonts/glyphicons-halflings-regular.eot b/api/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 0000000000000000000000000000000000000000..4a4ca865d67e86f961bc6e2ef00bffa4e34bb9ed GIT binary patch literal 20335 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

!oRptb@n+ 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 literal 0 HcmV?d00001 diff --git a/api/fonts/glyphicons-halflings-regular.svg b/api/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..25691af8f --- /dev/null +++ b/api/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/fonts/glyphicons-halflings-regular.ttf b/api/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..67fa00bf83801d2fa568546b982c80d27f6ef74e GIT binary patch literal 41280 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

PBK;&Wcg|NX{>QR@W3{K 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 literal 0 HcmV?d00001 diff --git a/api/fonts/glyphicons-halflings-regular.woff b/api/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..8c54182aa5d4d1ab3c9171976b615c1dcb1dc187 GIT binary patch literal 23320 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 mX4Rts + + + + + + + + + + x3d2 + + + + + + + + + + + + + + +

+ + +
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/alloc_tdsops.html b/api/interface/alloc_tdsops.html new file mode 100644 index 000000000..1d0d0adc8 --- /dev/null +++ b/api/interface/alloc_tdsops.html @@ -0,0 +1,394 @@ + + + + + + + + + + + + + alloc_tdsops – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

alloc_tdsops + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine alloc_tdsops(self, tdsops, dir, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + class(tdsops_t), + intent(inout), + allocatable + ::tdsops + +
+ + integer, + intent(in) + + ::dir + +
+ + character(len=*), + intent(in) + + ::operation + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + integer, + intent(in),optional + + ::n_halo + +
+ + character(len=*), + intent(in),optional + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + real(kind=dp), + intent(in),optional + + ::c_nu + +
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
+ +
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/allocator_t.html b/api/interface/allocator_t.html new file mode 100644 index 000000000..b4633cf66 --- /dev/null +++ b/api/interface/allocator_t.html @@ -0,0 +1,267 @@ + + + + + + + + + + + + + allocator_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

allocator_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface allocator_t

+ + + + +

Module Procedures

+
+

public function allocator_init(mesh, sz) result(allocator) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + type(mesh_t), + intent(inout), + target + ::mesh + +
+ + integer, + intent(in) + + ::sz + +
+ +

+ Return Value + type(allocator_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/copy_data_to_f.html b/api/interface/copy_data_to_f.html new file mode 100644 index 000000000..8cce28335 --- /dev/null +++ b/api/interface/copy_data_to_f.html @@ -0,0 +1,262 @@ + + + + + + + + + + + + + copy_data_to_f – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

copy_data_to_f + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine copy_data_to_f(self, f, data) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t), + intent(inout) + + ::self + +
+ + class(field_t), + intent(inout) + + ::f + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::data + +
+ +

Description

+

Copy the specialist data structure from device or host back +to a regular 3D data array in host memory.

+
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/copy_f_to_data.html b/api/interface/copy_f_to_data.html new file mode 100644 index 000000000..8404f4f34 --- /dev/null +++ b/api/interface/copy_f_to_data.html @@ -0,0 +1,262 @@ + + + + + + + + + + + + + copy_f_to_data – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

copy_f_to_data + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine copy_f_to_data(self, data, f) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::data + +
+ + class(field_t), + intent(in) + + ::f + +
+ +

Description

+

Copy a regular 3D array in host memory into the specialist +data structure field that lives on device or host

+
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/cuda_allocator_t.html b/api/interface/cuda_allocator_t.html new file mode 100644 index 000000000..a928b5451 --- /dev/null +++ b/api/interface/cuda_allocator_t.html @@ -0,0 +1,267 @@ + + + + + + + + + + + + + cuda_allocator_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

cuda_allocator_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface cuda_allocator_t

+ + + + +

Module Procedures

+
+

public function cuda_allocator_init(mesh, sz) result(allocator) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout) + + ::mesh + +
+ + integer, + intent(in) + + ::sz + +
+ +

+ Return Value + type(cuda_allocator_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/cuda_backend_t.html b/api/interface/cuda_backend_t.html new file mode 100644 index 000000000..bcce8f360 --- /dev/null +++ b/api/interface/cuda_backend_t.html @@ -0,0 +1,267 @@ + + + + + + + + + + + + + cuda_backend_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

cuda_backend_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface cuda_backend_t

+ + + + +

Module Procedures

+
+

public function init(mesh, allocator) result(backend) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout), + target + ::mesh + +
+ + class(allocator_t), + intent(inout), + target + ::allocator + +
+ +

+ Return Value + type(cuda_backend_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/cuda_field_t.html b/api/interface/cuda_field_t.html new file mode 100644 index 000000000..b1a3293d4 --- /dev/null +++ b/api/interface/cuda_field_t.html @@ -0,0 +1,282 @@ + + + + + + + + + + + + + cuda_field_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

cuda_field_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface cuda_field_t

+ + + + +

Module Procedures

+
+

public function cuda_field_init(ngrid, next, id) result(f) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::ngrid + +
+ + type(cuda_field_t), + intent(in), + pointer + ::next + +
+ + integer, + intent(in) + + ::id + +
+ +

+ Return Value + type(cuda_field_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/cuda_poisson_fft_t.html b/api/interface/cuda_poisson_fft_t.html new file mode 100644 index 000000000..1c2ac2269 --- /dev/null +++ b/api/interface/cuda_poisson_fft_t.html @@ -0,0 +1,296 @@ + + + + + + + + + + + + + cuda_poisson_fft_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

cuda_poisson_fft_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface cuda_poisson_fft_t

+ + + + +

Module Procedures

+
+

private function init(mesh, xdirps, ydirps, zdirps) result(poisson_fft) +

+
+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + type(dirps_t), + intent(in) + + ::xdirps + +
+ + type(dirps_t), + intent(in) + + ::ydirps + +
+ + type(dirps_t), + intent(in) + + ::zdirps + +
+ +

+ Return Value + type(cuda_poisson_fft_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/cuda_tdsops_t.html b/api/interface/cuda_tdsops_t.html new file mode 100644 index 000000000..e07f0da04 --- /dev/null +++ b/api/interface/cuda_tdsops_t.html @@ -0,0 +1,403 @@ + + + + + + + + + + + + + cuda_tdsops_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

cuda_tdsops_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface cuda_tdsops_t

+ + + + +

Module Procedures

+
+

public function cuda_tdsops_init(n, delta, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) result(tdsops) +

+
+ +

Constructor function for the cuda_tdsops_t class. +See tdsops_t for details.

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::n + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::operation + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + integer, + intent(in),optional + + ::n_halo + +
+ + character(len=*), + intent(in),optional + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + real(kind=dp), + intent(in),optional + + ::c_nu + +
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
+ +

+ Return Value + type(cuda_tdsops_t) +

+

return value of the function

+ +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/fft_backward.html b/api/interface/fft_backward.html new file mode 100644 index 000000000..6d87651df --- /dev/null +++ b/api/interface/fft_backward.html @@ -0,0 +1,244 @@ + + + + + + + + + + + + + fft_backward – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

fft_backward + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine fft_backward(self, f_out) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(poisson_fft_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::f_out + +
+ +
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/fft_forward.html b/api/interface/fft_forward.html new file mode 100644 index 000000000..e4381585b --- /dev/null +++ b/api/interface/fft_forward.html @@ -0,0 +1,244 @@ + + + + + + + + + + + + + fft_forward – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

fft_forward + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine fft_forward(self, f_in) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(poisson_fft_t) + + + ::self + +
+ + class(field_t), + intent(in) + + ::f_in + +
+ +
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/fft_postprocess.html b/api/interface/fft_postprocess.html new file mode 100644 index 000000000..9ade6d8f4 --- /dev/null +++ b/api/interface/fft_postprocess.html @@ -0,0 +1,229 @@ + + + + + + + + + + + + + fft_postprocess – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

fft_postprocess + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine fft_postprocess(self) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(poisson_fft_t) + + + ::self + +
+ +
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/field_t.html b/api/interface/field_t.html new file mode 100644 index 000000000..76b1e3b07 --- /dev/null +++ b/api/interface/field_t.html @@ -0,0 +1,282 @@ + + + + + + + + + + + + + field_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

field_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface field_t

+ + + + +

Module Procedures

+
+

public function field_init(ngrid, next, id) result(f) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::ngrid + +
+ + type(field_t), + intent(in), + pointer + ::next + +
+ + integer, + intent(in) + + ::id + +
+ +

+ Return Value + type(field_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/get_index_reordering.html b/api/interface/get_index_reordering.html new file mode 100644 index 000000000..158c375de --- /dev/null +++ b/api/interface/get_index_reordering.html @@ -0,0 +1,514 @@ + + + + + + + + + + + + + get_index_reordering – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_index_reordering + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface get_index_reordering

+ + + + +

Module Procedures

+
+

public pure subroutine get_index_reordering_rdr(out_i, out_j, out_k, in_i, in_j, in_k, reorder_dir, mesh) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::out_i + +
+ + integer, + intent(out) + + ::out_j + +
+ + integer, + intent(out) + + ::out_k + +
+ + integer, + intent(in) + + ::in_i + +
+ + integer, + intent(in) + + ::in_j + +
+ + integer, + intent(in) + + ::in_k + +
+ + integer, + intent(in) + + ::reorder_dir + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + +
+
+ +
+

public pure subroutine get_index_reordering_dirs(out_i, out_j, out_k, in_i, in_j, in_k, dir_from, dir_to, mesh) +

+
+ +

Converts a set of application storage directional index to an other direction. +The two directions are defined by the reorder_dir variable, RDR_X2Y will go from storage in X to Y etc.

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::out_i + +
+ + integer, + intent(out) + + ::out_j + +
+ + integer, + intent(out) + + ::out_k + +
+ + integer, + intent(in) + + ::in_i + +
+ + integer, + intent(in) + + ::in_j + +
+ + integer, + intent(in) + + ::in_k + +
+ + integer, + intent(in) + + ::dir_from + +
+ + integer, + intent(in) + + ::dir_to + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/init_poisson_fft.html b/api/interface/init_poisson_fft.html new file mode 100644 index 000000000..0f26b260e --- /dev/null +++ b/api/interface/init_poisson_fft.html @@ -0,0 +1,289 @@ + + + + + + + + + + + + + init_poisson_fft – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

init_poisson_fft + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine init_poisson_fft(self, mesh, xdirps, ydirps, zdirps) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + type(dirps_t), + intent(in) + + ::xdirps + +
+ + type(dirps_t), + intent(in) + + ::ydirps + +
+ + type(dirps_t), + intent(in) + + ::zdirps + +
+ +
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/mesh_t.html b/api/interface/mesh_t.html new file mode 100644 index 000000000..f36c01ce0 --- /dev/null +++ b/api/interface/mesh_t.html @@ -0,0 +1,329 @@ + + + + + + + + + + + + + mesh_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

mesh_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface mesh_t

+ + + + +

Module Procedures

+
+

public function mesh_init(dims_global, nproc_dir, L_global, BC_x, BC_y, BC_z) result(mesh) +

+
+ +

Completely initialise the mesh object. +Upon initialisation the mesh object can be read-only and shouldn't be edited +Takes as argument global information about the mesh like its length, number of cells and decomposition in each direction

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in), + dimension(3) + ::dims_global + +
+ + integer, + intent(in), + dimension(3) + ::nproc_dir + +
+ + real(kind=dp), + intent(in), + dimension(3) + ::L_global + +
+ + character(len=*), + intent(in), + dimension(2) + ::BC_x + +
+ + character(len=*), + intent(in), + dimension(2) + ::BC_y + +
+ + character(len=*), + intent(in), + dimension(2) + ::BC_z + +
+ +

+ Return Value + type(mesh_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/omp_backend_t.html b/api/interface/omp_backend_t.html new file mode 100644 index 000000000..dd3ece10c --- /dev/null +++ b/api/interface/omp_backend_t.html @@ -0,0 +1,267 @@ + + + + + + + + + + + + + omp_backend_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

omp_backend_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface omp_backend_t

+ + + + +

Module Procedures

+
+

public function init(mesh, allocator) result(backend) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout), + target + ::mesh + +
+ + class(allocator_t), + intent(inout), + target + ::allocator + +
+ +

+ Return Value + type(omp_backend_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/omp_poisson_fft_t.html b/api/interface/omp_poisson_fft_t.html new file mode 100644 index 000000000..0a3d90d09 --- /dev/null +++ b/api/interface/omp_poisson_fft_t.html @@ -0,0 +1,296 @@ + + + + + + + + + + + + + omp_poisson_fft_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

omp_poisson_fft_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface omp_poisson_fft_t

+ + + + +

Module Procedures

+
+

private function init(mesh, xdirps, ydirps, zdirps) result(poisson_fft) +

+
+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + class(dirps_t), + intent(in) + + ::xdirps + +
+ + class(dirps_t), + intent(in) + + ::ydirps + +
+ + class(dirps_t), + intent(in) + + ::zdirps + +
+ +

+ Return Value + type(omp_poisson_fft_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/poisson_solver.html b/api/interface/poisson_solver.html new file mode 100644 index 000000000..0e3d97626 --- /dev/null +++ b/api/interface/poisson_solver.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + poisson_solver – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

poisson_solver + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine poisson_solver(self, pressure, div_u) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::pressure + +
+ + class(field_t), + intent(in) + + ::div_u + +
+ +
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/reorder.html b/api/interface/reorder.html new file mode 100644 index 000000000..a0ebf9285 --- /dev/null +++ b/api/interface/reorder.html @@ -0,0 +1,279 @@ + + + + + + + + + + + + + reorder – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine reorder(self, u_, u, direction) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u_ + +
+ + class(field_t), + intent(in) + + ::u + +
+ + integer, + intent(in) + + ::direction + +
+ +

Description

+

reorder subroutines are straightforward, they rearrange +data into our specialist data structure so that regardless +of the direction tridiagonal systems are solved efficiently +and fast.

+
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/scalar_product.html b/api/interface/scalar_product.html new file mode 100644 index 000000000..9f2c95f32 --- /dev/null +++ b/api/interface/scalar_product.html @@ -0,0 +1,263 @@ + + + + + + + + + + + + + scalar_product – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

scalar_product + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public function scalar_product(self, x, y) result(s) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + class(field_t), + intent(in) + + ::x + +
+ + class(field_t), + intent(in) + + ::y + +
+ +

Return Value real(kind=dp)

+ +

Description

+

Calculates the scalar product of two input fields

+
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/solver_t.html b/api/interface/solver_t.html new file mode 100644 index 000000000..b3862121d --- /dev/null +++ b/api/interface/solver_t.html @@ -0,0 +1,282 @@ + + + + + + + + + + + + + solver_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

solver_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface solver_t

+ + + + +

Module Procedures

+
+

public function init(backend, mesh, host_allocator) result(solver) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t), + intent(inout), + target + ::backend + +
+ + type(mesh_t), + intent(inout), + target + ::mesh + +
+ + type(allocator_t), + intent(inout), + target + ::host_allocator + +
+ +

+ Return Value + type(solver_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/stepper_func.html b/api/interface/stepper_func.html new file mode 100644 index 000000000..eefd26825 --- /dev/null +++ b/api/interface/stepper_func.html @@ -0,0 +1,244 @@ + + + + + + + + + + + + + stepper_func – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

stepper_func + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine stepper_func(self, dt) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(time_intg_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(in) + + ::dt + +
+ +
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/sum_intox.html b/api/interface/sum_intox.html new file mode 100644 index 000000000..46c78c641 --- /dev/null +++ b/api/interface/sum_intox.html @@ -0,0 +1,262 @@ + + + + + + + + + + + + + sum_intox – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sum_intox + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine sum_intox(self, u, u_) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u + +
+ + class(field_t), + intent(in) + + ::u_ + +
+ +

Description

+

sum9into3 subroutine combines all the directional velocity +derivatives into the corresponding x directional fields.

+
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/tds_solve.html b/api/interface/tds_solve.html new file mode 100644 index 000000000..8cf601808 --- /dev/null +++ b/api/interface/tds_solve.html @@ -0,0 +1,280 @@ + + + + + + + + + + + + + tds_solve – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

tds_solve + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine tds_solve(self, du, u, tdsops) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(tdsops_t), + intent(in) + + ::tdsops + +
+ +

Description

+

transeq equation obtains the derivatives direction by +direction, and the exact algorithm used to obtain these +derivatives are decided at runtime. Backend implementations +are responsible from directing calls to tds_solve to the +correct algorithm.

+
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/tdsops_t.html b/api/interface/tdsops_t.html new file mode 100644 index 000000000..37eee9530 --- /dev/null +++ b/api/interface/tdsops_t.html @@ -0,0 +1,402 @@ + + + + + + + + + + + + + tdsops_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

tdsops_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface tdsops_t

+ + + + +

Module Procedures

+
+

public function tdsops_init(tds_n, delta, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) result(tdsops) +

+
+ +

Constructor function for the tdsops_t class.

Read more… + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::tds_n + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::operation + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + integer, + intent(in),optional + + ::n_halo +

Number of halo cells

+
+ + character(len=*), + intent(in),optional + + ::from_to +

'v2p' or 'p2v'

+
+ + character(len=*), + intent(in),optional + + ::bc_start +

Boundary Cond.

+
+ + character(len=*), + intent(in),optional + + ::bc_end +

Boundary Cond.

+
+ + logical, + intent(in),optional + + ::sym +

(==npaire), only for Neumann BCs

+
+ + real(kind=dp), + intent(in),optional + + ::c_nu +

params for hypervisc.

+
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu +

params for hypervisc.

+
+ +

+ Return Value + type(tdsops_t) +

+

return value of the function

+ +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/time_intg_t.html b/api/interface/time_intg_t.html new file mode 100644 index 000000000..a9586a3eb --- /dev/null +++ b/api/interface/time_intg_t.html @@ -0,0 +1,297 @@ + + + + + + + + + + + + + time_intg_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

time_intg_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface time_intg_t

+ + + + +

Module Procedures

+
+

public function init(backend, allocator, method, nvars) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t), + + pointer + ::backend + +
+ + class(allocator_t), + + pointer + ::allocator + +
+ + character(len=3), + intent(in) + + ::method + +
+ + integer, + intent(in),optional + + ::nvars + +
+ +

+ Return Value + type(time_intg_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/transeq_ders.html b/api/interface/transeq_ders.html new file mode 100644 index 000000000..3a9348306 --- /dev/null +++ b/api/interface/transeq_ders.html @@ -0,0 +1,340 @@ + + + + + + + + + + + + + transeq_ders – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_ders + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine transeq_ders(self, du, dv, dw, u, v, w, dirps) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ +

Description

+

transeq equation obtains the derivatives direction by +direction, and the exact algorithm used to obtain these +derivatives are decided at runtime. Backend implementations +are responsible from directing calls to transeq_ders into +the correct algorithm.

+
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/vecadd.html b/api/interface/vecadd.html new file mode 100644 index 000000000..ce3953d93 --- /dev/null +++ b/api/interface/vecadd.html @@ -0,0 +1,291 @@ + + + + + + + + + + + + + vecadd – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

vecadd + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

interface
+ public subroutine vecadd(self, a, x, b, y) +

+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + real(kind=dp), + intent(in) + + ::a + +
+ + class(field_t), + intent(in) + + ::x + +
+ + real(kind=dp), + intent(in) + + ::b + +
+ + class(field_t), + intent(inout) + + ::y + +
+ +

Description

+

adds two vectors together: y = ax + by

+
+ +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/interface/vector_calculus_t.html b/api/interface/vector_calculus_t.html new file mode 100644 index 000000000..078d47b26 --- /dev/null +++ b/api/interface/vector_calculus_t.html @@ -0,0 +1,252 @@ + + + + + + + + + + + + + vector_calculus_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

vector_calculus_t + Interface + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public interface vector_calculus_t

+ + + + +

Module Procedures

+
+

public function init(backend) result(vector_calculus) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t), + intent(inout), + target + ::backend + +
+ +

+ Return Value + type(vector_calculus_t) +

+ + +
+
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/js/MathJax-config/.gitignore b/api/js/MathJax-config/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/api/js/svg-pan-zoom.min.js b/api/js/svg-pan-zoom.min.js new file mode 100644 index 000000000..328f5e742 --- /dev/null +++ b/api/js/svg-pan-zoom.min.js @@ -0,0 +1,3 @@ +// svg-pan-zoom v3.2.3 +// https://github.com/ariutta/svg-pan-zoom +!function t(e,o,n){function i(r,a){if(!o[r]){if(!e[r]){var l="function"==typeof require&&require;if(!a&&l)return l(r,!0);if(s)return s(r,!0);var u=new Error("Cannot find module '"+r+"'");throw u.code="MODULE_NOT_FOUND",u}var h=o[r]={exports:{}};e[r][0].call(h.exports,function(t){var o=e[r][1][t];return i(o?o:t)},h,h.exports,t,e,o,n)}return o[r].exports}for(var s="function"==typeof require&&require,r=0;r=0;n--)this.eventListeners.hasOwnProperty(o[n])&&delete this.eventListeners[o[n]]}for(var i in this.eventListeners)this.svg.addEventListener(i,this.eventListeners[i],!1);this.options.mouseWheelZoomEnabled&&(this.options.mouseWheelZoomEnabled=!1,this.enableMouseWheelZoom())},a.prototype.enableMouseWheelZoom=function(){if(!this.options.mouseWheelZoomEnabled){var t=this;this.wheelListener=function(e){return t.handleMouseWheel(e)},o.on(this.svg,this.wheelListener,!1),this.options.mouseWheelZoomEnabled=!0}},a.prototype.disableMouseWheelZoom=function(){this.options.mouseWheelZoomEnabled&&(o.off(this.svg,this.wheelListener,!1),this.options.mouseWheelZoomEnabled=!1)},a.prototype.handleMouseWheel=function(t){if(this.options.zoomEnabled&&"none"===this.state){this.options.preventMouseEventsDefault&&(t.preventDefault?t.preventDefault():t.returnValue=!1);var e=t.deltaY||1,o=Date.now()-this.lastMouseWheelEventTime,n=3+Math.max(0,30-o);this.lastMouseWheelEventTime=Date.now(),"deltaMode"in t&&0===t.deltaMode&&t.wheelDelta&&(e=0===t.deltaY?0:Math.abs(t.wheelDelta)/t.deltaY),e=e>-.3&&.3>e?e:(e>0?1:-1)*Math.log(Math.abs(e)+10)/n;var i=this.svg.getScreenCTM().inverse(),r=s.getEventPoint(t,this.svg).matrixTransform(i),a=Math.pow(1+this.options.zoomScaleSensitivity,-1*e);this.zoomAtPoint(a,r)}},a.prototype.zoomAtPoint=function(t,e,o){var n=this.viewport.getOriginalState();o?(t=Math.max(this.options.minZoom*n.zoom,Math.min(this.options.maxZoom*n.zoom,t)),t/=this.getZoom()):this.getZoom()*tthis.options.maxZoom*n.zoom&&(t=this.options.maxZoom*n.zoom/this.getZoom());var i=this.viewport.getCTM(),s=e.matrixTransform(i.inverse()),r=this.svg.createSVGMatrix().translate(s.x,s.y).scale(t).translate(-s.x,-s.y),a=i.multiply(r);a.a!==i.a&&this.viewport.setCTM(a)},a.prototype.zoom=function(t,e){this.zoomAtPoint(t,s.getSvgCenterPoint(this.svg,this.width,this.height),e)},a.prototype.publicZoom=function(t,e){e&&(t=this.computeFromRelativeZoom(t)),this.zoom(t,e)},a.prototype.publicZoomAtPoint=function(t,e,o){if(o&&(t=this.computeFromRelativeZoom(t)),!("SVGPoint"!==i.getType(e)&&"x"in e&&"y"in e))throw new Error("Given point is invalid");e=s.createSVGPoint(this.svg,e.x,e.y),this.zoomAtPoint(t,e,o)},a.prototype.getZoom=function(){return this.viewport.getZoom()},a.prototype.getRelativeZoom=function(){return this.viewport.getRelativeZoom()},a.prototype.computeFromRelativeZoom=function(t){return t*this.viewport.getOriginalState().zoom},a.prototype.resetZoom=function(){var t=this.viewport.getOriginalState();this.zoom(t.zoom,!0)},a.prototype.resetPan=function(){this.pan(this.viewport.getOriginalState())},a.prototype.reset=function(){this.resetZoom(),this.resetPan()},a.prototype.handleDblClick=function(t){if(this.options.preventMouseEventsDefault&&(t.preventDefault?t.preventDefault():t.returnValue=!1),this.options.controlIconsEnabled){var e=t.target.getAttribute("class")||"";if(e.indexOf("svg-pan-zoom-control")>-1)return!1}var o;o=t.shiftKey?1/(2*(1+this.options.zoomScaleSensitivity)):2*(1+this.options.zoomScaleSensitivity);var n=s.getEventPoint(t,this.svg).matrixTransform(this.svg.getScreenCTM().inverse());this.zoomAtPoint(o,n)},a.prototype.handleMouseDown=function(t,e){this.options.preventMouseEventsDefault&&(t.preventDefault?t.preventDefault():t.returnValue=!1),i.mouseAndTouchNormalize(t,this.svg),this.options.dblClickZoomEnabled&&i.isDblClick(t,e)?this.handleDblClick(t):(this.state="pan",this.firstEventCTM=this.viewport.getCTM(),this.stateOrigin=s.getEventPoint(t,this.svg).matrixTransform(this.firstEventCTM.inverse()))},a.prototype.handleMouseMove=function(t){if(this.options.preventMouseEventsDefault&&(t.preventDefault?t.preventDefault():t.returnValue=!1),"pan"===this.state&&this.options.panEnabled){var e=s.getEventPoint(t,this.svg).matrixTransform(this.firstEventCTM.inverse()),o=this.firstEventCTM.translate(e.x-this.stateOrigin.x,e.y-this.stateOrigin.y);this.viewport.setCTM(o)}},a.prototype.handleMouseUp=function(t){this.options.preventMouseEventsDefault&&(t.preventDefault?t.preventDefault():t.returnValue=!1),"pan"===this.state&&(this.state="none")},a.prototype.fit=function(){var t=this.viewport.getViewBox(),e=Math.min(this.width/t.width,this.height/t.height);this.zoom(e,!0)},a.prototype.contain=function(){var t=this.viewport.getViewBox(),e=Math.max(this.width/t.width,this.height/t.height);this.zoom(e,!0)},a.prototype.center=function(){var t=this.viewport.getViewBox(),e=.5*(this.width-(t.width+2*t.x)*this.getZoom()),o=.5*(this.height-(t.height+2*t.y)*this.getZoom());this.getPublicInstance().pan({x:e,y:o})},a.prototype.updateBBox=function(){this.viewport.recacheViewBox()},a.prototype.pan=function(t){var e=this.viewport.getCTM();e.e=t.x,e.f=t.y,this.viewport.setCTM(e)},a.prototype.panBy=function(t){var e=this.viewport.getCTM();e.e+=t.x,e.f+=t.y,this.viewport.setCTM(e)},a.prototype.getPan=function(){var t=this.viewport.getState();return{x:t.x,y:t.y}},a.prototype.resize=function(){var t=s.getBoundingClientRectNormalized(this.svg);this.width=t.width,this.height=t.height,this.options.controlIconsEnabled&&(this.getPublicInstance().disableControlIcons(),this.getPublicInstance().enableControlIcons())},a.prototype.destroy=function(){var t=this;this.beforeZoom=null,this.onZoom=null,this.beforePan=null,this.onPan=null,null!=this.options.customEventsHandler&&this.options.customEventsHandler.destroy({svgElement:this.svg,instance:this.getPublicInstance()});for(var e in this.eventListeners)this.svg.removeEventListener(e,this.eventListeners[e],!1);this.disableMouseWheelZoom(),this.getPublicInstance().disableControlIcons(),this.reset(),u=u.filter(function(e){return e.svg!==t.svg}),delete this.options,delete this.publicInstance,delete this.pi,this.getPublicInstance=function(){return null}},a.prototype.getPublicInstance=function(){var t=this;return this.publicInstance||(this.publicInstance=this.pi={enablePan:function(){return t.options.panEnabled=!0,t.pi},disablePan:function(){return t.options.panEnabled=!1,t.pi},isPanEnabled:function(){return!!t.options.panEnabled},pan:function(e){return t.pan(e),t.pi},panBy:function(e){return t.panBy(e),t.pi},getPan:function(){return t.getPan()},setBeforePan:function(e){return t.options.beforePan=null===e?null:i.proxy(e,t.publicInstance),t.pi},setOnPan:function(e){return t.options.onPan=null===e?null:i.proxy(e,t.publicInstance),t.pi},enableZoom:function(){return t.options.zoomEnabled=!0,t.pi},disableZoom:function(){return t.options.zoomEnabled=!1,t.pi},isZoomEnabled:function(){return!!t.options.zoomEnabled},enableControlIcons:function(){return t.options.controlIconsEnabled||(t.options.controlIconsEnabled=!0,n.enable(t)),t.pi},disableControlIcons:function(){return t.options.controlIconsEnabled&&(t.options.controlIconsEnabled=!1,n.disable(t)),t.pi},isControlIconsEnabled:function(){return!!t.options.controlIconsEnabled},enableDblClickZoom:function(){return t.options.dblClickZoomEnabled=!0,t.pi},disableDblClickZoom:function(){return t.options.dblClickZoomEnabled=!1,t.pi},isDblClickZoomEnabled:function(){return!!t.options.dblClickZoomEnabled},enableMouseWheelZoom:function(){return t.enableMouseWheelZoom(),t.pi},disableMouseWheelZoom:function(){return t.disableMouseWheelZoom(),t.pi},isMouseWheelZoomEnabled:function(){return!!t.options.mouseWheelZoomEnabled},setZoomScaleSensitivity:function(e){return t.options.zoomScaleSensitivity=e,t.pi},setMinZoom:function(e){return t.options.minZoom=e,t.pi},setMaxZoom:function(e){return t.options.maxZoom=e,t.pi},setBeforeZoom:function(e){return t.options.beforeZoom=null===e?null:i.proxy(e,t.publicInstance),t.pi},setOnZoom:function(e){return t.options.onZoom=null===e?null:i.proxy(e,t.publicInstance),t.pi},zoom:function(e){return t.publicZoom(e,!0),t.pi},zoomBy:function(e){return t.publicZoom(e,!1),t.pi},zoomAtPoint:function(e,o){return t.publicZoomAtPoint(e,o,!0),t.pi},zoomAtPointBy:function(e,o){return t.publicZoomAtPoint(e,o,!1),t.pi},zoomIn:function(){return this.zoomBy(1+t.options.zoomScaleSensitivity),t.pi},zoomOut:function(){return this.zoomBy(1/(1+t.options.zoomScaleSensitivity)),t.pi},getZoom:function(){return t.getRelativeZoom()},resetZoom:function(){return t.resetZoom(),t.pi},resetPan:function(){return t.resetPan(),t.pi},reset:function(){return t.reset(),t.pi},fit:function(){return t.fit(),t.pi},contain:function(){return t.contain(),t.pi},center:function(){return t.center(),t.pi},updateBBox:function(){return t.updateBBox(),t.pi},resize:function(){return t.resize(),t.pi},getSizes:function(){return{width:t.width,height:t.height,realZoom:t.getZoom(),viewBox:t.viewport.getViewBox()}},destroy:function(){return t.destroy(),t.pi}}),this.publicInstance};var u=[],h=function(t,e){var o=i.getSvg(t);if(null===o)return null;for(var n=u.length-1;n>=0;n--)if(u[n].svg===o)return u[n].instance.getPublicInstance();return u.push({svg:o,instance:new a(o,e)}),u[u.length-1].instance.getPublicInstance()};e.exports=h},{"./control-icons":2,"./shadow-viewport":3,"./svg-utilities":5,"./uniwheel":6,"./utilities":7}],5:[function(t,e){var o=t("./utilities"),n="unknown";document.documentMode&&(n="ie"),e.exports={svgNS:"http://www.w3.org/2000/svg",xmlNS:"http://www.w3.org/XML/1998/namespace",xmlnsNS:"http://www.w3.org/2000/xmlns/",xlinkNS:"http://www.w3.org/1999/xlink",evNS:"http://www.w3.org/2001/xml-events",getBoundingClientRectNormalized:function(t){if(t.clientWidth&&t.clientHeight)return{width:t.clientWidth,height:t.clientHeight};if(t.getBoundingClientRect())return t.getBoundingClientRect();throw new Error("Cannot get BoundingClientRect for SVG.")},getOrCreateViewport:function(t,e){var n=null;if(n=o.isElement(e)?e:t.querySelector(e),!n){var i=Array.prototype.slice.call(t.childNodes||t.children).filter(function(t){return"defs"!==t.nodeName&&"#text"!==t.nodeName});1===i.length&&"g"===i[0].nodeName&&null===i[0].getAttribute("transform")&&(n=i[0])}if(!n){var s="viewport-"+(new Date).toISOString().replace(/\D/g,"");n=document.createElementNS(this.svgNS,"g"),n.setAttribute("id",s);var r=t.childNodes||t.children;if(r&&r.length>0)for(var a=r.length;a>0;a--)"defs"!==r[r.length-a].nodeName&&n.appendChild(r[r.length-a]);t.appendChild(n)}var l=[];return n.getAttribute("class")&&(l=n.getAttribute("class").split(" ")),~l.indexOf("svg-pan-zoom_viewport")||(l.push("svg-pan-zoom_viewport"),n.setAttribute("class",l.join(" "))),n},setupSvgAttributes:function(t){if(t.setAttribute("xmlns",this.svgNS),t.setAttributeNS(this.xmlnsNS,"xmlns:xlink",this.xlinkNS),t.setAttributeNS(this.xmlnsNS,"xmlns:ev",this.evNS),null!==t.parentNode){var e=t.getAttribute("style")||"";-1===e.toLowerCase().indexOf("overflow")&&t.setAttribute("style","overflow: hidden; "+e)}},internetExplorerRedisplayInterval:300,refreshDefsGlobal:o.throttle(function(){for(var t=document.querySelectorAll("defs"),e=t.length,o=0;e>o;o++){var n=t[o];n.parentNode.insertBefore(n,n)}},this.internetExplorerRedisplayInterval),setCTM:function(t,e,o){var i=this,s="matrix("+e.a+","+e.b+","+e.c+","+e.d+","+e.e+","+e.f+")";t.setAttributeNS(null,"transform",s),"ie"===n&&o&&(o.parentNode.insertBefore(o,o),window.setTimeout(function(){i.refreshDefsGlobal()},i.internetExplorerRedisplayInterval))},getEventPoint:function(t,e){var n=e.createSVGPoint();return o.mouseAndTouchNormalize(t,e),n.x=t.clientX,n.y=t.clientY,n},getSvgCenterPoint:function(t,e,o){return this.createSVGPoint(t,e/2,o/2)},createSVGPoint:function(t,e,o){var n=t.createSVGPoint();return n.x=e,n.y=o,n}}},{"./utilities":7}],6:[function(t,e){e.exports=function(){function t(t,e,o){var n=function(t){!t&&(t=window.event);var o={originalEvent:t,target:t.target||t.srcElement,type:"wheel",deltaMode:"MozMousePixelScroll"==t.type?0:1,deltaX:0,delatZ:0,preventDefault:function(){t.preventDefault?t.preventDefault():t.returnValue=!1}};return"mousewheel"==u?(o.deltaY=-1/40*t.wheelDelta,t.wheelDeltaX&&(o.deltaX=-1/40*t.wheelDeltaX)):o.deltaY=t.detail,e(o)};return c.push({element:t,fn:n,capture:o}),n}function e(t,e){for(var o=0;oo&&10>n}return!1},now:Date.now||function(){return(new Date).getTime()},throttle:function(t,e,o){var n,i,s,r=this,a=null,l=0;o||(o={});var u=function(){l=o.leading===!1?0:r.now(),a=null,s=t.apply(n,i),a||(n=i=null)};return function(){var h=r.now();l||o.leading!==!1||(l=h);var c=e-(h-l);return n=this,i=arguments,0>=c||c>e?(clearTimeout(a),a=null,l=h,s=t.apply(n,i),a||(n=i=null)):a||o.trailing===!1||(a=setTimeout(u,c)),s}},createRequestAnimationFrame:function(t){var e=null;return"auto"!==t&&60>t&&t>1&&(e=Math.floor(1e3/t)),null===e?window.requestAnimationFrame||o(33):o(e)}}},{}]},{},[1]); \ No newline at end of file diff --git a/api/lists/absint.html b/api/lists/absint.html new file mode 100644 index 000000000..250982fa7 --- /dev/null +++ b/api/lists/absint.html @@ -0,0 +1,217 @@ + + + + + + + + + + + + + All Abstract Interfaces – x3d2 + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+

Abstract Interfaces

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Abstract InterfaceLocationDescription
alloc_tdsopsm_base_backend
copy_data_to_fm_base_backend

Copy the specialist data structure from device or host back +to a regular 3D data array in host memory.

copy_f_to_datam_base_backend

Copy a regular 3D array in host memory into the specialist +data structure field that lives on device or host

fft_backwardm_poisson_fft
fft_forwardm_poisson_fft
fft_postprocessm_poisson_fft
init_poisson_fftm_base_backend
poisson_solverm_solver
reorderm_base_backend

reorder subroutines are straightforward, they rearrange +data into our specialist data structure so that regardless +of the direction tridiagonal systems are solved efficiently +and fast.

scalar_productm_base_backend

Calculates the scalar product of two input fields

stepper_funcm_time_integrator
sum_intoxm_base_backend

sum9into3 subroutine combines all the directional velocity +derivatives into the corresponding x directional fields.

tds_solvem_base_backend

transeq equation obtains the derivatives direction by +direction, and the exact algorithm used to obtain these +derivatives are decided at runtime. Backend implementations +are responsible from directing calls to tds_solve to the +correct algorithm.

transeq_dersm_base_backend

transeq equation obtains the derivatives direction by +direction, and the exact algorithm used to obtain these +derivatives are decided at runtime. Backend implementations +are responsible from directing calls to transeq_ders into +the correct algorithm.

vecaddm_base_backend

adds two vectors together: y = ax + by

+
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/lists/files.html b/api/lists/files.html new file mode 100644 index 000000000..63af9d1b6 --- /dev/null +++ b/api/lists/files.html @@ -0,0 +1,248 @@ + + + + + + + + + + + + + All Files – x3d2 + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+

Source Files

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileDescription
allocator.f90
allocator.f90
backend.f90
backend.f90
backend.f90
common.f90
common.f90
common.f90
distributed.f90
distributed.f90
exec_dist.f90
exec_dist.f90
exec_thom.f90
field.f90
mesh.f90
ordering.f90
poisson_fft.f90
poisson_fft.f90
poisson_fft.f90
reorder.f90
sendrecv.f90
sendrecv.f90
solver.f90
spectral_processing.f90
tdsops.f90
tdsops.f90
thomas.f90
time_integrator.f90
vector_calculus.f90
xcompact.f90
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/lists/modules.html b/api/lists/modules.html new file mode 100644 index 000000000..746781f7c --- /dev/null +++ b/api/lists/modules.html @@ -0,0 +1,274 @@ + + + + + + + + + + + + + All Modules – x3d2 + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+

Modules

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ModuleSource FileDescription
m_allocatorallocator.f90
m_base_backendbackend.f90
m_commoncommon.f90
m_cuda_allocatorallocator.f90
m_cuda_backendbackend.f90
m_cuda_commoncommon.f90
m_cuda_exec_distexec_dist.f90
m_cuda_exec_thomexec_thom.f90
m_cuda_kernels_distdistributed.f90
m_cuda_kernels_reorderreorder.f90
m_cuda_kernels_thomthomas.f90
m_cuda_poisson_fftpoisson_fft.f90
m_cuda_sendrecvsendrecv.f90
m_cuda_spectralspectral_processing.f90
m_cuda_tdsopstdsops.f90
m_fieldfield.f90
m_meshmesh.f90
m_omp_backendbackend.f90
m_omp_commoncommon.f90
m_omp_exec_distexec_dist.f90
m_omp_kernels_distdistributed.f90
m_omp_poisson_fftpoisson_fft.f90
m_omp_sendrecvsendrecv.f90
m_orderingordering.f90

"Application storage" stores spatial data with a directionality for better cache locality + This set of functions converts indices from this application storage (_dir) to cartesian indices (_ijk)

m_poisson_fftpoisson_fft.f90
m_solversolver.f90
m_tdsopstdsops.f90
m_time_integratortime_integrator.f90
m_vector_calculusvector_calculus.f90
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/lists/namelists.html b/api/lists/namelists.html new file mode 100644 index 000000000..ac5cbef7b --- /dev/null +++ b/api/lists/namelists.html @@ -0,0 +1,138 @@ + + + + + + + + + + + + + All namelists – x3d2 + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+

Namelists

+ + + + + + + + + + + + + + +
NamelistLocationDescription
domain_paramsxcompact
solver_paramsinit
+
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/lists/procedures.html b/api/lists/procedures.html new file mode 100644 index 000000000..e8abb6ea5 --- /dev/null +++ b/api/lists/procedures.html @@ -0,0 +1,1112 @@ + + + + + + + + + + + + + All Procedures – x3d2 + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+

Procedures

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProcedureLocationProcedure TypeDescription
alloc_cuda_tdsopsm_cuda_backendSubroutine
alloc_omp_tdsopsm_omp_backendSubroutine
allocate_tdsopsm_solverSubroutine
allocator_initm_allocatorFunction
allocator_tm_allocatorInterface
axpbym_cuda_kernels_reorderSubroutine
base_initm_base_backendSubroutine
base_initm_poisson_fftSubroutine
buffer_copym_cuda_kernels_reorderSubroutine
compute_padded_dimsm_allocatorSubroutine
copy_data_to_f_cudam_cuda_backendSubroutine
copy_data_to_f_ompm_omp_backendSubroutine
copy_f_to_data_cudam_cuda_backendSubroutine
copy_f_to_data_ompm_omp_backendSubroutine
copy_into_buffersm_cuda_backendSubroutine
copy_into_buffersm_omp_backendSubroutine
create_blockm_allocatorFunction

Allocate memory for a new block and return a pointer to a new +m_allocator object.

create_cuda_blockm_cuda_allocatorFunction
cuda_allocator_initm_cuda_allocatorFunction
cuda_allocator_tm_cuda_allocatorInterface
cuda_backend_tm_cuda_backendInterface
cuda_field_initm_cuda_allocatorFunction
cuda_field_tm_cuda_allocatorInterface
cuda_poisson_fft_tm_cuda_poisson_fftInterface
cuda_tdsops_initm_cuda_tdsopsFunction

Constructor function for the cuda_tdsops_t class. +See tdsops_t for details.

cuda_tdsops_tm_cuda_tdsopsInterface
curlm_solverSubroutine

Wrapper for curl

curlm_vector_calculusSubroutine

Curl of a vector field (u, v, w).

Read more…
der_univ_distm_cuda_kernels_distSubroutine
der_univ_distm_omp_kernels_distSubroutine
der_univ_subsm_cuda_kernels_distSubroutine
der_univ_subsm_omp_kernels_distSubroutine
der_univ_thomm_cuda_kernels_thomSubroutine
der_univ_thom_perm_cuda_kernels_thomSubroutine
deriv_1stm_tdsopsSubroutine
deriv_2ndm_tdsopsSubroutine
destroym_allocatorSubroutine

Go through the block list from head to tail, deallocating each +memory block in turn. Deallocation of a +m_allocator object automatically +deallocates its internal allocatable +data array.

divergence_v2cm_vector_calculusSubroutine

Divergence of a vector field (u, v, w).

Read more…
divergence_v2pm_solverSubroutine

Wrapper for divergence_v2p

domain_decompositionm_meshSubroutine

Supports 1D, 2D, and 3D domain decomposition.

Read more…
exec_dist_tds_compactm_cuda_exec_distSubroutine
exec_dist_tds_compactm_omp_exec_distSubroutine
exec_dist_transeq_3fusedm_cuda_exec_distSubroutine
exec_dist_transeq_compactm_omp_exec_distSubroutine
exec_thom_tds_compactm_cuda_exec_thomSubroutine
fft_backward_cudam_cuda_poisson_fftSubroutine
fft_backward_ompm_omp_poisson_fftSubroutine
fft_forward_cudam_cuda_poisson_fftSubroutine
fft_forward_ompm_omp_poisson_fftSubroutine
fft_postprocess_cudam_cuda_poisson_fftSubroutine
fft_postprocess_ompm_omp_poisson_fftSubroutine
field_initm_fieldFunction
field_tm_fieldInterface
finalizem_time_integratorSubroutine
get_blockm_allocatorFunction

Return a pointer to the first available memory block, i.e. the +current head of the block list. If the list is empty, allocate +a new block with create_block +first.

Read more…
get_block_idsm_allocatorFunction

Utility function that returns a array made of the id of the +block currently in the block list. Return the array [0] if +block list is empty.

get_coordinatesm_meshFunction

Get the physical location of a cell center with i,j,k local indices

get_dimsm_meshFunction

Getter for local domain dimensions

get_dims_datalocm_meshFunction

Getter for domain dimensions

get_dirs_from_rdrm_commonSubroutine
get_field_datam_base_backendSubroutine

Extract data from field f optionally reordering into dir orientation. +To output in same orientation as f, use call ...%get_field_data(data, f, f%dir)

get_field_dims_dirm_meshFunction

Getter for the dimensions of an array directed along dir where data would be located on data_loc

get_field_dims_phim_meshFunction

Getter for the dimensions of field phi

get_field_dims_phi_datalocm_meshFunction

Getter for the dimensions of field phi where data is located on data_loc

get_global_dimsm_meshFunction

Getter for local domain dimensions

get_index_dirm_orderingSubroutine

Get application storage directional index from cartesian index

get_index_ijkm_orderingSubroutine

Get cartesian index from application storage directional one

get_index_reorderingm_orderingInterface
get_index_reordering_dirsm_orderingSubroutine

Converts a set of application storage directional index to an other direction. +The two directions are defined by the reorder_dir variable, RDR_X2Y will go from storage in X to Y etc.

get_index_reordering_rdrm_orderingSubroutine
get_n_dirm_meshFunction

Getter for the main dimension a field oriented along dir with data on data_loc

get_n_groups_dirm_meshFunction

Getter for the number of groups for fields in direction dir

get_n_groups_phim_meshFunction

Getter for the number of groups for fields phi

get_n_phim_meshFunction

Getter for the main dimension of field phi

get_padded_dims_dirm_meshFunction

Getter for padded dimensions with structure in dir direction

get_padded_dims_phim_meshFunction

Getter for padded dimensions for field phi +Gets the field direction from the field itself

get_rdr_from_dirsm_commonFunction

Returns RDR_?2? value based on two direction inputs

get_szm_meshFunction

Getter for parameter SZ

get_tds_nm_tdsopsFunction

Get the tds_n size based on the from_to value (and the mesh)

gradient_c2vm_vector_calculusSubroutine

Gradient of a scalar field 'p'.

Read more…
gradient_p2vm_solverSubroutine

Wrapper for gradient_p2v

initm_time_integratorFunction
initm_solverFunction
initm_cuda_backendFunction
initm_vector_calculusFunction
initm_omp_backendFunction
init_cuda_poisson_fftm_cuda_backendSubroutine
init_omp_poisson_fftm_omp_backendSubroutine
interpl_midm_tdsopsSubroutine
is_rootm_meshFunction

Returns whether or not the current rank is the root rank

laplacianm_vector_calculusSubroutine

Laplacian of a scalar field 'u'.

Read more…
mesh_initm_meshFunction

Completely initialise the mesh object. +Upon initialisation the mesh object can be read-only and shouldn't be edited +Takes as argument global information about the mesh like its length, number of cells and decomposition in each direction

mesh_tm_meshInterface
omp_backend_tm_omp_backendInterface
omp_poisson_fft_tm_omp_poisson_fftInterface
outputm_solverSubroutine
poisson_cgm_solverSubroutine
poisson_fftm_solverSubroutine
preprocess_distm_tdsopsSubroutine
preprocess_thomm_tdsopsSubroutine
process_spectral_div_um_cuda_spectralSubroutine

Post-processes the divergence of velocity in spectral space, including +scaling w.r.t. grid size.

Read more…
release_blockm_allocatorSubroutine

Release memory block pointed to by HANDLE to the block list. +It is pushed to the front of the block list, in other words it +is made the head block.

reorder_c2xm_cuda_kernels_reorderSubroutine
reorder_cudam_cuda_backendSubroutine
reorder_ompm_omp_backendSubroutine
reorder_x2cm_cuda_kernels_reorderSubroutine
reorder_x2ym_cuda_kernels_reorderSubroutine
reorder_x2zm_cuda_kernels_reorderSubroutine
reorder_y2xm_cuda_kernels_reorderSubroutine
reorder_y2zm_cuda_kernels_reorderSubroutine
reorder_z2xm_cuda_kernels_reorderSubroutine
reorder_z2ym_cuda_kernels_reorderSubroutine
resolve_field_tm_cuda_backendSubroutine
rotatem_time_integratorSubroutine
runm_solverSubroutine
scalar_productm_cuda_kernels_reorderSubroutine
scalar_product_cudam_cuda_backendFunction
scalar_product_ompm_omp_backendFunction
sendrecv_3fieldsm_cuda_sendrecvSubroutine
sendrecv_fieldsm_cuda_sendrecvSubroutine
sendrecv_fieldsm_omp_sendrecvSubroutine
set_data_locm_fieldSubroutine
set_field_datam_base_backendSubroutine
set_padded_dimsm_meshSubroutine
set_shapem_fieldSubroutine
set_shape_cudam_cuda_allocatorSubroutine
set_szm_meshSubroutine
solver_tm_solverInterface
stagder_1stm_tdsopsSubroutine
stepm_time_integratorSubroutine
sum_intox_ompm_omp_backendSubroutine
sum_yintoxm_cuda_kernels_reorderSubroutine
sum_yintox_cudam_cuda_backendSubroutine
sum_yintox_ompm_omp_backendSubroutine
sum_zintoxm_cuda_kernels_reorderSubroutine
sum_zintox_cudam_cuda_backendSubroutine
sum_zintox_ompm_omp_backendSubroutine
tds_solve_cudam_cuda_backendSubroutine
tds_solve_distm_cuda_backendSubroutine
tds_solve_distm_omp_backendSubroutine
tds_solve_ompm_omp_backendSubroutine
tdsops_initm_tdsopsFunction

Constructor function for the tdsops_t class.

Read more…
tdsops_tm_tdsopsInterface
time_intg_tm_time_integratorInterface
transeqm_solverSubroutine

Skew-symmetric form of convection-diffusion terms in the +incompressible Navier-Stokes momemtum equations, excluding +pressure terms. +Inputs from velocity grid and outputs to velocity grid.

transeq_3fused_distm_cuda_kernels_distSubroutine
transeq_3fused_subsm_cuda_kernels_distSubroutine
transeq_cuda_distm_cuda_backendSubroutine
transeq_cuda_thomm_cuda_backendSubroutine

Thomas algorithm implementation. So much more easier than the +distributed algorithm. It is intended to work only on a single rank +so there is no MPI communication.

transeq_omp_distm_omp_backendSubroutine
transeq_x_cudam_cuda_backendSubroutine
transeq_x_ompm_omp_backendSubroutine
transeq_y_cudam_cuda_backendSubroutine
transeq_y_ompm_omp_backendSubroutine
transeq_z_cudam_cuda_backendSubroutine
transeq_z_ompm_omp_backendSubroutine
vecadd_cudam_cuda_backendSubroutine
vecadd_ompm_omp_backendSubroutine
vector_calculus_tm_vector_calculusInterface
waves_setm_poisson_fftSubroutine

Spectral equivalence constants

Read more…
+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/lists/types.html b/api/lists/types.html new file mode 100644 index 000000000..94de60e18 --- /dev/null +++ b/api/lists/types.html @@ -0,0 +1,257 @@ + + + + + + + + + + + + + All Types – x3d2 + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+

Derived Types

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeLocationExtendsDescription
allocator_tm_allocatorNone

An instance of type allocator_t is responsible for the +maintenance of a linked list of instances of equal size +m_allocator objects:

Read more…
base_backend_tm_base_backendNone

base_backend class defines all the abstract operations that the +solver class requires.

Read more…
cuda_allocator_tm_cuda_allocatorallocator_t
cuda_backend_tm_cuda_backendbase_backend_t
cuda_field_tm_cuda_allocatorfield_t
cuda_poisson_fft_tm_cuda_poisson_fftpoisson_fft_t

FFT based Poisson solver

cuda_tdsops_tm_cuda_tdsopstdsops_t

CUDA extension of the Tridiagonal Solver Operators class.

Read more…
dirps_tm_tdsopsNone

Directional tridiagonal solver container.

Read more…
field_tm_fieldNone

Memory block type holding both a data field and a pointer +to the next block. The field_t type also holds a integer +refcount that counts the number of references to this +field. User code is currently responsible for incrementing +the reference count.

flist_tm_allocatorNone
geo_tm_meshNone
mesh_tm_meshNone
omp_backend_tm_omp_backendbase_backend_t
omp_poisson_fft_tm_omp_poisson_fftpoisson_fft_t

FFT based Poisson solver +It can only handle 1D decompositions along z direction.

parallel_tm_meshNone
poisson_fft_tm_poisson_fftNone

FFT based Poisson solver

solver_tm_solverNone

solver class defines the Incompact3D algorithm at a very high level.

Read more…
tdsops_tm_tdsopsNone

Tridiagonal Solver Operators class.

Read more…
time_intg_tm_time_integratorNone
vector_calculus_tm_vector_calculusNone

Defines vector calculus operators

+ +
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_allocator.html b/api/module/m_allocator.html new file mode 100644 index 000000000..2506f94c0 --- /dev/null +++ b/api/module/m_allocator.html @@ -0,0 +1,962 @@ + + + + + + + + + + + + + m_allocator – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_allocator + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface allocator_t +

+
+
    +
  • +

    + public function allocator_init(mesh, sz) result(allocator) + +

    + + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + type(mesh_t), + intent(inout), + target + ::mesh + +
    + + integer, + intent(in) + + ::sz + +
    + +

    + Return Value + type(allocator_t) +

    + + +
  • +
+
+ +
+
+ + +
+

Derived Types

+
+
+ +

+ type, public ::  + allocator_t + +

+
+
+

An instance of type allocator_t is responsible for the +maintenance of a linked list of instances of equal size +m_allocator objects:

Read more… + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + integer, + public + + ::ngrid + +
+ + integer, + public + + ::next_id =0 +

The id for the next allocated block. This counter is +incremented each time a new block is allocated.

+
+ + class(mesh_t), + public, + pointer + ::mesh +

The pointer to the first block on the list. Non associated if +the list is empty

+
+ + class(field_t), + public, + pointer + ::first=> null() + +
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + allocator_init + (mesh, sz) +
+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
procedure, public :: + get_block
procedure, public :: + release_block
procedure, public :: + create_block
procedure, public :: + get_block_ids
procedure, public :: + destroy
procedure, public :: + compute_padded_dims
+
+
+ +
+
+ +

+ type, public ::  + flist_t + +

+
+
+ + +

Components

+ + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + class(field_t), + public, + pointer + ::ptr + +
+ + + + +
+
+ +
+
+ +
+

Functions

+
+

public function allocator_init(mesh, sz) result(allocator) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + type(mesh_t), + intent(inout), + target + ::mesh + +
+ + integer, + intent(in) + + ::sz + +
+ +

+ Return Value + type(allocator_t) +

+ + +
+
+ +
+

public function create_block(self, next) result(ptr) +

+
+ +

Allocate memory for a new block and return a pointer to a new +m_allocator object.

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ + type(field_t), + intent(in), + pointer + ::next + +
+ +

+ Return Value + class(field_t), pointer +

+ + +
+
+ +
+

public function get_block(self, dir, data_loc) result(handle) +

+
+ +

Return a pointer to the first available memory block, i.e. the +current head of the block list. If the list is empty, allocate +a new block with create_block +first.

Read more… + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ + integer, + intent(in) + + ::dir + +
+ + integer, + intent(in),optional + + ::data_loc + +
+ +

+ Return Value + class(field_t), pointer +

+ + +
+
+ +
+

public function get_block_ids(self) +

+
+ +

Utility function that returns a array made of the id of the +block currently in the block list. Return the array [0] if +block list is empty.

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ +

+ Return Value + integer, allocatable, (:) +

+ + +
+
+ +
+
+ +
+

Subroutines

+
+

public subroutine compute_padded_dims(self, sz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ + integer, + intent(in) + + ::sz + +
+ + +
+
+ +
+

public subroutine release_block(self, handle) +

+
+ +

Release memory block pointed to by HANDLE to the block list. +It is pushed to the front of the block list, in other words it +is made the head block.

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ + class(field_t), + + pointer + ::handle + +
+ + +
+
+ +
+

public subroutine destroy(self) +

+
+ +

Go through the block list from head to tail, deallocating each +memory block in turn. Deallocation of a +m_allocator object automatically +deallocates its internal allocatable +data array.

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_base_backend.html b/api/module/m_base_backend.html new file mode 100644 index 000000000..4d3c85349 --- /dev/null +++ b/api/module/m_base_backend.html @@ -0,0 +1,1754 @@ + + + + + + + + + + + + + m_base_backend – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_base_backend + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + + +
+

Abstract Interfaces

+
+

abstract interface + +

+
    +
  • +

    + public subroutine transeq_ders(self, du, dv, dw, u, v, w, dirps) + +

    +

    transeq equation obtains the derivatives direction by +direction, and the exact algorithm used to obtain these +derivatives are decided at runtime. Backend implementations +are responsible from directing calls to transeq_ders into +the correct algorithm.

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t) + + + ::self + +
    + + class(field_t), + intent(inout) + + ::du + +
    + + class(field_t), + intent(inout) + + ::dv + +
    + + class(field_t), + intent(inout) + + ::dw + +
    + + class(field_t), + intent(in) + + ::u + +
    + + class(field_t), + intent(in) + + ::v + +
    + + class(field_t), + intent(in) + + ::w + +
    + + type(dirps_t), + intent(in) + + ::dirps + +
    + + +
  • +
+
+ +
+

abstract interface + +

+
    +
  • +

    + public subroutine tds_solve(self, du, u, tdsops) + +

    +

    transeq equation obtains the derivatives direction by +direction, and the exact algorithm used to obtain these +derivatives are decided at runtime. Backend implementations +are responsible from directing calls to tds_solve to the +correct algorithm.

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t) + + + ::self + +
    + + class(field_t), + intent(inout) + + ::du + +
    + + class(field_t), + intent(in) + + ::u + +
    + + class(tdsops_t), + intent(in) + + ::tdsops + +
    + + +
  • +
+
+ +
+

abstract interface + +

+
    +
  • +

    + public subroutine reorder(self, u_, u, direction) + +

    +

    reorder subroutines are straightforward, they rearrange +data into our specialist data structure so that regardless +of the direction tridiagonal systems are solved efficiently +and fast.

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t) + + + ::self + +
    + + class(field_t), + intent(inout) + + ::u_ + +
    + + class(field_t), + intent(in) + + ::u + +
    + + integer, + intent(in) + + ::direction + +
    + + +
  • +
+
+ +
+

abstract interface + +

+
    +
  • +

    + public subroutine sum_intox(self, u, u_) + +

    +

    sum9into3 subroutine combines all the directional velocity +derivatives into the corresponding x directional fields.

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t) + + + ::self + +
    + + class(field_t), + intent(inout) + + ::u + +
    + + class(field_t), + intent(in) + + ::u_ + +
    + + +
  • +
+
+ +
+

abstract interface + +

+
    +
  • +

    + public subroutine vecadd(self, a, x, b, y) + +

    +

    adds two vectors together: y = ax + by

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t) + + + ::self + +
    + + real(kind=dp), + intent(in) + + ::a + +
    + + class(field_t), + intent(in) + + ::x + +
    + + real(kind=dp), + intent(in) + + ::b + +
    + + class(field_t), + intent(inout) + + ::y + +
    + + +
  • +
+
+ +
+

abstract interface + +

+
    +
  • +

    + public function scalar_product(self, x, y) result(s) + +

    +

    Calculates the scalar product of two input fields

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t) + + + ::self + +
    + + class(field_t), + intent(in) + + ::x + +
    + + class(field_t), + intent(in) + + ::y + +
    + +

    + Return Value + real(kind=dp) +

    + + +
  • +
+
+ +
+

abstract interface + +

+
    +
  • +

    + public subroutine copy_data_to_f(self, f, data) + +

    +

    Copy the specialist data structure from device or host back +to a regular 3D data array in host memory.

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t), + intent(inout) + + ::self + +
    + + class(field_t), + intent(inout) + + ::f + +
    + + real(kind=dp), + intent(in), + dimension(:, :, :) + ::data + +
    + + +
  • +
+
+ +
+

abstract interface + +

+
    +
  • +

    + public subroutine copy_f_to_data(self, data, f) + +

    +

    Copy a regular 3D array in host memory into the specialist +data structure field that lives on device or host

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t), + intent(inout) + + ::self + +
    + + real(kind=dp), + intent(out), + dimension(:, :, :) + ::data + +
    + + class(field_t), + intent(in) + + ::f + +
    + + +
  • +
+
+ +
+

abstract interface + +

+
    +
  • +

    + public subroutine alloc_tdsops(self, tdsops, dir, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) + +

    + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t) + + + ::self + +
    + + class(tdsops_t), + intent(inout), + allocatable + ::tdsops + +
    + + integer, + intent(in) + + ::dir + +
    + + character(len=*), + intent(in) + + ::operation + +
    + + character(len=*), + intent(in) + + ::scheme + +
    + + integer, + intent(in),optional + + ::n_halo + +
    + + character(len=*), + intent(in),optional + + ::from_to + +
    + + character(len=*), + intent(in),optional + + ::bc_start + +
    + + character(len=*), + intent(in),optional + + ::bc_end + +
    + + logical, + intent(in),optional + + ::sym + +
    + + real(kind=dp), + intent(in),optional + + ::c_nu + +
    + + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
    + + +
  • +
+
+ +
+

abstract interface + +

+
    +
  • +

    + public subroutine init_poisson_fft(self, mesh, xdirps, ydirps, zdirps) + +

    + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t) + + + ::self + +
    + + class(mesh_t), + intent(in) + + ::mesh + +
    + + type(dirps_t), + intent(in) + + ::xdirps + +
    + + type(dirps_t), + intent(in) + + ::ydirps + +
    + + type(dirps_t), + intent(in) + + ::zdirps + +
    + + +
  • +
+
+ +
+
+ +
+

Derived Types

+
+
+ +

+ type, public, abstract ::  + base_backend_t + +

+
+
+

base_backend class defines all the abstract operations that the +solver class requires.

Read more… + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + real(kind=dp), + public + + ::nu + +
+ + class(mesh_t), + public, + pointer + ::mesh + +
+ + class(allocator_t), + public, + pointer + ::allocator + +
+ + class(poisson_fft_t), + public, + pointer + ::poisson_fft + +
+ + + + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
procedure(transeq_ders), public, deferred :: + transeq_x
procedure(transeq_ders), public, deferred :: + transeq_y
procedure(transeq_ders), public, deferred :: + transeq_z
procedure(tds_solve), public, deferred :: + tds_solve
procedure(reorder), public, deferred :: + reorder
procedure(sum_intox), public, deferred :: + sum_yintox
procedure(sum_intox), public, deferred :: + sum_zintox
procedure(vecadd), public, deferred :: + vecadd
procedure(scalar_product), public, deferred :: + scalar_product
procedure(copy_data_to_f), public, deferred :: + copy_data_to_f
procedure(copy_f_to_data), public, deferred :: + copy_f_to_data
procedure(alloc_tdsops), public, deferred :: + alloc_tdsops
procedure(init_poisson_fft), public, deferred :: + init_poisson_fft
procedure, public :: + base_init
procedure, public :: + get_field_data
procedure, public :: + set_field_data
+
+
+ +
+
+ + +
+

Subroutines

+
+

public subroutine base_init(self) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + +
+
+ +
+

public subroutine get_field_data(self, data, f, dir) +

+
+ +

Extract data from field f optionally reordering into dir orientation. +To output in same orientation as f, use call ...%get_field_data(data, f, f%dir)

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::data +

Output array

+
+ + class(field_t), + intent(in) + + ::f +

Field

+
+ + integer, + intent(in),optional + + ::dir +

Desired orientation of output array (defaults to Cartesian)

+
+ + +
+
+ +
+

public subroutine set_field_data(self, f, data, dir) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::f +

Field

+
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::data +

Input array

+
+ + integer, + intent(in),optional + + ::dir +

Orientation of input array (defaults to Cartesian)

+
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_common.html b/api/module/m_common.html new file mode 100644 index 000000000..63ed32001 --- /dev/null +++ b/api/module/m_common.html @@ -0,0 +1,986 @@ + + + + + + + + + + + + + m_common – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_common + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ + + +
+ + +
+

Variables

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + integer, + public, +parameter + ::dp =kind(0.0d0) + +
+ + real(kind=dp), + public, +parameter + ::pi =4*atan(1.0_dp) + +
+ + integer, + public, +parameter + ::RDR_X2Y =12 + +
+ + integer, + public, +parameter + ::RDR_X2Z =13 + +
+ + integer, + public, +parameter + ::RDR_Y2X =21 + +
+ + integer, + public, +parameter + ::RDR_Y2Z =23 + +
+ + integer, + public, +parameter + ::RDR_Z2X =31 + +
+ + integer, + public, +parameter + ::RDR_Z2Y =32 + +
+ + integer, + public, +parameter + ::RDR_C2X =41 + +
+ + integer, + public, +parameter + ::RDR_C2Y =42 + +
+ + integer, + public, +parameter + ::RDR_C2Z =43 + +
+ + integer, + public, +parameter + ::RDR_X2C =14 + +
+ + integer, + public, +parameter + ::RDR_Y2C =24 + +
+ + integer, + public, +parameter + ::RDR_Z2C =34 + +
+ + integer, + public, +parameter + ::DIR_X =1 + +
+ + integer, + public, +parameter + ::DIR_Y =2 + +
+ + integer, + public, +parameter + ::DIR_Z =3 + +
+ + integer, + public, +parameter + ::DIR_C =4 + +
+ + integer, + public, +parameter + ::POISSON_SOLVER_FFT =0 + +
+ + integer, + public, +parameter + ::POISSON_SOLVER_CG =1 + +
+ + integer, + public, +parameter + ::VERT =0000 + +
+ + integer, + public, +parameter + ::CELL =1110 + +
+ + integer, + public, +parameter + ::X_FACE =0110 + +
+ + integer, + public, +parameter + ::Y_FACE =1010 + +
+ + integer, + public, +parameter + ::Z_FACE =1100 + +
+ + integer, + public, +parameter + ::X_EDGE =1000 + +
+ + integer, + public, +parameter + ::Y_EDGE =0100 + +
+ + integer, + public, +parameter + ::Z_EDGE =0010 + +
+ + integer, + public, +parameter + ::none =-0001 + +
+ + integer, + public, +parameter + ::BC_PERIODIC =0 + +
+ + integer, + public, +parameter + ::BC_NEUMANN =1 + +
+ + integer, + public, +parameter + ::BC_DIRICHLET =2 + +
+ + integer, + protected + + ::rdr_map(4,4) =reshape([0, RDR_Y2X, RDR_Z2X, RDR_C2X, RDR_X2Y, 0, RDR_Z2Y, RDR_C2Y, RDR_X2Z, RDR_Y2Z, 0, RDR_C2Z, RDR_X2C, RDR_Y2C, RDR_Z2C, 0], shape=[4, 4]) + +
+ +
+
+ + + + + +
+

Functions

+
+

public pure function get_rdr_from_dirs(dir_from, dir_to) result(rdr_dir) +

+
+ +

Returns RDR_?2? value based on two direction inputs

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::dir_from + +
+ + integer, + intent(in) + + ::dir_to + +
+ +

+ Return Value + integer +

+ + +
+
+ +
+
+ +
+

Subroutines

+
+

public pure subroutine get_dirs_from_rdr(dir_from, dir_to, rdr_dir) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::dir_from + +
+ + integer, + intent(out) + + ::dir_to + +
+ + integer, + intent(in) + + ::rdr_dir + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_allocator.html b/api/module/m_cuda_allocator.html new file mode 100644 index 000000000..fd74b707a --- /dev/null +++ b/api/module/m_cuda_allocator.html @@ -0,0 +1,1024 @@ + + + + + + + + + + + + + m_cuda_allocator – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_allocator + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface cuda_allocator_t +

+
+
    +
  • +

    + public function cuda_allocator_init(mesh, sz) result(allocator) + +

    + + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(mesh_t), + intent(inout) + + ::mesh + +
    + + integer, + intent(in) + + ::sz + +
    + +

    + Return Value + type(cuda_allocator_t) +

    + + +
  • +
+
+ +
+
+ +

public interface cuda_field_t +

+
+
    +
  • +

    + public function cuda_field_init(ngrid, next, id) result(f) + +

    + + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + integer, + intent(in) + + ::ngrid + +
    + + type(cuda_field_t), + intent(in), + pointer + ::next + +
    + + integer, + intent(in) + + ::id + +
    + +

    + Return Value + type(cuda_field_t) +

    + + +
  • +
+
+ +
+
+ + +
+

Derived Types

+
+
+ +

+ type, public, extends(allocator_t) ::  + cuda_allocator_t + +

+
+
+ + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + integer, + public + + ::ngrid + +
+ + integer, + public + + ::next_id =0 +

The id for the next allocated block. This counter is +incremented each time a new block is allocated.

+
+ + class(mesh_t), + public, + pointer + ::mesh +

The pointer to the first block on the list. Non associated if +the list is empty

+
+ + class(field_t), + public, + pointer + ::first=> null() + +
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + cuda_allocator_init + (mesh, sz) +
+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
procedure, public :: + get_block
procedure, public :: + release_block
procedure, public :: + get_block_ids
procedure, public :: + destroy
procedure, public :: + compute_padded_dims
procedure, public :: + create_block => create_cuda_block
+
+
+ +
+
+ +

+ type, public, extends(field_t) ::  + cuda_field_t + +

+
+
+ + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + class(field_t), + public, + pointer + ::next + +
+ + real(kind=dp), + public, + pointer, contiguous + ::data(:,:,:) + +
+ + integer, + public + + ::dir + +
+ + integer, + public + + ::data_loc + +
+ + integer, + public + + ::refcount =0 + +
+ + integer, + public + + ::id +

An integer identifying the memory block.

+
+ + real(kind=dp), + public, + device, pointer, contiguous + ::data_d(:,:,:) + +
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + cuda_field_init + (ngrid, next, id) +
+ + +

Type-Bound Procedures

+ + + + + + + + + + + +
procedure, public :: + set_data_loc
procedure, public :: + set_shape => set_shape_cuda
+
+
+ +
+
+ +
+

Functions

+
+

public function cuda_field_init(ngrid, next, id) result(f) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::ngrid + +
+ + type(cuda_field_t), + intent(in), + pointer + ::next + +
+ + integer, + intent(in) + + ::id + +
+ +

+ Return Value + type(cuda_field_t) +

+ + +
+
+ +
+

public function cuda_allocator_init(mesh, sz) result(allocator) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout) + + ::mesh + +
+ + integer, + intent(in) + + ::sz + +
+ +

+ Return Value + type(cuda_allocator_t) +

+ + +
+
+ +
+

public function create_cuda_block(self, next) result(ptr) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_allocator_t), + intent(inout) + + ::self + +
+ + type(cuda_field_t), + intent(in), + pointer + ::next + +
+ +

+ Return Value + class(field_t), pointer +

+ + +
+
+ +
+
+ +
+

Subroutines

+
+

public subroutine set_shape_cuda(self, dims) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_field_t) + + + ::self + +
+ + integer, + intent(in) + + ::dims(3) + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_backend.html b/api/module/m_cuda_backend.html new file mode 100644 index 000000000..bcc055cac --- /dev/null +++ b/api/module/m_cuda_backend.html @@ -0,0 +1,3125 @@ + + + + + + + + + + + + + m_cuda_backend – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_backend + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface cuda_backend_t +

+
+
    +
  • +

    + public function init(mesh, allocator) result(backend) + +

    + + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(mesh_t), + intent(inout), + target + ::mesh + +
    + + class(allocator_t), + intent(inout), + target + ::allocator + +
    + +

    + Return Value + type(cuda_backend_t) +

    + + +
  • +
+
+ +
+
+ + +
+

Derived Types

+
+
+ +

+ type, public, extends(base_backend_t) ::  + cuda_backend_t + +

+
+
+ + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + real(kind=dp), + public + + ::nu + +
+ + class(mesh_t), + public, + pointer + ::mesh + +
+ + class(allocator_t), + public, + pointer + ::allocator + +
+ + class(poisson_fft_t), + public, + pointer + ::poisson_fft + +
+ + integer, + public + + ::MPI_FP_PREC =dp + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::u_recv_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::u_recv_e_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::u_send_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::u_send_e_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::v_recv_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::v_recv_e_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::v_send_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::v_send_e_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::w_recv_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::w_recv_e_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::w_send_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::w_send_e_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::du_send_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::du_send_e_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::du_recv_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::du_recv_e_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::dud_send_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::dud_send_e_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::dud_recv_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::dud_recv_e_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::d2u_send_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::d2u_send_e_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::d2u_recv_s_dev + +
+ + real(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::d2u_recv_e_dev + +
+ + type(dim3), + public + + ::xblocks + +
+ + type(dim3), + public + + ::xthreads + +
+ + type(dim3), + public + + ::yblocks + +
+ + type(dim3), + public + + ::ythreads + +
+ + type(dim3), + public + + ::zblocks + +
+ + type(dim3), + public + + ::zthreads + +
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + init + (mesh, allocator) +
+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
procedure, public :: + base_init
procedure, public :: + get_field_data
procedure, public :: + set_field_data
procedure, public :: + alloc_tdsops => alloc_cuda_tdsops
procedure, public :: + transeq_x => transeq_x_cuda
procedure, public :: + transeq_y => transeq_y_cuda
procedure, public :: + transeq_z => transeq_z_cuda
procedure, public :: + tds_solve => tds_solve_cuda
procedure, public :: + reorder => reorder_cuda
procedure, public :: + sum_yintox => sum_yintox_cuda
procedure, public :: + sum_zintox => sum_zintox_cuda
procedure, public :: + vecadd => vecadd_cuda
procedure, public :: + scalar_product => scalar_product_cuda
procedure, public :: + copy_data_to_f => copy_data_to_f_cuda
procedure, public :: + copy_f_to_data => copy_f_to_data_cuda
procedure, public :: + init_poisson_fft => init_cuda_poisson_fft
procedure, public :: + transeq_cuda_dist
procedure, public :: + transeq_cuda_thom
procedure, public :: + tds_solve_dist
+
+
+ +
+
+ +
+

Functions

+
+

public function init(mesh, allocator) result(backend) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout), + target + ::mesh + +
+ + class(allocator_t), + intent(inout), + target + ::allocator + +
+ +

+ Return Value + type(cuda_backend_t) +

+ + +
+
+ +
+

public function scalar_product_cuda(self, x, y) result(s) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(in) + + ::x + +
+ + class(field_t), + intent(in) + + ::y + +
+ +

+ Return Value + real(kind=dp) +

+ + +
+
+ +
+
+ +
+

Subroutines

+
+

public subroutine alloc_cuda_tdsops(self, tdsops, dir, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(tdsops_t), + intent(inout), + allocatable + ::tdsops + +
+ + integer, + intent(in) + + ::dir + +
+ + character(len=*), + intent(in) + + ::operation + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + integer, + intent(in),optional + + ::n_halo + +
+ + character(len=*), + intent(in),optional + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + real(kind=dp), + intent(in),optional + + ::c_nu + +
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
+ + +
+
+ +
+

public subroutine transeq_x_cuda(self, du, dv, dw, u, v, w, dirps) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ + +
+
+ +
+

public subroutine transeq_y_cuda(self, du, dv, dw, u, v, w, dirps) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ + +
+
+ +
+

public subroutine transeq_z_cuda(self, du, dv, dw, u, v, w, dirps) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ + +
+
+ +
+

public subroutine transeq_cuda_dist(self, du, dv, dw, u, v, w, dirps, blocks, threads) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ + type(dim3), + intent(in) + + ::blocks + +
+ + type(dim3), + intent(in) + + ::threads + +
+ + +
+
+ +
+

public subroutine transeq_cuda_thom(self, du, dv, dw, u, v, w, dirps) +

+
+ +

Thomas algorithm implementation. So much more easier than the +distributed algorithm. It is intended to work only on a single rank +so there is no MPI communication.

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ + +
+
+ +
+

public subroutine tds_solve_cuda(self, du, u, tdsops) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(tdsops_t), + intent(in) + + ::tdsops + +
+ + +
+
+ +
+

public subroutine tds_solve_dist(self, du, u, tdsops, blocks, threads) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(tdsops_t), + intent(in) + + ::tdsops + +
+ + type(dim3), + intent(in) + + ::blocks + +
+ + type(dim3), + intent(in) + + ::threads + +
+ + +
+
+ +
+

public subroutine reorder_cuda(self, u_o, u_i, direction) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u_o + +
+ + class(field_t), + intent(in) + + ::u_i + +
+ + integer, + intent(in) + + ::direction + +
+ + +
+
+ +
+

public subroutine sum_yintox_cuda(self, u, u_y) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u + +
+ + class(field_t), + intent(in) + + ::u_y + +
+ + +
+
+ +
+

public subroutine sum_zintox_cuda(self, u, u_z) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u + +
+ + class(field_t), + intent(in) + + ::u_z + +
+ + +
+
+ +
+

public subroutine vecadd_cuda(self, a, x, b, y) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + real(kind=dp), + intent(in) + + ::a + +
+ + class(field_t), + intent(in) + + ::x + +
+ + real(kind=dp), + intent(in) + + ::b + +
+ + class(field_t), + intent(inout) + + ::y + +
+ + +
+
+ +
+

public subroutine copy_into_buffers(u_send_s_dev, u_send_e_dev, u_dev, n) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_send_s_dev + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_send_e_dev + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_dev + +
+ + integer, + intent(in) + + ::n + +
+ + +
+
+ +
+

public subroutine copy_data_to_f_cuda(self, f, data) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t), + intent(inout) + + ::self + +
+ + class(field_t), + intent(inout) + + ::f + +
+ + real(kind=dp), + intent(inout), + dimension(:, :, :) + ::data + +
+ + +
+
+ +
+

public subroutine copy_f_to_data_cuda(self, data, f) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::data + +
+ + class(field_t), + intent(in) + + ::f + +
+ + +
+
+ +
+

public subroutine init_cuda_poisson_fft(self, mesh, xdirps, ydirps, zdirps) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + type(dirps_t), + intent(in) + + ::xdirps + +
+ + type(dirps_t), + intent(in) + + ::ydirps + +
+ + type(dirps_t), + intent(in) + + ::zdirps + +
+ + +
+
+ +
+

public subroutine resolve_field_t(u_dev, u) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, pointer, dimension(:, :, :) + ::u_dev + +
+ + class(field_t), + intent(in) + + ::u + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_common.html b/api/module/m_cuda_common.html new file mode 100644 index 000000000..76152dff1 --- /dev/null +++ b/api/module/m_cuda_common.html @@ -0,0 +1,250 @@ + + + + + + + + + + + + + m_cuda_common – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_common + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ + + +
+ + +
+

Variables

+ + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + integer, + public, +parameter + ::SZ =32 + +
+ +
+
+ + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_exec_dist.html b/api/module/m_cuda_exec_dist.html new file mode 100644 index 000000000..f334fe6be --- /dev/null +++ b/api/module/m_cuda_exec_dist.html @@ -0,0 +1,949 @@ + + + + + + + + + + + + + m_cuda_exec_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_exec_dist + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+ +
+ + +
+ + + + + + + + +
+

Subroutines

+
+

public subroutine exec_dist_tds_compact(du, u, u_recv_s, u_recv_e, du_send_s, du_send_e, du_recv_s, du_recv_e, tdsops, nproc, pprev, pnext, blocks, threads) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_recv_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_send_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_send_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_recv_e + +
+ + type(cuda_tdsops_t), + intent(in) + + ::tdsops + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::pprev + +
+ + integer, + intent(in) + + ::pnext + +
+ + type(dim3), + intent(in) + + ::blocks + +
+ + type(dim3), + intent(in) + + ::threads + +
+ + +
+
+ +
+

public subroutine exec_dist_transeq_3fused(r_u, u, u_recv_s, u_recv_e, v, v_recv_s, v_recv_e, du, dud, d2u, du_send_s, du_send_e, du_recv_s, du_recv_e, dud_send_s, dud_send_e, dud_recv_s, dud_recv_e, d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e, der1st, der2nd, nu, nproc, pprev, pnext, blocks, threads) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::r_u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_recv_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_recv_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v_recv_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_send_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_send_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud_send_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud_send_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u_send_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u_send_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u_recv_e + +
+ + type(cuda_tdsops_t), + intent(in) + + ::der1st + +
+ + type(cuda_tdsops_t), + intent(in) + + ::der2nd + +
+ + real(kind=dp), + intent(in) + + ::nu + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::pprev + +
+ + integer, + intent(in) + + ::pnext + +
+ + type(dim3), + intent(in) + + ::blocks + +
+ + type(dim3), + intent(in) + + ::threads + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_exec_thom.html b/api/module/m_cuda_exec_thom.html new file mode 100644 index 000000000..053240124 --- /dev/null +++ b/api/module/m_cuda_exec_thom.html @@ -0,0 +1,335 @@ + + + + + + + + + + + + + m_cuda_exec_thom – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_exec_thom + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + + + + + +
+

Subroutines

+
+

public subroutine exec_thom_tds_compact(du, u, tdsops, blocks, threads) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + type(cuda_tdsops_t), + intent(in) + + ::tdsops + +
+ + type(dim3), + intent(in) + + ::blocks + +
+ + type(dim3), + intent(in) + + ::threads + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_kernels_dist.html b/api/module/m_cuda_kernels_dist.html new file mode 100644 index 000000000..f70cc9a01 --- /dev/null +++ b/api/module/m_cuda_kernels_dist.html @@ -0,0 +1,1296 @@ + + + + + + + + + + + + + m_cuda_kernels_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_kernels_dist + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + + + + + +
+

Subroutines

+
+

public subroutine der_univ_dist(du, send_u_s, send_u_e, u, u_s, u_e, coeffs_s, coeffs_e, coeffs, n, ffr, fbc, faf) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_u_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_u_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :) + ::coeffs_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :) + ::coeffs_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::coeffs + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::ffr + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::fbc + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::faf + +
+ + +
+
+ +
+

public subroutine der_univ_subs(du, recv_u_s, recv_u_e, n, dist_sa, dist_sc) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_u_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_u_e + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::dist_sa + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::dist_sc + +
+ + +
+
+ +
+

public subroutine transeq_3fused_dist(du, dud, d2u, send_du_s, send_du_e, send_dud_s, send_dud_e, send_d2u_s, send_d2u_e, u, u_s, u_e, v, v_s, v_e, n, d1_coeffs_s, d1_coeffs_e, d1_coeffs, d1_fw, d1_bw, d1_af, d2_coeffs_s, d2_coeffs_e, d2_coeffs, d2_fw, d2_bw, d2_af) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_du_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_du_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_dud_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_dud_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_d2u_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_d2u_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v_e + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + device + ::d1_coeffs_s(:,:) + +
+ + real(kind=dp), + intent(in), + device + ::d1_coeffs_e(:,:) + +
+ + real(kind=dp), + intent(in), + device + ::d1_coeffs(:) + +
+ + real(kind=dp), + intent(in), + device + ::d1_fw(:) + +
+ + real(kind=dp), + intent(in), + device + ::d1_bw(:) + +
+ + real(kind=dp), + intent(in), + device + ::d1_af(:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_coeffs_s(:,:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_coeffs_e(:,:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_coeffs(:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_fw(:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_bw(:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_af(:) + +
+ + +
+
+ +
+

public subroutine transeq_3fused_subs(r_u, conv, du, dud, d2u, recv_du_s, recv_du_e, recv_dud_s, recv_dud_e, recv_d2u_s, recv_d2u_e, d1_sa, d1_sc, d2_sa, d2_sc, n, nu) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::r_u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::conv + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::dud + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::d2u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_du_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_du_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_dud_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_dud_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_d2u_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_d2u_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::d1_sa + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::d1_sc + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::d2_sa + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::d2_sc + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + value + ::nu + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_kernels_reorder.html b/api/module/m_cuda_kernels_reorder.html new file mode 100644 index 000000000..1ced1acf5 --- /dev/null +++ b/api/module/m_cuda_kernels_reorder.html @@ -0,0 +1,1261 @@ + + + + + + + + + + + + + m_cuda_kernels_reorder – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_kernels_reorder + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + + + + + +
+

Subroutines

+
+

public subroutine reorder_c2x(u_x, u_c, nz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_c + +
+ + integer, + intent(in), + value + ::nz + +
+ + +
+
+ +
+

public subroutine reorder_x2c(u_c, u_x, nz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_c + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_x + +
+ + integer, + intent(in), + value + ::nz + +
+ + +
+
+ +
+

public subroutine reorder_x2y(u_y, u_x, nz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_y + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_x + +
+ + integer, + intent(in), + value + ::nz + +
+ + +
+
+ +
+

public subroutine reorder_x2z(u_z, u_x, nz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_z + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_x + +
+ + integer, + intent(in), + value + ::nz + +
+ + +
+
+ +
+

public subroutine reorder_y2x(u_x, u_y, nz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_y + +
+ + integer, + intent(in), + value + ::nz + +
+ + +
+
+ +
+

public subroutine reorder_y2z(u_z, u_y, nx, nz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_z + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_y + +
+ + integer, + intent(in), + value + ::nx + +
+ + integer, + intent(in), + value + ::nz + +
+ + +
+
+ +
+

public subroutine reorder_z2x(u_x, u_z, nz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_z + +
+ + integer, + intent(in), + value + ::nz + +
+ + +
+
+ +
+

public subroutine reorder_z2y(u_y, u_z, nx, nz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_y + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_z + +
+ + integer, + intent(in), + value + ::nx + +
+ + integer, + intent(in), + value + ::nz + +
+ + +
+
+ +
+

public subroutine sum_yintox(u_x, u_y, nz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::u_x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_y + +
+ + integer, + intent(in), + value + ::nz + +
+ + +
+
+ +
+

public subroutine sum_zintox(u_x, u_z, nz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::u_x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_z + +
+ + integer, + intent(in), + value + ::nz + +
+ + +
+
+ +
+

public subroutine axpby(n, alpha, x, beta, y) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + value + ::alpha + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::x + +
+ + real(kind=dp), + intent(in), + value + ::beta + +
+ + real(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::y + +
+ + +
+
+ +
+

public subroutine scalar_product(s, x, y, n) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(inout), + device + ::s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::y + +
+ + integer, + intent(in), + value + ::n + +
+ + +
+
+ +
+

public subroutine buffer_copy(u_send_s, u_send_e, u, n, n_halo) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::u_send_s + +
+ + real(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::u_send_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + integer, + intent(in), + value + ::n + +
+ + integer, + intent(in), + value + ::n_halo + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_kernels_thom.html b/api/module/m_cuda_kernels_thom.html new file mode 100644 index 000000000..bf3929264 --- /dev/null +++ b/api/module/m_cuda_kernels_thom.html @@ -0,0 +1,554 @@ + + + + + + + + + + + + + m_cuda_kernels_thom – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_kernels_thom + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + + + + + +
+

Subroutines

+
+

public subroutine der_univ_thom(du, u, coeffs_s, coeffs_e, coeffs, n, thom_f, thom_s, thom_w) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :) + ::coeffs_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :) + ::coeffs_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::coeffs + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_f + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_w + +
+ + +
+
+ +
+

public subroutine der_univ_thom_per(du, u, coeffs, n, alpha, thom_f, thom_s, thom_w, thom_p) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::coeffs + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + value + ::alpha + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_f + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_w + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_p + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_poisson_fft.html b/api/module/m_cuda_poisson_fft.html new file mode 100644 index 000000000..acf083209 --- /dev/null +++ b/api/module/m_cuda_poisson_fft.html @@ -0,0 +1,1113 @@ + + + + + + + + + + + + + m_cuda_poisson_fft – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_poisson_fft + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface cuda_poisson_fft_t +

+
+
    +
  • +

    + private function init(mesh, xdirps, ydirps, zdirps) result(poisson_fft) + +

    + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(mesh_t), + intent(in) + + ::mesh + +
    + + type(dirps_t), + intent(in) + + ::xdirps + +
    + + type(dirps_t), + intent(in) + + ::ydirps + +
    + + type(dirps_t), + intent(in) + + ::zdirps + +
    + +

    + Return Value + type(cuda_poisson_fft_t) +

    + + +
  • +
+
+ +
+
+ + +
+

Derived Types

+
+
+ +

+ type, public, extends(poisson_fft_t) ::  + cuda_poisson_fft_t + +

+
+
+

FFT based Poisson solver

+ +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + integer, + public + + ::nx_glob +

Global dimensions

+
+ + integer, + public + + ::ny_glob +

Global dimensions

+
+ + integer, + public + + ::nz_glob +

Global dimensions

+
+ + integer, + public + + ::nx_loc +

Local dimensions

+
+ + integer, + public + + ::ny_loc +

Local dimensions

+
+ + integer, + public + + ::nz_loc +

Local dimensions

+
+ + integer, + public + + ::nx_perm +

Local dimensions in the permuted slabs

+
+ + integer, + public + + ::ny_perm +

Local dimensions in the permuted slabs

+
+ + integer, + public + + ::nz_perm +

Local dimensions in the permuted slabs

+
+ + integer, + public + + ::nx_spec +

Local dimensions in the permuted slabs in spectral space

+
+ + integer, + public + + ::ny_spec +

Local dimensions in the permuted slabs in spectral space

+
+ + integer, + public + + ::nz_spec +

Local dimensions in the permuted slabs in spectral space

+
+ + integer, + public + + ::y_sp_st +

Offset in y direction in the permuted slabs in spectral space

+
+ + complex(kind=dp), + public, + allocatable, dimension(:, :, :) + ::waves +

Local domain sized array storing the spectral equivalence constants

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::ax +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::bx +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::ay +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::by +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::az +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::bz +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + device, allocatable, dimension(:, :, :) + ::waves_dev +

Local domain sized array storing the spectral equivalence constants

+
+ + real(kind=dp), + public, + device, allocatable, dimension(:) + ::ax_dev +

Wave numbers in x, y, and z

+
+ + real(kind=dp), + public, + device, allocatable, dimension(:) + ::bx_dev +

Wave numbers in x, y, and z

+
+ + real(kind=dp), + public, + device, allocatable, dimension(:) + ::ay_dev +

Wave numbers in x, y, and z

+
+ + real(kind=dp), + public, + device, allocatable, dimension(:) + ::by_dev +

Wave numbers in x, y, and z

+
+ + real(kind=dp), + public, + device, allocatable, dimension(:) + ::az_dev +

Wave numbers in x, y, and z

+
+ + real(kind=dp), + public, + device, allocatable, dimension(:) + ::bz_dev +

Wave numbers in x, y, and z

+
+ + integer, + public + + ::plan3D_fw +

Forward and backward FFT transform plans

+
+ + integer, + public + + ::plan3D_bw +

Forward and backward FFT transform plans

+
+ + type(cudaLibXtDesc), + public, + pointer + ::xtdesc +

cuFFTMp object manages decomposition and data storage

+
+ + +

Constructor

+ + + + + + + + +
+ private + + + function + init + (mesh, xdirps, ydirps, zdirps) +
+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + +
procedure, public :: + base_init
procedure, public :: + waves_set
procedure, public :: + fft_forward => fft_forward_cuda
procedure, public :: + fft_backward => fft_backward_cuda
procedure, public :: + fft_postprocess => fft_postprocess_cuda
+
+
+ +
+
+ + +
+

Subroutines

+
+

public subroutine fft_forward_cuda(self, f) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_poisson_fft_t) + + + ::self + +
+ + class(field_t), + intent(in) + + ::f + +
+ + +
+
+ +
+

public subroutine fft_backward_cuda(self, f) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_poisson_fft_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::f + +
+ + +
+
+ +
+

public subroutine fft_postprocess_cuda(self) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_poisson_fft_t) + + + ::self + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_sendrecv.html b/api/module/m_cuda_sendrecv.html new file mode 100644 index 000000000..657905277 --- /dev/null +++ b/api/module/m_cuda_sendrecv.html @@ -0,0 +1,645 @@ + + + + + + + + + + + + + m_cuda_sendrecv – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_sendrecv + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + + + + + +
+

Subroutines

+
+

public subroutine sendrecv_fields(f_recv_s, f_recv_e, f_send_s, f_send_e, n_data, nproc, prev, next) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f_recv_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f_send_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f_send_e + +
+ + integer, + intent(in) + + ::n_data + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::prev + +
+ + integer, + intent(in) + + ::next + +
+ + +
+
+ +
+

public subroutine sendrecv_3fields(f1_recv_s, f1_recv_e, f2_recv_s, f2_recv_e, f3_recv_s, f3_recv_e, f1_send_s, f1_send_e, f2_send_s, f2_send_e, f3_send_s, f3_send_e, n_data, nproc, prev, next) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f1_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f1_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f2_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f2_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f3_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f3_recv_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f1_send_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f1_send_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f2_send_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f2_send_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f3_send_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f3_send_e + +
+ + integer, + intent(in) + + ::n_data + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::prev + +
+ + integer, + intent(in) + + ::next + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_spectral.html b/api/module/m_cuda_spectral.html new file mode 100644 index 000000000..f92b8ed29 --- /dev/null +++ b/api/module/m_cuda_spectral.html @@ -0,0 +1,469 @@ + + + + + + + + + + + + + m_cuda_spectral – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_spectral + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + + + + + +
+

Subroutines

+
+

public subroutine process_spectral_div_u(div_u, waves, nx_spec, ny_spec, y_sp_st, nx, ny, nz, ax, bx, ay, by, az, bz) +

+
+ +

Post-processes the divergence of velocity in spectral space, including +scaling w.r.t. grid size.

Read more… + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + complex(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::div_u +

Divergence of velocity in spectral space

+
+ + complex(kind=dp), + intent(in), + device, dimension(:, :, :) + ::waves +

Spectral equivalence constants

+
+ + integer, + intent(in), + value + ::nx_spec +

Grid size in spectral space

+
+ + integer, + intent(in), + value + ::ny_spec +

Grid size in spectral space

+
+ + integer, + intent(in), + value + ::y_sp_st +

Offset in y direction in the permuted slabs in spectral space

+
+ + integer, + intent(in), + value + ::nx +

Grid size

+
+ + integer, + intent(in), + value + ::ny +

Grid size

+
+ + integer, + intent(in), + value + ::nz +

Grid size

+
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::ax + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::bx + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::ay + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::by + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::az + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::bz + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_cuda_tdsops.html b/api/module/m_cuda_tdsops.html new file mode 100644 index 000000000..dc71d0bfe --- /dev/null +++ b/api/module/m_cuda_tdsops.html @@ -0,0 +1,1321 @@ + + + + + + + + + + + + + m_cuda_tdsops – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_cuda_tdsops + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface cuda_tdsops_t +

+
+
    +
  • +

    + public function cuda_tdsops_init(n, delta, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) result(tdsops) + +

    + +

    Constructor function for the cuda_tdsops_t class. +See tdsops_t for details.

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + integer, + intent(in) + + ::n + +
    + + real(kind=dp), + intent(in) + + ::delta + +
    + + character(len=*), + intent(in) + + ::operation + +
    + + character(len=*), + intent(in) + + ::scheme + +
    + + integer, + intent(in),optional + + ::n_halo + +
    + + character(len=*), + intent(in),optional + + ::from_to + +
    + + character(len=*), + intent(in),optional + + ::bc_start + +
    + + character(len=*), + intent(in),optional + + ::bc_end + +
    + + logical, + intent(in),optional + + ::sym + +
    + + real(kind=dp), + intent(in),optional + + ::c_nu + +
    + + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
    + +

    + Return Value + type(cuda_tdsops_t) +

    +

    return value of the function

    + +
  • +
+
+ +
+
+ + +
+

Derived Types

+
+
+ +

+ type, public, extends(tdsops_t) ::  + cuda_tdsops_t + +

+
+
+

CUDA extension of the Tridiagonal Solver Operators class.

Read more… + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::dist_fw +

fw/bw phase +back subs. +the auxiliary factors

+
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::dist_bw +

fw/bw phase +back subs. +the auxiliary factors

+
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::dist_sa +

fw/bw phase +back subs. +the auxiliary factors

+
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::dist_sc +

fw/bw phase +back subs. +the auxiliary factors

+
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::dist_af +

fw/bw phase +back subs. +the auxiliary factors

+
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::thom_f + +
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::thom_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::thom_w + +
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::thom_p + +
+ + real(kind=dp), + public, + allocatable + ::coeffs(:) + +
+ + real(kind=dp), + public, + allocatable + ::coeffs_s(:,:) + +
+ + real(kind=dp), + public, + allocatable + ::coeffs_e(:,:) + +
+ + real(kind=dp), + public + + ::alpha + +
+ + real(kind=dp), + public + + ::a + +
+ + real(kind=dp), + public + + ::b + +
+ + real(kind=dp), + public + + ::c =0._dp + +
+ + real(kind=dp), + public + + ::d =0._dp + +
+ + logical, + public + + ::periodic + +
+ + integer, + public + + ::tds_n + +
+ + integer, + public + + ::dir + +
+ + integer, + public + + ::n_halo + +
+ + real(kind=dp), + public, + device, allocatable + ::dist_fw_dev(:) + +
+ + real(kind=dp), + public, + device, allocatable + ::dist_bw_dev(:) + +
+ + real(kind=dp), + public, + device, allocatable + ::dist_sa_dev(:) + +
+ + real(kind=dp), + public, + device, allocatable + ::dist_sc_dev(:) + +
+ + real(kind=dp), + public, + device, allocatable + ::dist_af_dev(:) + +
+ + real(kind=dp), + public, + device, allocatable + ::thom_f_dev(:) + +
+ + real(kind=dp), + public, + device, allocatable + ::thom_s_dev(:) + +
+ + real(kind=dp), + public, + device, allocatable + ::thom_w_dev(:) + +
+ + real(kind=dp), + public, + device, allocatable + ::thom_p_dev(:) + +
+ + real(kind=dp), + public, + device, allocatable + ::coeffs_dev(:) + +
+ + real(kind=dp), + public, + device, allocatable + ::coeffs_s_dev(:,:) + +
+ + real(kind=dp), + public, + device, allocatable + ::coeffs_e_dev(:,:) + +
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + cuda_tdsops_init + (n, delta, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) +

Constructor function for the cuda_tdsops_t class. +See tdsops_t for details.

+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
procedure, public :: + stagder_1st
procedure, public :: + interpl_mid
procedure, public :: + deriv_2nd
procedure, public :: + deriv_1st
procedure, public :: + preprocess_thom
procedure, public :: + preprocess_dist
+
+
+ +
+
+ +
+

Functions

+
+

public function cuda_tdsops_init(n, delta, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) result(tdsops) +

+
+ +

Constructor function for the cuda_tdsops_t class. +See tdsops_t for details.

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::n + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::operation + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + integer, + intent(in),optional + + ::n_halo + +
+ + character(len=*), + intent(in),optional + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + real(kind=dp), + intent(in),optional + + ::c_nu + +
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
+ +

+ Return Value + type(cuda_tdsops_t) +

+

return value of the function

+ +
+
+ +
+
+ + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_field.html b/api/module/m_field.html new file mode 100644 index 000000000..622b8efeb --- /dev/null +++ b/api/module/m_field.html @@ -0,0 +1,717 @@ + + + + + + + + + + + + + m_field – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_field + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface field_t +

+
+
    +
  • +

    + public function field_init(ngrid, next, id) result(f) + +

    + + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + integer, + intent(in) + + ::ngrid + +
    + + type(field_t), + intent(in), + pointer + ::next + +
    + + integer, + intent(in) + + ::id + +
    + +

    + Return Value + type(field_t) +

    + + +
  • +
+
+ +
+
+ + +
+

Derived Types

+
+
+ +

+ type, public ::  + field_t + +

+
+
+

Memory block type holding both a data field and a pointer +to the next block. The field_t type also holds a integer +refcount that counts the number of references to this +field. User code is currently responsible for incrementing +the reference count.

+ +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + class(field_t), + public, + pointer + ::next + +
+ + real(kind=dp), + public, + pointer, contiguous + ::data(:,:,:) + +
+ + integer, + public + + ::dir + +
+ + integer, + public + + ::data_loc + +
+ + integer, + public + + ::refcount =0 + +
+ + integer, + public + + ::id +

An integer identifying the memory block.

+
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + field_init + (ngrid, next, id) +
+ + +

Type-Bound Procedures

+ + + + + + + + + + + +
procedure, public :: + set_shape
procedure, public :: + set_data_loc
+
+
+ +
+
+ +
+

Functions

+
+

public function field_init(ngrid, next, id) result(f) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::ngrid + +
+ + type(field_t), + intent(in), + pointer + ::next + +
+ + integer, + intent(in) + + ::id + +
+ +

+ Return Value + type(field_t) +

+ + +
+
+ +
+
+ +
+

Subroutines

+
+

public subroutine set_data_loc(self, data_loc) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(field_t) + + + ::self + +
+ + integer, + intent(in) + + ::data_loc + +
+ + +
+
+ +
+

public subroutine set_shape(self, dims) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(field_t) + + + ::self + +
+ + integer, + intent(in) + + ::dims(3) + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_mesh.html b/api/module/m_mesh.html new file mode 100644 index 000000000..7effbb3f1 --- /dev/null +++ b/api/module/m_mesh.html @@ -0,0 +1,2075 @@ + + + + + + + + + + + + + m_mesh – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_mesh + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface mesh_t +

+
+
    +
  • +

    + public function mesh_init(dims_global, nproc_dir, L_global, BC_x, BC_y, BC_z) result(mesh) + +

    + +

    Completely initialise the mesh object. +Upon initialisation the mesh object can be read-only and shouldn't be edited +Takes as argument global information about the mesh like its length, number of cells and decomposition in each direction

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + integer, + intent(in), + dimension(3) + ::dims_global + +
    + + integer, + intent(in), + dimension(3) + ::nproc_dir + +
    + + real(kind=dp), + intent(in), + dimension(3) + ::L_global + +
    + + character(len=*), + intent(in), + dimension(2) + ::BC_x + +
    + + character(len=*), + intent(in), + dimension(2) + ::BC_y + +
    + + character(len=*), + intent(in), + dimension(2) + ::BC_z + +
    + +

    + Return Value + type(mesh_t) +

    + + +
  • +
+
+ +
+
+ + +
+

Derived Types

+
+
+ +

+ type, public ::  + geo_t + +

+
+
+ + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + real(kind=dp), + public, + dimension(3) + ::d + +
+ + real(kind=dp), + public, + dimension(3) + ::L + +
+ + + + +
+
+ +
+
+ +

+ type, public ::  + parallel_t + +

+
+
+ + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + integer, + public + + ::nrank + +
+ + integer, + public + + ::nproc + +
+ + integer, + public, + dimension(3) + ::nrank_dir + +
+ + integer, + public, + dimension(3) + ::nproc_dir + +
+ + integer, + public, + dimension(3) + ::n_offset + +
+ + integer, + public, + dimension(3) + ::pnext + +
+ + integer, + public, + dimension(3) + ::pprev + +
+ + + + +

Type-Bound Procedures

+ + + + + + + +
procedure, public :: + is_root
+
+
+ +
+
+ +

+ type, public ::  + mesh_t + +

+
+
+ + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + type(geo_t), + public, + allocatable + ::geo + +
+ + type(parallel_t), + public, + allocatable + ::par + +
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + mesh_init + (dims_global, nproc_dir, L_global, BC_x, BC_y, BC_z) +

Completely initialise the mesh object. +Upon initialisation the mesh object can be read-only and shouldn't be edited +Takes as argument global information about the mesh like its length, number of cells and decomposition in each direction

+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
procedure, public :: + get_SZ => get_sz
procedure, public :: + get_dims
procedure, public :: + get_global_dims
procedure, public :: + get_n_groups_dir
procedure, public :: + get_n_groups_phi
generic, public :: + get_n_groups => get_n_groups_dir, get_n_groups_phi
procedure, public :: + get_field_dims_dir
procedure, public :: + get_field_dims_phi
procedure, public :: + get_field_dims_phi_dataloc
generic, public :: + get_field_dims => get_field_dims_dir, get_field_dims_phi, get_field_dims_phi_dataloc
procedure, public :: + get_n_dir
procedure, public :: + get_n_phi
generic, public :: + get_n => get_n_dir, get_n_phi
procedure, public :: + get_padded_dims_phi
procedure, public :: + get_padded_dims_dir
generic, public :: + get_padded_dims => get_padded_dims_dir, get_padded_dims_phi
procedure, public :: + get_coordinates
procedure, public :: + set_sz
procedure, public :: + set_padded_dims
+
+
+ +
+
+ +
+

Functions

+
+

public function mesh_init(dims_global, nproc_dir, L_global, BC_x, BC_y, BC_z) result(mesh) +

+
+ +

Completely initialise the mesh object. +Upon initialisation the mesh object can be read-only and shouldn't be edited +Takes as argument global information about the mesh like its length, number of cells and decomposition in each direction

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in), + dimension(3) + ::dims_global + +
+ + integer, + intent(in), + dimension(3) + ::nproc_dir + +
+ + real(kind=dp), + intent(in), + dimension(3) + ::L_global + +
+ + character(len=*), + intent(in), + dimension(2) + ::BC_x + +
+ + character(len=*), + intent(in), + dimension(2) + ::BC_y + +
+ + character(len=*), + intent(in), + dimension(2) + ::BC_z + +
+ +

+ Return Value + type(mesh_t) +

+ + +
+
+ +
+

public pure function get_sz(self) result(sz) +

+
+ +

Getter for parameter SZ

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ +

+ Return Value + integer +

+ + +
+
+ +
+

public pure function get_dims(self, data_loc) result(dims) +

+
+ +

Getter for local domain dimensions

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::data_loc + +
+ +

+ Return Value + integer, dimension(3) +

+ + +
+
+ +
+

public pure function get_global_dims(self, data_loc) result(dims) +

+
+ +

Getter for local domain dimensions

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::data_loc + +
+ +

+ Return Value + integer, dimension(3) +

+ + +
+
+ +
+

public pure function get_dims_dataloc(data_loc, vert_dims, cell_dims) result(dims) +

+
+ +

Getter for domain dimensions

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::data_loc + +
+ + integer, + intent(in), + dimension(3) + ::vert_dims + +
+ + integer, + intent(in), + dimension(3) + ::cell_dims + +
+ +

+ Return Value + integer, dimension(3) +

+ + +
+
+ +
+

public pure function get_padded_dims_dir(self, dir) result(dims_padded) +

+
+ +

Getter for padded dimensions with structure in dir direction

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::dir + +
+ +

+ Return Value + integer, dimension(3) +

+ + +
+
+ +
+

public pure function get_padded_dims_phi(self, phi) result(dims_padded) +

+
+ +

Getter for padded dimensions for field phi +Gets the field direction from the field itself

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + class(field_t), + intent(in) + + ::phi + +
+ +

+ Return Value + integer, dimension(3) +

+ + +
+
+ +
+

public pure function get_n_groups_dir(self, dir) result(n_groups) +

+
+ +

Getter for the number of groups for fields in direction dir

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::dir + +
+ +

+ Return Value + integer +

+ + +
+
+ +
+

public pure function get_n_groups_phi(self, phi) result(n_groups) +

+
+ +

Getter for the number of groups for fields phi

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + class(field_t), + intent(in) + + ::phi + +
+ +

+ Return Value + integer +

+ + +
+
+ +
+

public pure function get_field_dims_phi(self, phi) result(dims) +

+
+ +

Getter for the dimensions of field phi

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + class(field_t), + intent(in) + + ::phi + +
+ +

+ Return Value + integer, dimension(3) +

+ + +
+
+ +
+

public pure function get_field_dims_phi_dataloc(self, phi, data_loc) result(dims) +

+
+ +

Getter for the dimensions of field phi where data is located on data_loc

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + class(field_t), + intent(in) + + ::phi + +
+ + integer, + intent(in) + + ::data_loc + +
+ +

+ Return Value + integer, dimension(3) +

+ + +
+
+ +
+

public pure function get_field_dims_dir(self, dir, data_loc) result(dims) +

+
+ +

Getter for the dimensions of an array directed along dir where data would be located on data_loc

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::dir + +
+ + integer, + intent(in) + + ::data_loc + +
+ +

+ Return Value + integer, dimension(3) +

+ + +
+
+ +
+

public pure function get_n_phi(self, phi) result(n) +

+
+ +

Getter for the main dimension of field phi

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + class(field_t), + intent(in) + + ::phi + +
+ +

+ Return Value + integer +

+ + +
+
+ +
+

public pure function get_n_dir(self, dir, data_loc) result(n) +

+
+ +

Getter for the main dimension a field oriented along dir with data on data_loc

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::dir + +
+ + integer, + intent(in) + + ::data_loc + +
+ +

+ Return Value + integer +

+ + +
+
+ +
+

public pure function get_coordinates(self, i, j, k) result(xloc) +

+
+ +

Get the physical location of a cell center with i,j,k local indices

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::i + +
+ + integer, + intent(in) + + ::j + +
+ + integer, + intent(in) + + ::k + +
+ +

+ Return Value + real(kind=dp), dimension(3) +

+ + +
+
+ +
+

public pure function is_root(self) result(is_root_rank) +

+
+ +

Returns whether or not the current rank is the root rank

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(parallel_t), + intent(in) + + ::self + +
+ +

+ Return Value + logical +

+ + +
+
+ +
+
+ +
+

Subroutines

+
+

public subroutine set_padded_dims(self, vert_dims) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout) + + ::self + +
+ + integer, + intent(in), + dimension(3) + ::vert_dims + +
+ + +
+
+ +
+

public subroutine set_sz(self, sz) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout) + + ::self + +
+ + integer, + intent(in) + + ::sz + +
+ + +
+
+ +
+

public subroutine domain_decomposition(mesh) +

+
+ +

Supports 1D, 2D, and 3D domain decomposition.

Read more… + +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout) + + ::mesh + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_omp_backend.html b/api/module/m_omp_backend.html new file mode 100644 index 000000000..8fc5cc6bf --- /dev/null +++ b/api/module/m_omp_backend.html @@ -0,0 +1,2843 @@ + + + + + + + + + + + + + m_omp_backend – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_omp_backend + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface omp_backend_t +

+
+
    +
  • +

    + public function init(mesh, allocator) result(backend) + +

    + + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(mesh_t), + intent(inout), + target + ::mesh + +
    + + class(allocator_t), + intent(inout), + target + ::allocator + +
    + +

    + Return Value + type(omp_backend_t) +

    + + +
  • +
+
+ +
+
+ + +
+

Derived Types

+
+
+ +

+ type, public, extends(base_backend_t) ::  + omp_backend_t + +

+
+
+ + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + real(kind=dp), + public + + ::nu + +
+ + class(mesh_t), + public, + pointer + ::mesh + +
+ + class(allocator_t), + public, + pointer + ::allocator + +
+ + class(poisson_fft_t), + public, + pointer + ::poisson_fft + +
+ + integer, + public + + ::MPI_FP_PREC =dp + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::u_recv_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::u_recv_e + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::u_send_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::u_send_e + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::v_recv_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::v_recv_e + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::v_send_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::v_send_e + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::w_recv_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::w_recv_e + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::w_send_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::w_send_e + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::du_send_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::du_send_e + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::du_recv_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::du_recv_e + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::dud_send_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::dud_send_e + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::dud_recv_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::dud_recv_e + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::d2u_send_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::d2u_send_e + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::d2u_recv_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:, :, :) + ::d2u_recv_e + +
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + init + (mesh, allocator) +
+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
procedure, public :: + base_init
procedure, public :: + get_field_data
procedure, public :: + set_field_data
procedure, public :: + alloc_tdsops => alloc_omp_tdsops
procedure, public :: + transeq_x => transeq_x_omp
procedure, public :: + transeq_y => transeq_y_omp
procedure, public :: + transeq_z => transeq_z_omp
procedure, public :: + tds_solve => tds_solve_omp
procedure, public :: + reorder => reorder_omp
procedure, public :: + sum_yintox => sum_yintox_omp
procedure, public :: + sum_zintox => sum_zintox_omp
procedure, public :: + vecadd => vecadd_omp
procedure, public :: + scalar_product => scalar_product_omp
procedure, public :: + copy_data_to_f => copy_data_to_f_omp
procedure, public :: + copy_f_to_data => copy_f_to_data_omp
procedure, public :: + init_poisson_fft => init_omp_poisson_fft
procedure, public :: + transeq_omp_dist
+
+
+ +
+
+ +
+

Functions

+
+

public function init(mesh, allocator) result(backend) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout), + target + ::mesh + +
+ + class(allocator_t), + intent(inout), + target + ::allocator + +
+ +

+ Return Value + type(omp_backend_t) +

+ + +
+
+ +
+

public function scalar_product_omp(self, x, y) result(s) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(in) + + ::x + +
+ + class(field_t), + intent(in) + + ::y + +
+ +

+ Return Value + real(kind=dp) +

+ + +
+
+ +
+
+ +
+

Subroutines

+
+

public subroutine alloc_omp_tdsops(self, tdsops, dir, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(tdsops_t), + intent(inout), + allocatable + ::tdsops + +
+ + integer, + intent(in) + + ::dir + +
+ + character(len=*), + intent(in) + + ::operation + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + integer, + intent(in),optional + + ::n_halo + +
+ + character(len=*), + intent(in),optional + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + real(kind=dp), + intent(in),optional + + ::c_nu + +
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
+ + +
+
+ +
+

public subroutine transeq_x_omp(self, du, dv, dw, u, v, w, dirps) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ + +
+
+ +
+

public subroutine transeq_y_omp(self, du, dv, dw, u, v, w, dirps) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ + +
+
+ +
+

public subroutine transeq_z_omp(self, du, dv, dw, u, v, w, dirps) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ + +
+
+ +
+

public subroutine transeq_omp_dist(self, du, dv, dw, u, v, w, dirps) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ + +
+
+ +
+

public subroutine tds_solve_omp(self, du, u, tdsops) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(tdsops_t), + intent(in) + + ::tdsops + +
+ + +
+
+ +
+

public subroutine tds_solve_dist(self, du, u, tdsops) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(tdsops_t), + intent(in) + + ::tdsops + +
+ + +
+
+ +
+

public subroutine reorder_omp(self, u_, u, direction) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u_ + +
+ + class(field_t), + intent(in) + + ::u + +
+ + integer, + intent(in) + + ::direction + +
+ + +
+
+ +
+

public subroutine sum_yintox_omp(self, u, u_) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u + +
+ + class(field_t), + intent(in) + + ::u_ + +
+ + +
+
+ +
+

public subroutine sum_zintox_omp(self, u, u_) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u + +
+ + class(field_t), + intent(in) + + ::u_ + +
+ + +
+
+ +
+

public subroutine sum_intox_omp(self, u, u_, dir_to) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u + +
+ + class(field_t), + intent(in) + + ::u_ + +
+ + integer, + intent(in) + + ::dir_to + +
+ + +
+
+ +
+

public subroutine vecadd_omp(self, a, x, b, y) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + real(kind=dp), + intent(in) + + ::a + +
+ + class(field_t), + intent(in) + + ::x + +
+ + real(kind=dp), + intent(in) + + ::b + +
+ + class(field_t), + intent(inout) + + ::y + +
+ + +
+
+ +
+

public subroutine copy_into_buffers(u_send_s, u_send_e, u, n, n_groups) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::u_send_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::u_send_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u + +
+ + integer, + intent(in) + + ::n + +
+ + integer, + intent(in) + + ::n_groups + +
+ + +
+
+ +
+

public subroutine copy_data_to_f_omp(self, f, data) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t), + intent(inout) + + ::self + +
+ + class(field_t), + intent(inout) + + ::f + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::data + +
+ + +
+
+ +
+

public subroutine copy_f_to_data_omp(self, data, f) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::data + +
+ + class(field_t), + intent(in) + + ::f + +
+ + +
+
+ +
+

public subroutine init_omp_poisson_fft(self, mesh, xdirps, ydirps, zdirps) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + type(dirps_t), + intent(in) + + ::xdirps + +
+ + type(dirps_t), + intent(in) + + ::ydirps + +
+ + type(dirps_t), + intent(in) + + ::zdirps + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_omp_common.html b/api/module/m_omp_common.html new file mode 100644 index 000000000..a85ec296b --- /dev/null +++ b/api/module/m_omp_common.html @@ -0,0 +1,250 @@ + + + + + + + + + + + + + m_omp_common – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_omp_common + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ + + +
+ + +
+

Variables

+ + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + integer, + public, +parameter + ::SZ =16 + +
+ +
+
+ + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_omp_exec_dist.html b/api/module/m_omp_exec_dist.html new file mode 100644 index 000000000..e015835f7 --- /dev/null +++ b/api/module/m_omp_exec_dist.html @@ -0,0 +1,933 @@ + + + + + + + + + + + + + m_omp_exec_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_omp_exec_dist + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+ +
+ + +
+ + + + + + + + +
+

Subroutines

+
+

public subroutine exec_dist_tds_compact(du, u, u_recv_s, u_recv_e, du_send_s, du_send_e, du_recv_s, du_recv_e, tdsops, nproc, pprev, pnext, n_groups) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u_recv_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u_recv_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_send_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_send_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_recv_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_recv_e + +
+ + type(tdsops_t), + intent(in) + + ::tdsops + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::pprev + +
+ + integer, + intent(in) + + ::pnext + +
+ + integer, + intent(in) + + ::n_groups + +
+ + +
+
+ +
+

public subroutine exec_dist_transeq_compact(rhs, du, dud, d2u, du_send_s, du_send_e, du_recv_s, du_recv_e, dud_send_s, dud_send_e, dud_recv_s, dud_recv_e, d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e, u, u_recv_s, u_recv_e, v, v_recv_s, v_recv_e, tdsops_du, tdsops_dud, tdsops_d2u, nu, nproc, pprev, pnext, n_groups) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::rhs + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::dud + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::d2u + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_send_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_send_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_recv_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_recv_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::dud_send_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::dud_send_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::dud_recv_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::dud_recv_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::d2u_send_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::d2u_send_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::d2u_recv_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::d2u_recv_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u_recv_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u_recv_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::v + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::v_recv_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::v_recv_e + +
+ + type(tdsops_t), + intent(in) + + ::tdsops_du + +
+ + type(tdsops_t), + intent(in) + + ::tdsops_dud + +
+ + type(tdsops_t), + intent(in) + + ::tdsops_d2u + +
+ + real(kind=dp), + intent(in) + + ::nu + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::pprev + +
+ + integer, + intent(in) + + ::pnext + +
+ + integer, + intent(in) + + ::n_groups + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_omp_kernels_dist.html b/api/module/m_omp_kernels_dist.html new file mode 100644 index 000000000..e05b24617 --- /dev/null +++ b/api/module/m_omp_kernels_dist.html @@ -0,0 +1,570 @@ + + + + + + + + + + + + + m_omp_kernels_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_omp_kernels_dist + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + + + + + +
+

Subroutines

+
+

public subroutine der_univ_dist(du, send_u_s, send_u_e, u, u_s, u_e, coeffs_s, coeffs_e, coeffs, n, ffr, fbc, faf) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :) + ::du + +
+ + real(kind=dp), + intent(out), + dimension(:, :) + ::send_u_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :) + ::send_u_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::u + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::u_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::u_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::coeffs_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::coeffs_e + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::coeffs + +
+ + integer, + intent(in) + + ::n + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::ffr + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::fbc + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::faf + +
+ + +
+
+ +
+

public subroutine der_univ_subs(du, recv_u_s, recv_u_e, n, dist_sa, dist_sc) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :) + ::du + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::recv_u_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::recv_u_e + +
+ + integer, + intent(in) + + ::n + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::dist_sa + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::dist_sc + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_omp_poisson_fft.html b/api/module/m_omp_poisson_fft.html new file mode 100644 index 000000000..580f026be --- /dev/null +++ b/api/module/m_omp_poisson_fft.html @@ -0,0 +1,987 @@ + + + + + + + + + + + + + m_omp_poisson_fft – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_omp_poisson_fft + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+ +
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface omp_poisson_fft_t +

+
+
    +
  • +

    + private function init(mesh, xdirps, ydirps, zdirps) result(poisson_fft) + +

    + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(mesh_t), + intent(in) + + ::mesh + +
    + + class(dirps_t), + intent(in) + + ::xdirps + +
    + + class(dirps_t), + intent(in) + + ::ydirps + +
    + + class(dirps_t), + intent(in) + + ::zdirps + +
    + +

    + Return Value + type(omp_poisson_fft_t) +

    + + +
  • +
+
+ +
+
+ + +
+

Derived Types

+
+
+ +

+ type, public, extends(poisson_fft_t) ::  + omp_poisson_fft_t + +

+
+
+

FFT based Poisson solver +It can only handle 1D decompositions along z direction.

+ +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + integer, + public + + ::nx_glob +

Global dimensions

+
+ + integer, + public + + ::ny_glob +

Global dimensions

+
+ + integer, + public + + ::nz_glob +

Global dimensions

+
+ + integer, + public + + ::nx_loc +

Local dimensions

+
+ + integer, + public + + ::ny_loc +

Local dimensions

+
+ + integer, + public + + ::nz_loc +

Local dimensions

+
+ + integer, + public + + ::nx_perm +

Local dimensions in the permuted slabs

+
+ + integer, + public + + ::ny_perm +

Local dimensions in the permuted slabs

+
+ + integer, + public + + ::nz_perm +

Local dimensions in the permuted slabs

+
+ + integer, + public + + ::nx_spec +

Local dimensions in the permuted slabs in spectral space

+
+ + integer, + public + + ::ny_spec +

Local dimensions in the permuted slabs in spectral space

+
+ + integer, + public + + ::nz_spec +

Local dimensions in the permuted slabs in spectral space

+
+ + integer, + public + + ::y_sp_st +

Offset in y direction in the permuted slabs in spectral space

+
+ + complex(kind=dp), + public, + allocatable, dimension(:, :, :) + ::waves +

Local domain sized array storing the spectral equivalence constants

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::ax +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::bx +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::ay +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::by +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::az +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::bz +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:, :, :) + ::c_x + +
+ + complex(kind=dp), + public, + allocatable, dimension(:, :, :) + ::c_y + +
+ + complex(kind=dp), + public, + allocatable, dimension(:, :, :) + ::c_z + +
+ + +

Constructor

+ + + + + + + + +
+ private + + + function + init + (mesh, xdirps, ydirps, zdirps) +
+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + +
procedure, public :: + base_init
procedure, public :: + waves_set
procedure, public :: + fft_forward => fft_forward_omp
procedure, public :: + fft_backward => fft_backward_omp
procedure, public :: + fft_postprocess => fft_postprocess_omp
+
+
+ +
+
+ + +
+

Subroutines

+
+

public subroutine fft_forward_omp(self, f_in) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_poisson_fft_t) + + + ::self + +
+ + class(field_t), + intent(in) + + ::f_in + +
+ + +
+
+ +
+

public subroutine fft_backward_omp(self, f_out) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_poisson_fft_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::f_out + +
+ + +
+
+ +
+

public subroutine fft_postprocess_omp(self) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_poisson_fft_t) + + + ::self + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_omp_sendrecv.html b/api/module/m_omp_sendrecv.html new file mode 100644 index 000000000..50e08ba4a --- /dev/null +++ b/api/module/m_omp_sendrecv.html @@ -0,0 +1,378 @@ + + + + + + + + + + + + + m_omp_sendrecv – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_omp_sendrecv + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + + + + + +
+

Subroutines

+
+

public subroutine sendrecv_fields(f_recv_s, f_recv_e, f_send_s, f_send_e, n_data, nproc, prev, next) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::f_recv_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::f_recv_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::f_send_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::f_send_e + +
+ + integer, + intent(in) + + ::n_data + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::prev + +
+ + integer, + intent(in) + + ::next + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_ordering.html b/api/module/m_ordering.html new file mode 100644 index 000000000..c12fb3a4b --- /dev/null +++ b/api/module/m_ordering.html @@ -0,0 +1,1252 @@ + + + + + + + + + + + + + m_ordering – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_ordering + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+

"Application storage" stores spatial data with a directionality for better cache locality + This set of functions converts indices from this application storage (_dir) to cartesian indices (_ijk)

+
+

Uses

+
+ +
+
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface get_index_reordering +

+
+
    +
  • +

    + public pure subroutine get_index_reordering_rdr(out_i, out_j, out_k, in_i, in_j, in_k, reorder_dir, mesh) + +

    + + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + integer, + intent(out) + + ::out_i + +
    + + integer, + intent(out) + + ::out_j + +
    + + integer, + intent(out) + + ::out_k + +
    + + integer, + intent(in) + + ::in_i + +
    + + integer, + intent(in) + + ::in_j + +
    + + integer, + intent(in) + + ::in_k + +
    + + integer, + intent(in) + + ::reorder_dir + +
    + + class(mesh_t), + intent(in) + + ::mesh + +
    + + +
  • +
  • +

    + public pure subroutine get_index_reordering_dirs(out_i, out_j, out_k, in_i, in_j, in_k, dir_from, dir_to, mesh) + +

    + +

    Converts a set of application storage directional index to an other direction. +The two directions are defined by the reorder_dir variable, RDR_X2Y will go from storage in X to Y etc.

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + integer, + intent(out) + + ::out_i + +
    + + integer, + intent(out) + + ::out_j + +
    + + integer, + intent(out) + + ::out_k + +
    + + integer, + intent(in) + + ::in_i + +
    + + integer, + intent(in) + + ::in_j + +
    + + integer, + intent(in) + + ::in_k + +
    + + integer, + intent(in) + + ::dir_from + +
    + + integer, + intent(in) + + ::dir_to + +
    + + class(mesh_t), + intent(in) + + ::mesh + +
    + + +
  • +
+
+ +
+
+ + + + +
+

Subroutines

+
+

public pure subroutine get_index_ijk(i, j, k, dir_i, dir_j, dir_k, dir, SZ, nx_padded, ny_padded, nz_padded) +

+
+ +

Get cartesian index from application storage directional one

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::i + +
+ + integer, + intent(out) + + ::j + +
+ + integer, + intent(out) + + ::k + +
+ + integer, + intent(in) + + ::dir_i + +
+ + integer, + intent(in) + + ::dir_j + +
+ + integer, + intent(in) + + ::dir_k + +
+ + integer, + intent(in) + + ::dir + +
+ + integer, + intent(in) + + ::SZ + +
+ + integer, + intent(in) + + ::nx_padded + +
+ + integer, + intent(in) + + ::ny_padded + +
+ + integer, + intent(in) + + ::nz_padded + +
+ + +
+
+ +
+

public pure subroutine get_index_dir(dir_i, dir_j, dir_k, i, j, k, dir, SZ, nx_padded, ny_padded, nz_padded) +

+
+ +

Get application storage directional index from cartesian index

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::dir_i + +
+ + integer, + intent(out) + + ::dir_j + +
+ + integer, + intent(out) + + ::dir_k + +
+ + integer, + intent(in) + + ::i + +
+ + integer, + intent(in) + + ::j + +
+ + integer, + intent(in) + + ::k + +
+ + integer, + intent(in) + + ::dir + +
+ + integer, + intent(in) + + ::SZ + +
+ + integer, + intent(in) + + ::nx_padded + +
+ + integer, + intent(in) + + ::ny_padded + +
+ + integer, + intent(in) + + ::nz_padded + +
+ + +
+
+ +
+

public pure subroutine get_index_reordering_dirs(out_i, out_j, out_k, in_i, in_j, in_k, dir_from, dir_to, mesh) +

+
+ +

Converts a set of application storage directional index to an other direction. +The two directions are defined by the reorder_dir variable, RDR_X2Y will go from storage in X to Y etc.

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::out_i + +
+ + integer, + intent(out) + + ::out_j + +
+ + integer, + intent(out) + + ::out_k + +
+ + integer, + intent(in) + + ::in_i + +
+ + integer, + intent(in) + + ::in_j + +
+ + integer, + intent(in) + + ::in_k + +
+ + integer, + intent(in) + + ::dir_from + +
+ + integer, + intent(in) + + ::dir_to + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + +
+
+ +
+

public pure subroutine get_index_reordering_rdr(out_i, out_j, out_k, in_i, in_j, in_k, reorder_dir, mesh) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::out_i + +
+ + integer, + intent(out) + + ::out_j + +
+ + integer, + intent(out) + + ::out_k + +
+ + integer, + intent(in) + + ::in_i + +
+ + integer, + intent(in) + + ::in_j + +
+ + integer, + intent(in) + + ::in_k + +
+ + integer, + intent(in) + + ::reorder_dir + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_poisson_fft.html b/api/module/m_poisson_fft.html new file mode 100644 index 000000000..0924da873 --- /dev/null +++ b/api/module/m_poisson_fft.html @@ -0,0 +1,1038 @@ + + + + + + + + + + + + + m_poisson_fft – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_poisson_fft + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + + +
+

Abstract Interfaces

+
+

abstract interface + +

+
    +
  • +

    + public subroutine fft_forward(self, f_in) + +

    + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(poisson_fft_t) + + + ::self + +
    + + class(field_t), + intent(in) + + ::f_in + +
    + + +
  • +
+
+ +
+

abstract interface + +

+
    +
  • +

    + public subroutine fft_backward(self, f_out) + +

    + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(poisson_fft_t) + + + ::self + +
    + + class(field_t), + intent(inout) + + ::f_out + +
    + + +
  • +
+
+ +
+

abstract interface + +

+
    +
  • +

    + public subroutine fft_postprocess(self) + +

    + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(poisson_fft_t) + + + ::self + +
    + + +
  • +
+
+ +
+
+ +
+

Derived Types

+
+
+ +

+ type, public, abstract ::  + poisson_fft_t + +

+
+
+

FFT based Poisson solver

+ +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + integer, + public + + ::nx_glob +

Global dimensions

+
+ + integer, + public + + ::ny_glob +

Global dimensions

+
+ + integer, + public + + ::nz_glob +

Global dimensions

+
+ + integer, + public + + ::nx_loc +

Local dimensions

+
+ + integer, + public + + ::ny_loc +

Local dimensions

+
+ + integer, + public + + ::nz_loc +

Local dimensions

+
+ + integer, + public + + ::nx_perm +

Local dimensions in the permuted slabs

+
+ + integer, + public + + ::ny_perm +

Local dimensions in the permuted slabs

+
+ + integer, + public + + ::nz_perm +

Local dimensions in the permuted slabs

+
+ + integer, + public + + ::nx_spec +

Local dimensions in the permuted slabs in spectral space

+
+ + integer, + public + + ::ny_spec +

Local dimensions in the permuted slabs in spectral space

+
+ + integer, + public + + ::nz_spec +

Local dimensions in the permuted slabs in spectral space

+
+ + integer, + public + + ::y_sp_st +

Offset in y direction in the permuted slabs in spectral space

+
+ + complex(kind=dp), + public, + allocatable, dimension(:, :, :) + ::waves +

Local domain sized array storing the spectral equivalence constants

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::ax +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::bx +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::ay +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::by +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::az +

Wave numbers in x, y, and z

+
+ + complex(kind=dp), + public, + allocatable, dimension(:) + ::bz +

Wave numbers in x, y, and z

+
+ + + + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + +
procedure(fft_forward), public, deferred :: + fft_forward
procedure(fft_backward), public, deferred :: + fft_backward
procedure(fft_postprocess), public, deferred :: + fft_postprocess
procedure, public :: + base_init
procedure, public :: + waves_set
+
+
+ +
+
+ + +
+

Subroutines

+
+

public subroutine base_init(self, mesh, xdirps, ydirps, zdirps) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(poisson_fft_t) + + + ::self + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + type(dirps_t), + intent(in) + + ::xdirps + +
+ + type(dirps_t), + intent(in) + + ::ydirps + +
+ + type(dirps_t), + intent(in) + + ::zdirps + +
+ + +
+
+ +
+

public subroutine waves_set(self, geo, xdirps, ydirps, zdirps) +

+
+ +

Spectral equivalence constants

Read more… + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(poisson_fft_t) + + + ::self + +
+ + type(geo_t), + intent(in) + + ::geo + +
+ + type(dirps_t), + intent(in) + + ::xdirps + +
+ + type(dirps_t), + intent(in) + + ::ydirps + +
+ + type(dirps_t), + intent(in) + + ::zdirps + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_solver.html b/api/module/m_solver.html new file mode 100644 index 000000000..7b4a79189 --- /dev/null +++ b/api/module/m_solver.html @@ -0,0 +1,1729 @@ + + + + + + + + + + + + + m_solver – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_solver + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface solver_t +

+
+
    +
  • +

    + public function init(backend, mesh, host_allocator) result(solver) + +

    + + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t), + intent(inout), + target + ::backend + +
    + + type(mesh_t), + intent(inout), + target + ::mesh + +
    + + type(allocator_t), + intent(inout), + target + ::host_allocator + +
    + +

    + Return Value + type(solver_t) +

    + + +
  • +
+
+ +
+
+ +
+

Abstract Interfaces

+
+

abstract interface + +

+
    +
  • +

    + public subroutine poisson_solver(self, pressure, div_u) + +

    + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(solver_t) + + + ::self + +
    + + class(field_t), + intent(inout) + + ::pressure + +
    + + class(field_t), + intent(in) + + ::div_u + +
    + + +
  • +
+
+ +
+
+ +
+

Derived Types

+
+
+ +

+ type, public ::  + solver_t + +

+
+
+

solver class defines the Incompact3D algorithm at a very high level.

Read more… + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + real(kind=dp), + public + + ::dt + +
+ + real(kind=dp), + public + + ::nu + +
+ + integer, + public + + ::n_iters + +
+ + integer, + public + + ::n_output + +
+ + integer, + public + + ::ngrid + +
+ + class(field_t), + public, + pointer + ::u + +
+ + class(field_t), + public, + pointer + ::v + +
+ + class(field_t), + public, + pointer + ::w + +
+ + class(base_backend_t), + public, + pointer + ::backend + +
+ + class(mesh_t), + public, + pointer + ::mesh + +
+ + type(time_intg_t), + public + + ::time_integrator + +
+ + type(allocator_t), + public, + pointer + ::host_allocator + +
+ + type(dirps_t), + public, + pointer + ::xdirps + +
+ + type(dirps_t), + public, + pointer + ::ydirps + +
+ + type(dirps_t), + public, + pointer + ::zdirps + +
+ + type(vector_calculus_t), + public + + ::vector_calculus + +
+ + procedure(poisson_solver), + public, + pointer + ::poisson=> null() + +
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + init + (backend, mesh, host_allocator) +
+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
procedure, public :: + transeq
procedure, public :: + divergence_v2p
procedure, public :: + gradient_p2v
procedure, public :: + curl
procedure, public :: + output
procedure, public :: + run
+
+
+ +
+
+ +
+

Functions

+
+

public function init(backend, mesh, host_allocator) result(solver) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t), + intent(inout), + target + ::backend + +
+ + type(mesh_t), + intent(inout), + target + ::mesh + +
+ + type(allocator_t), + intent(inout), + target + ::host_allocator + +
+ +

+ Return Value + type(solver_t) +

+ + +
+
+ +
+
+ +
+

Subroutines

+
+

public subroutine allocate_tdsops(dirps, backend, der1st_scheme, der2nd_scheme, interpl_scheme, stagder_scheme) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + type(dirps_t), + intent(inout) + + ::dirps + +
+ + class(base_backend_t), + intent(in) + + ::backend + +
+ + character(len=*), + intent(in) + + ::der1st_scheme + +
+ + character(len=*), + intent(in) + + ::der2nd_scheme + +
+ + character(len=*), + intent(in) + + ::interpl_scheme + +
+ + character(len=*), + intent(in) + + ::stagder_scheme + +
+ + +
+
+ +
+

public subroutine transeq(self, du, dv, dw, u, v, w) +

+
+ +

Skew-symmetric form of convection-diffusion terms in the +incompressible Navier-Stokes momemtum equations, excluding +pressure terms. +Inputs from velocity grid and outputs to velocity grid.

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + +
+
+ +
+

public subroutine divergence_v2p(self, div_u, u, v, w) +

+
+ +

Wrapper for divergence_v2p

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::div_u + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + +
+
+ +
+

public subroutine gradient_p2v(self, dpdx, dpdy, dpdz, pressure) +

+
+ +

Wrapper for gradient_p2v

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::dpdx + +
+ + class(field_t), + intent(inout) + + ::dpdy + +
+ + class(field_t), + intent(inout) + + ::dpdz + +
+ + class(field_t), + intent(in) + + ::pressure + +
+ + +
+
+ +
+

public subroutine curl(self, o_i_hat, o_j_hat, o_k_hat, u, v, w) +

+
+ +

Wrapper for curl

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::o_i_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(inout) + + ::o_j_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(inout) + + ::o_k_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + +
+
+ +
+

public subroutine poisson_fft(self, pressure, div_u) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::pressure + +
+ + class(field_t), + intent(in) + + ::div_u + +
+ + +
+
+ +
+

public subroutine poisson_cg(self, pressure, div_u) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::pressure + +
+ + class(field_t), + intent(in) + + ::div_u + +
+ + +
+
+ +
+

public subroutine output(self, t) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t), + intent(in) + + ::self + +
+ + real(kind=dp), + intent(in) + + ::t + +
+ + +
+
+ +
+

public subroutine run(self) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t), + intent(inout) + + ::self + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_tdsops.html b/api/module/m_tdsops.html new file mode 100644 index 000000000..ae81305f4 --- /dev/null +++ b/api/module/m_tdsops.html @@ -0,0 +1,2026 @@ + + + + + + + + + + + + + m_tdsops – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_tdsops + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface tdsops_t +

+
+
    +
  • +

    + public function tdsops_init(tds_n, delta, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) result(tdsops) + +

    + +

    Constructor function for the tdsops_t class.

    +

    'n', 'delta', 'operation', and 'scheme' are necessary arguments. +Number of points 'n', distance between two points 'delta', the +'operation' the tridiagonal system defines, and the 'scheme' that +specifies the exact scheme we choose to apply for the operation. +The remaining arguments are optional. +'from_to' is necessary for interpolation and staggared derivative, and +it can be 'v2p' or 'p2v'. +If the specific region the instance is operating is not a boundary +region, then 'bc_start' and 'bc_end' are either 'null' or not defined. +'sym' is relevant when the boundary condition is free-slip. If sym is +.true. then it means the field we operate on is assumed to be an even +function (symmetric) accross the boundary. If it is .false. it means +that the field is assumed to be an odd function (anti-symmetric). +'c_nu', 'nu0_nu' are relevant when operation is second order +derivative and scheme is compact6-hyperviscous.

    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + integer, + intent(in) + + ::tds_n + +
    + + real(kind=dp), + intent(in) + + ::delta + +
    + + character(len=*), + intent(in) + + ::operation + +
    + + character(len=*), + intent(in) + + ::scheme + +
    + + integer, + intent(in),optional + + ::n_halo +

    Number of halo cells

    +
    + + character(len=*), + intent(in),optional + + ::from_to +

    'v2p' or 'p2v'

    +
    + + character(len=*), + intent(in),optional + + ::bc_start +

    Boundary Cond.

    +
    + + character(len=*), + intent(in),optional + + ::bc_end +

    Boundary Cond.

    +
    + + logical, + intent(in),optional + + ::sym +

    (==npaire), only for Neumann BCs

    +
    + + real(kind=dp), + intent(in),optional + + ::c_nu +

    params for hypervisc.

    +
    + + real(kind=dp), + intent(in),optional + + ::nu0_nu +

    params for hypervisc.

    +
    + +

    + Return Value + type(tdsops_t) +

    +

    return value of the function

    + +
  • +
+
+ +
+
+ + +
+

Derived Types

+
+
+ +

+ type, public ::  + tdsops_t + +

+
+
+

Tridiagonal Solver Operators class.

Read more… + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::dist_fw +

fw/bw phase +back subs. +the auxiliary factors

+
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::dist_bw +

fw/bw phase +back subs. +the auxiliary factors

+
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::dist_sa +

fw/bw phase +back subs. +the auxiliary factors

+
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::dist_sc +

fw/bw phase +back subs. +the auxiliary factors

+
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::dist_af +

fw/bw phase +back subs. +the auxiliary factors

+
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::thom_f + +
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::thom_s + +
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::thom_w + +
+ + real(kind=dp), + public, + allocatable, dimension(:) + ::thom_p + +
+ + real(kind=dp), + public, + allocatable + ::coeffs(:) + +
+ + real(kind=dp), + public, + allocatable + ::coeffs_s(:,:) + +
+ + real(kind=dp), + public, + allocatable + ::coeffs_e(:,:) + +
+ + real(kind=dp), + public + + ::alpha + +
+ + real(kind=dp), + public + + ::a + +
+ + real(kind=dp), + public + + ::b + +
+ + real(kind=dp), + public + + ::c =0._dp + +
+ + real(kind=dp), + public + + ::d =0._dp + +
+ + logical, + public + + ::periodic + +
+ + integer, + public + + ::tds_n + +
+ + integer, + public + + ::dir + +
+ + integer, + public + + ::n_halo + +
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + tdsops_init + (tds_n, delta, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) +

Constructor function for the tdsops_t class.

Read more…
+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
procedure, public :: + stagder_1st
procedure, public :: + interpl_mid
procedure, public :: + deriv_2nd
procedure, public :: + deriv_1st
procedure, public :: + preprocess_thom
procedure, public :: + preprocess_dist
+
+
+ +
+
+ +

+ type, public ::  + dirps_t + +

+
+
+

Directional tridiagonal solver container.

Read more… + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + class(tdsops_t), + public, + allocatable + ::der1st + +
+ + class(tdsops_t), + public, + allocatable + ::der1st_sym + +
+ + class(tdsops_t), + public, + allocatable + ::der2nd + +
+ + class(tdsops_t), + public, + allocatable + ::der2nd_sym + +
+ + class(tdsops_t), + public, + allocatable + ::stagder_v2p + +
+ + class(tdsops_t), + public, + allocatable + ::stagder_p2v + +
+ + class(tdsops_t), + public, + allocatable + ::interpl_v2p + +
+ + class(tdsops_t), + public, + allocatable + ::interpl_p2v + +
+ + integer, + public + + ::dir + +
+ + + + +
+
+ +
+
+ +
+

Functions

+
+

public function tdsops_init(tds_n, delta, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) result(tdsops) +

+
+ +

Constructor function for the tdsops_t class.

Read more… + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::tds_n + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::operation + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + integer, + intent(in),optional + + ::n_halo +

Number of halo cells

+
+ + character(len=*), + intent(in),optional + + ::from_to +

'v2p' or 'p2v'

+
+ + character(len=*), + intent(in),optional + + ::bc_start +

Boundary Cond.

+
+ + character(len=*), + intent(in),optional + + ::bc_end +

Boundary Cond.

+
+ + logical, + intent(in),optional + + ::sym +

(==npaire), only for Neumann BCs

+
+ + real(kind=dp), + intent(in),optional + + ::c_nu +

params for hypervisc.

+
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu +

params for hypervisc.

+
+ +

+ Return Value + type(tdsops_t) +

+

return value of the function

+ +
+
+ +
+

public pure function get_tds_n(mesh, dir, from_to) result(tds_n) +

+
+ +

Get the tds_n size based on the from_to value (and the mesh)

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + integer, + intent(in) + + ::dir + +
+ + character(len=*), + intent(in),optional + + ::from_to + +
+ +

+ Return Value + integer +

+ + +
+
+ +
+
+ +
+

Subroutines

+
+

public subroutine deriv_1st(self, delta, scheme, bc_start, bc_end, sym) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + +
+
+ +
+

public subroutine deriv_2nd(self, delta, scheme, bc_start, bc_end, sym, c_nu, nu0_nu) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + real(kind=dp), + intent(in),optional + + ::c_nu + +
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
+ + +
+
+ +
+

public subroutine interpl_mid(self, scheme, from_to, bc_start, bc_end, sym) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + character(len=*), + intent(in) + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + +
+
+ +
+

public subroutine stagder_1st(self, delta, scheme, from_to, bc_start, bc_end, sym) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + character(len=*), + intent(in) + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + +
+
+ +
+

public subroutine preprocess_dist(self, dist_b) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::dist_b + +
+ + +
+
+ +
+

public subroutine preprocess_thom(self, b) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::b + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_time_integrator.html b/api/module/m_time_integrator.html new file mode 100644 index 000000000..a4e54a021 --- /dev/null +++ b/api/module/m_time_integrator.html @@ -0,0 +1,1166 @@ + + + + + + + + + + + + + m_time_integrator – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_time_integrator + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+
+ +
+
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface time_intg_t +

+
+
    +
  • +

    + public function init(backend, allocator, method, nvars) + +

    + + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t), + + pointer + ::backend + +
    + + class(allocator_t), + + pointer + ::allocator + +
    + + character(len=3), + intent(in) + + ::method + +
    + + integer, + intent(in),optional + + ::nvars + +
    + +

    + Return Value + type(time_intg_t) +

    + + +
  • +
+
+ +
+
+ +
+

Abstract Interfaces

+
+

abstract interface + +

+
    +
  • +

    + public subroutine stepper_func(self, dt) + +

    + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(time_intg_t), + intent(inout) + + ::self + +
    + + real(kind=dp), + intent(in) + + ::dt + +
    + + +
  • +
+
+ +
+
+ +
+

Derived Types

+
+
+ +

+ type, public ::  + time_intg_t + +

+
+
+ + +

Components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + integer, + public + + ::method + +
+ + integer, + public + + ::istep + +
+ + integer, + public + + ::istage + +
+ + integer, + public + + ::order + +
+ + integer, + public + + ::nstep + +
+ + integer, + public + + ::nstage + +
+ + integer, + public + + ::nvars + +
+ + integer, + public + + ::nolds + +
+ + real(kind=dp), + public + + ::coeffs(4,4) + +
+ + real(kind=dp), + public + + ::rk_b(4,4) + +
+ + real(kind=dp), + public + + ::rk_a(3,3,4) + +
+ + character(len=3), + public + + ::sname + +
+ + type(flist_t), + public, + allocatable + ::olds(:,:) + +
+ + type(flist_t), + public, + allocatable + ::curr(:) + +
+ + type(flist_t), + public, + allocatable + ::deriv(:) + +
+ + class(base_backend_t), + public, + pointer + ::backend + +
+ + class(allocator_t), + public, + pointer + ::allocator + +
+ + procedure(stepper_func), + public, + pointer + ::stepper=> null() + +
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + init + (backend, allocator, method, nvars) +
+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + +
procedure, public :: + finalize
procedure, public :: + step
procedure, public :: + runge_kutta
procedure, public :: + adams_bashforth
+
+
+ +
+
+ +
+

Functions

+
+

public function init(backend, allocator, method, nvars) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t), + + pointer + ::backend + +
+ + class(allocator_t), + + pointer + ::allocator + +
+ + character(len=3), + intent(in) + + ::method + +
+ + integer, + intent(in),optional + + ::nvars + +
+ +

+ Return Value + type(time_intg_t) +

+ + +
+
+ +
+
+ +
+

Subroutines

+
+

public subroutine finalize(self) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(time_intg_t), + intent(inout) + + ::self + +
+ + +
+
+ +
+

public subroutine step(self, u, v, w, du, dv, dw, dt) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(time_intg_t), + intent(inout) + + ::self + +
+ + class(field_t), + intent(inout), + target + ::u + +
+ + class(field_t), + intent(inout), + target + ::v + +
+ + class(field_t), + intent(inout), + target + ::w + +
+ + class(field_t), + intent(in), + target + ::du + +
+ + class(field_t), + intent(in), + target + ::dv + +
+ + class(field_t), + intent(in), + target + ::dw + +
+ + real(kind=dp), + intent(in) + + ::dt + +
+ + +
+
+ +
+

public subroutine rotate(sol, n) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + type(flist_t), + intent(inout) + + ::sol(:) + +
+ + integer, + intent(in) + + ::n + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/module/m_vector_calculus.html b/api/module/m_vector_calculus.html new file mode 100644 index 000000000..b0ee604db --- /dev/null +++ b/api/module/m_vector_calculus.html @@ -0,0 +1,1144 @@ + + + + + + + + + + + + + m_vector_calculus – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

m_vector_calculus + Module + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ +
+

Uses

+ +
+ + +
+ + + + +
+

Interfaces

+
+
+ +

public interface vector_calculus_t +

+
+
    +
  • +

    + public function init(backend) result(vector_calculus) + +

    + + + +

    Arguments

    + + + + + + + + + + + + + + + + + + + +
    TypeIntentOptional AttributesName
    + + class(base_backend_t), + intent(inout), + target + ::backend + +
    + +

    + Return Value + type(vector_calculus_t) +

    + + +
  • +
+
+ +
+
+ + +
+

Derived Types

+
+
+ +

+ type, public ::  + vector_calculus_t + +

+
+
+

Defines vector calculus operators

+ +

Components

+ + + + + + + + + + + + + + + + + + + + + +
TypeVisibility AttributesNameInitial
+ + class(base_backend_t), + public, + pointer + ::backend + +
+ + +

Constructor

+ + + + + + + + +
+ public + + + function + init + (backend) +
+ + +

Type-Bound Procedures

+ + + + + + + + + + + + + + + + + + + +
procedure, public :: + curl
procedure, public :: + divergence_v2c
procedure, public :: + gradient_c2v
procedure, public :: + laplacian
+
+
+ +
+
+ +
+

Functions

+
+

public function init(backend) result(vector_calculus) +

+
+ + + +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t), + intent(inout), + target + ::backend + +
+ +

+ Return Value + type(vector_calculus_t) +

+ + +
+
+ +
+
+ +
+

Subroutines

+
+

public subroutine curl(self, o_i_hat, o_j_hat, o_k_hat, u, v, w, x_der1st, y_der1st, z_der1st) +

+
+ +

Curl of a vector field (u, v, w).

Read more… + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(vector_calculus_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::o_i_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(inout) + + ::o_j_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(inout) + + ::o_k_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + class(tdsops_t), + intent(in) + + ::x_der1st + +
+ + class(tdsops_t), + intent(in) + + ::y_der1st + +
+ + class(tdsops_t), + intent(in) + + ::z_der1st + +
+ + +
+
+ +
+

public subroutine divergence_v2c(self, div_u, u, v, w, x_stagder_v2c, x_interpl_v2c, y_stagder_v2c, y_interpl_v2c, z_stagder_v2c, z_interpl_v2c) +

+
+ +

Divergence of a vector field (u, v, w).

Read more… + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(vector_calculus_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::div_u + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + class(tdsops_t), + intent(in) + + ::x_stagder_v2c + +
+ + class(tdsops_t), + intent(in) + + ::x_interpl_v2c + +
+ + class(tdsops_t), + intent(in) + + ::y_stagder_v2c + +
+ + class(tdsops_t), + intent(in) + + ::y_interpl_v2c + +
+ + class(tdsops_t), + intent(in) + + ::z_stagder_v2c + +
+ + class(tdsops_t), + intent(in) + + ::z_interpl_v2c + +
+ + +
+
+ +
+

public subroutine gradient_c2v(self, dpdx, dpdy, dpdz, p, x_stagder_c2v, x_interpl_c2v, y_stagder_c2v, y_interpl_c2v, z_stagder_c2v, z_interpl_c2v) +

+
+ +

Gradient of a scalar field 'p'.

Read more… + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(vector_calculus_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::dpdx + +
+ + class(field_t), + intent(inout) + + ::dpdy + +
+ + class(field_t), + intent(inout) + + ::dpdz + +
+ + class(field_t), + intent(in) + + ::p + +
+ + class(tdsops_t), + intent(in) + + ::x_stagder_c2v + +
+ + class(tdsops_t), + intent(in) + + ::x_interpl_c2v + +
+ + class(tdsops_t), + intent(in) + + ::y_stagder_c2v + +
+ + class(tdsops_t), + intent(in) + + ::y_interpl_c2v + +
+ + class(tdsops_t), + intent(in) + + ::z_stagder_c2v + +
+ + class(tdsops_t), + intent(in) + + ::z_interpl_c2v + +
+ + +
+
+ +
+

public subroutine laplacian(self, lapl_u, u, x_der2nd, y_der2nd, z_der2nd) +

+
+ +

Laplacian of a scalar field 'u'.

Read more… + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(vector_calculus_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::lapl_u + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(tdsops_t), + intent(in) + + ::x_der2nd + +
+ + class(tdsops_t), + intent(in) + + ::y_der2nd + +
+ + class(tdsops_t), + intent(in) + + ::z_der2nd + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/namelist/domain_params.html b/api/namelist/domain_params.html new file mode 100644 index 000000000..da73cac1e --- /dev/null +++ b/api/namelist/domain_params.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + domain_params – x3d2 + + + + + + + + + + + + + + + +
+ +
+ +
+
+

domain_params

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+
+

Variables

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
L_globalreal(kind=dp)None
dims_globalintegerNone
nproc_dirinteger0
BC_xcharacter(len=20)None
BC_ycharacter(len=20)None
BC_zcharacter(len=20)None
+
+ +
+
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/namelist/solver_params.html b/api/namelist/solver_params.html new file mode 100644 index 000000000..3078900b2 --- /dev/null +++ b/api/namelist/solver_params.html @@ -0,0 +1,292 @@ + + + + + + + + + + + + + solver_params – x3d2 + + + + + + + + + + + + + + + +
+ +
+ +
+
+

solver_params

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+
+

Variables

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
Rereal(kind=dp)None
dtreal(kind=dp)None
n_itersintegerNone
n_outputintegerNone
poisson_solver_typecharacter(len=3)None
time_intgcharacter(len=3)None
der1st_schemecharacter(len=30)None
der2nd_schemecharacter(len=30)None
interpl_schemecharacter(len=30)None
stagder_schemecharacter(len=30)None
+
+ +
+
+
+
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/alloc_cuda_tdsops.html b/api/proc/alloc_cuda_tdsops.html new file mode 100644 index 000000000..e820a2bd2 --- /dev/null +++ b/api/proc/alloc_cuda_tdsops.html @@ -0,0 +1,406 @@ + + + + + + + + + + + + + alloc_cuda_tdsops – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

alloc_cuda_tdsops + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine alloc_cuda_tdsops(self, tdsops, dir, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(tdsops_t), + intent(inout), + allocatable + ::tdsops + +
+ + integer, + intent(in) + + ::dir + +
+ + character(len=*), + intent(in) + + ::operation + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + integer, + intent(in),optional + + ::n_halo + +
+ + character(len=*), + intent(in),optional + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + real(kind=dp), + intent(in),optional + + ::c_nu + +
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/alloc_omp_tdsops.html b/api/proc/alloc_omp_tdsops.html new file mode 100644 index 000000000..c296d9ac0 --- /dev/null +++ b/api/proc/alloc_omp_tdsops.html @@ -0,0 +1,406 @@ + + + + + + + + + + + + + alloc_omp_tdsops – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

alloc_omp_tdsops + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine alloc_omp_tdsops(self, tdsops, dir, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(tdsops_t), + intent(inout), + allocatable + ::tdsops + +
+ + integer, + intent(in) + + ::dir + +
+ + character(len=*), + intent(in) + + ::operation + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + integer, + intent(in),optional + + ::n_halo + +
+ + character(len=*), + intent(in),optional + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + real(kind=dp), + intent(in),optional + + ::c_nu + +
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/allocate_tdsops.html b/api/proc/allocate_tdsops.html new file mode 100644 index 000000000..305664f30 --- /dev/null +++ b/api/proc/allocate_tdsops.html @@ -0,0 +1,314 @@ + + + + + + + + + + + + + allocate_tdsops – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

allocate_tdsops + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine allocate_tdsops(dirps, backend, der1st_scheme, der2nd_scheme, interpl_scheme, stagder_scheme) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + type(dirps_t), + intent(inout) + + ::dirps + +
+ + class(base_backend_t), + intent(in) + + ::backend + +
+ + character(len=*), + intent(in) + + ::der1st_scheme + +
+ + character(len=*), + intent(in) + + ::der2nd_scheme + +
+ + character(len=*), + intent(in) + + ::interpl_scheme + +
+ + character(len=*), + intent(in) + + ::stagder_scheme + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/allocator_init.html b/api/proc/allocator_init.html new file mode 100644 index 000000000..c43acd7d2 --- /dev/null +++ b/api/proc/allocator_init.html @@ -0,0 +1,261 @@ + + + + + + + + + + + + + allocator_init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

allocator_init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function allocator_init(mesh, sz) result(allocator) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + type(mesh_t), + intent(inout), + target + ::mesh + +
+ + integer, + intent(in) + + ::sz + +
+ +

Return Value + + + type(allocator_t) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/axpby.html b/api/proc/axpby.html new file mode 100644 index 000000000..558ad6fbb --- /dev/null +++ b/api/proc/axpby.html @@ -0,0 +1,299 @@ + + + + + + + + + + + + + axpby – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

axpby + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine axpby(n, alpha, x, beta, y) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + value + ::alpha + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::x + +
+ + real(kind=dp), + intent(in), + value + ::beta + +
+ + real(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::y + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/base_init.html b/api/proc/base_init.html new file mode 100644 index 000000000..1be2063da --- /dev/null +++ b/api/proc/base_init.html @@ -0,0 +1,241 @@ + + + + + + + + + + + + + base_init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

base_init + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine base_init(self) +

+ + + +

Type Bound

+

base_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/base_init~2.html b/api/proc/base_init~2.html new file mode 100644 index 000000000..486428558 --- /dev/null +++ b/api/proc/base_init~2.html @@ -0,0 +1,301 @@ + + + + + + + + + + + + + base_init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

base_init + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine base_init(self, mesh, xdirps, ydirps, zdirps) +

+ + + +

Type Bound

+

poisson_fft_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(poisson_fft_t) + + + ::self + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + type(dirps_t), + intent(in) + + ::xdirps + +
+ + type(dirps_t), + intent(in) + + ::ydirps + +
+ + type(dirps_t), + intent(in) + + ::zdirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/buffer_copy.html b/api/proc/buffer_copy.html new file mode 100644 index 000000000..417a9ff86 --- /dev/null +++ b/api/proc/buffer_copy.html @@ -0,0 +1,299 @@ + + + + + + + + + + + + + buffer_copy – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

buffer_copy + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine buffer_copy(u_send_s, u_send_e, u, n, n_halo) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::u_send_s + +
+ + real(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::u_send_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + integer, + intent(in), + value + ::n + +
+ + integer, + intent(in), + value + ::n_halo + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/compute_padded_dims.html b/api/proc/compute_padded_dims.html new file mode 100644 index 000000000..ef80e5dcf --- /dev/null +++ b/api/proc/compute_padded_dims.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + compute_padded_dims – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

compute_padded_dims + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine compute_padded_dims(self, sz) +

+ + + +

Type Bound

+

allocator_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ + integer, + intent(in) + + ::sz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/copy_data_to_f_cuda.html b/api/proc/copy_data_to_f_cuda.html new file mode 100644 index 000000000..429fc6995 --- /dev/null +++ b/api/proc/copy_data_to_f_cuda.html @@ -0,0 +1,271 @@ + + + + + + + + + + + + + copy_data_to_f_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

copy_data_to_f_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine copy_data_to_f_cuda(self, f, data) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t), + intent(inout) + + ::self + +
+ + class(field_t), + intent(inout) + + ::f + +
+ + real(kind=dp), + intent(inout), + dimension(:, :, :) + ::data + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/copy_data_to_f_omp.html b/api/proc/copy_data_to_f_omp.html new file mode 100644 index 000000000..32119570b --- /dev/null +++ b/api/proc/copy_data_to_f_omp.html @@ -0,0 +1,271 @@ + + + + + + + + + + + + + copy_data_to_f_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

copy_data_to_f_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine copy_data_to_f_omp(self, f, data) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t), + intent(inout) + + ::self + +
+ + class(field_t), + intent(inout) + + ::f + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::data + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/copy_f_to_data_cuda.html b/api/proc/copy_f_to_data_cuda.html new file mode 100644 index 000000000..7f6849c03 --- /dev/null +++ b/api/proc/copy_f_to_data_cuda.html @@ -0,0 +1,271 @@ + + + + + + + + + + + + + copy_f_to_data_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

copy_f_to_data_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine copy_f_to_data_cuda(self, data, f) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::data + +
+ + class(field_t), + intent(in) + + ::f + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/copy_f_to_data_omp.html b/api/proc/copy_f_to_data_omp.html new file mode 100644 index 000000000..6ff57ed8c --- /dev/null +++ b/api/proc/copy_f_to_data_omp.html @@ -0,0 +1,271 @@ + + + + + + + + + + + + + copy_f_to_data_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

copy_f_to_data_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine copy_f_to_data_omp(self, data, f) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::data + +
+ + class(field_t), + intent(in) + + ::f + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/copy_into_buffers.html b/api/proc/copy_into_buffers.html new file mode 100644 index 000000000..65f450a88 --- /dev/null +++ b/api/proc/copy_into_buffers.html @@ -0,0 +1,284 @@ + + + + + + + + + + + + + copy_into_buffers – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

copy_into_buffers + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine copy_into_buffers(u_send_s_dev, u_send_e_dev, u_dev, n) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_send_s_dev + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_send_e_dev + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_dev + +
+ + integer, + intent(in) + + ::n + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/copy_into_buffers~2.html b/api/proc/copy_into_buffers~2.html new file mode 100644 index 000000000..65e459f27 --- /dev/null +++ b/api/proc/copy_into_buffers~2.html @@ -0,0 +1,299 @@ + + + + + + + + + + + + + copy_into_buffers – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

copy_into_buffers + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine copy_into_buffers(u_send_s, u_send_e, u, n, n_groups) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::u_send_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::u_send_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u + +
+ + integer, + intent(in) + + ::n + +
+ + integer, + intent(in) + + ::n_groups + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/create_block.html b/api/proc/create_block.html new file mode 100644 index 000000000..610541210 --- /dev/null +++ b/api/proc/create_block.html @@ -0,0 +1,265 @@ + + + + + + + + + + + + + create_block – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

create_block + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function create_block(self, next) result(ptr) +

+ + +

Allocate memory for a new block and return a pointer to a new +m_allocator object.

+ +

Type Bound

+

allocator_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ + type(field_t), + intent(in), + pointer + ::next + +
+ +

Return Value + + + class(field_t), pointer + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/create_cuda_block.html b/api/proc/create_cuda_block.html new file mode 100644 index 000000000..1fae99aec --- /dev/null +++ b/api/proc/create_cuda_block.html @@ -0,0 +1,263 @@ + + + + + + + + + + + + + create_cuda_block – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

create_cuda_block + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function create_cuda_block(self, next) result(ptr) +

+ + + +

Type Bound

+

cuda_allocator_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_allocator_t), + intent(inout) + + ::self + +
+ + type(cuda_field_t), + intent(in), + pointer + ::next + +
+ +

Return Value + + + class(field_t), pointer + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/cuda_allocator_init.html b/api/proc/cuda_allocator_init.html new file mode 100644 index 000000000..f1e003030 --- /dev/null +++ b/api/proc/cuda_allocator_init.html @@ -0,0 +1,261 @@ + + + + + + + + + + + + + cuda_allocator_init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

cuda_allocator_init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function cuda_allocator_init(mesh, sz) result(allocator) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout) + + ::mesh + +
+ + integer, + intent(in) + + ::sz + +
+ +

Return Value + + + type(cuda_allocator_t) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/cuda_field_init.html b/api/proc/cuda_field_init.html new file mode 100644 index 000000000..9ab0dd822 --- /dev/null +++ b/api/proc/cuda_field_init.html @@ -0,0 +1,276 @@ + + + + + + + + + + + + + cuda_field_init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

cuda_field_init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function cuda_field_init(ngrid, next, id) result(f) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::ngrid + +
+ + type(cuda_field_t), + intent(in), + pointer + ::next + +
+ + integer, + intent(in) + + ::id + +
+ +

Return Value + + + type(cuda_field_t) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/cuda_tdsops_init.html b/api/proc/cuda_tdsops_init.html new file mode 100644 index 000000000..8fa19b21c --- /dev/null +++ b/api/proc/cuda_tdsops_init.html @@ -0,0 +1,398 @@ + + + + + + + + + + + + + cuda_tdsops_init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

cuda_tdsops_init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function cuda_tdsops_init(n, delta, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) result(tdsops) +

+ + +

Constructor function for the cuda_tdsops_t class. +See tdsops_t for details.

+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::n + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::operation + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + integer, + intent(in),optional + + ::n_halo + +
+ + character(len=*), + intent(in),optional + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + real(kind=dp), + intent(in),optional + + ::c_nu + +
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
+ +

Return Value + + + type(cuda_tdsops_t) + +

+

return value of the function

+
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/curl.html b/api/proc/curl.html new file mode 100644 index 000000000..3f1ff51ad --- /dev/null +++ b/api/proc/curl.html @@ -0,0 +1,332 @@ + + + + + + + + + + + + + curl – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

curl + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine curl(self, o_i_hat, o_j_hat, o_k_hat, u, v, w) +

+ + +

Wrapper for curl

+ +

Type Bound

+

solver_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::o_i_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(inout) + + ::o_j_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(inout) + + ::o_k_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/curl~2.html b/api/proc/curl~2.html new file mode 100644 index 000000000..f0b506d78 --- /dev/null +++ b/api/proc/curl~2.html @@ -0,0 +1,379 @@ + + + + + + + + + + + + + curl – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

curl + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine curl(self, o_i_hat, o_j_hat, o_k_hat, u, v, w, x_der1st, y_der1st, z_der1st) +

+ + +

Curl of a vector field (u, v, w).

+

Evaluated at the data_loc defined by u, v, w fields.

+

All the input and output fields are in DIR_X layout.

+ +

Type Bound

+

vector_calculus_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(vector_calculus_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::o_i_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(inout) + + ::o_j_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(inout) + + ::o_k_hat +

Vector components of the output vector field Omega

+
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + class(tdsops_t), + intent(in) + + ::x_der1st + +
+ + class(tdsops_t), + intent(in) + + ::y_der1st + +
+ + class(tdsops_t), + intent(in) + + ::z_der1st + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/der_univ_dist.html b/api/proc/der_univ_dist.html new file mode 100644 index 000000000..f5fb71de8 --- /dev/null +++ b/api/proc/der_univ_dist.html @@ -0,0 +1,419 @@ + + + + + + + + + + + + + der_univ_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

der_univ_dist + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine der_univ_dist(du, send_u_s, send_u_e, u, u_s, u_e, coeffs_s, coeffs_e, coeffs, n, ffr, fbc, faf) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_u_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_u_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :) + ::coeffs_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :) + ::coeffs_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::coeffs + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::ffr + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::fbc + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::faf + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/der_univ_dist~2.html b/api/proc/der_univ_dist~2.html new file mode 100644 index 000000000..1d118b834 --- /dev/null +++ b/api/proc/der_univ_dist~2.html @@ -0,0 +1,419 @@ + + + + + + + + + + + + + der_univ_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

der_univ_dist + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine der_univ_dist(du, send_u_s, send_u_e, u, u_s, u_e, coeffs_s, coeffs_e, coeffs, n, ffr, fbc, faf) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :) + ::du + +
+ + real(kind=dp), + intent(out), + dimension(:, :) + ::send_u_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :) + ::send_u_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::u + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::u_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::u_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::coeffs_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::coeffs_e + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::coeffs + +
+ + integer, + intent(in) + + ::n + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::ffr + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::fbc + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::faf + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/der_univ_subs.html b/api/proc/der_univ_subs.html new file mode 100644 index 000000000..c1083e432 --- /dev/null +++ b/api/proc/der_univ_subs.html @@ -0,0 +1,314 @@ + + + + + + + + + + + + + der_univ_subs – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

der_univ_subs + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine der_univ_subs(du, recv_u_s, recv_u_e, n, dist_sa, dist_sc) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_u_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_u_e + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::dist_sa + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::dist_sc + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/der_univ_subs~2.html b/api/proc/der_univ_subs~2.html new file mode 100644 index 000000000..2fcc68fbf --- /dev/null +++ b/api/proc/der_univ_subs~2.html @@ -0,0 +1,314 @@ + + + + + + + + + + + + + der_univ_subs – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

der_univ_subs + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine der_univ_subs(du, recv_u_s, recv_u_e, n, dist_sa, dist_sc) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :) + ::du + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::recv_u_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :) + ::recv_u_e + +
+ + integer, + intent(in) + + ::n + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::dist_sa + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::dist_sc + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/der_univ_thom.html b/api/proc/der_univ_thom.html new file mode 100644 index 000000000..af8010d16 --- /dev/null +++ b/api/proc/der_univ_thom.html @@ -0,0 +1,359 @@ + + + + + + + + + + + + + der_univ_thom – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

der_univ_thom + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine der_univ_thom(du, u, coeffs_s, coeffs_e, coeffs, n, thom_f, thom_s, thom_w) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :) + ::coeffs_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :) + ::coeffs_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::coeffs + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_f + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_w + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/der_univ_thom_per.html b/api/proc/der_univ_thom_per.html new file mode 100644 index 000000000..624a91500 --- /dev/null +++ b/api/proc/der_univ_thom_per.html @@ -0,0 +1,359 @@ + + + + + + + + + + + + + der_univ_thom_per – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

der_univ_thom_per + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine der_univ_thom_per(du, u, coeffs, n, alpha, thom_f, thom_s, thom_w, thom_p) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::coeffs + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + value + ::alpha + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_f + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_w + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::thom_p + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/deriv_1st.html b/api/proc/deriv_1st.html new file mode 100644 index 000000000..a101c797a --- /dev/null +++ b/api/proc/deriv_1st.html @@ -0,0 +1,316 @@ + + + + + + + + + + + + + deriv_1st – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

deriv_1st + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine deriv_1st(self, delta, scheme, bc_start, bc_end, sym) +

+ + + +

Type Bound

+

tdsops_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/deriv_2nd.html b/api/proc/deriv_2nd.html new file mode 100644 index 000000000..a7c816894 --- /dev/null +++ b/api/proc/deriv_2nd.html @@ -0,0 +1,346 @@ + + + + + + + + + + + + + deriv_2nd – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

deriv_2nd + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine deriv_2nd(self, delta, scheme, bc_start, bc_end, sym, c_nu, nu0_nu) +

+ + + +

Type Bound

+

tdsops_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ + real(kind=dp), + intent(in),optional + + ::c_nu + +
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/destroy.html b/api/proc/destroy.html new file mode 100644 index 000000000..501a462d5 --- /dev/null +++ b/api/proc/destroy.html @@ -0,0 +1,246 @@ + + + + + + + + + + + + + destroy – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

destroy + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine destroy(self) +

+ + +

Go through the block list from head to tail, deallocating each +memory block in turn. Deallocation of a +m_allocator object automatically +deallocates its internal allocatable +data array.

+ +

Type Bound

+

allocator_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/divergence_v2c.html b/api/proc/divergence_v2c.html new file mode 100644 index 000000000..c4c2f768c --- /dev/null +++ b/api/proc/divergence_v2c.html @@ -0,0 +1,396 @@ + + + + + + + + + + + + + divergence_v2c – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

divergence_v2c + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine divergence_v2c(self, div_u, u, v, w, x_stagder_v2c, x_interpl_v2c, y_stagder_v2c, y_interpl_v2c, z_stagder_v2c, z_interpl_v2c) +

+ + +

Divergence of a vector field (u, v, w).

+

Evaluated at the cell centers (data_loc=CELL) +Input fields are at vertices (data_loc=VERT)

+

Input fields are in DIR_X data layout. +Output field is in DIR_Z data layout.

+ +

Type Bound

+

vector_calculus_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(vector_calculus_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::div_u + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + class(tdsops_t), + intent(in) + + ::x_stagder_v2c + +
+ + class(tdsops_t), + intent(in) + + ::x_interpl_v2c + +
+ + class(tdsops_t), + intent(in) + + ::y_stagder_v2c + +
+ + class(tdsops_t), + intent(in) + + ::y_interpl_v2c + +
+ + class(tdsops_t), + intent(in) + + ::z_stagder_v2c + +
+ + class(tdsops_t), + intent(in) + + ::z_interpl_v2c + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/divergence_v2p.html b/api/proc/divergence_v2p.html new file mode 100644 index 000000000..1cf529888 --- /dev/null +++ b/api/proc/divergence_v2p.html @@ -0,0 +1,302 @@ + + + + + + + + + + + + + divergence_v2p – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

divergence_v2p + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine divergence_v2p(self, div_u, u, v, w) +

+ + +

Wrapper for divergence_v2p

+ +

Type Bound

+

solver_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::div_u + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/domain_decomposition.html b/api/proc/domain_decomposition.html new file mode 100644 index 000000000..2d9e7b5b6 --- /dev/null +++ b/api/proc/domain_decomposition.html @@ -0,0 +1,242 @@ + + + + + + + + + + + + + domain_decomposition – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

domain_decomposition + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine domain_decomposition(mesh) +

+ + +

Supports 1D, 2D, and 3D domain decomposition.

+

Current implementation allows only constant sub-domain size across a +given direction.

+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout) + + ::mesh + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/exec_dist_tds_compact.html b/api/proc/exec_dist_tds_compact.html new file mode 100644 index 000000000..8c3afc5e4 --- /dev/null +++ b/api/proc/exec_dist_tds_compact.html @@ -0,0 +1,434 @@ + + + + + + + + + + + + + exec_dist_tds_compact – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

exec_dist_tds_compact + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine exec_dist_tds_compact(du, u, u_recv_s, u_recv_e, du_send_s, du_send_e, du_recv_s, du_recv_e, tdsops, nproc, pprev, pnext, blocks, threads) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_recv_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_send_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_send_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_recv_e + +
+ + type(cuda_tdsops_t), + intent(in) + + ::tdsops + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::pprev + +
+ + integer, + intent(in) + + ::pnext + +
+ + type(dim3), + intent(in) + + ::blocks + +
+ + type(dim3), + intent(in) + + ::threads + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/exec_dist_tds_compact~2.html b/api/proc/exec_dist_tds_compact~2.html new file mode 100644 index 000000000..10439b77f --- /dev/null +++ b/api/proc/exec_dist_tds_compact~2.html @@ -0,0 +1,419 @@ + + + + + + + + + + + + + exec_dist_tds_compact – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

exec_dist_tds_compact + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine exec_dist_tds_compact(du, u, u_recv_s, u_recv_e, du_send_s, du_send_e, du_recv_s, du_recv_e, tdsops, nproc, pprev, pnext, n_groups) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u_recv_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u_recv_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_send_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_send_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_recv_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_recv_e + +
+ + type(tdsops_t), + intent(in) + + ::tdsops + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::pprev + +
+ + integer, + intent(in) + + ::pnext + +
+ + integer, + intent(in) + + ::n_groups + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/exec_dist_transeq_3fused.html b/api/proc/exec_dist_transeq_3fused.html new file mode 100644 index 000000000..fa7d164be --- /dev/null +++ b/api/proc/exec_dist_transeq_3fused.html @@ -0,0 +1,674 @@ + + + + + + + + + + + + + exec_dist_transeq_3fused – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

exec_dist_transeq_3fused + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine exec_dist_transeq_3fused(r_u, u, u_recv_s, u_recv_e, v, v_recv_s, v_recv_e, du, dud, d2u, du_send_s, du_send_e, du_recv_s, du_recv_e, dud_send_s, dud_send_e, dud_recv_s, dud_recv_e, d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e, der1st, der2nd, nu, nproc, pprev, pnext, blocks, threads) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::r_u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_recv_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_recv_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v_recv_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_send_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_send_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud_send_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud_send_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u_send_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u_send_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u_recv_e + +
+ + type(cuda_tdsops_t), + intent(in) + + ::der1st + +
+ + type(cuda_tdsops_t), + intent(in) + + ::der2nd + +
+ + real(kind=dp), + intent(in) + + ::nu + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::pprev + +
+ + integer, + intent(in) + + ::pnext + +
+ + type(dim3), + intent(in) + + ::blocks + +
+ + type(dim3), + intent(in) + + ::threads + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/exec_dist_transeq_compact.html b/api/proc/exec_dist_transeq_compact.html new file mode 100644 index 000000000..e174a8d5e --- /dev/null +++ b/api/proc/exec_dist_transeq_compact.html @@ -0,0 +1,674 @@ + + + + + + + + + + + + + exec_dist_transeq_compact – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

exec_dist_transeq_compact + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine exec_dist_transeq_compact(rhs, du, dud, d2u, du_send_s, du_send_e, du_recv_s, du_recv_e, dud_send_s, dud_send_e, dud_recv_s, dud_recv_e, d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e, u, u_recv_s, u_recv_e, v, v_recv_s, v_recv_e, tdsops_du, tdsops_dud, tdsops_d2u, nu, nproc, pprev, pnext, n_groups) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::rhs + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::dud + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::d2u + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_send_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_send_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_recv_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::du_recv_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::dud_send_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::dud_send_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::dud_recv_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::dud_recv_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::d2u_send_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::d2u_send_e + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::d2u_recv_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::d2u_recv_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u_recv_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::u_recv_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::v + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::v_recv_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::v_recv_e + +
+ + type(tdsops_t), + intent(in) + + ::tdsops_du + +
+ + type(tdsops_t), + intent(in) + + ::tdsops_dud + +
+ + type(tdsops_t), + intent(in) + + ::tdsops_d2u + +
+ + real(kind=dp), + intent(in) + + ::nu + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::pprev + +
+ + integer, + intent(in) + + ::pnext + +
+ + integer, + intent(in) + + ::n_groups + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/exec_thom_tds_compact.html b/api/proc/exec_thom_tds_compact.html new file mode 100644 index 000000000..5c04c7937 --- /dev/null +++ b/api/proc/exec_thom_tds_compact.html @@ -0,0 +1,299 @@ + + + + + + + + + + + + + exec_thom_tds_compact – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

exec_thom_tds_compact + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine exec_thom_tds_compact(du, u, tdsops, blocks, threads) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + type(cuda_tdsops_t), + intent(in) + + ::tdsops + +
+ + type(dim3), + intent(in) + + ::blocks + +
+ + type(dim3), + intent(in) + + ::threads + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/fft_backward_cuda.html b/api/proc/fft_backward_cuda.html new file mode 100644 index 000000000..f6a78a899 --- /dev/null +++ b/api/proc/fft_backward_cuda.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + fft_backward_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

fft_backward_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine fft_backward_cuda(self, f) +

+ + + +

Type Bound

+

cuda_poisson_fft_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_poisson_fft_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::f + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/fft_backward_omp.html b/api/proc/fft_backward_omp.html new file mode 100644 index 000000000..961b0ab44 --- /dev/null +++ b/api/proc/fft_backward_omp.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + fft_backward_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

fft_backward_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine fft_backward_omp(self, f_out) +

+ + + +

Type Bound

+

omp_poisson_fft_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_poisson_fft_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::f_out + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/fft_forward_cuda.html b/api/proc/fft_forward_cuda.html new file mode 100644 index 000000000..47d5c22db --- /dev/null +++ b/api/proc/fft_forward_cuda.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + fft_forward_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

fft_forward_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine fft_forward_cuda(self, f) +

+ + + +

Type Bound

+

cuda_poisson_fft_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_poisson_fft_t) + + + ::self + +
+ + class(field_t), + intent(in) + + ::f + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/fft_forward_omp.html b/api/proc/fft_forward_omp.html new file mode 100644 index 000000000..a71bef2f9 --- /dev/null +++ b/api/proc/fft_forward_omp.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + fft_forward_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

fft_forward_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine fft_forward_omp(self, f_in) +

+ + + +

Type Bound

+

omp_poisson_fft_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_poisson_fft_t) + + + ::self + +
+ + class(field_t), + intent(in) + + ::f_in + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/fft_postprocess_cuda.html b/api/proc/fft_postprocess_cuda.html new file mode 100644 index 000000000..965e50206 --- /dev/null +++ b/api/proc/fft_postprocess_cuda.html @@ -0,0 +1,241 @@ + + + + + + + + + + + + + fft_postprocess_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

fft_postprocess_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine fft_postprocess_cuda(self) +

+ + + +

Type Bound

+

cuda_poisson_fft_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_poisson_fft_t) + + + ::self + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/fft_postprocess_omp.html b/api/proc/fft_postprocess_omp.html new file mode 100644 index 000000000..3535935f9 --- /dev/null +++ b/api/proc/fft_postprocess_omp.html @@ -0,0 +1,241 @@ + + + + + + + + + + + + + fft_postprocess_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

fft_postprocess_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine fft_postprocess_omp(self) +

+ + + +

Type Bound

+

omp_poisson_fft_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_poisson_fft_t) + + + ::self + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/field_init.html b/api/proc/field_init.html new file mode 100644 index 000000000..bb90859f9 --- /dev/null +++ b/api/proc/field_init.html @@ -0,0 +1,276 @@ + + + + + + + + + + + + + field_init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

field_init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function field_init(ngrid, next, id) result(f) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::ngrid + +
+ + type(field_t), + intent(in), + pointer + ::next + +
+ + integer, + intent(in) + + ::id + +
+ +

Return Value + + + type(field_t) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/finalize.html b/api/proc/finalize.html new file mode 100644 index 000000000..4506f30c6 --- /dev/null +++ b/api/proc/finalize.html @@ -0,0 +1,241 @@ + + + + + + + + + + + + + finalize – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

finalize + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine finalize(self) +

+ + + +

Type Bound

+

time_intg_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(time_intg_t), + intent(inout) + + ::self + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_block.html b/api/proc/get_block.html new file mode 100644 index 000000000..2c9f8f783 --- /dev/null +++ b/api/proc/get_block.html @@ -0,0 +1,285 @@ + + + + + + + + + + + + + get_block – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_block + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function get_block(self, dir, data_loc) result(handle) +

+ + +

Return a pointer to the first available memory block, i.e. the +current head of the block list. If the list is empty, allocate +a new block with create_block +first.

+

Example

+
f%data => get_block()
+
+ +

Type Bound

+

allocator_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ + integer, + intent(in) + + ::dir + +
+ + integer, + intent(in),optional + + ::data_loc + +
+ +

Return Value + + + class(field_t), pointer + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_block_ids.html b/api/proc/get_block_ids.html new file mode 100644 index 000000000..21a4bbbb9 --- /dev/null +++ b/api/proc/get_block_ids.html @@ -0,0 +1,251 @@ + + + + + + + + + + + + + get_block_ids – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_block_ids + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function get_block_ids(self) +

+ + +

Utility function that returns a array made of the id of the +block currently in the block list. Return the array [0] if +block list is empty.

+ +

Type Bound

+

allocator_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ +

Return Value + + + integer, allocatable, (:) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_coordinates.html b/api/proc/get_coordinates.html new file mode 100644 index 000000000..ebe9bd8e2 --- /dev/null +++ b/api/proc/get_coordinates.html @@ -0,0 +1,294 @@ + + + + + + + + + + + + + get_coordinates – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_coordinates + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_coordinates(self, i, j, k) result(xloc) +

+ + +

Get the physical location of a cell center with i,j,k local indices

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::i + +
+ + integer, + intent(in) + + ::j + +
+ + integer, + intent(in) + + ::k + +
+ +

Return Value + + + real(kind=dp), dimension(3) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_dims.html b/api/proc/get_dims.html new file mode 100644 index 000000000..04098e3ab --- /dev/null +++ b/api/proc/get_dims.html @@ -0,0 +1,264 @@ + + + + + + + + + + + + + get_dims – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_dims + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_dims(self, data_loc) result(dims) +

+ + +

Getter for local domain dimensions

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::data_loc + +
+ +

Return Value + + + integer, dimension(3) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_dims_dataloc.html b/api/proc/get_dims_dataloc.html new file mode 100644 index 000000000..7412baa09 --- /dev/null +++ b/api/proc/get_dims_dataloc.html @@ -0,0 +1,277 @@ + + + + + + + + + + + + + get_dims_dataloc – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_dims_dataloc + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_dims_dataloc(data_loc, vert_dims, cell_dims) result(dims) +

+ + +

Getter for domain dimensions

+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::data_loc + +
+ + integer, + intent(in), + dimension(3) + ::vert_dims + +
+ + integer, + intent(in), + dimension(3) + ::cell_dims + +
+ +

Return Value + + + integer, dimension(3) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_dirs_from_rdr.html b/api/proc/get_dirs_from_rdr.html new file mode 100644 index 000000000..0be36e47b --- /dev/null +++ b/api/proc/get_dirs_from_rdr.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + get_dirs_from_rdr – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_dirs_from_rdr + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure subroutine get_dirs_from_rdr(dir_from, dir_to, rdr_dir) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::dir_from + +
+ + integer, + intent(out) + + ::dir_to + +
+ + integer, + intent(in) + + ::rdr_dir + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_field_data.html b/api/proc/get_field_data.html new file mode 100644 index 000000000..18b252ad0 --- /dev/null +++ b/api/proc/get_field_data.html @@ -0,0 +1,288 @@ + + + + + + + + + + + + + get_field_data – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_field_data + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine get_field_data(self, data, f, dir) +

+ + +

Extract data from field f optionally reordering into dir orientation. +To output in same orientation as f, use call ...%get_field_data(data, f, f%dir)

+ +

Type Bound

+

base_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::data +

Output array

+
+ + class(field_t), + intent(in) + + ::f +

Field

+
+ + integer, + intent(in),optional + + ::dir +

Desired orientation of output array (defaults to Cartesian)

+
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_field_dims_dir.html b/api/proc/get_field_dims_dir.html new file mode 100644 index 000000000..979ca95d4 --- /dev/null +++ b/api/proc/get_field_dims_dir.html @@ -0,0 +1,279 @@ + + + + + + + + + + + + + get_field_dims_dir – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_field_dims_dir + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_field_dims_dir(self, dir, data_loc) result(dims) +

+ + +

Getter for the dimensions of an array directed along dir where data would be located on data_loc

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::dir + +
+ + integer, + intent(in) + + ::data_loc + +
+ +

Return Value + + + integer, dimension(3) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_field_dims_phi.html b/api/proc/get_field_dims_phi.html new file mode 100644 index 000000000..0d6bdacd2 --- /dev/null +++ b/api/proc/get_field_dims_phi.html @@ -0,0 +1,264 @@ + + + + + + + + + + + + + get_field_dims_phi – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_field_dims_phi + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_field_dims_phi(self, phi) result(dims) +

+ + +

Getter for the dimensions of field phi

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + class(field_t), + intent(in) + + ::phi + +
+ +

Return Value + + + integer, dimension(3) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_field_dims_phi_dataloc.html b/api/proc/get_field_dims_phi_dataloc.html new file mode 100644 index 000000000..3eda104cc --- /dev/null +++ b/api/proc/get_field_dims_phi_dataloc.html @@ -0,0 +1,279 @@ + + + + + + + + + + + + + get_field_dims_phi_dataloc – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_field_dims_phi_dataloc + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_field_dims_phi_dataloc(self, phi, data_loc) result(dims) +

+ + +

Getter for the dimensions of field phi where data is located on data_loc

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + class(field_t), + intent(in) + + ::phi + +
+ + integer, + intent(in) + + ::data_loc + +
+ +

Return Value + + + integer, dimension(3) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_global_dims.html b/api/proc/get_global_dims.html new file mode 100644 index 000000000..5a5f4549f --- /dev/null +++ b/api/proc/get_global_dims.html @@ -0,0 +1,264 @@ + + + + + + + + + + + + + get_global_dims – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_global_dims + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_global_dims(self, data_loc) result(dims) +

+ + +

Getter for local domain dimensions

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::data_loc + +
+ +

Return Value + + + integer, dimension(3) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_index_dir.html b/api/proc/get_index_dir.html new file mode 100644 index 000000000..a2d8f822d --- /dev/null +++ b/api/proc/get_index_dir.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + get_index_dir – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_index_dir + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure subroutine get_index_dir(dir_i, dir_j, dir_k, i, j, k, dir, SZ, nx_padded, ny_padded, nz_padded) +

+ + +

Get application storage directional index from cartesian index

+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::dir_i + +
+ + integer, + intent(out) + + ::dir_j + +
+ + integer, + intent(out) + + ::dir_k + +
+ + integer, + intent(in) + + ::i + +
+ + integer, + intent(in) + + ::j + +
+ + integer, + intent(in) + + ::k + +
+ + integer, + intent(in) + + ::dir + +
+ + integer, + intent(in) + + ::SZ + +
+ + integer, + intent(in) + + ::nx_padded + +
+ + integer, + intent(in) + + ::ny_padded + +
+ + integer, + intent(in) + + ::nz_padded + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_index_ijk.html b/api/proc/get_index_ijk.html new file mode 100644 index 000000000..cc2c81f5b --- /dev/null +++ b/api/proc/get_index_ijk.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + get_index_ijk – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_index_ijk + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure subroutine get_index_ijk(i, j, k, dir_i, dir_j, dir_k, dir, SZ, nx_padded, ny_padded, nz_padded) +

+ + +

Get cartesian index from application storage directional one

+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::i + +
+ + integer, + intent(out) + + ::j + +
+ + integer, + intent(out) + + ::k + +
+ + integer, + intent(in) + + ::dir_i + +
+ + integer, + intent(in) + + ::dir_j + +
+ + integer, + intent(in) + + ::dir_k + +
+ + integer, + intent(in) + + ::dir + +
+ + integer, + intent(in) + + ::SZ + +
+ + integer, + intent(in) + + ::nx_padded + +
+ + integer, + intent(in) + + ::ny_padded + +
+ + integer, + intent(in) + + ::nz_padded + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_index_reordering_dirs.html b/api/proc/get_index_reordering_dirs.html new file mode 100644 index 000000000..fd89b181a --- /dev/null +++ b/api/proc/get_index_reordering_dirs.html @@ -0,0 +1,361 @@ + + + + + + + + + + + + + get_index_reordering_dirs – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_index_reordering_dirs + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure subroutine get_index_reordering_dirs(out_i, out_j, out_k, in_i, in_j, in_k, dir_from, dir_to, mesh) +

+ + +

Converts a set of application storage directional index to an other direction. +The two directions are defined by the reorder_dir variable, RDR_X2Y will go from storage in X to Y etc.

+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::out_i + +
+ + integer, + intent(out) + + ::out_j + +
+ + integer, + intent(out) + + ::out_k + +
+ + integer, + intent(in) + + ::in_i + +
+ + integer, + intent(in) + + ::in_j + +
+ + integer, + intent(in) + + ::in_k + +
+ + integer, + intent(in) + + ::dir_from + +
+ + integer, + intent(in) + + ::dir_to + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_index_reordering_rdr.html b/api/proc/get_index_reordering_rdr.html new file mode 100644 index 000000000..fd958d47e --- /dev/null +++ b/api/proc/get_index_reordering_rdr.html @@ -0,0 +1,344 @@ + + + + + + + + + + + + + get_index_reordering_rdr – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_index_reordering_rdr + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure subroutine get_index_reordering_rdr(out_i, out_j, out_k, in_i, in_j, in_k, reorder_dir, mesh) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(out) + + ::out_i + +
+ + integer, + intent(out) + + ::out_j + +
+ + integer, + intent(out) + + ::out_k + +
+ + integer, + intent(in) + + ::in_i + +
+ + integer, + intent(in) + + ::in_j + +
+ + integer, + intent(in) + + ::in_k + +
+ + integer, + intent(in) + + ::reorder_dir + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_n_dir.html b/api/proc/get_n_dir.html new file mode 100644 index 000000000..63584cd14 --- /dev/null +++ b/api/proc/get_n_dir.html @@ -0,0 +1,279 @@ + + + + + + + + + + + + + get_n_dir – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_n_dir + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_n_dir(self, dir, data_loc) result(n) +

+ + +

Getter for the main dimension a field oriented along dir with data on data_loc

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::dir + +
+ + integer, + intent(in) + + ::data_loc + +
+ +

Return Value + + + integer + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_n_groups_dir.html b/api/proc/get_n_groups_dir.html new file mode 100644 index 000000000..4cd387c34 --- /dev/null +++ b/api/proc/get_n_groups_dir.html @@ -0,0 +1,264 @@ + + + + + + + + + + + + + get_n_groups_dir – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_n_groups_dir + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_n_groups_dir(self, dir) result(n_groups) +

+ + +

Getter for the number of groups for fields in direction dir

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::dir + +
+ +

Return Value + + + integer + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_n_groups_phi.html b/api/proc/get_n_groups_phi.html new file mode 100644 index 000000000..55e7125b2 --- /dev/null +++ b/api/proc/get_n_groups_phi.html @@ -0,0 +1,264 @@ + + + + + + + + + + + + + get_n_groups_phi – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_n_groups_phi + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_n_groups_phi(self, phi) result(n_groups) +

+ + +

Getter for the number of groups for fields phi

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + class(field_t), + intent(in) + + ::phi + +
+ +

Return Value + + + integer + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_n_phi.html b/api/proc/get_n_phi.html new file mode 100644 index 000000000..2c5b07bd9 --- /dev/null +++ b/api/proc/get_n_phi.html @@ -0,0 +1,264 @@ + + + + + + + + + + + + + get_n_phi – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_n_phi + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_n_phi(self, phi) result(n) +

+ + +

Getter for the main dimension of field phi

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + class(field_t), + intent(in) + + ::phi + +
+ +

Return Value + + + integer + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_padded_dims_dir.html b/api/proc/get_padded_dims_dir.html new file mode 100644 index 000000000..c580898d1 --- /dev/null +++ b/api/proc/get_padded_dims_dir.html @@ -0,0 +1,264 @@ + + + + + + + + + + + + + get_padded_dims_dir – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_padded_dims_dir + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_padded_dims_dir(self, dir) result(dims_padded) +

+ + +

Getter for padded dimensions with structure in dir direction

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + integer, + intent(in) + + ::dir + +
+ +

Return Value + + + integer, dimension(3) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_padded_dims_phi.html b/api/proc/get_padded_dims_phi.html new file mode 100644 index 000000000..d64f3c6a2 --- /dev/null +++ b/api/proc/get_padded_dims_phi.html @@ -0,0 +1,265 @@ + + + + + + + + + + + + + get_padded_dims_phi – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_padded_dims_phi + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_padded_dims_phi(self, phi) result(dims_padded) +

+ + +

Getter for padded dimensions for field phi +Gets the field direction from the field itself

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ + class(field_t), + intent(in) + + ::phi + +
+ +

Return Value + + + integer, dimension(3) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_rdr_from_dirs.html b/api/proc/get_rdr_from_dirs.html new file mode 100644 index 000000000..103cb0a75 --- /dev/null +++ b/api/proc/get_rdr_from_dirs.html @@ -0,0 +1,262 @@ + + + + + + + + + + + + + get_rdr_from_dirs – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_rdr_from_dirs + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_rdr_from_dirs(dir_from, dir_to) result(rdr_dir) +

+ + +

Returns RDR_?2? value based on two direction inputs

+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::dir_from + +
+ + integer, + intent(in) + + ::dir_to + +
+ +

Return Value + + + integer + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_sz.html b/api/proc/get_sz.html new file mode 100644 index 000000000..2ea1dadaf --- /dev/null +++ b/api/proc/get_sz.html @@ -0,0 +1,249 @@ + + + + + + + + + + + + + get_sz – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_sz + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_sz(self) result(sz) +

+ + +

Getter for parameter SZ

+ +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::self + +
+ +

Return Value + + + integer + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/get_tds_n.html b/api/proc/get_tds_n.html new file mode 100644 index 000000000..16607915f --- /dev/null +++ b/api/proc/get_tds_n.html @@ -0,0 +1,277 @@ + + + + + + + + + + + + + get_tds_n – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

get_tds_n + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function get_tds_n(mesh, dir, from_to) result(tds_n) +

+ + +

Get the tds_n size based on the from_to value (and the mesh)

+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + integer, + intent(in) + + ::dir + +
+ + character(len=*), + intent(in),optional + + ::from_to + +
+ +

Return Value + + + integer + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/gradient_c2v.html b/api/proc/gradient_c2v.html new file mode 100644 index 000000000..bfea4722a --- /dev/null +++ b/api/proc/gradient_c2v.html @@ -0,0 +1,396 @@ + + + + + + + + + + + + + gradient_c2v – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

gradient_c2v + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine gradient_c2v(self, dpdx, dpdy, dpdz, p, x_stagder_c2v, x_interpl_c2v, y_stagder_c2v, y_interpl_c2v, z_stagder_c2v, z_interpl_c2v) +

+ + +

Gradient of a scalar field 'p'.

+

Evaluated at the vertices (data_loc=VERT) +Input field is at cell centers (data_loc=CELL)

+

Input field is in DIR_Z data layout. +Output fields (dpdx, dpdy, dpdz) are in DIR_X data layout.

+ +

Type Bound

+

vector_calculus_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(vector_calculus_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::dpdx + +
+ + class(field_t), + intent(inout) + + ::dpdy + +
+ + class(field_t), + intent(inout) + + ::dpdz + +
+ + class(field_t), + intent(in) + + ::p + +
+ + class(tdsops_t), + intent(in) + + ::x_stagder_c2v + +
+ + class(tdsops_t), + intent(in) + + ::x_interpl_c2v + +
+ + class(tdsops_t), + intent(in) + + ::y_stagder_c2v + +
+ + class(tdsops_t), + intent(in) + + ::y_interpl_c2v + +
+ + class(tdsops_t), + intent(in) + + ::z_stagder_c2v + +
+ + class(tdsops_t), + intent(in) + + ::z_interpl_c2v + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/gradient_p2v.html b/api/proc/gradient_p2v.html new file mode 100644 index 000000000..bed5e909f --- /dev/null +++ b/api/proc/gradient_p2v.html @@ -0,0 +1,302 @@ + + + + + + + + + + + + + gradient_p2v – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

gradient_p2v + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine gradient_p2v(self, dpdx, dpdy, dpdz, pressure) +

+ + +

Wrapper for gradient_p2v

+ +

Type Bound

+

solver_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::dpdx + +
+ + class(field_t), + intent(inout) + + ::dpdy + +
+ + class(field_t), + intent(inout) + + ::dpdz + +
+ + class(field_t), + intent(in) + + ::pressure + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/init.html b/api/proc/init.html new file mode 100644 index 000000000..ec34b1cd2 --- /dev/null +++ b/api/proc/init.html @@ -0,0 +1,291 @@ + + + + + + + + + + + + + init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function init(backend, allocator, method, nvars) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t), + + pointer + ::backend + +
+ + class(allocator_t), + + pointer + ::allocator + +
+ + character(len=3), + intent(in) + + ::method + +
+ + integer, + intent(in),optional + + ::nvars + +
+ +

Return Value + + + type(time_intg_t) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/init_cuda_poisson_fft.html b/api/proc/init_cuda_poisson_fft.html new file mode 100644 index 000000000..87f74aeea --- /dev/null +++ b/api/proc/init_cuda_poisson_fft.html @@ -0,0 +1,301 @@ + + + + + + + + + + + + + init_cuda_poisson_fft – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

init_cuda_poisson_fft + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine init_cuda_poisson_fft(self, mesh, xdirps, ydirps, zdirps) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + type(dirps_t), + intent(in) + + ::xdirps + +
+ + type(dirps_t), + intent(in) + + ::ydirps + +
+ + type(dirps_t), + intent(in) + + ::zdirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/init_omp_poisson_fft.html b/api/proc/init_omp_poisson_fft.html new file mode 100644 index 000000000..e3ff56ad4 --- /dev/null +++ b/api/proc/init_omp_poisson_fft.html @@ -0,0 +1,301 @@ + + + + + + + + + + + + + init_omp_poisson_fft – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

init_omp_poisson_fft + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine init_omp_poisson_fft(self, mesh, xdirps, ydirps, zdirps) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(mesh_t), + intent(in) + + ::mesh + +
+ + type(dirps_t), + intent(in) + + ::xdirps + +
+ + type(dirps_t), + intent(in) + + ::ydirps + +
+ + type(dirps_t), + intent(in) + + ::zdirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/init~4.html b/api/proc/init~4.html new file mode 100644 index 000000000..63f4b1209 --- /dev/null +++ b/api/proc/init~4.html @@ -0,0 +1,385 @@ + + + + + + + + + + + + + init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function init(backend, mesh, host_allocator) result(solver) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t), + intent(inout), + target + ::backend + +
+ + type(mesh_t), + intent(inout), + target + ::mesh + +
+ + type(allocator_t), + intent(inout), + target + ::host_allocator + +
+ +

Return Value + + + type(solver_t) + +

+ +
+ + + + + + + + + + +
+

Namelists

+
+
+ +

Namelist solver_params

+
+
+ +
+
+

Variables

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
Rereal(kind=dp)None
dtreal(kind=dp)None
n_itersintegerNone
n_outputintegerNone
poisson_solver_typecharacter(len=3)None
time_intgcharacter(len=3)None
der1st_schemecharacter(len=30)None
der2nd_schemecharacter(len=30)None
interpl_schemecharacter(len=30)None
stagder_schemecharacter(len=30)None
+
+ +
+
+ +
+ + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/init~5.html b/api/proc/init~5.html new file mode 100644 index 000000000..129c729ec --- /dev/null +++ b/api/proc/init~5.html @@ -0,0 +1,261 @@ + + + + + + + + + + + + + init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function init(mesh, allocator) result(backend) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout), + target + ::mesh + +
+ + class(allocator_t), + intent(inout), + target + ::allocator + +
+ +

Return Value + + + type(cuda_backend_t) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/init~6.html b/api/proc/init~6.html new file mode 100644 index 000000000..794384e64 --- /dev/null +++ b/api/proc/init~6.html @@ -0,0 +1,246 @@ + + + + + + + + + + + + + init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function init(backend) result(vector_calculus) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t), + intent(inout), + target + ::backend + +
+ +

Return Value + + + type(vector_calculus_t) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/init~7.html b/api/proc/init~7.html new file mode 100644 index 000000000..8f1aeff74 --- /dev/null +++ b/api/proc/init~7.html @@ -0,0 +1,261 @@ + + + + + + + + + + + + + init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function init(mesh, allocator) result(backend) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout), + target + ::mesh + +
+ + class(allocator_t), + intent(inout), + target + ::allocator + +
+ +

Return Value + + + type(omp_backend_t) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/interpl_mid.html b/api/proc/interpl_mid.html new file mode 100644 index 000000000..e108e7ce4 --- /dev/null +++ b/api/proc/interpl_mid.html @@ -0,0 +1,316 @@ + + + + + + + + + + + + + interpl_mid – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

interpl_mid + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine interpl_mid(self, scheme, from_to, bc_start, bc_end, sym) +

+ + + +

Type Bound

+

tdsops_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + character(len=*), + intent(in) + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/is_root.html b/api/proc/is_root.html new file mode 100644 index 000000000..ec465e6e3 --- /dev/null +++ b/api/proc/is_root.html @@ -0,0 +1,249 @@ + + + + + + + + + + + + + is_root – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

is_root + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public pure function is_root(self) result(is_root_rank) +

+ + +

Returns whether or not the current rank is the root rank

+ +

Type Bound

+

parallel_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(parallel_t), + intent(in) + + ::self + +
+ +

Return Value + + + logical + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/laplacian.html b/api/proc/laplacian.html new file mode 100644 index 000000000..edc8cae06 --- /dev/null +++ b/api/proc/laplacian.html @@ -0,0 +1,319 @@ + + + + + + + + + + + + + laplacian – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

laplacian + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine laplacian(self, lapl_u, u, x_der2nd, y_der2nd, z_der2nd) +

+ + +

Laplacian of a scalar field 'u'.

+

Evaluated at the data_loc defined by the input u field

+

Input and output fields are in DIR_X layout.

+ +

Type Bound

+

vector_calculus_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(vector_calculus_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::lapl_u + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(tdsops_t), + intent(in) + + ::x_der2nd + +
+ + class(tdsops_t), + intent(in) + + ::y_der2nd + +
+ + class(tdsops_t), + intent(in) + + ::z_der2nd + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/mesh_init.html b/api/proc/mesh_init.html new file mode 100644 index 000000000..31ffed4ef --- /dev/null +++ b/api/proc/mesh_init.html @@ -0,0 +1,324 @@ + + + + + + + + + + + + + mesh_init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

mesh_init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function mesh_init(dims_global, nproc_dir, L_global, BC_x, BC_y, BC_z) result(mesh) +

+ + +

Completely initialise the mesh object. +Upon initialisation the mesh object can be read-only and shouldn't be edited +Takes as argument global information about the mesh like its length, number of cells and decomposition in each direction

+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in), + dimension(3) + ::dims_global + +
+ + integer, + intent(in), + dimension(3) + ::nproc_dir + +
+ + real(kind=dp), + intent(in), + dimension(3) + ::L_global + +
+ + character(len=*), + intent(in), + dimension(2) + ::BC_x + +
+ + character(len=*), + intent(in), + dimension(2) + ::BC_y + +
+ + character(len=*), + intent(in), + dimension(2) + ::BC_z + +
+ +

Return Value + + + type(mesh_t) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/output.html b/api/proc/output.html new file mode 100644 index 000000000..64e48c585 --- /dev/null +++ b/api/proc/output.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + output – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

output + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine output(self, t) +

+ + + +

Type Bound

+

solver_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t), + intent(in) + + ::self + +
+ + real(kind=dp), + intent(in) + + ::t + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/poisson_cg.html b/api/proc/poisson_cg.html new file mode 100644 index 000000000..92388b9d6 --- /dev/null +++ b/api/proc/poisson_cg.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + poisson_cg – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

poisson_cg + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine poisson_cg(self, pressure, div_u) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::pressure + +
+ + class(field_t), + intent(in) + + ::div_u + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/poisson_fft.html b/api/proc/poisson_fft.html new file mode 100644 index 000000000..09541665c --- /dev/null +++ b/api/proc/poisson_fft.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + poisson_fft – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

poisson_fft + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine poisson_fft(self, pressure, div_u) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::pressure + +
+ + class(field_t), + intent(in) + + ::div_u + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/preprocess_dist.html b/api/proc/preprocess_dist.html new file mode 100644 index 000000000..ff86780a3 --- /dev/null +++ b/api/proc/preprocess_dist.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + preprocess_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

preprocess_dist + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine preprocess_dist(self, dist_b) +

+ + + +

Type Bound

+

tdsops_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::dist_b + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/preprocess_thom.html b/api/proc/preprocess_thom.html new file mode 100644 index 000000000..b4ec67484 --- /dev/null +++ b/api/proc/preprocess_thom.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + preprocess_thom – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

preprocess_thom + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine preprocess_thom(self, b) +

+ + + +

Type Bound

+

tdsops_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(in), + dimension(:) + ::b + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/process_spectral_div_u.html b/api/proc/process_spectral_div_u.html new file mode 100644 index 000000000..738ee07fc --- /dev/null +++ b/api/proc/process_spectral_div_u.html @@ -0,0 +1,437 @@ + + + + + + + + + + + + + process_spectral_div_u – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

process_spectral_div_u + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine process_spectral_div_u(div_u, waves, nx_spec, ny_spec, y_sp_st, nx, ny, nz, ax, bx, ay, by, az, bz) +

+ + +

Post-processes the divergence of velocity in spectral space, including +scaling w.r.t. grid size.

+

Ref. JCP 228 (2009), 5989–6015, Sec 4

+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + complex(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::div_u +

Divergence of velocity in spectral space

+
+ + complex(kind=dp), + intent(in), + device, dimension(:, :, :) + ::waves +

Spectral equivalence constants

+
+ + integer, + intent(in), + value + ::nx_spec +

Grid size in spectral space

+
+ + integer, + intent(in), + value + ::ny_spec +

Grid size in spectral space

+
+ + integer, + intent(in), + value + ::y_sp_st +

Offset in y direction in the permuted slabs in spectral space

+
+ + integer, + intent(in), + value + ::nx +

Grid size

+
+ + integer, + intent(in), + value + ::ny +

Grid size

+
+ + integer, + intent(in), + value + ::nz +

Grid size

+
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::ax + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::bx + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::ay + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::by + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::az + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::bz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/release_block.html b/api/proc/release_block.html new file mode 100644 index 000000000..a781c06da --- /dev/null +++ b/api/proc/release_block.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + release_block – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

release_block + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine release_block(self, handle) +

+ + +

Release memory block pointed to by HANDLE to the block list. +It is pushed to the front of the block list, in other words it +is made the head block.

+ +

Type Bound

+

allocator_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(allocator_t), + intent(inout) + + ::self + +
+ + class(field_t), + + pointer + ::handle + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/reorder_c2x.html b/api/proc/reorder_c2x.html new file mode 100644 index 000000000..763b96bfb --- /dev/null +++ b/api/proc/reorder_c2x.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + reorder_c2x – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder_c2x + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine reorder_c2x(u_x, u_c, nz) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_c + +
+ + integer, + intent(in), + value + ::nz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/reorder_cuda.html b/api/proc/reorder_cuda.html new file mode 100644 index 000000000..98bc277e3 --- /dev/null +++ b/api/proc/reorder_cuda.html @@ -0,0 +1,286 @@ + + + + + + + + + + + + + reorder_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine reorder_cuda(self, u_o, u_i, direction) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u_o + +
+ + class(field_t), + intent(in) + + ::u_i + +
+ + integer, + intent(in) + + ::direction + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/reorder_omp.html b/api/proc/reorder_omp.html new file mode 100644 index 000000000..e5f76f598 --- /dev/null +++ b/api/proc/reorder_omp.html @@ -0,0 +1,286 @@ + + + + + + + + + + + + + reorder_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine reorder_omp(self, u_, u, direction) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u_ + +
+ + class(field_t), + intent(in) + + ::u + +
+ + integer, + intent(in) + + ::direction + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/reorder_x2c.html b/api/proc/reorder_x2c.html new file mode 100644 index 000000000..cc60d309e --- /dev/null +++ b/api/proc/reorder_x2c.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + reorder_x2c – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder_x2c + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine reorder_x2c(u_c, u_x, nz) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_c + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_x + +
+ + integer, + intent(in), + value + ::nz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/reorder_x2y.html b/api/proc/reorder_x2y.html new file mode 100644 index 000000000..cca29c97f --- /dev/null +++ b/api/proc/reorder_x2y.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + reorder_x2y – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder_x2y + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine reorder_x2y(u_y, u_x, nz) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_y + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_x + +
+ + integer, + intent(in), + value + ::nz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/reorder_x2z.html b/api/proc/reorder_x2z.html new file mode 100644 index 000000000..9130af5fa --- /dev/null +++ b/api/proc/reorder_x2z.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + reorder_x2z – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder_x2z + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine reorder_x2z(u_z, u_x, nz) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_z + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_x + +
+ + integer, + intent(in), + value + ::nz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/reorder_y2x.html b/api/proc/reorder_y2x.html new file mode 100644 index 000000000..caa39c1e5 --- /dev/null +++ b/api/proc/reorder_y2x.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + reorder_y2x – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder_y2x + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine reorder_y2x(u_x, u_y, nz) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_y + +
+ + integer, + intent(in), + value + ::nz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/reorder_y2z.html b/api/proc/reorder_y2z.html new file mode 100644 index 000000000..f996e488f --- /dev/null +++ b/api/proc/reorder_y2z.html @@ -0,0 +1,284 @@ + + + + + + + + + + + + + reorder_y2z – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder_y2z + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine reorder_y2z(u_z, u_y, nx, nz) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_z + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_y + +
+ + integer, + intent(in), + value + ::nx + +
+ + integer, + intent(in), + value + ::nz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/reorder_z2x.html b/api/proc/reorder_z2x.html new file mode 100644 index 000000000..fb3c2cbd2 --- /dev/null +++ b/api/proc/reorder_z2x.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + reorder_z2x – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder_z2x + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine reorder_z2x(u_x, u_z, nz) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_z + +
+ + integer, + intent(in), + value + ::nz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/reorder_z2y.html b/api/proc/reorder_z2y.html new file mode 100644 index 000000000..43ecc137b --- /dev/null +++ b/api/proc/reorder_z2y.html @@ -0,0 +1,284 @@ + + + + + + + + + + + + + reorder_z2y – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder_z2y + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine reorder_z2y(u_y, u_z, nx, nz) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::u_y + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_z + +
+ + integer, + intent(in), + value + ::nx + +
+ + integer, + intent(in), + value + ::nz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/resolve_field_t.html b/api/proc/resolve_field_t.html new file mode 100644 index 000000000..ad4d2cac5 --- /dev/null +++ b/api/proc/resolve_field_t.html @@ -0,0 +1,254 @@ + + + + + + + + + + + + + resolve_field_t – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

resolve_field_t + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine resolve_field_t(u_dev, u) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, pointer, dimension(:, :, :) + ::u_dev + +
+ + class(field_t), + intent(in) + + ::u + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/rotate.html b/api/proc/rotate.html new file mode 100644 index 000000000..44231e83d --- /dev/null +++ b/api/proc/rotate.html @@ -0,0 +1,254 @@ + + + + + + + + + + + + + rotate – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

rotate + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine rotate(sol, n) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + type(flist_t), + intent(inout) + + ::sol(:) + +
+ + integer, + intent(in) + + ::n + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/run.html b/api/proc/run.html new file mode 100644 index 000000000..c25d83d11 --- /dev/null +++ b/api/proc/run.html @@ -0,0 +1,241 @@ + + + + + + + + + + + + + run – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

run + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine run(self) +

+ + + +

Type Bound

+

solver_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t), + intent(inout) + + ::self + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/scalar_product.html b/api/proc/scalar_product.html new file mode 100644 index 000000000..31cf5a831 --- /dev/null +++ b/api/proc/scalar_product.html @@ -0,0 +1,284 @@ + + + + + + + + + + + + + scalar_product – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

scalar_product + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine scalar_product(s, x, y, n) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(inout), + device + ::s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::y + +
+ + integer, + intent(in), + value + ::n + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/scalar_product_cuda.html b/api/proc/scalar_product_cuda.html new file mode 100644 index 000000000..40bf36bed --- /dev/null +++ b/api/proc/scalar_product_cuda.html @@ -0,0 +1,278 @@ + + + + + + + + + + + + + scalar_product_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

scalar_product_cuda + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function scalar_product_cuda(self, x, y) result(s) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(in) + + ::x + +
+ + class(field_t), + intent(in) + + ::y + +
+ +

Return Value + + + real(kind=dp) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/scalar_product_omp.html b/api/proc/scalar_product_omp.html new file mode 100644 index 000000000..e9e94cfcb --- /dev/null +++ b/api/proc/scalar_product_omp.html @@ -0,0 +1,291 @@ + + + + + + + + + + + + + scalar_product_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

scalar_product_omp + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function scalar_product_omp(self, x, y) result(s) +

+
+

Uses

+
+ +
+
+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(in) + + ::x + +
+ + class(field_t), + intent(in) + + ::y + +
+ +

Return Value + + + real(kind=dp) + +

+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/sendrecv_3fields.html b/api/proc/sendrecv_3fields.html new file mode 100644 index 000000000..3f4c7322b --- /dev/null +++ b/api/proc/sendrecv_3fields.html @@ -0,0 +1,464 @@ + + + + + + + + + + + + + sendrecv_3fields – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sendrecv_3fields + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine sendrecv_3fields(f1_recv_s, f1_recv_e, f2_recv_s, f2_recv_e, f3_recv_s, f3_recv_e, f1_send_s, f1_send_e, f2_send_s, f2_send_e, f3_send_s, f3_send_e, n_data, nproc, prev, next) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f1_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f1_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f2_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f2_recv_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f3_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f3_recv_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f1_send_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f1_send_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f2_send_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f2_send_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f3_send_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f3_send_e + +
+ + integer, + intent(in) + + ::n_data + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::prev + +
+ + integer, + intent(in) + + ::next + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/sendrecv_fields.html b/api/proc/sendrecv_fields.html new file mode 100644 index 000000000..2b6657e15 --- /dev/null +++ b/api/proc/sendrecv_fields.html @@ -0,0 +1,344 @@ + + + + + + + + + + + + + sendrecv_fields – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sendrecv_fields + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine sendrecv_fields(f_recv_s, f_recv_e, f_send_s, f_send_e, n_data, nproc, prev, next) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f_recv_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::f_recv_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f_send_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::f_send_e + +
+ + integer, + intent(in) + + ::n_data + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::prev + +
+ + integer, + intent(in) + + ::next + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/sendrecv_fields~2.html b/api/proc/sendrecv_fields~2.html new file mode 100644 index 000000000..c1921a3bd --- /dev/null +++ b/api/proc/sendrecv_fields~2.html @@ -0,0 +1,344 @@ + + + + + + + + + + + + + sendrecv_fields – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sendrecv_fields + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine sendrecv_fields(f_recv_s, f_recv_e, f_send_s, f_send_e, n_data, nproc, prev, next) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::f_recv_s + +
+ + real(kind=dp), + intent(out), + dimension(:, :, :) + ::f_recv_e + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::f_send_s + +
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::f_send_e + +
+ + integer, + intent(in) + + ::n_data + +
+ + integer, + intent(in) + + ::nproc + +
+ + integer, + intent(in) + + ::prev + +
+ + integer, + intent(in) + + ::next + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/set_data_loc.html b/api/proc/set_data_loc.html new file mode 100644 index 000000000..8b41e9f0f --- /dev/null +++ b/api/proc/set_data_loc.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + set_data_loc – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

set_data_loc + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine set_data_loc(self, data_loc) +

+ + + +

Type Bound

+

field_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(field_t) + + + ::self + +
+ + integer, + intent(in) + + ::data_loc + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/set_field_data.html b/api/proc/set_field_data.html new file mode 100644 index 000000000..37103ecde --- /dev/null +++ b/api/proc/set_field_data.html @@ -0,0 +1,286 @@ + + + + + + + + + + + + + set_field_data – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

set_field_data + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine set_field_data(self, f, data, dir) +

+ + + +

Type Bound

+

base_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(base_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::f +

Field

+
+ + real(kind=dp), + intent(in), + dimension(:, :, :) + ::data +

Input array

+
+ + integer, + intent(in),optional + + ::dir +

Orientation of input array (defaults to Cartesian)

+
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/set_padded_dims.html b/api/proc/set_padded_dims.html new file mode 100644 index 000000000..cdb754116 --- /dev/null +++ b/api/proc/set_padded_dims.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + set_padded_dims – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

set_padded_dims + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine set_padded_dims(self, vert_dims) +

+ + + +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout) + + ::self + +
+ + integer, + intent(in), + dimension(3) + ::vert_dims + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/set_shape.html b/api/proc/set_shape.html new file mode 100644 index 000000000..8eb12a164 --- /dev/null +++ b/api/proc/set_shape.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + set_shape – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

set_shape + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine set_shape(self, dims) +

+ + + +

Type Bound

+

field_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(field_t) + + + ::self + +
+ + integer, + intent(in) + + ::dims(3) + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/set_shape_cuda.html b/api/proc/set_shape_cuda.html new file mode 100644 index 000000000..080c140a9 --- /dev/null +++ b/api/proc/set_shape_cuda.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + set_shape_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

set_shape_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine set_shape_cuda(self, dims) +

+ + + +

Type Bound

+

cuda_field_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_field_t) + + + ::self + +
+ + integer, + intent(in) + + ::dims(3) + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/set_sz.html b/api/proc/set_sz.html new file mode 100644 index 000000000..381f04ee0 --- /dev/null +++ b/api/proc/set_sz.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + set_sz – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

set_sz + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine set_sz(self, sz) +

+ + + +

Type Bound

+

mesh_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(mesh_t), + intent(inout) + + ::self + +
+ + integer, + intent(in) + + ::sz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/stagder_1st.html b/api/proc/stagder_1st.html new file mode 100644 index 000000000..71b8c1724 --- /dev/null +++ b/api/proc/stagder_1st.html @@ -0,0 +1,331 @@ + + + + + + + + + + + + + stagder_1st – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

stagder_1st + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine stagder_1st(self, delta, scheme, from_to, bc_start, bc_end, sym) +

+ + + +

Type Bound

+

tdsops_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(tdsops_t), + intent(inout) + + ::self + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + character(len=*), + intent(in) + + ::from_to + +
+ + character(len=*), + intent(in),optional + + ::bc_start + +
+ + character(len=*), + intent(in),optional + + ::bc_end + +
+ + logical, + intent(in),optional + + ::sym + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/step.html b/api/proc/step.html new file mode 100644 index 000000000..bb3662294 --- /dev/null +++ b/api/proc/step.html @@ -0,0 +1,346 @@ + + + + + + + + + + + + + step – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

step + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine step(self, u, v, w, du, dv, dw, dt) +

+ + + +

Type Bound

+

time_intg_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(time_intg_t), + intent(inout) + + ::self + +
+ + class(field_t), + intent(inout), + target + ::u + +
+ + class(field_t), + intent(inout), + target + ::v + +
+ + class(field_t), + intent(inout), + target + ::w + +
+ + class(field_t), + intent(in), + target + ::du + +
+ + class(field_t), + intent(in), + target + ::dv + +
+ + class(field_t), + intent(in), + target + ::dw + +
+ + real(kind=dp), + intent(in) + + ::dt + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/sum_intox_omp.html b/api/proc/sum_intox_omp.html new file mode 100644 index 000000000..7ddbeeb08 --- /dev/null +++ b/api/proc/sum_intox_omp.html @@ -0,0 +1,284 @@ + + + + + + + + + + + + + sum_intox_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sum_intox_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine sum_intox_omp(self, u, u_, dir_to) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u + +
+ + class(field_t), + intent(in) + + ::u_ + +
+ + integer, + intent(in) + + ::dir_to + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/sum_yintox.html b/api/proc/sum_yintox.html new file mode 100644 index 000000000..63a1a8b7f --- /dev/null +++ b/api/proc/sum_yintox.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + sum_yintox – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sum_yintox + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine sum_yintox(u_x, u_y, nz) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::u_x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_y + +
+ + integer, + intent(in), + value + ::nz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/sum_yintox_cuda.html b/api/proc/sum_yintox_cuda.html new file mode 100644 index 000000000..5d57c39e4 --- /dev/null +++ b/api/proc/sum_yintox_cuda.html @@ -0,0 +1,271 @@ + + + + + + + + + + + + + sum_yintox_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sum_yintox_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine sum_yintox_cuda(self, u, u_y) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u + +
+ + class(field_t), + intent(in) + + ::u_y + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/sum_yintox_omp.html b/api/proc/sum_yintox_omp.html new file mode 100644 index 000000000..db168c6b2 --- /dev/null +++ b/api/proc/sum_yintox_omp.html @@ -0,0 +1,271 @@ + + + + + + + + + + + + + sum_yintox_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sum_yintox_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine sum_yintox_omp(self, u, u_) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u + +
+ + class(field_t), + intent(in) + + ::u_ + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/sum_zintox.html b/api/proc/sum_zintox.html new file mode 100644 index 000000000..800168ad6 --- /dev/null +++ b/api/proc/sum_zintox.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + sum_zintox – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sum_zintox + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine sum_zintox(u_x, u_z, nz) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(inout), + device, dimension(:, :, :) + ::u_x + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_z + +
+ + integer, + intent(in), + value + ::nz + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/sum_zintox_cuda.html b/api/proc/sum_zintox_cuda.html new file mode 100644 index 000000000..88d6b83d7 --- /dev/null +++ b/api/proc/sum_zintox_cuda.html @@ -0,0 +1,271 @@ + + + + + + + + + + + + + sum_zintox_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sum_zintox_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine sum_zintox_cuda(self, u, u_z) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u + +
+ + class(field_t), + intent(in) + + ::u_z + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/sum_zintox_omp.html b/api/proc/sum_zintox_omp.html new file mode 100644 index 000000000..64391edac --- /dev/null +++ b/api/proc/sum_zintox_omp.html @@ -0,0 +1,271 @@ + + + + + + + + + + + + + sum_zintox_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sum_zintox_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine sum_zintox_omp(self, u, u_) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::u + +
+ + class(field_t), + intent(in) + + ::u_ + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/tds_solve_cuda.html b/api/proc/tds_solve_cuda.html new file mode 100644 index 000000000..954788a7a --- /dev/null +++ b/api/proc/tds_solve_cuda.html @@ -0,0 +1,286 @@ + + + + + + + + + + + + + tds_solve_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

tds_solve_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine tds_solve_cuda(self, du, u, tdsops) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(tdsops_t), + intent(in) + + ::tdsops + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/tds_solve_dist.html b/api/proc/tds_solve_dist.html new file mode 100644 index 000000000..0008b6bc7 --- /dev/null +++ b/api/proc/tds_solve_dist.html @@ -0,0 +1,316 @@ + + + + + + + + + + + + + tds_solve_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

tds_solve_dist + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine tds_solve_dist(self, du, u, tdsops, blocks, threads) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(tdsops_t), + intent(in) + + ::tdsops + +
+ + type(dim3), + intent(in) + + ::blocks + +
+ + type(dim3), + intent(in) + + ::threads + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/tds_solve_dist~2.html b/api/proc/tds_solve_dist~2.html new file mode 100644 index 000000000..3ab77f970 --- /dev/null +++ b/api/proc/tds_solve_dist~2.html @@ -0,0 +1,284 @@ + + + + + + + + + + + + + tds_solve_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

tds_solve_dist + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine tds_solve_dist(self, du, u, tdsops) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(tdsops_t), + intent(in) + + ::tdsops + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/tds_solve_omp.html b/api/proc/tds_solve_omp.html new file mode 100644 index 000000000..fa1615ff3 --- /dev/null +++ b/api/proc/tds_solve_omp.html @@ -0,0 +1,286 @@ + + + + + + + + + + + + + tds_solve_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

tds_solve_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine tds_solve_omp(self, du, u, tdsops) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(tdsops_t), + intent(in) + + ::tdsops + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/tdsops_init.html b/api/proc/tdsops_init.html new file mode 100644 index 000000000..630549b7a --- /dev/null +++ b/api/proc/tdsops_init.html @@ -0,0 +1,412 @@ + + + + + + + + + + + + + tdsops_init – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

tdsops_init + Function + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public function tdsops_init(tds_n, delta, operation, scheme, n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu) result(tdsops) +

+ + +

Constructor function for the tdsops_t class.

+

'n', 'delta', 'operation', and 'scheme' are necessary arguments. +Number of points 'n', distance between two points 'delta', the +'operation' the tridiagonal system defines, and the 'scheme' that +specifies the exact scheme we choose to apply for the operation. +The remaining arguments are optional. +'from_to' is necessary for interpolation and staggared derivative, and +it can be 'v2p' or 'p2v'. +If the specific region the instance is operating is not a boundary +region, then 'bc_start' and 'bc_end' are either 'null' or not defined. +'sym' is relevant when the boundary condition is free-slip. If sym is +.true. then it means the field we operate on is assumed to be an even +function (symmetric) accross the boundary. If it is .false. it means +that the field is assumed to be an odd function (anti-symmetric). +'c_nu', 'nu0_nu' are relevant when operation is second order +derivative and scheme is compact6-hyperviscous.

+ + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + integer, + intent(in) + + ::tds_n + +
+ + real(kind=dp), + intent(in) + + ::delta + +
+ + character(len=*), + intent(in) + + ::operation + +
+ + character(len=*), + intent(in) + + ::scheme + +
+ + integer, + intent(in),optional + + ::n_halo +

Number of halo cells

+
+ + character(len=*), + intent(in),optional + + ::from_to +

'v2p' or 'p2v'

+
+ + character(len=*), + intent(in),optional + + ::bc_start +

Boundary Cond.

+
+ + character(len=*), + intent(in),optional + + ::bc_end +

Boundary Cond.

+
+ + logical, + intent(in),optional + + ::sym +

(==npaire), only for Neumann BCs

+
+ + real(kind=dp), + intent(in),optional + + ::c_nu +

params for hypervisc.

+
+ + real(kind=dp), + intent(in),optional + + ::nu0_nu +

params for hypervisc.

+
+ +

Return Value + + + type(tdsops_t) + +

+

return value of the function

+
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq.html b/api/proc/transeq.html new file mode 100644 index 000000000..d058b1d7f --- /dev/null +++ b/api/proc/transeq.html @@ -0,0 +1,335 @@ + + + + + + + + + + + + + transeq – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq(self, du, dv, dw, u, v, w) +

+ + +

Skew-symmetric form of convection-diffusion terms in the +incompressible Navier-Stokes momemtum equations, excluding +pressure terms. +Inputs from velocity grid and outputs to velocity grid.

+ +

Type Bound

+

solver_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(solver_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq_3fused_dist.html b/api/proc/transeq_3fused_dist.html new file mode 100644 index 000000000..5986268e0 --- /dev/null +++ b/api/proc/transeq_3fused_dist.html @@ -0,0 +1,644 @@ + + + + + + + + + + + + + transeq_3fused_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_3fused_dist + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq_3fused_dist(du, dud, d2u, send_du_s, send_du_e, send_dud_s, send_dud_e, send_d2u_s, send_d2u_e, u, u_s, u_e, v, v_s, v_e, n, d1_coeffs_s, d1_coeffs_e, d1_coeffs, d1_fw, d1_bw, d1_af, d2_coeffs_s, d2_coeffs_e, d2_coeffs, d2_fw, d2_bw, d2_af) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::dud + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::d2u + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_du_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_du_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_dud_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_dud_e + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_d2u_s + +
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::send_d2u_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::u_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::v_e + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + device + ::d1_coeffs_s(:,:) + +
+ + real(kind=dp), + intent(in), + device + ::d1_coeffs_e(:,:) + +
+ + real(kind=dp), + intent(in), + device + ::d1_coeffs(:) + +
+ + real(kind=dp), + intent(in), + device + ::d1_fw(:) + +
+ + real(kind=dp), + intent(in), + device + ::d1_bw(:) + +
+ + real(kind=dp), + intent(in), + device + ::d1_af(:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_coeffs_s(:,:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_coeffs_e(:,:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_coeffs(:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_fw(:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_bw(:) + +
+ + real(kind=dp), + intent(in), + device + ::d2_af(:) + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq_3fused_subs.html b/api/proc/transeq_3fused_subs.html new file mode 100644 index 000000000..91b3fc86a --- /dev/null +++ b/api/proc/transeq_3fused_subs.html @@ -0,0 +1,479 @@ + + + + + + + + + + + + + transeq_3fused_subs – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_3fused_subs + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq_3fused_subs(r_u, conv, du, dud, d2u, recv_du_s, recv_du_e, recv_dud_s, recv_dud_e, recv_d2u_s, recv_d2u_e, d1_sa, d1_sc, d2_sa, d2_sc, n, nu) +

+ + + + +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + real(kind=dp), + intent(out), + device, dimension(:, :, :) + ::r_u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::conv + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::du + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::dud + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::d2u + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_du_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_du_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_dud_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_dud_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_d2u_s + +
+ + real(kind=dp), + intent(in), + device, dimension(:, :, :) + ::recv_d2u_e + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::d1_sa + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::d1_sc + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::d2_sa + +
+ + real(kind=dp), + intent(in), + device, dimension(:) + ::d2_sc + +
+ + integer, + intent(in), + value + ::n + +
+ + real(kind=dp), + intent(in), + value + ::nu + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq_cuda_dist.html b/api/proc/transeq_cuda_dist.html new file mode 100644 index 000000000..f4e1611a3 --- /dev/null +++ b/api/proc/transeq_cuda_dist.html @@ -0,0 +1,376 @@ + + + + + + + + + + + + + transeq_cuda_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_cuda_dist + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq_cuda_dist(self, du, dv, dw, u, v, w, dirps, blocks, threads) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ + type(dim3), + intent(in) + + ::blocks + +
+ + type(dim3), + intent(in) + + ::threads + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq_cuda_thom.html b/api/proc/transeq_cuda_thom.html new file mode 100644 index 000000000..5ffc7e5aa --- /dev/null +++ b/api/proc/transeq_cuda_thom.html @@ -0,0 +1,349 @@ + + + + + + + + + + + + + transeq_cuda_thom – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_cuda_thom + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq_cuda_thom(self, du, dv, dw, u, v, w, dirps) +

+ + +

Thomas algorithm implementation. So much more easier than the +distributed algorithm. It is intended to work only on a single rank +so there is no MPI communication.

+ +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq_omp_dist.html b/api/proc/transeq_omp_dist.html new file mode 100644 index 000000000..b9df7d0cf --- /dev/null +++ b/api/proc/transeq_omp_dist.html @@ -0,0 +1,346 @@ + + + + + + + + + + + + + transeq_omp_dist – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_omp_dist + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq_omp_dist(self, du, dv, dw, u, v, w, dirps) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq_x_cuda.html b/api/proc/transeq_x_cuda.html new file mode 100644 index 000000000..7595766a3 --- /dev/null +++ b/api/proc/transeq_x_cuda.html @@ -0,0 +1,346 @@ + + + + + + + + + + + + + transeq_x_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_x_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq_x_cuda(self, du, dv, dw, u, v, w, dirps) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq_x_omp.html b/api/proc/transeq_x_omp.html new file mode 100644 index 000000000..399ba6bb9 --- /dev/null +++ b/api/proc/transeq_x_omp.html @@ -0,0 +1,346 @@ + + + + + + + + + + + + + transeq_x_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_x_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq_x_omp(self, du, dv, dw, u, v, w, dirps) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq_y_cuda.html b/api/proc/transeq_y_cuda.html new file mode 100644 index 000000000..239a3cf30 --- /dev/null +++ b/api/proc/transeq_y_cuda.html @@ -0,0 +1,346 @@ + + + + + + + + + + + + + transeq_y_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_y_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq_y_cuda(self, du, dv, dw, u, v, w, dirps) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq_y_omp.html b/api/proc/transeq_y_omp.html new file mode 100644 index 000000000..5ff9340fa --- /dev/null +++ b/api/proc/transeq_y_omp.html @@ -0,0 +1,346 @@ + + + + + + + + + + + + + transeq_y_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_y_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq_y_omp(self, du, dv, dw, u, v, w, dirps) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq_z_cuda.html b/api/proc/transeq_z_cuda.html new file mode 100644 index 000000000..3ccd720f3 --- /dev/null +++ b/api/proc/transeq_z_cuda.html @@ -0,0 +1,346 @@ + + + + + + + + + + + + + transeq_z_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_z_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq_z_cuda(self, du, dv, dw, u, v, w, dirps) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/transeq_z_omp.html b/api/proc/transeq_z_omp.html new file mode 100644 index 000000000..cda6d1b32 --- /dev/null +++ b/api/proc/transeq_z_omp.html @@ -0,0 +1,346 @@ + + + + + + + + + + + + + transeq_z_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

transeq_z_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine transeq_z_omp(self, du, dv, dw, u, v, w, dirps) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + class(field_t), + intent(inout) + + ::du + +
+ + class(field_t), + intent(inout) + + ::dv + +
+ + class(field_t), + intent(inout) + + ::dw + +
+ + class(field_t), + intent(in) + + ::u + +
+ + class(field_t), + intent(in) + + ::v + +
+ + class(field_t), + intent(in) + + ::w + +
+ + type(dirps_t), + intent(in) + + ::dirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/vecadd_cuda.html b/api/proc/vecadd_cuda.html new file mode 100644 index 000000000..e37a5e3f8 --- /dev/null +++ b/api/proc/vecadd_cuda.html @@ -0,0 +1,301 @@ + + + + + + + + + + + + + vecadd_cuda – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

vecadd_cuda + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine vecadd_cuda(self, a, x, b, y) +

+ + + +

Type Bound

+

cuda_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(cuda_backend_t) + + + ::self + +
+ + real(kind=dp), + intent(in) + + ::a + +
+ + class(field_t), + intent(in) + + ::x + +
+ + real(kind=dp), + intent(in) + + ::b + +
+ + class(field_t), + intent(inout) + + ::y + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/vecadd_omp.html b/api/proc/vecadd_omp.html new file mode 100644 index 000000000..bd0e67eba --- /dev/null +++ b/api/proc/vecadd_omp.html @@ -0,0 +1,301 @@ + + + + + + + + + + + + + vecadd_omp – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

vecadd_omp + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine vecadd_omp(self, a, x, b, y) +

+ + + +

Type Bound

+

omp_backend_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(omp_backend_t) + + + ::self + +
+ + real(kind=dp), + intent(in) + + ::a + +
+ + class(field_t), + intent(in) + + ::x + +
+ + real(kind=dp), + intent(in) + + ::b + +
+ + class(field_t), + intent(inout) + + ::y + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/proc/waves_set.html b/api/proc/waves_set.html new file mode 100644 index 000000000..99de76f4c --- /dev/null +++ b/api/proc/waves_set.html @@ -0,0 +1,303 @@ + + + + + + + + + + + + + waves_set – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

waves_set + Subroutine + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+

public subroutine waves_set(self, geo, xdirps, ydirps, zdirps) +

+ + +

Spectral equivalence constants

+

Ref. JCP 228 (2009), 5989–6015, Sec 4

+ +

Type Bound

+

poisson_fft_t

+ +

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeIntentOptional AttributesName
+ + class(poisson_fft_t) + + + ::self + +
+ + type(geo_t), + intent(in) + + ::geo + +
+ + type(dirps_t), + intent(in) + + ::xdirps + +
+ + type(dirps_t), + intent(in) + + ::ydirps + +
+ + type(dirps_t), + intent(in) + + ::zdirps + +
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/program/xcompact.html b/api/program/xcompact.html new file mode 100644 index 000000000..03919c6da --- /dev/null +++ b/api/program/xcompact.html @@ -0,0 +1,726 @@ + + + + + + + + + + + + + xcompact – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

xcompact Program + +

+
+
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+ + + + + +
+

Variables

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeAttributesNameInitial
+ + class(base_backend_t), + + pointer + ::backend + +
+ + class(allocator_t), + + pointer + ::allocator + +
+ + type(mesh_t) + + + ::mesh + +
+ + type(allocator_t), + + pointer + ::host_allocator + +
+ + type(solver_t) + + + ::solver + +
+ + type(cuda_backend_t), + + target + ::cuda_backend + +
+ + type(cuda_allocator_t), + + target + ::cuda_allocator + +
+ + integer + + + ::ndevs + +
+ + integer + + + ::devnum + +
+ + type(omp_backend_t), + + target + ::omp_backend + +
+ + type(allocator_t), + + target + ::omp_allocator + +
+ + real(kind=dp) + + + ::t_start + +
+ + real(kind=dp) + + + ::t_end + +
+ + character(len=200) + + + ::input_file + +
+ + character(len=20) + + + ::BC_x(2) + +
+ + character(len=20) + + + ::BC_y(2) + +
+ + character(len=20) + + + ::BC_z(2) + +
+ + integer, + + dimension(3) + ::dims_global + +
+ + integer, + + dimension(3) + ::nproc_dir =0 + +
+ + real(kind=dp), + + dimension(3) + ::L_global + +
+ + integer + + + ::nrank + +
+ + integer + + + ::nproc + +
+ + integer + + + ::ierr + +
+ +
+
+ + + + + + + + +
+

Namelists

+
+
+ +

Namelist domain_params

+
+
+ +
+
+

Variables

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
L_globalreal(kind=dp)None
dims_globalintegerNone
nproc_dirinteger0
BC_xcharacter(len=20)None
BC_ycharacter(len=20)None
BC_zcharacter(len=20)None
+
+ +
+
+ +
+ + +
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/search.html b/api/search.html new file mode 100644 index 000000000..375cb8ef7 --- /dev/null +++ b/api/search.html @@ -0,0 +1,139 @@ + + + + + + + + + + + Search Results – x3d2 + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+

Search Results

+
+
+
+ + +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/allocator.f90.html b/api/sourcefile/allocator.f90.html new file mode 100644 index 000000000..c9fbf01ce --- /dev/null +++ b/api/sourcefile/allocator.f90.html @@ -0,0 +1,283 @@ + + + + + + + + + + + + + allocator.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

allocator.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_allocator
+  use m_allocator, only: allocator_t, field_t
+  use m_common, only: dp
+  use m_field, only: field_t
+  use m_mesh, only: mesh_t
+
+  implicit none
+
+  type, extends(allocator_t) :: cuda_allocator_t
+  contains
+    procedure :: create_block => create_cuda_block
+  end type cuda_allocator_t
+
+  interface cuda_allocator_t
+    module procedure cuda_allocator_init
+  end interface cuda_allocator_t
+
+  type, extends(field_t) :: cuda_field_t
+    real(dp), device, pointer, private :: p_data_d(:)
+    real(dp), device, pointer, contiguous :: data_d(:, :, :)
+  contains
+    procedure :: set_shape => set_shape_cuda
+  end type cuda_field_t
+
+  interface cuda_field_t
+    module procedure cuda_field_init
+  end interface cuda_field_t
+
+contains
+
+  function cuda_field_init(ngrid, next, id) result(f)
+    integer, intent(in) :: ngrid, id
+    type(cuda_field_t), pointer, intent(in) :: next
+    type(cuda_field_t) :: f
+
+    allocate (f%p_data_d(ngrid))
+    f%refcount = 0
+    f%next => next
+    f%id = id
+  end function cuda_field_init
+
+  subroutine set_shape_cuda(self, dims)
+    implicit none
+
+    class(cuda_field_t) :: self
+    integer, intent(in) :: dims(3)
+
+    self%data_d(1:dims(1), 1:dims(2), 1:dims(3)) => self%p_data_d
+
+  end subroutine set_shape_cuda
+
+  function cuda_allocator_init(mesh, sz) result(allocator)
+    class(mesh_t), intent(inout) :: mesh
+    integer, intent(in) :: sz
+    type(cuda_allocator_t) :: allocator
+
+    allocator%allocator_t = allocator_t(mesh, sz)
+  end function cuda_allocator_init
+
+  function create_cuda_block(self, next) result(ptr)
+    class(cuda_allocator_t), intent(inout) :: self
+    type(cuda_field_t), pointer, intent(in) :: next
+    type(cuda_field_t), pointer :: newblock
+    class(field_t), pointer :: ptr
+    allocate (newblock)
+    self%next_id = self%next_id + 1
+    newblock = cuda_field_t(self%ngrid, next, id=self%next_id)
+    ptr => newblock
+  end function create_cuda_block
+
+end module m_cuda_allocator
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/allocator.f90~2.html b/api/sourcefile/allocator.f90~2.html new file mode 100644 index 000000000..b9640269c --- /dev/null +++ b/api/sourcefile/allocator.f90~2.html @@ -0,0 +1,419 @@ + + + + + + + + + + + + + allocator.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

allocator.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_allocator
+  use iso_fortran_env, only: stderr => error_unit
+
+  use m_common, only: dp, DIR_X, DIR_Y, DIR_Z, DIR_C, none, VERT
+  use m_mesh, only: mesh_t
+  use m_field, only: field_t
+
+  implicit none
+
+  type :: allocator_t
+     !! An instance of type allocator_t is responsible for the
+     !! maintenance of a linked list of instances of equal size
+     !! [[m_allocator(module):field_t(type)]] objects:
+     !!
+     !! ```
+     !!       ---- ---- ----     ---- ---- ----
+     !! ...-->|id=1|data|next|-->|id=0|data|next|-->null()
+     !!       ---- ---- ----     ---- ---- ----
+     !! ```
+     !!
+     !! the last block's `next` pointer being non associated.
+     !!
+     !! User code can request access to a memory block by using the
+     !! type bound procedure
+     !! [[m_allocator(module):get_block(function)]].  If the list is
+     !! not empty, a pointer to the first block on the list is
+     !! returned and the block is detached from the list.  If the list
+     !! is empty (i.e. all initially allocated blocks are currently
+     !! referenced to) then a new block is allocated before a pointer
+     !! to it is returned.
+     !!
+     !! In order to reuse memory it is important that user code
+     !! release blocks when they are not needed anymore.  This is done
+     !! by calling the type bound procedure
+     !! [[m_allocator(module):release_block(subroutine)]].  The
+     !! released block is then pushed in front of the block list.
+
+    integer :: ngrid
+    !> The id for the next allocated block.  This counter is
+    !> incremented each time a new block is allocated.
+    integer :: next_id = 0
+    !> The pointer to the first block on the list.  Non associated if
+    !> the list is empty
+    ! TODO: Rename first to head
+    class(mesh_t), pointer :: mesh
+    class(field_t), pointer :: first => null()
+  contains
+    procedure :: get_block
+    procedure :: release_block
+    procedure :: create_block
+    procedure :: get_block_ids
+    procedure :: destroy
+    procedure :: compute_padded_dims
+  end type allocator_t
+
+  interface allocator_t
+    module procedure allocator_init
+  end interface allocator_t
+
+  type :: flist_t
+    class(field_t), pointer :: ptr
+  end type flist_t
+
+contains
+
+  function allocator_init(mesh, sz) result(allocator)
+    type(mesh_t), target, intent(inout) :: mesh
+    integer, intent(in) :: sz
+    type(allocator_t) :: allocator
+
+    allocator%mesh => mesh
+    call allocator%compute_padded_dims(sz)
+    allocator%ngrid = product(allocator%mesh%get_padded_dims(DIR_C))
+
+  end function allocator_init
+
+  subroutine compute_padded_dims(self, sz)
+    class(allocator_t), intent(inout) :: self
+    integer, intent(in) :: sz
+    integer, dimension(3) :: cdims
+    integer :: nx, ny, nz, nx_padded, ny_padded, nz_padded
+
+    cdims = self%mesh%get_dims(VERT)
+    nx = cdims(1)
+    ny = cdims(2)
+    nz = cdims(3)
+
+    ! Apply padding based on sz
+    nx_padded = nx - 1 + mod(-(nx - 1), sz) + sz
+    ny_padded = ny - 1 + mod(-(ny - 1), sz) + sz
+    ! Current reorder functions do not require a padding in z-direction.
+    nz_padded = nz
+    cdims = [nx_padded, ny_padded, nz_padded]
+
+    call self%mesh%set_sz(sz)
+    call self%mesh%set_padded_dims(cdims)
+
+  end subroutine
+
+  function create_block(self, next) result(ptr)
+    !! Allocate memory for a new block and return a pointer to a new
+    !! [[m_allocator(module):field_t(type)]] object.
+    class(allocator_t), intent(inout) :: self
+    type(field_t), pointer, intent(in) :: next
+    type(field_t), pointer :: newblock
+    class(field_t), pointer :: ptr
+    self%next_id = self%next_id + 1
+    allocate (newblock)
+    newblock = field_t(self%ngrid, next, id=self%next_id)
+    ptr => newblock
+  end function create_block
+
+  function get_block(self, dir, data_loc) result(handle)
+    !! Return a pointer to the first available memory block, i.e. the
+    !! current head of the block list.  If the list is empty, allocate
+    !! a new block with [[m_allocator(module):create_block(function)]]
+    !! first.
+    !!
+    !! Example
+    !! ```
+    !! f%data => get_block()
+    !! ```
+    class(allocator_t), intent(inout) :: self
+    class(field_t), pointer :: handle
+    integer, intent(in) :: dir
+    integer, intent(in), optional :: data_loc
+    integer :: dims(3)
+    ! If the list is empty, allocate a new block before returning a
+    ! pointer to it.
+    if (.not. associated(self%first)) then
+      ! Construct a field_t. This effectively allocates
+      ! storage space.
+      self%first => self%create_block(next=self%first)
+    end if
+    handle => self%first
+    self%first => self%first%next ! 2nd block becomes head block
+    handle%next => null() ! Detach ex-head block from the block list
+
+    ! Store direction info in the field type.
+    handle%dir = dir
+    if (present(data_loc)) then
+      handle%data_loc = data_loc
+    else
+      handle%data_loc = none
+    end if
+
+    ! Set dims based on direction
+    dims = self%mesh%get_padded_dims(dir)
+
+    ! Apply bounds remapping based on requested direction
+    call handle%set_shape(dims)
+  end function get_block
+
+  subroutine release_block(self, handle)
+    !! Release memory block pointed to by HANDLE to the block list.
+    !! It is pushed to the front of the block list, in other words it
+    !! is made the head block.
+    class(allocator_t), intent(inout) :: self
+    class(field_t), pointer :: handle
+    handle%next => self%first
+    self%first => handle
+  end subroutine release_block
+
+  subroutine destroy(self)
+    !! Go through the block list from head to tail, deallocating each
+    !! memory block in turn.  Deallocation of a
+    !! [[m_allocator(module):field_t(type)]] object automatically
+    !! deallocates its internal allocatable
+    !! [[field_t(type):data(variable)]] array.
+    class(allocator_t), intent(inout) :: self
+    type(field_t), pointer :: current
+    do
+      if (.not. associated(self%first)) exit
+      current => self%first
+      self%first => self%first%next
+      deallocate (current)
+      self%next_id = self%next_id - 1
+    end do
+  end subroutine destroy
+
+  function get_block_ids(self)
+    !! Utility function that returns a array made of the `id` of the
+    !! block currently in the block list.  Return the array [0] if
+    !! block list is empty.
+    ! TODO: Block indices should start at 1 or return [-1] in case of
+    ! empty block list.
+    class(allocator_t), intent(inout) :: self
+    integer, allocatable :: get_block_ids(:)
+    class(field_t), pointer :: current
+    integer :: i
+
+    current => self%first
+    if (.not. associated(current)) then
+      get_block_ids = [0]
+    else
+      i = current%id
+      get_block_ids = [current%id]
+      do
+        if (.not. associated(current%next)) exit
+        i = current%next%id
+        get_block_ids = [get_block_ids, current%next%id]
+        current => current%next
+      end do
+    end if
+  end function get_block_ids
+
+end module m_allocator
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/backend.f90.html b/api/sourcefile/backend.f90.html new file mode 100644 index 000000000..655e05e88 --- /dev/null +++ b/api/sourcefile/backend.f90.html @@ -0,0 +1,495 @@ + + + + + + + + + + + + + backend.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

backend.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_base_backend
+  use mpi
+
+  use m_allocator, only: allocator_t, field_t
+  use m_common, only: dp, DIR_C, get_rdr_from_dirs
+  use m_poisson_fft, only: poisson_fft_t
+  use m_tdsops, only: tdsops_t, dirps_t
+  use m_mesh, only: mesh_t
+
+  implicit none
+
+  type, abstract :: base_backend_t
+      !! base_backend class defines all the abstract operations that the
+      !! solver class requires.
+      !!
+      !! For example, transport equation in solver class evaluates the
+      !! derivatives in x, y, and z directions, and reorders the input
+      !! fields as required. Then finally, combines all the directional
+      !! derivatives to obtain the divergence of U*.
+      !!
+      !! All these high level operations solver class executes are
+      !! defined here using the abstract interfaces. Every backend
+      !! implementation extends the present abstact backend class to
+      !! define the specifics of these operations based on the target
+      !! architecture.
+
+    real(dp) :: nu
+    class(mesh_t), pointer :: mesh
+    class(allocator_t), pointer :: allocator
+    class(poisson_fft_t), pointer :: poisson_fft
+  contains
+    procedure(transeq_ders), deferred :: transeq_x
+    procedure(transeq_ders), deferred :: transeq_y
+    procedure(transeq_ders), deferred :: transeq_z
+    procedure(tds_solve), deferred :: tds_solve
+    procedure(reorder), deferred :: reorder
+    procedure(sum_intox), deferred :: sum_yintox
+    procedure(sum_intox), deferred :: sum_zintox
+    procedure(vecadd), deferred :: vecadd
+    procedure(scalar_product), deferred :: scalar_product
+    procedure(copy_data_to_f), deferred :: copy_data_to_f
+    procedure(copy_f_to_data), deferred :: copy_f_to_data
+    procedure(alloc_tdsops), deferred :: alloc_tdsops
+    procedure(init_poisson_fft), deferred :: init_poisson_fft
+    procedure :: base_init
+    procedure :: get_field_data
+    procedure :: set_field_data
+  end type base_backend_t
+
+  abstract interface
+    subroutine transeq_ders(self, du, dv, dw, u, v, w, dirps)
+         !! transeq equation obtains the derivatives direction by
+         !! direction, and the exact algorithm used to obtain these
+         !! derivatives are decided at runtime. Backend implementations
+         !! are responsible from directing calls to transeq_ders into
+         !! the correct algorithm.
+      import :: base_backend_t
+      import :: field_t
+      import :: dirps_t
+      implicit none
+
+      class(base_backend_t) :: self
+      class(field_t), intent(inout) :: du, dv, dw
+      class(field_t), intent(in) :: u, v, w
+      type(dirps_t), intent(in) :: dirps
+    end subroutine transeq_ders
+  end interface
+
+  abstract interface
+    subroutine tds_solve(self, du, u, tdsops)
+      !! transeq equation obtains the derivatives direction by
+      !! direction, and the exact algorithm used to obtain these
+      !! derivatives are decided at runtime. Backend implementations
+      !! are responsible from directing calls to tds_solve to the
+      !! correct algorithm.
+      import :: base_backend_t
+      import :: field_t
+      import :: tdsops_t
+      implicit none
+
+      class(base_backend_t) :: self
+      class(field_t), intent(inout) :: du
+      class(field_t), intent(in) :: u
+      class(tdsops_t), intent(in) :: tdsops
+    end subroutine tds_solve
+  end interface
+
+  abstract interface
+    subroutine reorder(self, u_, u, direction)
+         !! reorder subroutines are straightforward, they rearrange
+         !! data into our specialist data structure so that regardless
+         !! of the direction tridiagonal systems are solved efficiently
+         !! and fast.
+      import :: base_backend_t
+      import :: field_t
+      implicit none
+
+      class(base_backend_t) :: self
+      class(field_t), intent(inout) :: u_
+      class(field_t), intent(in) :: u
+      integer, intent(in) :: direction
+    end subroutine reorder
+  end interface
+
+  abstract interface
+    subroutine sum_intox(self, u, u_)
+         !! sum9into3 subroutine combines all the directional velocity
+         !! derivatives into the corresponding x directional fields.
+      import :: base_backend_t
+      import :: field_t
+      implicit none
+
+      class(base_backend_t) :: self
+      class(field_t), intent(inout) :: u
+      class(field_t), intent(in) :: u_
+    end subroutine sum_intox
+  end interface
+
+  abstract interface
+    subroutine vecadd(self, a, x, b, y)
+         !! adds two vectors together: y = a*x + b*y
+      import :: base_backend_t
+      import :: dp
+      import :: field_t
+      implicit none
+
+      class(base_backend_t) :: self
+      real(dp), intent(in) :: a
+      class(field_t), intent(in) :: x
+      real(dp), intent(in) :: b
+      class(field_t), intent(inout) :: y
+    end subroutine vecadd
+  end interface
+
+  abstract interface
+    real(dp) function scalar_product(self, x, y) result(s)
+         !! Calculates the scalar product of two input fields
+      import :: base_backend_t
+      import :: dp
+      import :: field_t
+      implicit none
+
+      class(base_backend_t) :: self
+      class(field_t), intent(in) :: x, y
+    end function scalar_product
+  end interface
+
+  abstract interface
+    subroutine copy_data_to_f(self, f, data)
+         !! Copy the specialist data structure from device or host back
+         !! to a regular 3D data array in host memory.
+      import :: base_backend_t
+      import :: dp
+      import :: field_t
+      implicit none
+
+      class(base_backend_t), intent(inout) :: self
+      class(field_t), intent(inout) :: f
+      real(dp), dimension(:, :, :), intent(in) :: data
+    end subroutine copy_data_to_f
+
+    subroutine copy_f_to_data(self, data, f)
+         !! Copy a regular 3D array in host memory into the specialist
+         !! data structure field that lives on device or host
+      import :: base_backend_t
+      import :: dp
+      import :: field_t
+      implicit none
+
+      class(base_backend_t), intent(inout) :: self
+      real(dp), dimension(:, :, :), intent(out) :: data
+      class(field_t), intent(in) :: f
+    end subroutine copy_f_to_data
+  end interface
+
+  abstract interface
+    subroutine alloc_tdsops(self, tdsops, dir, operation, scheme, n_halo, &
+                            from_to, bc_start, bc_end, sym, c_nu, nu0_nu)
+      import :: base_backend_t
+      import :: dp
+      import :: tdsops_t
+      implicit none
+
+      class(base_backend_t) :: self
+      class(tdsops_t), allocatable, intent(inout) :: tdsops
+      integer, intent(in) :: dir
+      character(*), intent(in) :: operation, scheme
+      integer, optional, intent(in) :: n_halo
+      character(*), optional, intent(in) :: from_to, bc_start, bc_end
+      logical, optional, intent(in) :: sym
+      real(dp), optional, intent(in) :: c_nu, nu0_nu
+    end subroutine alloc_tdsops
+  end interface
+
+  abstract interface
+    subroutine init_poisson_fft(self, mesh, xdirps, ydirps, zdirps)
+      import :: base_backend_t
+      import :: dirps_t
+      import :: mesh_t
+      implicit none
+
+      class(base_backend_t) :: self
+      class(mesh_t), intent(in) :: mesh
+      type(dirps_t), intent(in) :: xdirps, ydirps, zdirps
+    end subroutine init_poisson_fft
+  end interface
+
+contains
+
+  subroutine base_init(self)
+    implicit none
+
+    class(base_backend_t) :: self
+
+  end subroutine base_init
+
+  subroutine get_field_data(self, data, f, dir)
+   !! Extract data from field `f` optionally reordering into `dir` orientation.
+   !! To output in same orientation as `f`, use `call ...%get_field_data(data, f, f%dir)`
+    implicit none
+
+    class(base_backend_t) :: self
+    real(dp), dimension(:, :, :), intent(out) :: data !! Output array
+    class(field_t), intent(in) :: f !! Field
+    integer, optional, intent(in) :: dir !! Desired orientation of output array (defaults to Cartesian)
+
+    class(field_t), pointer :: f_temp
+    integer :: direction, rdr_dir
+
+    if (present(dir)) then
+      direction = dir
+    else
+      direction = DIR_C
+    end if
+
+    ! Returns 0 if no reorder required
+    rdr_dir = get_rdr_from_dirs(f%dir, direction)
+
+    ! Carry out a reorder if we need, and copy from field to data array
+    if (rdr_dir /= 0) then
+      f_temp => self%allocator%get_block(direction)
+      call self%reorder(f_temp, f, rdr_dir)
+      call self%copy_f_to_data(data, f_temp)
+      call self%allocator%release_block(f_temp)
+    else
+      call self%copy_f_to_data(data, f)
+    end if
+
+  end subroutine get_field_data
+
+  subroutine set_field_data(self, f, data, dir)
+    implicit none
+
+    class(base_backend_t) :: self
+    class(field_t), intent(inout) :: f !! Field
+    real(dp), dimension(:, :, :), intent(in) :: data !! Input array
+    integer, optional, intent(in) :: dir !! Orientation of input array (defaults to Cartesian)
+
+    class(field_t), pointer :: f_temp
+    integer :: direction, rdr_dir
+
+    if (present(dir)) then
+      direction = dir
+    else
+      direction = DIR_C
+    end if
+
+    ! Returns 0 if no reorder required
+    rdr_dir = get_rdr_from_dirs(direction, f%dir)
+
+    ! Carry out a reorder if we need, and copy from data array to field
+    if (rdr_dir /= 0) then
+      f_temp => self%allocator%get_block(direction)
+      call self%copy_data_to_f(f_temp, data)
+      call self%reorder(f, f_temp, rdr_dir)
+      call self%allocator%release_block(f_temp)
+    else
+      call self%copy_data_to_f(f, data)
+    end if
+
+  end subroutine set_field_data
+
+end module m_base_backend
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/backend.f90~2.html b/api/sourcefile/backend.f90~2.html new file mode 100644 index 000000000..b267207b3 --- /dev/null +++ b/api/sourcefile/backend.f90~2.html @@ -0,0 +1,912 @@ + + + + + + + + + + + + + backend.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

backend.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_backend
+  use iso_fortran_env, only: stderr => error_unit
+  use cudafor
+  use mpi
+
+  use m_allocator, only: allocator_t, field_t
+  use m_base_backend, only: base_backend_t
+  use m_common, only: dp, &
+                      RDR_X2Y, RDR_X2Z, RDR_Y2X, RDR_Y2Z, RDR_Z2X, RDR_Z2Y, &
+                      RDR_C2X, RDR_C2Y, RDR_C2Z, RDR_X2C, RDR_Y2C, RDR_Z2C, &
+                      DIR_X, DIR_Y, DIR_Z, DIR_C, VERT
+  use m_mesh, only: mesh_t
+  use m_poisson_fft, only: poisson_fft_t
+  use m_tdsops, only: dirps_t, tdsops_t, get_tds_n
+
+  use m_cuda_allocator, only: cuda_allocator_t, cuda_field_t
+  use m_cuda_common, only: SZ
+  use m_cuda_exec_dist, only: exec_dist_transeq_3fused, exec_dist_tds_compact
+  use m_cuda_poisson_fft, only: cuda_poisson_fft_t
+  use m_cuda_sendrecv, only: sendrecv_fields, sendrecv_3fields
+  use m_cuda_tdsops, only: cuda_tdsops_t
+  use m_cuda_kernels_dist, only: transeq_3fused_dist, transeq_3fused_subs
+  use m_cuda_kernels_reorder, only: &
+    reorder_x2y, reorder_x2z, reorder_y2x, reorder_y2z, reorder_z2x, &
+    reorder_z2y, reorder_c2x, reorder_x2c, &
+    sum_yintox, sum_zintox, scalar_product, axpby, buffer_copy
+
+  implicit none
+
+  private :: transeq_halo_exchange, transeq_dist_component
+
+  type, extends(base_backend_t) :: cuda_backend_t
+    !character(len=*), parameter :: name = 'cuda'
+    integer :: MPI_FP_PREC = dp
+    real(dp), device, allocatable, dimension(:, :, :) :: &
+      u_recv_s_dev, u_recv_e_dev, u_send_s_dev, u_send_e_dev, &
+      v_recv_s_dev, v_recv_e_dev, v_send_s_dev, v_send_e_dev, &
+      w_recv_s_dev, w_recv_e_dev, w_send_s_dev, w_send_e_dev, &
+      du_send_s_dev, du_send_e_dev, du_recv_s_dev, du_recv_e_dev, &
+      dud_send_s_dev, dud_send_e_dev, dud_recv_s_dev, dud_recv_e_dev, &
+      d2u_send_s_dev, d2u_send_e_dev, d2u_recv_s_dev, d2u_recv_e_dev
+    type(dim3) :: xblocks, xthreads, yblocks, ythreads, zblocks, zthreads
+  contains
+    procedure :: alloc_tdsops => alloc_cuda_tdsops
+    procedure :: transeq_x => transeq_x_cuda
+    procedure :: transeq_y => transeq_y_cuda
+    procedure :: transeq_z => transeq_z_cuda
+    procedure :: tds_solve => tds_solve_cuda
+    procedure :: reorder => reorder_cuda
+    procedure :: sum_yintox => sum_yintox_cuda
+    procedure :: sum_zintox => sum_zintox_cuda
+    procedure :: vecadd => vecadd_cuda
+    procedure :: scalar_product => scalar_product_cuda
+    procedure :: copy_data_to_f => copy_data_to_f_cuda
+    procedure :: copy_f_to_data => copy_f_to_data_cuda
+    procedure :: init_poisson_fft => init_cuda_poisson_fft
+    procedure :: transeq_cuda_dist
+    procedure :: transeq_cuda_thom
+    procedure :: tds_solve_dist
+  end type cuda_backend_t
+
+  interface cuda_backend_t
+    module procedure init
+  end interface cuda_backend_t
+
+contains
+
+  function init(mesh, allocator) result(backend)
+    implicit none
+
+    class(mesh_t), target, intent(inout) :: mesh
+    class(allocator_t), target, intent(inout) :: allocator
+    type(cuda_backend_t) :: backend
+
+    type(cuda_poisson_fft_t) :: cuda_poisson_fft
+    integer :: n_halo, n_groups
+
+    call backend%base_init()
+
+    select type (allocator)
+    type is (cuda_allocator_t)
+      ! class level access to the allocator
+      backend%allocator => allocator
+    end select
+    backend%mesh => mesh
+
+    backend%xthreads = dim3(SZ, 1, 1)
+    backend%xblocks = dim3(backend%mesh%get_n_groups(DIR_X), 1, 1)
+    backend%ythreads = dim3(SZ, 1, 1)
+    backend%yblocks = dim3(backend%mesh%get_n_groups(DIR_Y), 1, 1)
+    backend%zthreads = dim3(SZ, 1, 1)
+    backend%zblocks = dim3(backend%mesh%get_n_groups(DIR_Z), 1, 1)
+
+    n_halo = 4
+    ! Buffer size should be big enough for the largest MPI exchange.
+    n_groups = maxval([backend%mesh%get_n_groups(DIR_X), &
+                       backend%mesh%get_n_groups(DIR_Y), &
+                       backend%mesh%get_n_groups(DIR_Z)])
+
+    allocate (backend%u_send_s_dev(SZ, n_halo, n_groups))
+    allocate (backend%u_send_e_dev(SZ, n_halo, n_groups))
+    allocate (backend%u_recv_s_dev(SZ, n_halo, n_groups))
+    allocate (backend%u_recv_e_dev(SZ, n_halo, n_groups))
+    allocate (backend%v_send_s_dev(SZ, n_halo, n_groups))
+    allocate (backend%v_send_e_dev(SZ, n_halo, n_groups))
+    allocate (backend%v_recv_s_dev(SZ, n_halo, n_groups))
+    allocate (backend%v_recv_e_dev(SZ, n_halo, n_groups))
+    allocate (backend%w_send_s_dev(SZ, n_halo, n_groups))
+    allocate (backend%w_send_e_dev(SZ, n_halo, n_groups))
+    allocate (backend%w_recv_s_dev(SZ, n_halo, n_groups))
+    allocate (backend%w_recv_e_dev(SZ, n_halo, n_groups))
+
+    allocate (backend%du_send_s_dev(SZ, 1, n_groups))
+    allocate (backend%du_send_e_dev(SZ, 1, n_groups))
+    allocate (backend%du_recv_s_dev(SZ, 1, n_groups))
+    allocate (backend%du_recv_e_dev(SZ, 1, n_groups))
+    allocate (backend%dud_send_s_dev(SZ, 1, n_groups))
+    allocate (backend%dud_send_e_dev(SZ, 1, n_groups))
+    allocate (backend%dud_recv_s_dev(SZ, 1, n_groups))
+    allocate (backend%dud_recv_e_dev(SZ, 1, n_groups))
+    allocate (backend%d2u_send_s_dev(SZ, 1, n_groups))
+    allocate (backend%d2u_send_e_dev(SZ, 1, n_groups))
+    allocate (backend%d2u_recv_s_dev(SZ, 1, n_groups))
+    allocate (backend%d2u_recv_e_dev(SZ, 1, n_groups))
+
+  end function init
+
+  subroutine alloc_cuda_tdsops( &
+    self, tdsops, dir, operation, scheme, &
+    n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu &
+    )
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(tdsops_t), allocatable, intent(inout) :: tdsops
+    integer, intent(in) :: dir
+    character(*), intent(in) :: operation, scheme
+    integer, optional, intent(in) :: n_halo
+    character(*), optional, intent(in) :: from_to, bc_start, bc_end
+    logical, optional, intent(in) :: sym
+    real(dp), optional, intent(in) :: c_nu, nu0_nu
+    integer :: tds_n
+    real(dp) :: delta
+
+    allocate (cuda_tdsops_t :: tdsops)
+
+    select type (tdsops)
+    type is (cuda_tdsops_t)
+      tds_n = get_tds_n(self%mesh, dir, from_to)
+      delta = self%mesh%geo%d(dir)
+      tdsops = cuda_tdsops_t(tds_n, delta, operation, &
+                             scheme, n_halo, from_to, &
+                             bc_start, bc_end, sym, c_nu, nu0_nu)
+    end select
+
+  end subroutine alloc_cuda_tdsops
+
+  subroutine transeq_x_cuda(self, du, dv, dw, u, v, w, dirps)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(field_t), intent(inout) :: du, dv, dw
+    class(field_t), intent(in) :: u, v, w
+    type(dirps_t), intent(in) :: dirps
+
+    call self%transeq_cuda_dist(du, dv, dw, u, v, w, dirps, &
+                                self%xblocks, self%xthreads)
+
+  end subroutine transeq_x_cuda
+
+  subroutine transeq_y_cuda(self, du, dv, dw, u, v, w, dirps)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(field_t), intent(inout) :: du, dv, dw
+    class(field_t), intent(in) :: u, v, w
+    type(dirps_t), intent(in) :: dirps
+
+    ! u, v, w is reordered so that we pass v, u, w
+    call self%transeq_cuda_dist(dv, du, dw, v, u, w, dirps, &
+                                self%yblocks, self%ythreads)
+
+  end subroutine transeq_y_cuda
+
+  subroutine transeq_z_cuda(self, du, dv, dw, u, v, w, dirps)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(field_t), intent(inout) :: du, dv, dw
+    class(field_t), intent(in) :: u, v, w
+    type(dirps_t), intent(in) :: dirps
+
+    ! u, v, w is reordered so that we pass w, u, v
+    call self%transeq_cuda_dist(dw, du, dv, w, u, v, dirps, &
+                                self%zblocks, self%zthreads)
+
+  end subroutine transeq_z_cuda
+
+  subroutine transeq_cuda_dist(self, du, dv, dw, u, v, w, dirps, &
+                               blocks, threads)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(field_t), intent(inout) :: du, dv, dw
+    class(field_t), intent(in) :: u, v, w
+    type(dirps_t), intent(in) :: dirps
+    type(dim3), intent(in) :: blocks, threads
+
+    real(dp), device, pointer, dimension(:, :, :) :: u_dev, v_dev, w_dev, &
+                                                     du_dev, dv_dev, dw_dev
+
+    type(cuda_tdsops_t), pointer :: der1st, der1st_sym, der2nd, der2nd_sym
+
+    call resolve_field_t(u_dev, u)
+    call resolve_field_t(v_dev, v)
+    call resolve_field_t(w_dev, w)
+
+    call resolve_field_t(du_dev, du)
+    call resolve_field_t(dv_dev, dv)
+    call resolve_field_t(dw_dev, dw)
+
+    select type (tdsops => dirps%der1st)
+    type is (cuda_tdsops_t); der1st => tdsops
+    end select
+    select type (tdsops => dirps%der1st_sym)
+    type is (cuda_tdsops_t); der1st_sym => tdsops
+    end select
+    select type (tdsops => dirps%der2nd)
+    type is (cuda_tdsops_t); der2nd => tdsops
+    end select
+    select type (tdsops => dirps%der2nd_sym)
+    type is (cuda_tdsops_t); der2nd_sym => tdsops
+    end select
+
+    call transeq_halo_exchange(self, u_dev, v_dev, w_dev, dirps%dir)
+
+    call transeq_dist_component(self, du_dev, u_dev, u_dev, &
+                                self%u_recv_s_dev, self%u_recv_e_dev, &
+                                self%u_recv_s_dev, self%u_recv_e_dev, &
+                                der1st, der1st_sym, der2nd, dirps%dir, &
+                                blocks, threads)
+    call transeq_dist_component(self, dv_dev, v_dev, u_dev, &
+                                self%v_recv_s_dev, self%v_recv_e_dev, &
+                                self%u_recv_s_dev, self%u_recv_e_dev, &
+                                der1st_sym, der1st, der2nd_sym, dirps%dir, &
+                                blocks, threads)
+    call transeq_dist_component(self, dw_dev, w_dev, u_dev, &
+                                self%w_recv_s_dev, self%w_recv_e_dev, &
+                                self%u_recv_s_dev, self%u_recv_e_dev, &
+                                der1st_sym, der1st, der2nd_sym, dirps%dir, &
+                                blocks, threads)
+
+  end subroutine transeq_cuda_dist
+
+  subroutine transeq_halo_exchange(self, u_dev, v_dev, w_dev, dir)
+    class(cuda_backend_t) :: self
+    real(dp), device, dimension(:, :, :), intent(in) :: u_dev, v_dev, w_dev
+    integer, intent(in) :: dir
+    integer :: n_halo, n, nproc_dir, pprev, pnext
+    integer :: n_groups
+
+    ! TODO: don't hardcode n_halo
+    n_halo = 4
+    n_groups = self%mesh%get_n_groups(dir)
+    n = self%mesh%get_n(dir, VERT)
+    nproc_dir = self%mesh%par%nproc_dir(dir)
+    pprev = self%mesh%par%pprev(dir)
+    pnext = self%mesh%par%pnext(dir)
+
+    ! Copy halo data into buffer arrays
+    call copy_into_buffers(self%u_send_s_dev, self%u_send_e_dev, u_dev, n)
+    call copy_into_buffers(self%v_send_s_dev, self%v_send_e_dev, v_dev, n)
+    call copy_into_buffers(self%w_send_s_dev, self%w_send_e_dev, w_dev, n)
+
+    ! halo exchange
+    call sendrecv_3fields( &
+      self%u_recv_s_dev, self%u_recv_e_dev, &
+      self%v_recv_s_dev, self%v_recv_e_dev, &
+      self%w_recv_s_dev, self%w_recv_e_dev, &
+      self%u_send_s_dev, self%u_send_e_dev, &
+      self%v_send_s_dev, self%v_send_e_dev, &
+      self%w_send_s_dev, self%w_send_e_dev, &
+      SZ*n_halo*n_groups, nproc_dir, pprev, pnext)
+
+  end subroutine transeq_halo_exchange
+
+  subroutine transeq_dist_component(self, rhs_dev, u_dev, conv_dev, &
+                                    u_recv_s_dev, u_recv_e_dev, &
+                                    conv_recv_s_dev, conv_recv_e_dev, &
+                                    tdsops_du, tdsops_dud, tdsops_d2u, &
+                                    dir, blocks, threads)
+      !! Computes RHS_x^u following:
+      !!
+      !! rhs_x^u = -0.5*(conv*du/dx + d(u*conv)/dx) + nu*d2u/dx2
+    class(cuda_backend_t) :: self
+    real(dp), device, dimension(:, :, :), intent(inout) :: rhs_dev
+    real(dp), device, dimension(:, :, :), intent(in) :: u_dev, conv_dev
+    real(dp), device, dimension(:, :, :), intent(in) :: &
+      u_recv_s_dev, u_recv_e_dev, &
+      conv_recv_s_dev, conv_recv_e_dev
+    class(cuda_tdsops_t), intent(in) :: tdsops_du, tdsops_dud, tdsops_d2u
+    integer, intent(in) :: dir
+    type(dim3), intent(in) :: blocks, threads
+
+    class(field_t), pointer :: du, dud, d2u
+
+    real(dp), device, pointer, dimension(:, :, :) :: &
+      du_dev, dud_dev, d2u_dev
+
+    ! Get some fields for storing the intermediate results
+    du => self%allocator%get_block(dir, VERT)
+    dud => self%allocator%get_block(dir, VERT)
+    d2u => self%allocator%get_block(dir, VERT)
+
+    call resolve_field_t(du_dev, du)
+    call resolve_field_t(dud_dev, dud)
+    call resolve_field_t(d2u_dev, d2u)
+
+    call exec_dist_transeq_3fused( &
+      rhs_dev, &
+      u_dev, u_recv_s_dev, u_recv_e_dev, &
+      conv_dev, conv_recv_s_dev, conv_recv_e_dev, &
+      du_dev, dud_dev, d2u_dev, &
+      self%du_send_s_dev, self%du_send_e_dev, &
+      self%du_recv_s_dev, self%du_recv_e_dev, &
+      self%dud_send_s_dev, self%dud_send_e_dev, &
+      self%dud_recv_s_dev, self%dud_recv_e_dev, &
+      self%d2u_send_s_dev, self%d2u_send_e_dev, &
+      self%d2u_recv_s_dev, self%d2u_recv_e_dev, &
+      tdsops_du, tdsops_d2u, self%nu, &
+      self%mesh%par%nproc_dir(dir), self%mesh%par%pprev(dir), &
+      self%mesh%par%pnext(dir), blocks, threads &
+      )
+
+    ! Release temporary blocks
+    call self%allocator%release_block(du)
+    call self%allocator%release_block(dud)
+    call self%allocator%release_block(d2u)
+
+  end subroutine transeq_dist_component
+
+  subroutine transeq_cuda_thom(self, du, dv, dw, u, v, w, dirps)
+      !! Thomas algorithm implementation. So much more easier than the
+      !! distributed algorithm. It is intended to work only on a single rank
+      !! so there is no MPI communication.
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(field_t), intent(inout) :: du, dv, dw
+    class(field_t), intent(in) :: u, v, w
+    type(dirps_t), intent(in) :: dirps
+
+  end subroutine transeq_cuda_thom
+
+  subroutine tds_solve_cuda(self, du, u, tdsops)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(field_t), intent(inout) :: du
+    class(field_t), intent(in) :: u
+    class(tdsops_t), intent(in) :: tdsops
+
+    type(dim3) :: blocks, threads
+
+    ! Check if direction matches for both in/out fields and dirps
+    if (u%dir /= du%dir) then
+      error stop 'DIR mismatch between fields in tds_solve.'
+    end if
+
+    blocks = dim3(self%mesh%get_n_groups(u), 1, 1); threads = dim3(SZ, 1, 1)
+
+    call tds_solve_dist(self, du, u, tdsops, blocks, threads)
+
+  end subroutine tds_solve_cuda
+
+  subroutine tds_solve_dist(self, du, u, tdsops, blocks, threads)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(field_t), intent(inout) :: du
+    class(field_t), intent(in) :: u
+    class(tdsops_t), intent(in) :: tdsops
+    type(dim3), intent(in) :: blocks, threads
+
+    real(dp), device, pointer, dimension(:, :, :) :: du_dev, u_dev
+
+    type(cuda_tdsops_t), pointer :: tdsops_dev
+
+    integer :: n_halo, n_groups, dir
+
+    ! TODO: don't hardcode n_halo
+    n_halo = 4
+    dir = u%dir
+    n_groups = self%mesh%get_n_groups(u)
+
+    call resolve_field_t(du_dev, du)
+    call resolve_field_t(u_dev, u)
+
+    select type (tdsops)
+    type is (cuda_tdsops_t); tdsops_dev => tdsops
+    end select
+
+    call copy_into_buffers(self%u_send_s_dev, self%u_send_e_dev, u_dev, &
+                           tdsops_dev%tds_n)
+
+    call sendrecv_fields(self%u_recv_s_dev, self%u_recv_e_dev, &
+                         self%u_send_s_dev, self%u_send_e_dev, &
+                         SZ*n_halo*n_groups, &
+                         self%mesh%par%nproc_dir(dir), &
+                         self%mesh%par%pprev(dir), &
+                         self%mesh%par%pnext(dir))
+
+    ! call exec_dist
+    call exec_dist_tds_compact( &
+      du_dev, u_dev, &
+      self%u_recv_s_dev, self%u_recv_e_dev, &
+      self%du_send_s_dev, self%du_send_e_dev, &
+      self%du_recv_s_dev, self%du_recv_e_dev, &
+      tdsops_dev, self%mesh%par%nproc_dir(dir), &
+      self%mesh%par%pprev(dir), self%mesh%par%pnext(dir), &
+      blocks, threads &
+      )
+
+  end subroutine tds_solve_dist
+
+  subroutine reorder_cuda(self, u_o, u_i, direction)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(field_t), intent(inout) :: u_o
+    class(field_t), intent(in) :: u_i
+    integer, intent(in) :: direction
+
+    real(dp), device, pointer, dimension(:, :, :) :: u_o_d, u_i_d, u_temp_d
+    class(field_t), pointer :: u_temp
+    type(dim3) :: blocks, threads
+    integer :: nx_padded, ny_padded, nz_padded
+    integer, dimension(3) :: dims_padded
+
+    call resolve_field_t(u_o_d, u_o)
+    call resolve_field_t(u_i_d, u_i)
+
+    dims_padded = self%mesh%get_padded_dims(DIR_C)
+    nx_padded = dims_padded(1)
+    ny_padded = dims_padded(2)
+    nz_padded = dims_padded(3)
+
+    select case (direction)
+    case (RDR_X2Y)
+      blocks = dim3(nx_padded/SZ, nz_padded, ny_padded/SZ)
+      threads = dim3(SZ, SZ, 1)
+      call reorder_x2y<<<blocks, threads>>>(u_o_d, u_i_d, nz_padded) !&
+    case (RDR_X2Z)
+      blocks = dim3(nx_padded, ny_padded/SZ, 1)
+      threads = dim3(SZ, 1, 1)
+      call reorder_x2z<<<blocks, threads>>>(u_o_d, u_i_d, nz_padded) !&
+    case (RDR_Y2X)
+      blocks = dim3(nx_padded/SZ, ny_padded/SZ, nz_padded)
+      threads = dim3(SZ, SZ, 1)
+      call reorder_y2x<<<blocks, threads>>>(u_o_d, u_i_d, nz_padded) !&
+    case (RDR_Y2Z)
+      blocks = dim3(nx_padded/SZ, ny_padded/SZ, nz_padded)
+      threads = dim3(SZ, SZ, 1)
+      call reorder_y2z<<<blocks, threads>>>(u_o_d, u_i_d, & !&
+                                            nx_padded, nz_padded)
+    case (RDR_Z2X)
+      blocks = dim3(nx_padded, ny_padded/SZ, 1)
+      threads = dim3(SZ, 1, 1)
+      call reorder_z2x<<<blocks, threads>>>(u_o_d, u_i_d, nz_padded) !&
+    case (RDR_Z2Y)
+      blocks = dim3(nx_padded/SZ, ny_padded/SZ, nz_padded)
+      threads = dim3(SZ, SZ, 1)
+      call reorder_z2y<<<blocks, threads>>>(u_o_d, u_i_d, & !&
+                                            nx_padded, nz_padded)
+    case (RDR_C2X)
+      blocks = dim3(nx_padded/SZ, ny_padded/SZ, nz_padded)
+      threads = dim3(SZ, SZ, 1)
+      call reorder_c2x<<<blocks, threads>>>(u_o_d, u_i_d, nz_padded) !&
+    case (RDR_C2Y)
+      ! First reorder from C to X, then from X to Y
+      u_temp => self%allocator%get_block(DIR_X)
+      call resolve_field_t(u_temp_d, u_temp)
+
+      blocks = dim3(nx_padded/SZ, ny_padded/SZ, nz_padded)
+      threads = dim3(SZ, SZ, 1)
+      call reorder_c2x<<<blocks, threads>>>(u_temp_d, u_i_d, nz_padded) !&
+
+      blocks = dim3(nx_padded/SZ, nz_padded, ny_padded/SZ)
+      threads = dim3(SZ, SZ, 1)
+      call reorder_x2y<<<blocks, threads>>>(u_o_d, u_temp_d, nz_padded) !&
+
+      call self%allocator%release_block(u_temp)
+    case (RDR_C2Z)
+      ! First reorder from C to X, then from X to Z
+      u_temp => self%allocator%get_block(DIR_X)
+      call resolve_field_t(u_temp_d, u_temp)
+
+      blocks = dim3(nx_padded/SZ, ny_padded/SZ, nz_padded)
+      threads = dim3(SZ, SZ, 1)
+      call reorder_c2x<<<blocks, threads>>>(u_temp_d, u_i_d, nz_padded) !&
+
+      blocks = dim3(nx_padded, ny_padded/SZ, 1)
+      threads = dim3(SZ, 1, 1)
+      call reorder_x2z<<<blocks, threads>>>(u_o_d, u_temp_d, nz_padded) !&
+
+      call self%allocator%release_block(u_temp)
+    case (RDR_X2C)
+      blocks = dim3(nx_padded/SZ, ny_padded/SZ, nz_padded)
+      threads = dim3(SZ, SZ, 1)
+      call reorder_x2c<<<blocks, threads>>>(u_o_d, u_i_d, nz_padded) !&
+    case (RDR_Y2C)
+      ! First reorder from Y to X, then from X to C
+      u_temp => self%allocator%get_block(DIR_X)
+      call resolve_field_t(u_temp_d, u_temp)
+
+      blocks = dim3(nx_padded/SZ, ny_padded/SZ, nz_padded)
+      threads = dim3(SZ, SZ, 1)
+      call reorder_y2x<<<blocks, threads>>>(u_temp_d, u_i_d, nz_padded) !&
+
+      call reorder_x2c<<<blocks, threads>>>(u_o_d, u_temp_d, nz_padded) !&
+
+      call self%allocator%release_block(u_temp)
+    case (RDR_Z2C)
+      ! First reorder from Z to X, then from X to C
+      u_temp => self%allocator%get_block(DIR_X)
+      call resolve_field_t(u_temp_d, u_temp)
+
+      blocks = dim3(nx_padded, ny_padded/SZ, 1)
+      threads = dim3(SZ, 1, 1)
+      call reorder_z2x<<<blocks, threads>>>(u_temp_d, u_i_d, nz_padded) !&
+
+      blocks = dim3(nx_padded/SZ, ny_padded/SZ, nz_padded)
+      threads = dim3(SZ, SZ, 1)
+      call reorder_x2c<<<blocks, threads>>>(u_o_d, u_temp_d, nz_padded) !&
+
+      call self%allocator%release_block(u_temp)
+    case default
+      error stop 'Reorder direction is undefined.'
+    end select
+
+  end subroutine reorder_cuda
+
+  subroutine sum_yintox_cuda(self, u, u_y)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(field_t), intent(inout) :: u
+    class(field_t), intent(in) :: u_y
+
+    real(dp), device, pointer, dimension(:, :, :) :: u_d, u_y_d
+    type(dim3) :: blocks, threads
+    integer, dimension(3) :: dims_padded
+
+    call resolve_field_t(u_d, u)
+    call resolve_field_t(u_y_d, u_y)
+
+    dims_padded = self%mesh%get_padded_dims(DIR_C)
+
+    blocks = dim3(dims_padded(1)/SZ, dims_padded(2)/SZ, dims_padded(3))
+    threads = dim3(SZ, SZ, 1)
+    call sum_yintox<<<blocks, threads>>>(u_d, u_y_d, dims_padded(3)) !&
+
+  end subroutine sum_yintox_cuda
+
+  subroutine sum_zintox_cuda(self, u, u_z)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(field_t), intent(inout) :: u
+    class(field_t), intent(in) :: u_z
+
+    real(dp), device, pointer, dimension(:, :, :) :: u_d, u_z_d
+    type(dim3) :: blocks, threads
+    integer, dimension(3) :: dims_padded
+
+    call resolve_field_t(u_d, u)
+    call resolve_field_t(u_z_d, u_z)
+
+    dims_padded = self%mesh%get_padded_dims(DIR_C)
+
+    blocks = dim3(dims_padded(1), dims_padded(2)/SZ, 1)
+    threads = dim3(SZ, 1, 1)
+    call sum_zintox<<<blocks, threads>>>(u_d, u_z_d, dims_padded(3)) !&
+
+  end subroutine sum_zintox_cuda
+
+  subroutine vecadd_cuda(self, a, x, b, y)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    real(dp), intent(in) :: a
+    class(field_t), intent(in) :: x
+    real(dp), intent(in) :: b
+    class(field_t), intent(inout) :: y
+
+    real(dp), device, pointer, dimension(:, :, :) :: x_d, y_d
+    type(dim3) :: blocks, threads
+    integer :: nx
+
+    call resolve_field_t(x_d, x)
+    call resolve_field_t(y_d, y)
+
+    nx = size(x_d, dim=2)
+    blocks = dim3(size(x_d, dim=3), 1, 1)
+    threads = dim3(SZ, 1, 1)
+    call axpby<<<blocks, threads>>>(nx, a, x_d, b, y_d) !&
+
+  end subroutine vecadd_cuda
+
+  real(dp) function scalar_product_cuda(self, x, y) result(s)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(field_t), intent(in) :: x, y
+
+    real(dp), device, pointer, dimension(:, :, :) :: x_d, y_d
+    real(dp), device, allocatable :: sum_d
+    type(dim3) :: blocks, threads
+    integer :: n, ierr
+
+    call resolve_field_t(x_d, x)
+    call resolve_field_t(y_d, y)
+
+    allocate (sum_d)
+    sum_d = 0._dp
+
+    n = size(x_d, dim=2)
+    blocks = dim3(size(x_d, dim=3), 1, 1)
+    threads = dim3(SZ, 1, 1)
+    call scalar_product<<<blocks, threads>>>(sum_d, x_d, y_d, n) !&
+
+    s = sum_d
+
+    call MPI_Allreduce(MPI_IN_PLACE, s, 1, MPI_DOUBLE_PRECISION, MPI_SUM, &
+                       MPI_COMM_WORLD, ierr)
+
+  end function scalar_product_cuda
+
+  subroutine copy_into_buffers(u_send_s_dev, u_send_e_dev, u_dev, n)
+    implicit none
+
+    real(dp), device, dimension(:, :, :), intent(out) :: u_send_s_dev, &
+                                                         u_send_e_dev
+    real(dp), device, dimension(:, :, :), intent(in) :: u_dev
+    integer, intent(in) :: n
+
+    type(dim3) :: blocks, threads
+    integer :: n_halo = 4
+
+    blocks = dim3(size(u_dev, dim=3), 1, 1)
+    threads = dim3(SZ, 1, 1)
+    call buffer_copy<<<blocks, threads>>>(u_send_s_dev, u_send_e_dev, & !&
+                                          u_dev, n, n_halo)
+
+  end subroutine copy_into_buffers
+
+  subroutine copy_data_to_f_cuda(self, f, data)
+    class(cuda_backend_t), intent(inout) :: self
+    class(field_t), intent(inout) :: f
+    real(dp), dimension(:, :, :), intent(inout) :: data
+
+    select type (f); type is (cuda_field_t); f%data_d = data; end select
+  end subroutine copy_data_to_f_cuda
+
+  subroutine copy_f_to_data_cuda(self, data, f)
+    class(cuda_backend_t), intent(inout) :: self
+    real(dp), dimension(:, :, :), intent(out) :: data
+    class(field_t), intent(in) :: f
+
+    select type (f); type is (cuda_field_t); data = f%data_d; end select
+  end subroutine copy_f_to_data_cuda
+
+  subroutine init_cuda_poisson_fft(self, mesh, xdirps, ydirps, zdirps)
+    implicit none
+
+    class(cuda_backend_t) :: self
+    class(mesh_t), intent(in) :: mesh
+    type(dirps_t), intent(in) :: xdirps, ydirps, zdirps
+
+    allocate (cuda_poisson_fft_t :: self%poisson_fft)
+
+    select type (poisson_fft => self%poisson_fft)
+    type is (cuda_poisson_fft_t)
+      poisson_fft = cuda_poisson_fft_t(mesh, xdirps, ydirps, zdirps)
+    end select
+
+  end subroutine init_cuda_poisson_fft
+
+  subroutine resolve_field_t(u_dev, u)
+    real(dp), device, pointer, dimension(:, :, :), intent(out) :: u_dev
+    class(field_t), intent(in) :: u
+
+    select type (u)
+    type is (cuda_field_t)
+      u_dev => u%data_d
+    end select
+
+  end subroutine resolve_field_t
+
+end module m_cuda_backend
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/backend.f90~3.html b/api/sourcefile/backend.f90~3.html new file mode 100644 index 000000000..9a8950c65 --- /dev/null +++ b/api/sourcefile/backend.f90~3.html @@ -0,0 +1,785 @@ + + + + + + + + + + + + + backend.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

backend.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_omp_backend
+  use m_allocator, only: allocator_t, field_t
+  use m_base_backend, only: base_backend_t
+  use m_ordering, only: get_index_reordering
+  use m_common, only: dp, get_dirs_from_rdr, VERT, DIR_X, DIR_Y, DIR_Z, DIR_C
+  use m_tdsops, only: dirps_t, tdsops_t, get_tds_n
+  use m_omp_exec_dist, only: exec_dist_tds_compact, exec_dist_transeq_compact
+  use m_omp_sendrecv, only: sendrecv_fields
+
+  use m_omp_common, only: SZ
+  use m_omp_poisson_fft, only: omp_poisson_fft_t
+  use m_mesh, only: mesh_t
+
+  implicit none
+
+  private :: transeq_halo_exchange, transeq_dist_component
+
+  type, extends(base_backend_t) :: omp_backend_t
+    !character(len=*), parameter :: name = 'omp'
+    integer :: MPI_FP_PREC = dp
+    real(dp), allocatable, dimension(:, :, :) :: &
+      u_recv_s, u_recv_e, u_send_s, u_send_e, &
+      v_recv_s, v_recv_e, v_send_s, v_send_e, &
+      w_recv_s, w_recv_e, w_send_s, w_send_e, &
+      du_send_s, du_send_e, du_recv_s, du_recv_e, &
+      dud_send_s, dud_send_e, dud_recv_s, dud_recv_e, &
+      d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e
+  contains
+    procedure :: alloc_tdsops => alloc_omp_tdsops
+    procedure :: transeq_x => transeq_x_omp
+    procedure :: transeq_y => transeq_y_omp
+    procedure :: transeq_z => transeq_z_omp
+    procedure :: tds_solve => tds_solve_omp
+    procedure :: reorder => reorder_omp
+    procedure :: sum_yintox => sum_yintox_omp
+    procedure :: sum_zintox => sum_zintox_omp
+    procedure :: vecadd => vecadd_omp
+    procedure :: scalar_product => scalar_product_omp
+    procedure :: copy_data_to_f => copy_data_to_f_omp
+    procedure :: copy_f_to_data => copy_f_to_data_omp
+    procedure :: init_poisson_fft => init_omp_poisson_fft
+    procedure :: transeq_omp_dist
+  end type omp_backend_t
+
+  interface omp_backend_t
+    module procedure init
+  end interface omp_backend_t
+
+contains
+
+  function init(mesh, allocator) result(backend)
+    implicit none
+
+    class(mesh_t), target, intent(inout) :: mesh
+    class(allocator_t), target, intent(inout) :: allocator
+    type(omp_backend_t) :: backend
+
+    integer :: n_halo, n_groups
+
+    call backend%base_init()
+
+    select type (allocator)
+    type is (allocator_t)
+      ! class level access to the allocator
+      backend%allocator => allocator
+    end select
+
+    n_halo = 4
+    backend%mesh => mesh
+    n_groups = maxval([backend%mesh%get_n_groups(DIR_X), &
+                       backend%mesh%get_n_groups(DIR_Y), &
+                       backend%mesh%get_n_groups(DIR_Z)])
+
+    allocate (backend%u_send_s(SZ, n_halo, n_groups))
+    allocate (backend%u_send_e(SZ, n_halo, n_groups))
+    allocate (backend%u_recv_s(SZ, n_halo, n_groups))
+    allocate (backend%u_recv_e(SZ, n_halo, n_groups))
+    allocate (backend%v_send_s(SZ, n_halo, n_groups))
+    allocate (backend%v_send_e(SZ, n_halo, n_groups))
+    allocate (backend%v_recv_s(SZ, n_halo, n_groups))
+    allocate (backend%v_recv_e(SZ, n_halo, n_groups))
+    allocate (backend%w_send_s(SZ, n_halo, n_groups))
+    allocate (backend%w_send_e(SZ, n_halo, n_groups))
+    allocate (backend%w_recv_s(SZ, n_halo, n_groups))
+    allocate (backend%w_recv_e(SZ, n_halo, n_groups))
+
+    allocate (backend%du_send_s(SZ, 1, n_groups))
+    allocate (backend%du_send_e(SZ, 1, n_groups))
+    allocate (backend%du_recv_s(SZ, 1, n_groups))
+    allocate (backend%du_recv_e(SZ, 1, n_groups))
+    allocate (backend%dud_send_s(SZ, 1, n_groups))
+    allocate (backend%dud_send_e(SZ, 1, n_groups))
+    allocate (backend%dud_recv_s(SZ, 1, n_groups))
+    allocate (backend%dud_recv_e(SZ, 1, n_groups))
+    allocate (backend%d2u_send_s(SZ, 1, n_groups))
+    allocate (backend%d2u_send_e(SZ, 1, n_groups))
+    allocate (backend%d2u_recv_s(SZ, 1, n_groups))
+    allocate (backend%d2u_recv_e(SZ, 1, n_groups))
+
+  end function init
+
+  subroutine alloc_omp_tdsops( &
+    self, tdsops, dir, operation, scheme, &
+    n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu &
+    )
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(tdsops_t), allocatable, intent(inout) :: tdsops
+    integer, intent(in) :: dir
+    character(*), intent(in) :: operation, scheme
+    integer, optional, intent(in) :: n_halo
+    character(*), optional, intent(in) :: from_to, bc_start, bc_end
+    logical, optional, intent(in) :: sym
+    real(dp), optional, intent(in) :: c_nu, nu0_nu
+    integer :: tds_n
+    real(dp) :: delta
+
+    allocate (tdsops_t :: tdsops)
+
+    select type (tdsops)
+    type is (tdsops_t)
+      tds_n = get_tds_n(self%mesh, dir, from_to)
+      delta = self%mesh%geo%d(dir)
+      tdsops = tdsops_t(tds_n, delta, operation, scheme, n_halo, from_to, &
+                        bc_start, bc_end, sym, c_nu, nu0_nu)
+    end select
+
+  end subroutine alloc_omp_tdsops
+
+  subroutine transeq_x_omp(self, du, dv, dw, u, v, w, dirps)
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(field_t), intent(inout) :: du, dv, dw
+    class(field_t), intent(in) :: u, v, w
+    type(dirps_t), intent(in) :: dirps
+
+    call self%transeq_omp_dist(du, dv, dw, u, v, w, dirps)
+
+  end subroutine transeq_x_omp
+
+  subroutine transeq_y_omp(self, du, dv, dw, u, v, w, dirps)
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(field_t), intent(inout) :: du, dv, dw
+    class(field_t), intent(in) :: u, v, w
+    type(dirps_t), intent(in) :: dirps
+
+    ! u, v, w is reordered so that we pass v, u, w
+    call self%transeq_omp_dist(dv, du, dw, v, u, w, dirps)
+
+  end subroutine transeq_y_omp
+
+  subroutine transeq_z_omp(self, du, dv, dw, u, v, w, dirps)
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(field_t), intent(inout) :: du, dv, dw
+    class(field_t), intent(in) :: u, v, w
+    type(dirps_t), intent(in) :: dirps
+
+    ! u, v, w is reordered so that we pass w, u, v
+    call self%transeq_omp_dist(dw, du, dv, w, u, v, dirps)
+
+  end subroutine transeq_z_omp
+
+  subroutine transeq_omp_dist(self, du, dv, dw, u, v, w, dirps)
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(field_t), intent(inout) :: du, dv, dw
+    class(field_t), intent(in) :: u, v, w
+    type(dirps_t), intent(in) :: dirps
+
+    call transeq_halo_exchange(self, u, v, w, dirps%dir)
+
+    call transeq_dist_component(self, du, u, u, &
+                                self%u_recv_s, self%u_recv_e, &
+                                self%u_recv_s, self%u_recv_e, &
+                                dirps%der1st, dirps%der1st_sym, &
+                                dirps%der2nd, dirps%dir)
+    call transeq_dist_component(self, dv, v, u, &
+                                self%v_recv_s, self%v_recv_e, &
+                                self%u_recv_s, self%u_recv_e, &
+                                dirps%der1st_sym, dirps%der1st, &
+                                dirps%der2nd_sym, dirps%dir)
+    call transeq_dist_component(self, dw, w, u, &
+                                self%w_recv_s, self%w_recv_e, &
+                                self%u_recv_s, self%u_recv_e, &
+                                dirps%der1st_sym, dirps%der1st, &
+                                dirps%der2nd_sym, dirps%dir)
+
+  end subroutine transeq_omp_dist
+
+  subroutine transeq_halo_exchange(self, u, v, w, dir)
+    class(omp_backend_t) :: self
+    class(field_t), intent(in) :: u, v, w
+    integer, intent(in) :: dir
+    integer :: n_halo, n, nproc_dir, pprev, pnext
+    integer :: n_groups
+
+    ! TODO: don't hardcode n_halo
+    n_halo = 4
+    n_groups = self%mesh%get_n_groups(dir)
+    n = self%mesh%get_n(u)
+    nproc_dir = self%mesh%par%nproc_dir(dir)
+    pprev = self%mesh%par%pprev(dir)
+    pnext = self%mesh%par%pnext(dir)
+
+    call copy_into_buffers(self%u_send_s, self%u_send_e, u%data, &
+                           n, n_groups)
+    call copy_into_buffers(self%v_send_s, self%v_send_e, v%data, &
+                           n, n_groups)
+    call copy_into_buffers(self%w_send_s, self%w_send_e, w%data, &
+                           n, n_groups)
+
+    call sendrecv_fields(self%u_recv_s, self%u_recv_e, &
+                         self%u_send_s, self%u_send_e, &
+                         SZ*n_halo*n_groups, &
+                         nproc_dir, pprev, pnext)
+    call sendrecv_fields(self%v_recv_s, self%v_recv_e, &
+                         self%v_send_s, self%v_send_e, &
+                         SZ*n_halo*n_groups, &
+                         nproc_dir, pprev, pnext)
+    call sendrecv_fields(self%w_recv_s, self%w_recv_e, &
+                         self%w_send_s, self%w_send_e, &
+                         SZ*n_halo*n_groups, &
+                         nproc_dir, pprev, pnext)
+
+  end subroutine transeq_halo_exchange
+
+  subroutine transeq_dist_component(self, rhs, u, conv, &
+                                    u_recv_s, u_recv_e, &
+                                    conv_recv_s, conv_recv_e, &
+                                    tdsops_du, tdsops_dud, tdsops_d2u, dir)
+      !! Computes RHS_x^u following:
+      !!
+      !! rhs_x^u = -0.5*(conv*du/dx + d(u*conv)/dx) + nu*d2u/dx2
+    class(omp_backend_t) :: self
+    class(field_t), intent(inout) :: rhs
+    class(field_t), intent(in) :: u, conv
+    real(dp), dimension(:, :, :), intent(in) :: u_recv_s, u_recv_e, &
+                                                conv_recv_s, conv_recv_e
+    class(tdsops_t), intent(in) :: tdsops_du
+    class(tdsops_t), intent(in) :: tdsops_dud
+    class(tdsops_t), intent(in) :: tdsops_d2u
+    integer, intent(in) :: dir
+    class(field_t), pointer :: du, d2u, dud
+
+    du => self%allocator%get_block(dir, VERT)
+    dud => self%allocator%get_block(dir, VERT)
+    d2u => self%allocator%get_block(dir, VERT)
+
+    call exec_dist_transeq_compact( &
+      rhs%data, du%data, dud%data, d2u%data, &
+      self%du_send_s, self%du_send_e, self%du_recv_s, self%du_recv_e, &
+      self%dud_send_s, self%dud_send_e, self%dud_recv_s, self%dud_recv_e, &
+      self%d2u_send_s, self%d2u_send_e, self%d2u_recv_s, self%d2u_recv_e, &
+      u%data, u_recv_s, u_recv_e, &
+      conv%data, conv_recv_s, conv_recv_e, &
+      tdsops_du, tdsops_dud, tdsops_d2u, self%nu, &
+      self%mesh%par%nproc_dir(dir), self%mesh%par%pprev(dir), &
+      self%mesh%par%pnext(dir), self%mesh%get_n_groups(dir))
+
+    call self%allocator%release_block(du)
+    call self%allocator%release_block(dud)
+    call self%allocator%release_block(d2u)
+
+  end subroutine transeq_dist_component
+
+  subroutine tds_solve_omp(self, du, u, tdsops)
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(field_t), intent(inout) :: du
+    class(field_t), intent(in) :: u
+    class(tdsops_t), intent(in) :: tdsops
+
+    ! Check if direction matches for both in/out fields
+    if (u%dir /= du%dir) then
+      error stop 'DIR mismatch between fields in tds_solve.'
+    end if
+
+    call tds_solve_dist(self, du, u, tdsops)
+
+  end subroutine tds_solve_omp
+
+  subroutine tds_solve_dist(self, du, u, tdsops)
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(field_t), intent(inout) :: du
+    class(field_t), intent(in) :: u
+    class(tdsops_t), intent(in) :: tdsops
+    integer :: n_halo, n_groups, dir
+
+    ! TODO: don't hardcode n_halo
+    n_halo = 4
+    dir = u%dir
+    n_groups = self%mesh%get_n_groups(u)
+
+    call copy_into_buffers(self%u_send_s, self%u_send_e, u%data, &
+                           tdsops%tds_n, n_groups)
+
+    ! halo exchange
+    call sendrecv_fields(self%u_recv_s, self%u_recv_e, &
+                         self%u_send_s, self%u_send_e, &
+                         SZ*n_halo*n_groups, &
+                         self%mesh%par%nproc_dir(dir), &
+                         self%mesh%par%pprev(dir), &
+                         self%mesh%par%pnext(dir))
+
+    call exec_dist_tds_compact( &
+      du%data, u%data, self%u_recv_s, self%u_recv_e, &
+      self%du_send_s, self%du_send_e, self%du_recv_s, self%du_recv_e, &
+      tdsops, self%mesh%par%nproc_dir(dir), &
+      self%mesh%par%pprev(dir), self%mesh%par%pnext(dir), &
+      n_groups)
+
+  end subroutine tds_solve_dist
+
+  subroutine reorder_omp(self, u_, u, direction)
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(field_t), intent(inout) :: u_
+    class(field_t), intent(in) :: u
+    integer, intent(in) :: direction
+    integer, dimension(3) :: dims
+    integer :: i, j, k
+    integer :: out_i, out_j, out_k
+    integer :: dir_from, dir_to
+
+    dims = self%mesh%get_padded_dims(u)
+    call get_dirs_from_rdr(dir_from, dir_to, direction)
+
+    !$omp parallel do private(out_i, out_j, out_k) collapse(2)
+    do k = 1, dims(3)
+      do j = 1, dims(2)
+        do i = 1, dims(1)
+          call get_index_reordering( &
+            out_i, out_j, out_k, i, j, k, dir_from, dir_to, self%mesh)
+          u_%data(out_i, out_j, out_k) = u%data(i, j, k)
+        end do
+      end do
+    end do
+    !$omp end parallel do
+
+  end subroutine reorder_omp
+
+  subroutine sum_yintox_omp(self, u, u_)
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(field_t), intent(inout) :: u
+    class(field_t), intent(in) :: u_
+
+    call sum_intox_omp(self, u, u_, DIR_Y)
+
+  end subroutine sum_yintox_omp
+
+  subroutine sum_zintox_omp(self, u, u_)
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(field_t), intent(inout) :: u
+    class(field_t), intent(in) :: u_
+
+    call sum_intox_omp(self, u, u_, DIR_Z)
+
+  end subroutine sum_zintox_omp
+
+  subroutine sum_intox_omp(self, u, u_, dir_to)
+
+    class(omp_backend_t) :: self
+    class(field_t), intent(inout) :: u
+    class(field_t), intent(in) :: u_
+    integer, intent(in) :: dir_to
+
+    integer :: dir_from
+    integer, dimension(3) :: dims
+    integer :: i, j, k    ! Working indices
+    integer :: ii, jj, kk ! Transpose indices
+
+    dir_from = DIR_X
+
+    dims = self%mesh%get_padded_dims(u)
+    !$omp parallel do private(i, ii, jj, kk) collapse(2)
+    do k = 1, dims(3)
+      do j = 1, dims(2)
+        do i = 1, dims(1)
+          call get_index_reordering(ii, jj, kk, i, j, k, &
+                                    dir_from, dir_to, self%mesh)
+          u%data(i, j, k) = u%data(i, j, k) + u_%data(ii, jj, kk)
+        end do
+      end do
+    end do
+    !$omp end parallel do
+
+  end subroutine sum_intox_omp
+
+  subroutine vecadd_omp(self, a, x, b, y)
+    implicit none
+
+    class(omp_backend_t) :: self
+    real(dp), intent(in) :: a
+    class(field_t), intent(in) :: x
+    real(dp), intent(in) :: b
+    class(field_t), intent(inout) :: y
+    integer, dimension(3) :: dims
+    integer :: i, j, k, ii
+
+    integer :: nvec, remstart
+
+    if (x%dir /= y%dir) then
+      error stop "Called vector add with incompatible fields"
+    end if
+
+    dims = size(x%data)
+    nvec = dims(1)/SZ
+    remstart = nvec*SZ + 1
+
+    !$omp parallel do private(i, ii) collapse(2)
+    do k = 1, dims(3)
+      do j = 1, dims(2)
+        ! Execute inner vectorised loops
+        do ii = 1, nvec
+          !$omp simd
+          do i = 1, SZ
+            y%data(i + (ii - 1)*SZ, j, k) = &
+              a*x%data(i + (ii - 1)*SZ, j, k) + &
+              b*y%data(i + (ii - 1)*SZ, j, k)
+          end do
+          !$omp end simd
+        end do
+
+        ! Remainder loop
+        do i = remstart, dims(1)
+          y%data(i, j, k) = a*x%data(i, j, k) + b*y%data(i, j, k)
+        end do
+      end do
+    end do
+    !$omp end parallel do
+
+  end subroutine vecadd_omp
+
+  real(dp) function scalar_product_omp(self, x, y) result(s)
+
+    use mpi
+
+    use m_common, only: none, get_rdr_from_dirs
+
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(field_t), intent(in) :: x, y
+    class(field_t), pointer :: x_, y_
+    integer, dimension(3) :: dims
+    integer :: i, j, k, ii
+    integer :: nvec, remstart
+    integer :: ierr
+
+    if ((x%data_loc == none) .or. (y%data_loc == none)) then
+      error stop "You must set the data_loc before calling scalar product"
+    end if
+    if (x%data_loc /= y%data_loc) then
+      error stop "Called scalar product with incompatible fields"
+    end if
+
+    ! Reorient data into temporary DIR_C storage
+    x_ => self%allocator%get_block(DIR_C, x%data_loc)
+    call self%get_field_data(x_%data, x)
+    y_ => self%allocator%get_block(DIR_C, y%data_loc)
+    call self%get_field_data(y_%data, y)
+
+    dims = self%mesh%get_field_dims(x_)
+
+    nvec = dims(1)/SZ
+    remstart = nvec*SZ + 1
+
+    s = 0.0_dp
+    !$omp parallel do reduction(+:s) private(i, ii) collapse(2)
+    do k = 1, dims(3)
+      do j = 1, dims(2)
+        ! Execute inner vectorised loops
+        do ii = 1, nvec
+          !$omp simd reduction(+:s)
+          do i = 1, SZ
+            s = s + x_%data(i + (ii - 1)*SZ, j, k)* &
+                y_%data(i + (ii - 1)*SZ, j, k)
+          end do
+          !$omp end simd
+        end do
+
+        ! Remainder loop
+        do i = remstart, dims(1)
+          s = s + x_%data(i, j, k)*y_%data(i, j, k)
+        end do
+      end do
+    end do
+    !$omp end parallel do
+
+    ! Release temporary storage
+    call self%allocator%release_block(x_)
+    call self%allocator%release_block(y_)
+
+    ! Reduce the result
+    call MPI_Allreduce(MPI_IN_PLACE, s, 1, MPI_DOUBLE_PRECISION, &
+                       MPI_SUM, MPI_COMM_WORLD, &
+                       ierr)
+
+  end function scalar_product_omp
+
+  subroutine copy_into_buffers(u_send_s, u_send_e, u, n, n_groups)
+    implicit none
+
+    real(dp), dimension(:, :, :), intent(out) :: u_send_s, u_send_e
+    real(dp), dimension(:, :, :), intent(in) :: u
+    integer, intent(in) :: n
+    integer, intent(in) :: n_groups
+    integer :: i, j, k
+    integer :: n_halo = 4
+
+    !$omp parallel do
+    do k = 1, n_groups
+      do j = 1, n_halo
+        !$omp simd
+        do i = 1, SZ
+          u_send_s(i, j, k) = u(i, j, k)
+          u_send_e(i, j, k) = u(i, n - n_halo + j, k)
+        end do
+        !$omp end simd
+      end do
+    end do
+    !$omp end parallel do
+
+  end subroutine copy_into_buffers
+
+  subroutine copy_data_to_f_omp(self, f, data)
+    class(omp_backend_t), intent(inout) :: self
+    class(field_t), intent(inout) :: f
+    real(dp), dimension(:, :, :), intent(in) :: data
+
+    f%data = data
+  end subroutine copy_data_to_f_omp
+
+  subroutine copy_f_to_data_omp(self, data, f)
+    class(omp_backend_t), intent(inout) :: self
+    real(dp), dimension(:, :, :), intent(out) :: data
+    class(field_t), intent(in) :: f
+
+    data = f%data
+  end subroutine copy_f_to_data_omp
+
+  subroutine init_omp_poisson_fft(self, mesh, xdirps, ydirps, zdirps)
+    implicit none
+
+    class(omp_backend_t) :: self
+    class(mesh_t), intent(in) :: mesh
+    type(dirps_t), intent(in) :: xdirps, ydirps, zdirps
+
+    allocate (omp_poisson_fft_t :: self%poisson_fft)
+
+    select type (poisson_fft => self%poisson_fft)
+    type is (omp_poisson_fft_t)
+      poisson_fft = omp_poisson_fft_t(mesh, xdirps, ydirps, zdirps)
+    end select
+
+  end subroutine init_omp_poisson_fft
+
+end module m_omp_backend
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/common.f90.html b/api/sourcefile/common.f90.html new file mode 100644 index 000000000..06beeecb4 --- /dev/null +++ b/api/sourcefile/common.f90.html @@ -0,0 +1,218 @@ + + + + + + + + + + + + + common.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

common.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_common
+  implicit none
+
+  integer, parameter :: SZ = 32
+
+end module m_cuda_common
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/common.f90~2.html b/api/sourcefile/common.f90~2.html new file mode 100644 index 000000000..3b2e3d80b --- /dev/null +++ b/api/sourcefile/common.f90~2.html @@ -0,0 +1,261 @@ + + + + + + + + + + + + + common.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

common.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_common
+  implicit none
+
+  integer, parameter :: dp = kind(0.0d0)
+  real(dp), parameter :: pi = 4*atan(1.0_dp)
+
+  integer, parameter :: RDR_X2Y = 12, RDR_X2Z = 13, RDR_Y2X = 21, &
+                        RDR_Y2Z = 23, RDR_Z2X = 31, RDR_Z2Y = 32, &
+                        RDR_C2X = 41, RDR_C2Y = 42, RDR_C2Z = 43, &
+                        RDR_X2C = 14, RDR_Y2C = 24, RDR_Z2C = 34
+  integer, parameter :: DIR_X = 1, DIR_Y = 2, DIR_Z = 3, DIR_C = 4
+  integer, parameter :: POISSON_SOLVER_FFT = 0, POISSON_SOLVER_CG = 1
+  integer, parameter :: VERT = 0000, & ! Vertex centered data
+                        CELL = 1110, & ! Cell centered data
+                        X_FACE = 0110, &  ! Data on faces normal to X
+                        Y_FACE = 1010, &  ! Data on faces normal to Y
+                        Z_FACE = 1100, &  ! Data on faces normal to Z
+                        X_EDGE = 1000, & ! Data on edges along X
+                        Y_EDGE = 0100, & ! Data on edges along Y
+                        Z_EDGE = 0010, & ! Data on edges along Z
+                        none = -0001 ! The location of data isn't specified
+  integer, parameter :: BC_PERIODIC = 0, BC_NEUMANN = 1, BC_DIRICHLET = 2
+  integer, protected :: &
+    rdr_map(4, 4) = reshape([0, RDR_Y2X, RDR_Z2X, RDR_C2X, &
+                             RDR_X2Y, 0, RDR_Z2Y, RDR_C2Y, &
+                             RDR_X2Z, RDR_Y2Z, 0, RDR_C2Z, &
+                             RDR_X2C, RDR_Y2C, RDR_Z2C, 0], shape=[4, 4])
+
+contains
+
+  pure subroutine get_dirs_from_rdr(dir_from, dir_to, rdr_dir)
+    integer, intent(out) :: dir_from, dir_to
+    integer, intent(in) :: rdr_dir
+    integer, dimension(2) :: dirs
+
+    dirs = findloc(rdr_map, rdr_dir)
+    dir_from = dirs(1)
+    dir_to = dirs(2)
+
+  end subroutine
+
+  pure integer function get_rdr_from_dirs(dir_from, dir_to) result(rdr_dir)
+      !! Returns RDR_?2? value based on two direction inputs
+    integer, intent(in) :: dir_from, dir_to
+
+    rdr_dir = rdr_map(dir_from, dir_to)
+  end function get_rdr_from_dirs
+
+end module m_common
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/common.f90~3.html b/api/sourcefile/common.f90~3.html new file mode 100644 index 000000000..cdfde101f --- /dev/null +++ b/api/sourcefile/common.f90~3.html @@ -0,0 +1,218 @@ + + + + + + + + + + + + + common.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

common.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_omp_common
+  implicit none
+
+  integer, parameter :: SZ = 16
+
+end module m_omp_common
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/distributed.f90.html b/api/sourcefile/distributed.f90.html new file mode 100644 index 000000000..0b003e431 --- /dev/null +++ b/api/sourcefile/distributed.f90.html @@ -0,0 +1,865 @@ + + + + + + + + + + + + + distributed.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

distributed.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_kernels_dist
+  use cudafor
+
+  use m_common, only: dp
+
+  implicit none
+
+contains
+
+  attributes(global) subroutine der_univ_dist( &
+    du, send_u_s, send_u_e, u, u_s, u_e, coeffs_s, coeffs_e, coeffs, n, &
+    ffr, fbc, faf &
+    )
+    implicit none
+
+    ! Arguments
+    real(dp), device, intent(out), dimension(:, :, :) :: du, send_u_s, &
+                                                         send_u_e
+    real(dp), device, intent(in), dimension(:, :, :) :: u, u_s, u_e
+    real(dp), device, intent(in), dimension(:, :) :: coeffs_s, coeffs_e
+    real(dp), device, intent(in), dimension(:) :: coeffs
+    integer, value, intent(in) :: n
+    real(dp), device, intent(in), dimension(:) :: ffr, fbc, faf
+
+    ! Local variables
+    integer :: i, j, b, k, lj
+    integer :: jm2, jm1, jp1, jp2
+
+    real(dp) :: c_m4, c_m3, c_m2, c_m1, c_j, c_p1, c_p2, c_p3, c_p4, &
+                temp_du, alpha, last_r
+
+    i = threadIdx%x
+    b = blockIdx%x
+
+    ! store bulk coeffs in the registers
+    c_m4 = coeffs(1); c_m3 = coeffs(2); c_m2 = coeffs(3); c_m1 = coeffs(4)
+    c_j = coeffs(5)
+    c_p1 = coeffs(6); c_p2 = coeffs(7); c_p3 = coeffs(8); c_p4 = coeffs(9)
+    last_r = ffr(1)
+
+    du(i, 1, b) = coeffs_s(1, 1)*u_s(i, 1, b) &
+                  + coeffs_s(2, 1)*u_s(i, 2, b) &
+                  + coeffs_s(3, 1)*u_s(i, 3, b) &
+                  + coeffs_s(4, 1)*u_s(i, 4, b) &
+                  + coeffs_s(5, 1)*u(i, 1, b) &
+                  + coeffs_s(6, 1)*u(i, 2, b) &
+                  + coeffs_s(7, 1)*u(i, 3, b) &
+                  + coeffs_s(8, 1)*u(i, 4, b) &
+                  + coeffs_s(9, 1)*u(i, 5, b)
+    du(i, 1, b) = du(i, 1, b)*faf(1)
+    du(i, 2, b) = coeffs_s(1, 2)*u_s(i, 2, b) &
+                  + coeffs_s(2, 2)*u_s(i, 3, b) &
+                  + coeffs_s(3, 2)*u_s(i, 4, b) &
+                  + coeffs_s(4, 2)*u(i, 1, b) &
+                  + coeffs_s(5, 2)*u(i, 2, b) &
+                  + coeffs_s(6, 2)*u(i, 3, b) &
+                  + coeffs_s(7, 2)*u(i, 4, b) &
+                  + coeffs_s(8, 2)*u(i, 5, b) &
+                  + coeffs_s(9, 2)*u(i, 6, b)
+    du(i, 2, b) = du(i, 2, b)*faf(2)
+    du(i, 3, b) = coeffs_s(1, 3)*u_s(i, 3, b) &
+                  + coeffs_s(2, 3)*u_s(i, 4, b) &
+                  + coeffs_s(3, 3)*u(i, 1, b) &
+                  + coeffs_s(4, 3)*u(i, 2, b) &
+                  + coeffs_s(5, 3)*u(i, 3, b) &
+                  + coeffs_s(6, 3)*u(i, 4, b) &
+                  + coeffs_s(7, 3)*u(i, 5, b) &
+                  + coeffs_s(8, 3)*u(i, 6, b) &
+                  + coeffs_s(9, 3)*u(i, 7, b)
+    du(i, 3, b) = ffr(3)*(du(i, 3, b) - faf(3)*du(i, 2, b))
+    du(i, 4, b) = coeffs_s(1, 4)*u_s(i, 4, b) &
+                  + coeffs_s(2, 4)*u(i, 1, b) &
+                  + coeffs_s(3, 4)*u(i, 2, b) &
+                  + coeffs_s(4, 4)*u(i, 3, b) &
+                  + coeffs_s(5, 4)*u(i, 4, b) &
+                  + coeffs_s(6, 4)*u(i, 5, b) &
+                  + coeffs_s(7, 4)*u(i, 6, b) &
+                  + coeffs_s(8, 4)*u(i, 7, b) &
+                  + coeffs_s(9, 4)*u(i, 8, b)
+    du(i, 4, b) = ffr(4)*(du(i, 4, b) - faf(3)*du(i, 3, b))
+
+    alpha = faf(5)
+
+    do j = 5, n - 4
+      temp_du = c_m4*u(i, j - 4, b) + c_m3*u(i, j - 3, b) &
+                + c_m2*u(i, j - 2, b) + c_m1*u(i, j - 1, b) &
+                + c_j*u(i, j, b) &
+                + c_p1*u(i, j + 1, b) + c_p2*u(i, j + 2, b) &
+                + c_p3*u(i, j + 3, b) + c_p4*u(i, j + 4, b)
+      du(i, j, b) = ffr(j)*(temp_du - alpha*du(i, j - 1, b))
+    end do
+
+    j = n - 3
+    du(i, j, b) = coeffs_e(1, 1)*u(i, j - 4, b) &
+                  + coeffs_e(2, 1)*u(i, j - 3, b) &
+                  + coeffs_e(3, 1)*u(i, j - 2, b) &
+                  + coeffs_e(4, 1)*u(i, j - 1, b) &
+                  + coeffs_e(5, 1)*u(i, j, b) &
+                  + coeffs_e(6, 1)*u(i, j + 1, b) &
+                  + coeffs_e(7, 1)*u(i, j + 2, b) &
+                  + coeffs_e(8, 1)*u(i, j + 3, b) &
+                  + coeffs_e(9, 1)*u_e(i, 1, b)
+    du(i, j, b) = ffr(j)*(du(i, j, b) - faf(j)*du(i, j - 1, b))
+    j = n - 2
+    du(i, j, b) = coeffs_e(1, 2)*u(i, j - 4, b) &
+                  + coeffs_e(2, 2)*u(i, j - 3, b) &
+                  + coeffs_e(3, 2)*u(i, j - 2, b) &
+                  + coeffs_e(4, 2)*u(i, j - 1, b) &
+                  + coeffs_e(5, 2)*u(i, j, b) &
+                  + coeffs_e(6, 2)*u(i, j + 1, b) &
+                  + coeffs_e(7, 2)*u(i, j + 2, b) &
+                  + coeffs_e(8, 2)*u_e(i, 1, b) &
+                  + coeffs_e(9, 2)*u_e(i, 2, b)
+    du(i, j, b) = ffr(j)*(du(i, j, b) - faf(j)*du(i, j - 1, b))
+    j = n - 1
+    du(i, j, b) = coeffs_e(1, 3)*u(i, j - 4, b) &
+                  + coeffs_e(2, 3)*u(i, j - 3, b) &
+                  + coeffs_e(3, 3)*u(i, j - 2, b) &
+                  + coeffs_e(4, 3)*u(i, j - 1, b) &
+                  + coeffs_e(5, 3)*u(i, j, b) &
+                  + coeffs_e(6, 3)*u(i, j + 1, b) &
+                  + coeffs_e(7, 3)*u_e(i, 1, b) &
+                  + coeffs_e(8, 3)*u_e(i, 2, b) &
+                  + coeffs_e(9, 3)*u_e(i, 3, b)
+    du(i, j, b) = ffr(j)*(du(i, j, b) - faf(j)*du(i, j - 1, b))
+    j = n
+    du(i, j, b) = coeffs_e(1, 4)*u(i, j - 4, b) &
+                  + coeffs_e(2, 4)*u(i, j - 3, b) &
+                  + coeffs_e(3, 4)*u(i, j - 2, b) &
+                  + coeffs_e(4, 4)*u(i, j - 1, b) &
+                  + coeffs_e(5, 4)*u(i, j, b) &
+                  + coeffs_e(6, 4)*u_e(i, 1, b) &
+                  + coeffs_e(7, 4)*u_e(i, 2, b) &
+                  + coeffs_e(8, 4)*u_e(i, 3, b) &
+                  + coeffs_e(9, 4)*u_e(i, 4, b)
+    du(i, j, b) = ffr(j)*(du(i, j, b) - faf(j)*du(i, j - 1, b))
+
+    send_u_e(i, 1, b) = du(i, n, b)
+
+    ! Backward pass of the hybrid algorithm
+    do j = n - 2, 2, -1
+      du(i, j, b) = du(i, j, b) - fbc(j)*du(i, j + 1, b)
+    end do
+    du(i, 1, b) = last_r*(du(i, 1, b) - fbc(1)*du(i, 2, b))
+    send_u_s(i, 1, b) = du(i, 1, b)
+
+  end subroutine der_univ_dist
+
+  attributes(global) subroutine der_univ_subs(du, recv_u_s, recv_u_e, &
+                                              n, dist_sa, dist_sc)
+    implicit none
+
+    ! Arguments
+    real(dp), device, intent(out), dimension(:, :, :) :: du
+    real(dp), device, intent(in), dimension(:, :, :) :: recv_u_s, recv_u_e
+    real(dp), device, intent(in), dimension(:) :: dist_sa, dist_sc
+    integer, value, intent(in) :: n
+
+    ! Local variables
+    integer :: i, j, b
+    real(dp) :: ur, bl, recp, du_s, du_e
+
+    i = threadIdx%x
+    b = blockIdx%x
+
+    ! A small trick we do here is valid for symmetric Toeplitz matrices.
+    ! In our case our matrices satisfy this criteria in the (5:n-4) region
+    ! and as long as a rank has around at least 20 entries the assumptions
+    ! we make here are perfectly valid.
+
+    ! bl is the bottom left entry in the 2x2 matrix
+    ! ur is the upper right entry in the 2x2 matrix
+
+    ! Start
+    ! At the start we have the 'bl', and assume 'ur'
+    bl = dist_sa(1)
+    ur = dist_sa(1)
+    recp = 1._dp/(1._dp - ur*bl)
+    du_s = recp*(du(i, 1, b) - bl*recv_u_s(i, 1, b))
+
+    ! End
+    ! At the end we have the 'ur', and assume 'bl'
+    bl = dist_sc(n)
+    ur = dist_sc(n)
+    recp = 1._dp/(1._dp - ur*bl)
+    du_e = recp*(du(i, n, b) - ur*recv_u_e(i, 1, b))
+
+    du(i, 1, b) = du_s
+    do j = 2, n - 1
+      du(i, j, b) = (du(i, j, b) - dist_sa(j)*du_s - dist_sc(j)*du_e)
+    end do
+    du(i, n, b) = du_e
+
+  end subroutine der_univ_subs
+
+  attributes(global) subroutine transeq_3fused_dist( &
+    du, dud, d2u, &
+    send_du_s, send_du_e, send_dud_s, send_dud_e, send_d2u_s, send_d2u_e, &
+    u, u_s, u_e, v, v_s, v_e, n, &
+    d1_coeffs_s, d1_coeffs_e, d1_coeffs, d1_fw, d1_bw, d1_af, &
+    d2_coeffs_s, d2_coeffs_e, d2_coeffs, d2_fw, d2_bw, d2_af &
+    )
+    implicit none
+
+    ! Arguments
+    real(dp), device, intent(out), dimension(:, :, :) :: du, dud, d2u
+    real(dp), device, intent(out), dimension(:, :, :) :: &
+      send_du_s, send_du_e, send_dud_s, send_dud_e, send_d2u_s, send_d2u_e
+    real(dp), device, intent(in), dimension(:, :, :) :: u, u_s, u_e, &
+                                                        v, v_s, v_e
+    integer, value, intent(in) :: n
+    real(dp), device, intent(in) :: d1_coeffs_s(:, :), d1_coeffs_e(:, :), &
+                                    d1_coeffs(:)
+    real(dp), device, intent(in) :: d1_fw(:), d1_bw(:), d1_af(:)
+    real(dp), device, intent(in) :: d2_coeffs_s(:, :), d2_coeffs_e(:, :), &
+                                    d2_coeffs(:)
+    real(dp), device, intent(in) :: d2_fw(:), d2_bw(:), d2_af(:)
+
+    ! Local variables
+    integer :: i, j, b
+
+    real(dp) :: d1_c_m4, d1_c_m3, d1_c_m2, d1_c_m1, d1_c_j, &
+                d1_c_p1, d1_c_p2, d1_c_p3, d1_c_p4, &
+                d1_alpha, d1_last_r
+    real(dp) :: d2_c_m4, d2_c_m3, d2_c_m2, d2_c_m1, d2_c_j, &
+                d2_c_p1, d2_c_p2, d2_c_p3, d2_c_p4, &
+                d2_alpha, d2_last_r
+    real(dp) :: temp_du, temp_dud, temp_d2u
+    real(dp) :: u_m4, u_m3, u_m2, u_m1, u_j, u_p1, u_p2, u_p3, u_p4
+    real(dp) :: v_m4, v_m3, v_m2, v_m1, v_j, v_p1, v_p2, v_p3, v_p4
+    real(dp) :: old_du, old_dud, old_d2u
+
+    i = threadIdx%x
+    b = blockIdx%x
+
+    d1_last_r = d1_fw(1)
+    d2_last_r = d2_fw(1)
+
+    ! j = 1
+    temp_du = d1_coeffs_s(1, 1)*u_s(i, 1, b) &
+              + d1_coeffs_s(2, 1)*u_s(i, 2, b) &
+              + d1_coeffs_s(3, 1)*u_s(i, 3, b) &
+              + d1_coeffs_s(4, 1)*u_s(i, 4, b) &
+              + d1_coeffs_s(5, 1)*u(i, 1, b) &
+              + d1_coeffs_s(6, 1)*u(i, 2, b) &
+              + d1_coeffs_s(7, 1)*u(i, 3, b) &
+              + d1_coeffs_s(8, 1)*u(i, 4, b) &
+              + d1_coeffs_s(9, 1)*u(i, 5, b)
+    du(i, 1, b) = temp_du*d1_af(1)
+    temp_dud = d1_coeffs_s(1, 1)*u_s(i, 1, b)*v_s(i, 1, b) &
+               + d1_coeffs_s(2, 1)*u_s(i, 2, b)*v_s(i, 2, b) &
+               + d1_coeffs_s(3, 1)*u_s(i, 3, b)*v_s(i, 3, b) &
+               + d1_coeffs_s(4, 1)*u_s(i, 4, b)*v_s(i, 4, b) &
+               + d1_coeffs_s(5, 1)*u(i, 1, b)*v(i, 1, b) &
+               + d1_coeffs_s(6, 1)*u(i, 2, b)*v(i, 2, b) &
+               + d1_coeffs_s(7, 1)*u(i, 3, b)*v(i, 3, b) &
+               + d1_coeffs_s(8, 1)*u(i, 4, b)*v(i, 4, b) &
+               + d1_coeffs_s(9, 1)*u(i, 5, b)*v(i, 5, b)
+    dud(i, 1, b) = temp_dud*d1_af(1)
+    temp_d2u = d2_coeffs_s(1, 1)*u_s(i, 1, b) &
+               + d2_coeffs_s(2, 1)*u_s(i, 2, b) &
+               + d2_coeffs_s(3, 1)*u_s(i, 3, b) &
+               + d2_coeffs_s(4, 1)*u_s(i, 4, b) &
+               + d2_coeffs_s(5, 1)*u(i, 1, b) &
+               + d2_coeffs_s(6, 1)*u(i, 2, b) &
+               + d2_coeffs_s(7, 1)*u(i, 3, b) &
+               + d2_coeffs_s(8, 1)*u(i, 4, b) &
+               + d2_coeffs_s(9, 1)*u(i, 5, b)
+    d2u(i, 1, b) = temp_d2u*d2_af(1)
+    ! j = 2
+    temp_du = d1_coeffs_s(1, 2)*u_s(i, 2, b) &
+              + d1_coeffs_s(2, 2)*u_s(i, 3, b) &
+              + d1_coeffs_s(3, 2)*u_s(i, 4, b) &
+              + d1_coeffs_s(4, 2)*u(i, 1, b) &
+              + d1_coeffs_s(5, 2)*u(i, 2, b) &
+              + d1_coeffs_s(6, 2)*u(i, 3, b) &
+              + d1_coeffs_s(7, 2)*u(i, 4, b) &
+              + d1_coeffs_s(8, 2)*u(i, 5, b) &
+              + d1_coeffs_s(9, 2)*u(i, 6, b)
+    du(i, 2, b) = temp_du*d1_af(2)
+    temp_dud = d1_coeffs_s(1, 2)*u_s(i, 2, b)*v_s(i, 2, b) &
+               + d1_coeffs_s(2, 2)*u_s(i, 3, b)*v_s(i, 3, b) &
+               + d1_coeffs_s(3, 2)*u_s(i, 4, b)*v_s(i, 4, b) &
+               + d1_coeffs_s(4, 2)*u(i, 1, b)*v(i, 1, b) &
+               + d1_coeffs_s(5, 2)*u(i, 2, b)*v(i, 2, b) &
+               + d1_coeffs_s(6, 2)*u(i, 3, b)*v(i, 3, b) &
+               + d1_coeffs_s(7, 2)*u(i, 4, b)*v(i, 4, b) &
+               + d1_coeffs_s(8, 2)*u(i, 5, b)*v(i, 5, b) &
+               + d1_coeffs_s(9, 2)*u(i, 6, b)*v(i, 6, b)
+    dud(i, 2, b) = temp_dud*d1_af(2)
+    temp_d2u = d2_coeffs_s(1, 2)*u_s(i, 2, b) &
+               + d2_coeffs_s(2, 2)*u_s(i, 3, b) &
+               + d2_coeffs_s(3, 2)*u_s(i, 4, b) &
+               + d2_coeffs_s(4, 2)*u(i, 1, b) &
+               + d2_coeffs_s(5, 2)*u(i, 2, b) &
+               + d2_coeffs_s(6, 2)*u(i, 3, b) &
+               + d2_coeffs_s(7, 2)*u(i, 4, b) &
+               + d2_coeffs_s(8, 2)*u(i, 5, b) &
+               + d2_coeffs_s(9, 2)*u(i, 6, b)
+    d2u(i, 2, b) = temp_d2u*d2_af(2)
+    ! j = 3
+    temp_du = d1_coeffs_s(1, 3)*u_s(i, 3, b) &
+              + d1_coeffs_s(2, 3)*u_s(i, 4, b) &
+              + d1_coeffs_s(3, 3)*u(i, 1, b) &
+              + d1_coeffs_s(4, 3)*u(i, 2, b) &
+              + d1_coeffs_s(5, 3)*u(i, 3, b) &
+              + d1_coeffs_s(6, 3)*u(i, 4, b) &
+              + d1_coeffs_s(7, 3)*u(i, 5, b) &
+              + d1_coeffs_s(8, 3)*u(i, 6, b) &
+              + d1_coeffs_s(9, 3)*u(i, 7, b)
+    du(i, 3, b) = d1_fw(3)*(temp_du - d1_af(3)*du(i, 2, b))
+    temp_dud = d1_coeffs_s(1, 3)*u_s(i, 3, b)*v_s(i, 3, b) &
+               + d1_coeffs_s(2, 3)*u_s(i, 4, b)*v_s(i, 4, b) &
+               + d1_coeffs_s(3, 3)*u(i, 1, b)*v(i, 1, b) &
+               + d1_coeffs_s(4, 3)*u(i, 2, b)*v(i, 2, b) &
+               + d1_coeffs_s(5, 3)*u(i, 3, b)*v(i, 3, b) &
+               + d1_coeffs_s(6, 3)*u(i, 4, b)*v(i, 4, b) &
+               + d1_coeffs_s(7, 3)*u(i, 5, b)*v(i, 5, b) &
+               + d1_coeffs_s(8, 3)*u(i, 6, b)*v(i, 6, b) &
+               + d1_coeffs_s(9, 3)*u(i, 7, b)*v(i, 7, b)
+    dud(i, 3, b) = d1_fw(3)*(temp_dud - d1_af(3)*dud(i, 2, b))
+    temp_d2u = d2_coeffs_s(1, 3)*u_s(i, 3, b) &
+               + d2_coeffs_s(2, 3)*u_s(i, 4, b) &
+               + d2_coeffs_s(3, 3)*u(i, 1, b) &
+               + d2_coeffs_s(4, 3)*u(i, 2, b) &
+               + d2_coeffs_s(5, 3)*u(i, 3, b) &
+               + d2_coeffs_s(6, 3)*u(i, 4, b) &
+               + d2_coeffs_s(7, 3)*u(i, 5, b) &
+               + d2_coeffs_s(8, 3)*u(i, 6, b) &
+               + d2_coeffs_s(9, 3)*u(i, 7, b)
+    d2u(i, 3, b) = d2_fw(3)*(temp_d2u - d2_af(3)*d2u(i, 2, b))
+    ! j = 4
+    temp_du = d1_coeffs_s(1, 4)*u_s(i, 4, b) &
+              + d1_coeffs_s(2, 4)*u(i, 1, b) &
+              + d1_coeffs_s(3, 4)*u(i, 2, b) &
+              + d1_coeffs_s(4, 4)*u(i, 3, b) &
+              + d1_coeffs_s(5, 4)*u(i, 4, b) &
+              + d1_coeffs_s(6, 4)*u(i, 5, b) &
+              + d1_coeffs_s(7, 4)*u(i, 6, b) &
+              + d1_coeffs_s(8, 4)*u(i, 7, b) &
+              + d1_coeffs_s(9, 4)*u(i, 8, b)
+    du(i, 4, b) = d1_fw(4)*(temp_du - d1_af(3)*du(i, 3, b))
+    temp_dud = d1_coeffs_s(1, 4)*u_s(i, 4, b)*v_s(i, 4, b) &
+               + d1_coeffs_s(2, 4)*u(i, 1, b)*v(i, 1, b) &
+               + d1_coeffs_s(3, 4)*u(i, 2, b)*v(i, 2, b) &
+               + d1_coeffs_s(4, 4)*u(i, 3, b)*v(i, 3, b) &
+               + d1_coeffs_s(5, 4)*u(i, 4, b)*v(i, 4, b) &
+               + d1_coeffs_s(6, 4)*u(i, 5, b)*v(i, 5, b) &
+               + d1_coeffs_s(7, 4)*u(i, 6, b)*v(i, 6, b) &
+               + d1_coeffs_s(8, 4)*u(i, 7, b)*v(i, 7, b) &
+               + d1_coeffs_s(9, 4)*u(i, 8, b)*v(i, 8, b)
+    dud(i, 4, b) = d1_fw(4)*(temp_dud - d1_af(3)*dud(i, 3, b))
+    temp_d2u = d2_coeffs_s(1, 4)*u_s(i, 4, b) &
+               + d2_coeffs_s(2, 4)*u(i, 1, b) &
+               + d2_coeffs_s(3, 4)*u(i, 2, b) &
+               + d2_coeffs_s(4, 4)*u(i, 3, b) &
+               + d2_coeffs_s(5, 4)*u(i, 4, b) &
+               + d2_coeffs_s(6, 4)*u(i, 5, b) &
+               + d2_coeffs_s(7, 4)*u(i, 6, b) &
+               + d2_coeffs_s(8, 4)*u(i, 7, b) &
+               + d2_coeffs_s(9, 4)*u(i, 8, b)
+    d2u(i, 4, b) = d2_fw(4)*(temp_d2u - d2_af(3)*d2u(i, 3, b))
+
+    d1_alpha = d1_af(5)
+    d2_alpha = d2_af(5)
+
+    ! store bulk coeffs in the registers
+    d1_c_m4 = d1_coeffs(1); d1_c_m3 = d1_coeffs(2)
+    d1_c_m2 = d1_coeffs(3); d1_c_m1 = d1_coeffs(4)
+    d1_c_j = d1_coeffs(5)
+    d1_c_p1 = d1_coeffs(6); d1_c_p2 = d1_coeffs(7)
+    d1_c_p3 = d1_coeffs(8); d1_c_p4 = d1_coeffs(9)
+
+    d2_c_m4 = d2_coeffs(1); d2_c_m3 = d2_coeffs(2)
+    d2_c_m2 = d2_coeffs(3); d2_c_m1 = d2_coeffs(4)
+    d2_c_j = d2_coeffs(5)
+    d2_c_p1 = d2_coeffs(6); d2_c_p2 = d2_coeffs(7)
+    d2_c_p3 = d2_coeffs(8); d2_c_p4 = d2_coeffs(9)
+
+    ! It is better to access d?(i, j - 1, b) via old_d?
+    old_du = du(i, 4, b)
+    old_dud = dud(i, 4, b)
+    old_d2u = d2u(i, 4, b)
+
+    ! Populate registers with the u and v stencils
+    u_m4 = u(i, 1, b); u_m3 = u(i, 2, b)
+    u_m2 = u(i, 3, b); u_m1 = u(i, 4, b)
+    u_j = u(i, 5, b); u_p1 = u(i, 6, b)
+    u_p2 = u(i, 7, b); u_p3 = u(i, 8, b)
+    v_m4 = v(i, 1, b); v_m3 = v(i, 2, b)
+    v_m2 = v(i, 3, b); v_m1 = v(i, 4, b)
+    v_j = v(i, 5, b); v_p1 = v(i, 6, b)
+    v_p2 = v(i, 7, b); v_p3 = v(i, 8, b)
+
+    do j = 5, n - 4
+      u_p4 = u(i, j + 4, b); v_p4 = v(i, j + 4, b)
+
+      ! du
+      temp_du = d1_c_m4*u_m4 + d1_c_m3*u_m3 + d1_c_m2*u_m2 + d1_c_m1*u_m1 &
+                + d1_c_j*u_j &
+                + d1_c_p1*u_p1 + d1_c_p2*u_p2 + d1_c_p3*u_p3 + d1_c_p4*u_p4
+      du(i, j, b) = d1_fw(j)*(temp_du - d1_alpha*old_du)
+      old_du = du(i, j, b)
+
+      ! dud
+      temp_dud = d1_c_m4*u_m4*v_m4 + d1_c_m3*u_m3*v_m3 &
+                 + d1_c_m2*u_m2*v_m2 + d1_c_m1*u_m1*v_m1 &
+                 + d1_c_j*u_j*v_j &
+                 + d1_c_p1*u_p1*v_p1 + d1_c_p2*u_p2*v_p2 &
+                 + d1_c_p3*u_p3*v_p3 + d1_c_p4*u_p4*v_p4
+      dud(i, j, b) = d1_fw(j)*(temp_dud - d1_alpha*old_dud)
+      old_dud = dud(i, j, b)
+
+      ! d2u
+      temp_d2u = d2_c_m4*u_m4 + d2_c_m3*u_m3 + d2_c_m2*u_m2 + d2_c_m1*u_m1 &
+                 + d2_c_j*u_j &
+                 + d2_c_p1*u_p1 + d2_c_p2*u_p2 + d2_c_p3*u_p3 + d2_c_p4*u_p4
+      d2u(i, j, b) = d2_fw(j)*(temp_d2u - d2_alpha*old_d2u)
+      old_d2u = d2u(i, j, b)
+
+      ! Prepare registers for the next step
+      u_m4 = u_m3; u_m3 = u_m2; u_m2 = u_m1; u_m1 = u_j
+      u_j = u_p1; u_p1 = u_p2; u_p2 = u_p3; u_p3 = u_p4
+      v_m4 = v_m3; v_m3 = v_m2; v_m2 = v_m1; v_m1 = v_j
+      v_j = v_p1; v_p1 = v_p2; v_p2 = v_p3; v_p3 = v_p4
+    end do
+
+    j = n - 3
+    temp_du = d1_coeffs_e(1, 1)*u(i, j - 4, b) &
+              + d1_coeffs_e(2, 1)*u(i, j - 3, b) &
+              + d1_coeffs_e(3, 1)*u(i, j - 2, b) &
+              + d1_coeffs_e(4, 1)*u(i, j - 1, b) &
+              + d1_coeffs_e(5, 1)*u(i, j, b) &
+              + d1_coeffs_e(6, 1)*u(i, j + 1, b) &
+              + d1_coeffs_e(7, 1)*u(i, j + 2, b) &
+              + d1_coeffs_e(8, 1)*u(i, j + 3, b) &
+              + d1_coeffs_e(9, 1)*u_e(i, 1, b)
+    du(i, j, b) = d1_fw(j)*(temp_du - d1_af(j)*du(i, j - 1, b))
+    temp_dud = d1_coeffs_e(1, 1)*u(i, j - 4, b)*v(i, j - 4, b) &
+               + d1_coeffs_e(2, 1)*u(i, j - 3, b)*v(i, j - 3, b) &
+               + d1_coeffs_e(3, 1)*u(i, j - 2, b)*v(i, j - 2, b) &
+               + d1_coeffs_e(4, 1)*u(i, j - 1, b)*v(i, j - 1, b) &
+               + d1_coeffs_e(5, 1)*u(i, j, b)*v(i, j, b) &
+               + d1_coeffs_e(6, 1)*u(i, j + 1, b)*v(i, j + 1, b) &
+               + d1_coeffs_e(7, 1)*u(i, j + 2, b)*v(i, j + 2, b) &
+               + d1_coeffs_e(8, 1)*u(i, j + 3, b)*v(i, j + 3, b) &
+               + d1_coeffs_e(9, 1)*u_e(i, 1, b)*v_e(i, 1, b)
+    dud(i, j, b) = d1_fw(j)*(temp_dud - d1_af(j)*dud(i, j - 1, b))
+    temp_d2u = d1_coeffs_e(1, 1)*u(i, j - 4, b) &
+               + d2_coeffs_e(2, 1)*u(i, j - 3, b) &
+               + d2_coeffs_e(3, 1)*u(i, j - 2, b) &
+               + d2_coeffs_e(4, 1)*u(i, j - 1, b) &
+               + d2_coeffs_e(5, 1)*u(i, j, b) &
+               + d2_coeffs_e(6, 1)*u(i, j + 1, b) &
+               + d2_coeffs_e(7, 1)*u(i, j + 2, b) &
+               + d2_coeffs_e(8, 1)*u(i, j + 3, b) &
+               + d2_coeffs_e(9, 1)*u_e(i, 1, b)
+    d2u(i, j, b) = d2_fw(j)*(temp_d2u - d2_af(j)*d2u(i, j - 1, b))
+    j = n - 2
+    temp_du = d1_coeffs_e(1, 2)*u(i, j - 4, b) &
+              + d1_coeffs_e(2, 2)*u(i, j - 3, b) &
+              + d1_coeffs_e(3, 2)*u(i, j - 2, b) &
+              + d1_coeffs_e(4, 2)*u(i, j - 1, b) &
+              + d1_coeffs_e(5, 2)*u(i, j, b) &
+              + d1_coeffs_e(6, 2)*u(i, j + 1, b) &
+              + d1_coeffs_e(7, 2)*u(i, j + 2, b) &
+              + d1_coeffs_e(8, 2)*u_e(i, 1, b) &
+              + d1_coeffs_e(9, 2)*u_e(i, 2, b)
+    du(i, j, b) = d1_fw(j)*(temp_du - d1_af(j)*du(i, j - 1, b))
+    temp_dud = d1_coeffs_e(1, 2)*u(i, j - 4, b)*v(i, j - 4, b) &
+               + d1_coeffs_e(2, 2)*u(i, j - 3, b)*v(i, j - 3, b) &
+               + d1_coeffs_e(3, 2)*u(i, j - 2, b)*v(i, j - 2, b) &
+               + d1_coeffs_e(4, 2)*u(i, j - 1, b)*v(i, j - 1, b) &
+               + d1_coeffs_e(5, 2)*u(i, j, b)*v(i, j, b) &
+               + d1_coeffs_e(6, 2)*u(i, j + 1, b)*v(i, j + 1, b) &
+               + d1_coeffs_e(7, 2)*u(i, j + 2, b)*v(i, j + 2, b) &
+               + d1_coeffs_e(8, 2)*u_e(i, 1, b)*v_e(i, 1, b) &
+               + d1_coeffs_e(9, 2)*u_e(i, 2, b)*v_e(i, 2, b)
+    dud(i, j, b) = d1_fw(j)*(temp_dud - d1_af(j)*dud(i, j - 1, b))
+    temp_d2u = d2_coeffs_e(1, 2)*u(i, j - 4, b) &
+               + d2_coeffs_e(2, 2)*u(i, j - 3, b) &
+               + d2_coeffs_e(3, 2)*u(i, j - 2, b) &
+               + d2_coeffs_e(4, 2)*u(i, j - 1, b) &
+               + d2_coeffs_e(5, 2)*u(i, j, b) &
+               + d2_coeffs_e(6, 2)*u(i, j + 1, b) &
+               + d2_coeffs_e(7, 2)*u(i, j + 2, b) &
+               + d2_coeffs_e(8, 2)*u_e(i, 1, b) &
+               + d2_coeffs_e(9, 2)*u_e(i, 2, b)
+    d2u(i, j, b) = d2_fw(j)*(temp_d2u - d2_af(j)*d2u(i, j - 1, b))
+    j = n - 1
+    temp_du = d1_coeffs_e(1, 3)*u(i, j - 4, b) &
+              + d1_coeffs_e(2, 3)*u(i, j - 3, b) &
+              + d1_coeffs_e(3, 3)*u(i, j - 2, b) &
+              + d1_coeffs_e(4, 3)*u(i, j - 1, b) &
+              + d1_coeffs_e(5, 3)*u(i, j, b) &
+              + d1_coeffs_e(6, 3)*u(i, j + 1, b) &
+              + d1_coeffs_e(7, 3)*u_e(i, 1, b) &
+              + d1_coeffs_e(8, 3)*u_e(i, 2, b) &
+              + d1_coeffs_e(9, 3)*u_e(i, 3, b)
+    du(i, j, b) = d1_fw(j)*(temp_du - d1_af(j)*du(i, j - 1, b))
+    temp_dud = d1_coeffs_e(1, 3)*u(i, j - 4, b)*v(i, j - 4, b) &
+               + d1_coeffs_e(2, 3)*u(i, j - 3, b)*v(i, j - 3, b) &
+               + d1_coeffs_e(3, 3)*u(i, j - 2, b)*v(i, j - 2, b) &
+               + d1_coeffs_e(4, 3)*u(i, j - 1, b)*v(i, j - 1, b) &
+               + d1_coeffs_e(5, 3)*u(i, j, b)*v(i, j, b) &
+               + d1_coeffs_e(6, 3)*u(i, j + 1, b)*v(i, j + 1, b) &
+               + d1_coeffs_e(7, 3)*u_e(i, 1, b)*v_e(i, 1, b) &
+               + d1_coeffs_e(8, 3)*u_e(i, 2, b)*v_e(i, 2, b) &
+               + d1_coeffs_e(9, 3)*u_e(i, 3, b)*v_e(i, 3, b)
+    dud(i, j, b) = d1_fw(j)*(temp_dud - d1_af(j)*dud(i, j - 1, b))
+    temp_d2u = d2_coeffs_e(1, 3)*u(i, j - 4, b) &
+               + d2_coeffs_e(2, 3)*u(i, j - 3, b) &
+               + d2_coeffs_e(3, 3)*u(i, j - 2, b) &
+               + d2_coeffs_e(4, 3)*u(i, j - 1, b) &
+               + d2_coeffs_e(5, 3)*u(i, j, b) &
+               + d2_coeffs_e(6, 3)*u(i, j + 1, b) &
+               + d2_coeffs_e(7, 3)*u_e(i, 1, b) &
+               + d2_coeffs_e(8, 3)*u_e(i, 2, b) &
+               + d2_coeffs_e(9, 3)*u_e(i, 3, b)
+    d2u(i, j, b) = d2_fw(j)*(temp_d2u - d2_af(j)*d2u(i, j - 1, b))
+    j = n
+    temp_du = d1_coeffs_e(1, 4)*u(i, j - 4, b) &
+              + d1_coeffs_e(2, 4)*u(i, j - 3, b) &
+              + d1_coeffs_e(3, 4)*u(i, j - 2, b) &
+              + d1_coeffs_e(4, 4)*u(i, j - 1, b) &
+              + d1_coeffs_e(5, 4)*u(i, j, b) &
+              + d1_coeffs_e(6, 4)*u_e(i, 1, b) &
+              + d1_coeffs_e(7, 4)*u_e(i, 2, b) &
+              + d1_coeffs_e(8, 4)*u_e(i, 3, b) &
+              + d1_coeffs_e(9, 4)*u_e(i, 4, b)
+    du(i, j, b) = d1_fw(j)*(temp_du - d1_af(j)*du(i, j - 1, b))
+    temp_dud = d1_coeffs_e(1, 4)*u(i, j - 4, b)*v(i, j - 4, b) &
+               + d1_coeffs_e(2, 4)*u(i, j - 3, b)*v(i, j - 3, b) &
+               + d1_coeffs_e(3, 4)*u(i, j - 2, b)*v(i, j - 2, b) &
+               + d1_coeffs_e(4, 4)*u(i, j - 1, b)*v(i, j - 1, b) &
+               + d1_coeffs_e(5, 4)*u(i, j, b)*v(i, j, b) &
+               + d1_coeffs_e(6, 4)*u_e(i, 1, b)*v_e(i, 1, b) &
+               + d1_coeffs_e(7, 4)*u_e(i, 2, b)*v_e(i, 2, b) &
+               + d1_coeffs_e(8, 4)*u_e(i, 3, b)*v_e(i, 3, b) &
+               + d1_coeffs_e(9, 4)*u_e(i, 4, b)*v_e(i, 4, b)
+    dud(i, j, b) = d1_fw(j)*(temp_dud - d1_af(j)*dud(i, j - 1, b))
+    temp_d2u = d2_coeffs_e(1, 4)*u(i, j - 4, b) &
+               + d2_coeffs_e(2, 4)*u(i, j - 3, b) &
+               + d2_coeffs_e(3, 4)*u(i, j - 2, b) &
+               + d2_coeffs_e(4, 4)*u(i, j - 1, b) &
+               + d2_coeffs_e(5, 4)*u(i, j, b) &
+               + d2_coeffs_e(6, 4)*u_e(i, 1, b) &
+               + d2_coeffs_e(7, 4)*u_e(i, 2, b) &
+               + d2_coeffs_e(8, 4)*u_e(i, 3, b) &
+               + d2_coeffs_e(9, 4)*u_e(i, 4, b)
+    d2u(i, j, b) = d2_fw(j)*(temp_d2u - d2_af(j)*d2u(i, j - 1, b))
+
+    send_du_e(i, 1, b) = du(i, n, b)
+    send_dud_e(i, 1, b) = dud(i, n, b)
+    send_d2u_e(i, 1, b) = d2u(i, n, b)
+
+    ! Backward pass of the hybrid algorithm
+    do j = n - 2, 2, -1
+      du(i, j, b) = du(i, j, b) - d1_bw(j)*du(i, j + 1, b)
+      dud(i, j, b) = dud(i, j, b) - d1_bw(j)*dud(i, j + 1, b)
+      d2u(i, j, b) = d2u(i, j, b) - d2_bw(j)*d2u(i, j + 1, b)
+    end do
+    du(i, 1, b) = d1_last_r*(du(i, 1, b) - d1_bw(1)*du(i, 2, b))
+    dud(i, 1, b) = d1_last_r*(dud(i, 1, b) - d1_bw(1)*dud(i, 2, b))
+    d2u(i, 1, b) = d2_last_r*(d2u(i, 1, b) - d2_bw(1)*d2u(i, 2, b))
+
+    send_du_s(i, 1, b) = du(i, 1, b)
+    send_dud_s(i, 1, b) = dud(i, 1, b)
+    send_d2u_s(i, 1, b) = d2u(i, 1, b)
+
+  end subroutine transeq_3fused_dist
+
+  attributes(global) subroutine transeq_3fused_subs( &
+    r_u, conv, du, dud, d2u, &
+    recv_du_s, recv_du_e, recv_dud_s, recv_dud_e, recv_d2u_s, recv_d2u_e, &
+    d1_sa, d1_sc, d2_sa, d2_sc, n, nu &
+    )
+    implicit none
+
+    ! Arguments
+    real(dp), device, intent(out), dimension(:, :, :) :: r_u
+    real(dp), device, intent(in), dimension(:, :, :) :: conv, du, dud, d2u
+    real(dp), device, intent(in), dimension(:, :, :) :: &
+      recv_du_s, recv_du_e, recv_dud_s, recv_dud_e, recv_d2u_s, recv_d2u_e
+    real(dp), device, intent(in), dimension(:) :: d1_sa, d1_sc, d2_sa, d2_sc
+    integer, value, intent(in) :: n
+    real(dp), value, intent(in) :: nu
+
+    ! Local variables
+    integer :: i, j, b
+    real(dp) :: ur, bl, recp
+    real(dp) :: du_temp, dud_temp, d2u_temp
+    real(dp) :: du_s, du_e, dud_s, dud_e, d2u_s, d2u_e
+
+    i = threadIdx%x
+    b = blockIdx%x
+
+    ! A small trick we do here is valid for symmetric Toeplitz matrices.
+    ! In our case our matrices satisfy this criteria in the (5:n-4) region
+    ! and as long as a rank has around at least 20 entries the assumptions
+    ! we make here are perfectly valid.
+
+    ! bl is the bottom left entry in the 2x2 matrix
+    ! ur is the upper right entry in the 2x2 matrix
+
+    ! Start
+    ! At the start we have the 'bl', and assume 'ur'
+    ! first derivative
+    bl = d1_sa(1)
+    ur = d1_sa(1)
+    recp = 1._dp/(1._dp - ur*bl)
+
+    du_s = recp*(du(i, 1, b) - bl*recv_du_s(i, 1, b))
+    dud_s = recp*(dud(i, 1, b) - bl*recv_dud_s(i, 1, b))
+
+    ! second derivative
+    bl = d2_sa(1)
+    ur = d2_sa(1)
+    recp = 1._dp/(1._dp - ur*bl)
+
+    d2u_s = recp*(d2u(i, 1, b) - bl*recv_d2u_s(i, 1, b))
+
+    ! End
+    ! At the end we have the 'ur', and assume 'bl'
+    ! first derivative
+    bl = d1_sc(n)
+    ur = d1_sc(n)
+    recp = 1._dp/(1._dp - ur*bl)
+
+    du_e = recp*(du(i, n, b) - ur*recv_du_e(i, 1, b))
+    dud_e = recp*(dud(i, n, b) - ur*recv_dud_e(i, 1, b))
+
+    ! second derivative
+    bl = d2_sc(n)
+    ur = d2_sc(n)
+    recp = 1._dp/(1._dp - ur*bl)
+
+    d2u_e = recp*(d2u(i, n, b) - ur*recv_d2u_e(i, 1, b))
+
+    ! final substitution
+    r_u(i, 1, b) = -0.5_dp*(conv(i, 1, b)*du_s + dud_s) + nu*d2u_s
+    do j = 2, n - 1
+      du_temp = (du(i, j, b) - d1_sa(j)*du_s - d1_sc(j)*du_e)
+      dud_temp = (dud(i, j, b) - d1_sa(j)*dud_s - d1_sc(j)*dud_e)
+      d2u_temp = (d2u(i, j, b) - d2_sa(j)*d2u_s - d2_sc(j)*d2u_e)
+      r_u(i, j, b) = -0.5_dp*(conv(i, j, b)*du_temp + dud_temp) &
+                     + nu*d2u_temp
+    end do
+    r_u(i, n, b) = -0.5_dp*(conv(i, n, b)*du_e + dud_e) + nu*d2u_e
+
+  end subroutine transeq_3fused_subs
+
+end module m_cuda_kernels_dist
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/distributed.f90~2.html b/api/sourcefile/distributed.f90~2.html new file mode 100644 index 000000000..a8caba6ef --- /dev/null +++ b/api/sourcefile/distributed.f90~2.html @@ -0,0 +1,442 @@ + + + + + + + + + + + + + distributed.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

distributed.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_omp_kernels_dist
+  use omp_lib
+
+  use m_common, only: dp
+  use m_omp_common, only: SZ
+
+  implicit none
+
+contains
+
+  subroutine der_univ_dist( &
+    du, send_u_s, send_u_e, u, u_s, u_e, coeffs_s, coeffs_e, coeffs, n, &
+    ffr, fbc, faf &
+    )
+    implicit none
+
+    ! Arguments
+    real(dp), intent(out), dimension(:, :) :: du, send_u_s, send_u_e
+    real(dp), intent(in), dimension(:, :) :: u, u_s, u_e
+    real(dp), intent(in), dimension(:, :) :: coeffs_s, coeffs_e ! start/end
+    real(dp), intent(in), dimension(:) :: coeffs
+    integer, intent(in) :: n
+    real(dp), intent(in), dimension(:) :: ffr, fbc, faf
+
+    ! Local variables
+    integer :: i, j!, b
+
+    real(dp) :: c_m4, c_m3, c_m2, c_m1, c_j, c_p1, c_p2, c_p3, c_p4, &
+                alpha, last_r
+
+    ! store bulk coeffs in the registers
+    c_m4 = coeffs(1); c_m3 = coeffs(2); c_m2 = coeffs(3); c_m1 = coeffs(4)
+    c_j = coeffs(5)
+    c_p1 = coeffs(6); c_p2 = coeffs(7); c_p3 = coeffs(8); c_p4 = coeffs(9)
+    last_r = ffr(1)
+
+    !$omp simd
+    do i = 1, SZ
+      du(i, 1) = coeffs_s(1, 1)*u_s(i, 1) &
+                 + coeffs_s(2, 1)*u_s(i, 2) &
+                 + coeffs_s(3, 1)*u_s(i, 3) &
+                 + coeffs_s(4, 1)*u_s(i, 4) &
+                 + coeffs_s(5, 1)*u(i, 1) &
+                 + coeffs_s(6, 1)*u(i, 2) &
+                 + coeffs_s(7, 1)*u(i, 3) &
+                 + coeffs_s(8, 1)*u(i, 4) &
+                 + coeffs_s(9, 1)*u(i, 5)
+      du(i, 1) = du(i, 1)*faf(1)
+      du(i, 2) = coeffs_s(1, 2)*u_s(i, 2) &
+                 + coeffs_s(2, 2)*u_s(i, 3) &
+                 + coeffs_s(3, 2)*u_s(i, 4) &
+                 + coeffs_s(4, 2)*u(i, 1) &
+                 + coeffs_s(5, 2)*u(i, 2) &
+                 + coeffs_s(6, 2)*u(i, 3) &
+                 + coeffs_s(7, 2)*u(i, 4) &
+                 + coeffs_s(8, 2)*u(i, 5) &
+                 + coeffs_s(9, 2)*u(i, 6)
+      du(i, 2) = du(i, 2)*faf(2)
+      du(i, 3) = coeffs_s(1, 3)*u_s(i, 3) &
+                 + coeffs_s(2, 3)*u_s(i, 4) &
+                 + coeffs_s(3, 3)*u(i, 1) &
+                 + coeffs_s(4, 3)*u(i, 2) &
+                 + coeffs_s(5, 3)*u(i, 3) &
+                 + coeffs_s(6, 3)*u(i, 4) &
+                 + coeffs_s(7, 3)*u(i, 5) &
+                 + coeffs_s(8, 3)*u(i, 6) &
+                 + coeffs_s(9, 3)*u(i, 7)
+      du(i, 3) = ffr(3)*(du(i, 3) - faf(3)*du(i, 2))
+      du(i, 4) = coeffs_s(1, 4)*u_s(i, 4) &
+                 + coeffs_s(2, 4)*u(i, 1) &
+                 + coeffs_s(3, 4)*u(i, 2) &
+                 + coeffs_s(4, 4)*u(i, 3) &
+                 + coeffs_s(5, 4)*u(i, 4) &
+                 + coeffs_s(6, 4)*u(i, 5) &
+                 + coeffs_s(7, 4)*u(i, 6) &
+                 + coeffs_s(8, 4)*u(i, 7) &
+                 + coeffs_s(9, 4)*u(i, 8)
+      du(i, 4) = ffr(4)*(du(i, 4) - faf(4)*du(i, 3))
+    end do
+    !$omp end simd
+
+    ! alpha is always the same in the bulk region for us
+    alpha = faf(5)
+
+    do j = 5, n - 4
+      !$omp simd
+      do i = 1, SZ
+        du(i, j) = c_m4*u(i, j - 4) + c_m3*u(i, j - 3) &
+                   + c_m2*u(i, j - 2) + c_m1*u(i, j - 1) &
+                   + c_j*u(i, j) &
+                   + c_p1*u(i, j + 1) + c_p2*u(i, j + 2) &
+                   + c_p3*u(i, j + 3) + c_p4*u(i, j + 4)
+        du(i, j) = ffr(j)*(du(i, j) - alpha*du(i, j - 1))
+      end do
+      !$omp end simd
+    end do
+
+    !$omp simd
+    do i = 1, SZ
+      j = n - 3
+      du(i, j) = coeffs_e(1, 1)*u(i, j - 4) &
+                 + coeffs_e(2, 1)*u(i, j - 3) &
+                 + coeffs_e(3, 1)*u(i, j - 2) &
+                 + coeffs_e(4, 1)*u(i, j - 1) &
+                 + coeffs_e(5, 1)*u(i, j) &
+                 + coeffs_e(6, 1)*u(i, j + 1) &
+                 + coeffs_e(7, 1)*u(i, j + 2) &
+                 + coeffs_e(8, 1)*u(i, j + 3) &
+                 + coeffs_e(9, 1)*u_e(i, 1)
+      du(i, j) = ffr(j)*(du(i, j) - faf(j)*du(i, j - 1))
+      j = n - 2
+      du(i, j) = coeffs_e(1, 2)*u(i, j - 4) &
+                 + coeffs_e(2, 2)*u(i, j - 3) &
+                 + coeffs_e(3, 2)*u(i, j - 2) &
+                 + coeffs_e(4, 2)*u(i, j - 1) &
+                 + coeffs_e(5, 2)*u(i, j) &
+                 + coeffs_e(6, 2)*u(i, j + 1) &
+                 + coeffs_e(7, 2)*u(i, j + 2) &
+                 + coeffs_e(8, 2)*u_e(i, 1) &
+                 + coeffs_e(9, 2)*u_e(i, 2)
+      du(i, j) = ffr(j)*(du(i, j) - faf(j)*du(i, j - 1))
+      j = n - 1
+      du(i, j) = coeffs_e(1, 3)*u(i, j - 4) &
+                 + coeffs_e(2, 3)*u(i, j - 3) &
+                 + coeffs_e(3, 3)*u(i, j - 2) &
+                 + coeffs_e(4, 3)*u(i, j - 1) &
+                 + coeffs_e(5, 3)*u(i, j) &
+                 + coeffs_e(6, 3)*u(i, j + 1) &
+                 + coeffs_e(7, 3)*u_e(i, 1) &
+                 + coeffs_e(8, 3)*u_e(i, 2) &
+                 + coeffs_e(9, 3)*u_e(i, 3)
+      du(i, j) = ffr(j)*(du(i, j) - faf(j)*du(i, j - 1))
+      j = n
+      du(i, j) = coeffs_e(1, 4)*u(i, j - 4) &
+                 + coeffs_e(2, 4)*u(i, j - 3) &
+                 + coeffs_e(3, 4)*u(i, j - 2) &
+                 + coeffs_e(4, 4)*u(i, j - 1) &
+                 + coeffs_e(5, 4)*u(i, j) &
+                 + coeffs_e(6, 4)*u_e(i, 1) &
+                 + coeffs_e(7, 4)*u_e(i, 2) &
+                 + coeffs_e(8, 4)*u_e(i, 3) &
+                 + coeffs_e(9, 4)*u_e(i, 4)
+      du(i, j) = ffr(j)*(du(i, j) - faf(j)*du(i, j - 1))
+    end do
+    !$omp end simd
+
+    !$omp simd
+    do i = 1, SZ
+      send_u_e(i, 1) = du(i, n)
+    end do
+    !$omp end simd
+
+    ! Backward pass of the hybrid algorithm
+    do j = n - 2, 2, -1
+      !$omp simd
+      do i = 1, SZ
+        du(i, j) = du(i, j) - fbc(j)*du(i, j + 1)
+      end do
+      !$omp end simd
+    end do
+    !$omp simd
+    do i = 1, SZ
+      du(i, 1) = last_r*(du(i, 1) - fbc(1)*du(i, 2))
+      send_u_s(i, 1) = du(i, 1)
+    end do
+    !$omp end simd
+
+  end subroutine der_univ_dist
+
+  subroutine der_univ_subs(du, recv_u_s, recv_u_e, n, dist_sa, dist_sc)
+    implicit none
+
+    ! Arguments
+    real(dp), intent(out), dimension(:, :) :: du
+    real(dp), intent(in), dimension(:, :) :: recv_u_s, recv_u_e
+    real(dp), intent(in), dimension(:) :: dist_sa, dist_sc
+    integer, intent(in) :: n
+
+    ! Local variables
+    integer :: i, j!, b
+    real(dp) :: ur, bl, recp
+    real(dp), dimension(SZ) :: du_s, du_e
+
+    !$omp simd
+    do i = 1, SZ
+      ! A small trick we do here is valid for symmetric Toeplitz matrices.
+      ! In our case our matrices satisfy this criteria in the (5:n-4) region
+      ! and as long as a rank has around at least 20 entries the assumptions
+      ! we make here are perfectly valid.
+
+      ! bl is the bottom left entry in the 2x2 matrix
+      ! ur is the upper right entry in the 2x2 matrix
+
+      ! Start
+      ! At the start we have the 'bl', and assume 'ur'
+      bl = dist_sa(1)
+      ur = dist_sa(1)
+      recp = 1._dp/(1._dp - ur*bl)
+      du_s(i) = recp*(du(i, 1) - bl*recv_u_s(i, 1))
+
+      ! End
+      ! At the end we have the 'ur', and assume 'bl'
+      bl = dist_sc(n)
+      ur = dist_sc(n)
+      recp = 1._dp/(1._dp - ur*bl)
+      du_e(i) = recp*(du(i, n) - ur*recv_u_e(i, 1))
+    end do
+    !$omp end simd
+
+    !$omp simd
+    do i = 1, SZ
+      du(i, 1) = du_s(i)
+    end do
+    !$omp end simd
+    do j = 2, n - 1
+      !$omp simd
+      do i = 1, SZ
+        du(i, j) = (du(i, j) - dist_sa(j)*du_s(i) - dist_sc(j)*du_e(i))
+      end do
+      !$omp end simd
+    end do
+    !$omp simd
+    do i = 1, SZ
+      du(i, n) = du_e(i)
+    end do
+    !$omp end simd
+
+  end subroutine der_univ_subs
+
+end module m_omp_kernels_dist
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/exec_dist.f90.html b/api/sourcefile/exec_dist.f90.html new file mode 100644 index 000000000..5d036f811 --- /dev/null +++ b/api/sourcefile/exec_dist.f90.html @@ -0,0 +1,337 @@ + + + + + + + + + + + + + exec_dist.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

exec_dist.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_exec_dist
+  use cudafor
+  use mpi
+
+  use m_common, only: dp
+  use m_cuda_common, only: SZ
+  use m_cuda_kernels_dist, only: der_univ_dist, der_univ_subs, &
+                                 transeq_3fused_dist, transeq_3fused_subs
+  use m_cuda_sendrecv, only: sendrecv_fields, sendrecv_3fields
+  use m_cuda_tdsops, only: cuda_tdsops_t
+
+  implicit none
+
+contains
+
+  subroutine exec_dist_tds_compact( &
+    du, u, u_recv_s, u_recv_e, du_send_s, du_send_e, du_recv_s, du_recv_e, &
+    tdsops, nproc, pprev, pnext, blocks, threads &
+    )
+    implicit none
+
+    ! du = d(u)
+    real(dp), device, dimension(:, :, :), intent(out) :: du
+    real(dp), device, dimension(:, :, :), intent(in) :: u, u_recv_s, u_recv_e
+
+    ! The ones below are intent(out) just so that we can write data in them,
+    ! not because we actually need the data they store later where this
+    ! subroutine is called. We absolutely don't care the data they pass back
+    real(dp), device, dimension(:, :, :), intent(out) :: &
+      du_send_s, du_send_e, du_recv_s, du_recv_e
+
+    type(cuda_tdsops_t), intent(in) :: tdsops
+    integer, intent(in) :: nproc, pprev, pnext
+    type(dim3), intent(in) :: blocks, threads
+
+    integer :: n_data
+
+    n_data = SZ*1*blocks%x
+
+    call der_univ_dist<<<blocks, threads>>>( & !&
+      du, du_send_s, du_send_e, u, u_recv_s, u_recv_e, &
+      tdsops%coeffs_s_dev, tdsops%coeffs_e_dev, tdsops%coeffs_dev, &
+      tdsops%tds_n, tdsops%dist_fw_dev, tdsops%dist_bw_dev, &
+      tdsops%dist_af_dev &
+      )
+
+    ! halo exchange for 2x2 systems
+    call sendrecv_fields(du_recv_s, du_recv_e, du_send_s, du_send_e, &
+                         n_data, nproc, pprev, pnext)
+
+    call der_univ_subs<<<blocks, threads>>>( & !&
+      du, du_recv_s, du_recv_e, &
+      tdsops%tds_n, tdsops%dist_sa_dev, tdsops%dist_sc_dev &
+      )
+
+  end subroutine exec_dist_tds_compact
+
+  subroutine exec_dist_transeq_3fused( &
+    r_u, u, u_recv_s, u_recv_e, v, v_recv_s, v_recv_e, &
+    du, dud, d2u, &
+    du_send_s, du_send_e, du_recv_s, du_recv_e, &
+    dud_send_s, dud_send_e, dud_recv_s, dud_recv_e, &
+    d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e, &
+    der1st, der2nd, nu, nproc, pprev, pnext, blocks, threads &
+    )
+    implicit none
+
+    ! r_u = -1/2*(v*d1(u) + d1(u*v)) + nu*d2(u)
+    real(dp), device, dimension(:, :, :), intent(out) :: r_u
+    real(dp), device, dimension(:, :, :), intent(in) :: u, u_recv_s, u_recv_e
+    real(dp), device, dimension(:, :, :), intent(in) :: v, v_recv_s, v_recv_e
+
+    ! The ones below are intent(out) just so that we can write data in them,
+    ! not because we actually need the data they store later where this
+    ! subroutine is called. We absolutely don't care the data they pass back
+    real(dp), device, dimension(:, :, :), intent(out) :: du, dud, d2u
+    real(dp), device, dimension(:, :, :), intent(out) :: &
+      du_send_s, du_send_e, du_recv_s, du_recv_e, &
+      dud_send_s, dud_send_e, dud_recv_s, dud_recv_e, &
+      d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e
+
+    type(cuda_tdsops_t), intent(in) :: der1st, der2nd
+    real(dp), intent(in) :: nu
+    integer, intent(in) :: nproc, pprev, pnext
+    type(dim3), intent(in) :: blocks, threads
+
+    integer :: n_data
+
+    n_data = SZ*1*blocks%x
+
+    call transeq_3fused_dist<<<blocks, threads>>>( & !&
+      du, dud, d2u, &
+      du_send_s, du_send_e, &
+      dud_send_s, dud_send_e, &
+      d2u_send_s, d2u_send_e, &
+      u, u_recv_s, u_recv_e, &
+      v, v_recv_s, v_recv_e, der1st%tds_n, &
+      der1st%coeffs_s_dev, der1st%coeffs_e_dev, der1st%coeffs_dev, &
+      der1st%dist_fw_dev, der1st%dist_bw_dev, der1st%dist_af_dev, &
+      der2nd%coeffs_s_dev, der2nd%coeffs_e_dev, der2nd%coeffs_dev, &
+      der2nd%dist_fw_dev, der2nd%dist_bw_dev, der2nd%dist_af_dev &
+      )
+
+    ! halo exchange for 2x2 systems
+    call sendrecv_3fields( &
+      du_recv_s, du_recv_e, dud_recv_s, dud_recv_e, &
+      d2u_recv_s, d2u_recv_e, &
+      du_send_s, du_send_e, dud_send_s, dud_send_e, &
+      d2u_send_s, d2u_send_e, &
+      n_data, nproc, pprev, pnext &
+      )
+
+    call transeq_3fused_subs<<<blocks, threads>>>( & !&
+      r_u, v, du, dud, d2u, &
+      du_recv_s, du_recv_e, &
+      dud_recv_s, dud_recv_e, &
+      d2u_recv_s, d2u_recv_e, &
+      der1st%dist_sa_dev, der1st%dist_sc_dev, &
+      der2nd%dist_sa_dev, der2nd%dist_sc_dev, &
+      der1st%tds_n, nu &
+      )
+
+  end subroutine exec_dist_transeq_3fused
+
+end module m_cuda_exec_dist
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/exec_dist.f90~2.html b/api/sourcefile/exec_dist.f90~2.html new file mode 100644 index 000000000..2820e5c23 --- /dev/null +++ b/api/sourcefile/exec_dist.f90~2.html @@ -0,0 +1,403 @@ + + + + + + + + + + + + + exec_dist.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

exec_dist.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_omp_exec_dist
+  use mpi
+
+  use m_common, only: dp, VERT
+  use m_omp_common, only: SZ
+  use m_omp_kernels_dist, only: der_univ_dist, der_univ_subs
+  use m_tdsops, only: tdsops_t
+  use m_omp_sendrecv, only: sendrecv_fields
+
+  implicit none
+
+contains
+
+  subroutine exec_dist_tds_compact( &
+    du, u, u_recv_s, u_recv_e, du_send_s, du_send_e, du_recv_s, du_recv_e, &
+    tdsops, nproc, pprev, pnext, n_groups)
+    implicit none
+
+    ! du = d(u)
+    real(dp), dimension(:, :, :), intent(out) :: du
+    real(dp), dimension(:, :, :), intent(in) :: u, u_recv_s, u_recv_e
+
+    ! The ones below are intent(out) just so that we can write data in them,
+    ! not because we actually need the data they store later where this
+    ! subroutine is called. We absolutely don't care about the data they pass back
+    real(dp), dimension(:, :, :), intent(out) :: &
+      du_send_s, du_send_e, du_recv_s, du_recv_e
+
+    type(tdsops_t), intent(in) :: tdsops
+    integer, intent(in) :: nproc, pprev, pnext
+    integer, intent(in) :: n_groups
+
+    integer :: n_data
+    integer :: k
+
+    n_data = SZ*n_groups
+
+    !$omp parallel do
+    do k = 1, n_groups
+      call der_univ_dist( &
+        du(:, :, k), du_send_s(:, :, k), du_send_e(:, :, k), u(:, :, k), &
+        u_recv_s(:, :, k), u_recv_e(:, :, k), &
+        tdsops%coeffs_s, tdsops%coeffs_e, tdsops%coeffs, tdsops%tds_n, &
+        tdsops%dist_fw, tdsops%dist_bw, tdsops%dist_af &
+        )
+    end do
+    !$omp end parallel do
+
+    ! halo exchange for 2x2 systems
+    call sendrecv_fields(du_recv_s, du_recv_e, du_send_s, du_send_e, &
+                         n_data, nproc, pprev, pnext)
+
+    !$omp parallel do
+    do k = 1, n_groups
+      call der_univ_subs(du(:, :, k), &
+                         du_recv_s(:, :, k), du_recv_e(:, :, k), &
+                         tdsops%tds_n, tdsops%dist_sa, tdsops%dist_sc)
+    end do
+    !$omp end parallel do
+
+  end subroutine exec_dist_tds_compact
+
+  subroutine exec_dist_transeq_compact( &
+    rhs, du, dud, d2u, &
+    du_send_s, du_send_e, du_recv_s, du_recv_e, &
+    dud_send_s, dud_send_e, dud_recv_s, dud_recv_e, &
+    d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e, &
+    u, u_recv_s, u_recv_e, &
+    v, v_recv_s, v_recv_e, &
+    tdsops_du, tdsops_dud, tdsops_d2u, nu, nproc, pprev, pnext, n_groups)
+
+    implicit none
+
+    ! du = d(u)
+    real(dp), dimension(:, :, :), intent(out) :: rhs, du, dud, d2u
+
+    ! The ones below are intent(out) just so that we can write data in them,
+    ! not because we actually need the data they store later where this
+    ! subroutine is called. We absolutely don't care about the data they pass back
+    real(dp), dimension(:, :, :), intent(out) :: &
+      du_send_s, du_send_e, du_recv_s, du_recv_e
+    real(dp), dimension(:, :, :), intent(out) :: &
+      dud_send_s, dud_send_e, dud_recv_s, dud_recv_e
+    real(dp), dimension(:, :, :), intent(out) :: &
+      d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e
+
+    real(dp), dimension(:, :, :), intent(in) :: u, u_recv_s, u_recv_e
+    real(dp), dimension(:, :, :), intent(in) :: v, v_recv_s, v_recv_e
+
+    type(tdsops_t), intent(in) :: tdsops_du, tdsops_dud, tdsops_d2u
+    real(dp), intent(in) :: nu
+    integer, intent(in) :: nproc, pprev, pnext
+    integer, intent(in) :: n_groups
+
+    real(dp), dimension(:, :), allocatable :: ud, ud_recv_s, ud_recv_e
+
+    integer :: n_data, n_halo
+    integer :: k, i, j, n
+
+    ! TODO: don't hardcode n_halo
+    n_halo = 4
+    n = tdsops_du%tds_n
+    n_data = SZ*n_groups
+
+    allocate (ud(SZ, n))
+    allocate (ud_recv_e(SZ, n_halo))
+    allocate (ud_recv_s(SZ, n_halo))
+
+    !$omp parallel do private(ud, ud_recv_e, ud_recv_s)
+    do k = 1, n_groups
+      call der_univ_dist( &
+        du(:, :, k), du_send_s(:, :, k), du_send_e(:, :, k), u(:, :, k), &
+        u_recv_s(:, :, k), u_recv_e(:, :, k), &
+        tdsops_du%coeffs_s, tdsops_du%coeffs_e, tdsops_du%coeffs, &
+        n, tdsops_du%dist_fw, tdsops_du%dist_bw, tdsops_du%dist_af &
+        )
+
+      call der_univ_dist( &
+        d2u(:, :, k), d2u_send_s(:, :, k), d2u_send_e(:, :, k), u(:, :, k), &
+        u_recv_s(:, :, k), u_recv_e(:, :, k), &
+        tdsops_d2u%coeffs_s, tdsops_d2u%coeffs_e, tdsops_d2u%coeffs, &
+        n, tdsops_d2u%dist_fw, tdsops_d2u%dist_bw, &
+        tdsops_d2u%dist_af &
+        )
+
+      ! Handle dud by locally generating u*v
+      do j = 1, n
+        !$omp simd
+        do i = 1, SZ
+          ud(i, j) = u(i, j, k)*v(i, j, k)
+        end do
+        !$omp end simd
+      end do
+
+      do j = 1, n_halo
+        !$omp simd
+        do i = 1, SZ
+          ud_recv_s(i, j) = u_recv_s(i, j, k)*v_recv_s(i, j, k)
+          ud_recv_e(i, j) = u_recv_e(i, j, k)*v_recv_e(i, j, k)
+        end do
+        !$omp end simd
+      end do
+
+      call der_univ_dist( &
+        dud(:, :, k), dud_send_s(:, :, k), dud_send_e(:, :, k), ud(:, :), &
+        ud_recv_s(:, :), ud_recv_e(:, :), &
+        tdsops_dud%coeffs_s, tdsops_dud%coeffs_e, tdsops_dud%coeffs, &
+        n, tdsops_dud%dist_fw, tdsops_dud%dist_bw, &
+        tdsops_dud%dist_af &
+        )
+
+    end do
+    !$omp end parallel do
+
+    ! halo exchange for 2x2 systems
+    call sendrecv_fields(du_recv_s, du_recv_e, du_send_s, du_send_e, &
+                         n_data, nproc, pprev, pnext)
+    call sendrecv_fields(dud_recv_s, dud_recv_e, dud_send_s, dud_send_e, &
+                         n_data, nproc, pprev, pnext)
+    call sendrecv_fields(d2u_recv_s, d2u_recv_e, d2u_send_s, d2u_send_e, &
+                         n_data, nproc, pprev, pnext)
+
+    !$omp parallel do
+    do k = 1, n_groups
+      call der_univ_subs(du(:, :, k), &
+                         du_recv_s(:, :, k), du_recv_e(:, :, k), &
+                         n, tdsops_du%dist_sa, tdsops_du%dist_sc)
+
+      call der_univ_subs(dud(:, :, k), &
+                         dud_recv_s(:, :, k), dud_recv_e(:, :, k), &
+                         n, tdsops_dud%dist_sa, tdsops_dud%dist_sc)
+
+      call der_univ_subs(d2u(:, :, k), &
+                         d2u_recv_s(:, :, k), d2u_recv_e(:, :, k), &
+                         n, tdsops_d2u%dist_sa, tdsops_d2u%dist_sc)
+
+      do j = 1, n
+        !$omp simd
+        do i = 1, SZ
+          rhs(i, j, k) = -0.5_dp*(v(i, j, k)*du(i, j, k) + dud(i, j, k)) &
+                         + nu*d2u(i, j, k)
+        end do
+        !$omp end simd
+      end do
+
+    end do
+    !$omp end parallel do
+
+  end subroutine exec_dist_transeq_compact
+
+end module m_omp_exec_dist
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/exec_thom.f90.html b/api/sourcefile/exec_thom.f90.html new file mode 100644 index 000000000..85c074a27 --- /dev/null +++ b/api/sourcefile/exec_thom.f90.html @@ -0,0 +1,249 @@ + + + + + + + + + + + + + exec_thom.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

exec_thom.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_exec_thom
+  use cudafor
+
+  use m_common, only: dp
+  use m_cuda_kernels_thom, only: der_univ_thom, der_univ_thom_per
+  use m_cuda_tdsops, only: cuda_tdsops_t
+
+  implicit none
+
+contains
+
+  subroutine exec_thom_tds_compact(du, u, tdsops, blocks, threads)
+    implicit none
+
+    real(dp), device, dimension(:, :, :), intent(out) :: du
+    real(dp), device, dimension(:, :, :), intent(in) :: u
+    type(cuda_tdsops_t), intent(in) :: tdsops
+    type(dim3), intent(in) :: blocks, threads
+
+    if (tdsops%periodic) then
+      call der_univ_thom_per<<<blocks, threads>>>( & !&
+        du, u, tdsops%coeffs_dev, tdsops%tds_n, tdsops%alpha, &
+        tdsops%thom_f_dev, tdsops%thom_s_dev, &
+        tdsops%thom_w_dev, tdsops%thom_p_dev &
+        )
+    else
+      call der_univ_thom<<<blocks, threads>>>( & !&
+        du, u, &
+        tdsops%coeffs_s_dev, tdsops%coeffs_e_dev, tdsops%coeffs_dev, &
+        tdsops%tds_n, tdsops%thom_f_dev, tdsops%thom_s_dev, &
+        tdsops%thom_w_dev &
+        )
+    end if
+
+  end subroutine exec_thom_tds_compact
+
+end module m_cuda_exec_thom
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/field.f90.html b/api/sourcefile/field.f90.html new file mode 100644 index 000000000..ae3411f5e --- /dev/null +++ b/api/sourcefile/field.f90.html @@ -0,0 +1,270 @@ + + + + + + + + + + + + + field.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

field.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_field
+
+  use m_common, only: dp, DIR_X, DIR_Y, DIR_Z, DIR_C
+
+  type :: field_t
+     !! Memory block type holding both a data field and a pointer
+     !! to the next block.  The `field_t` type also holds a integer
+     !! `refcount` that counts the number of references to this
+     !! field.  User code is currently responsible for incrementing
+     !! the reference count.
+    class(field_t), pointer :: next
+    real(dp), pointer, private :: p_data(:)
+    real(dp), pointer, contiguous :: data(:, :, :)
+    integer :: dir
+    integer :: data_loc
+    integer :: refcount = 0
+    integer :: id !! An integer identifying the memory block.
+  contains
+    procedure :: set_shape
+    procedure :: set_data_loc
+  end type field_t
+
+  interface field_t
+    module procedure field_init
+  end interface field_t
+
+contains
+
+  subroutine set_data_loc(self, data_loc)
+    class(field_t) :: self
+    integer, intent(in) :: data_loc
+
+    self%data_loc = data_loc
+
+  end subroutine
+
+  subroutine set_shape(self, dims)
+    implicit none
+
+    class(field_t) :: self
+    integer, intent(in) :: dims(3)
+
+    self%data(1:dims(1), 1:dims(2), 1:dims(3)) => self%p_data
+
+  end subroutine set_shape
+
+  function field_init(ngrid, next, id) result(f)
+    integer, intent(in) :: ngrid, id
+    type(field_t), pointer, intent(in) :: next
+    type(field_t) :: f
+
+    allocate (f%p_data(ngrid))
+    f%refcount = 0
+    f%next => next
+    f%id = id
+  end function field_init
+
+end module m_field
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/mesh.f90.html b/api/sourcefile/mesh.f90.html new file mode 100644 index 000000000..df15f3d21 --- /dev/null +++ b/api/sourcefile/mesh.f90.html @@ -0,0 +1,693 @@ + + + + + + + + + + + + + mesh.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

mesh.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_mesh
+  use iso_fortran_env, only: stderr => error_unit
+
+  use mpi
+  use m_common, only: dp, DIR_X, DIR_Y, DIR_Z, DIR_C, &
+                      CELL, VERT, X_FACE, Y_FACE, Z_FACE, &
+                      X_EDGE, Y_EDGE, Z_EDGE, &
+                      BC_PERIODIC, BC_NEUMANN, BC_DIRICHLET
+  use m_field, only: field_t
+
+  implicit none
+
+  ! Stores geometry information
+  type :: geo_t
+    real(dp), dimension(3) :: d ! size of a cell in each direction (=edge length, distance between centers, distance between vertices)
+    real(dp), dimension(3) :: L ! Global dimensions of the domain in each direction
+  end type
+
+  ! Stores parallel domain related information
+  type :: parallel_t
+    integer :: nrank ! local rank ID
+    integer :: nproc ! total number of ranks/proc participating in the domain decomposition
+    integer, dimension(3) :: nrank_dir ! local rank ID in each direction
+    integer, dimension(3) :: nproc_dir ! total number of proc in each direction
+    integer, dimension(3) :: n_offset  ! number of cells offset in each direction due to domain decomposition
+    integer, dimension(3) :: pnext ! rank ID of the previous rank in each direction
+    integer, dimension(3) :: pprev ! rank ID of the next rank in each direction
+  contains
+    procedure :: is_root ! returns if the current rank is the root rank
+  end type
+
+  ! The mesh class stores all the information about the global and local (due to domain decomposition) mesh
+  ! It also includes getter functions to access some of its parameters
+  type :: mesh_t
+    integer, dimension(3), private :: global_vert_dims ! global number of vertices in each direction without padding (cartesian structure)
+    integer, dimension(3), private :: global_cell_dims ! global number of cells in each direction without padding (cartesian structure)
+
+    integer, dimension(3), private :: vert_dims_padded ! local domain size including padding (cartesian structure)
+    integer, dimension(3), private :: vert_dims ! local number of vertices in each direction without padding (cartesian structure)
+    integer, dimension(3), private :: cell_dims ! local number of cells in each direction without padding (cartesian structure)
+    logical, dimension(3), private :: periodic_BC ! Whether or not a direction has a periodic BC
+    integer, dimension(3, 2), private :: BCs_global
+    integer, dimension(3, 2), private :: BCs
+    integer, private :: sz
+    type(geo_t), allocatable :: geo ! object containing geometry information
+    type(parallel_t), allocatable :: par ! object containing parallel domain decomposition information
+  contains
+    procedure :: get_SZ
+
+    procedure :: get_dims
+    procedure :: get_global_dims
+
+    procedure :: get_n_groups_dir
+    procedure :: get_n_groups_phi
+    generic :: get_n_groups => get_n_groups_dir, get_n_groups_phi
+
+    procedure :: get_field_dims_dir
+    procedure :: get_field_dims_phi
+    procedure :: get_field_dims_phi_dataloc
+    generic :: get_field_dims => get_field_dims_dir, get_field_dims_phi, &
+      get_field_dims_phi_dataloc
+
+    procedure :: get_n_dir
+    procedure :: get_n_phi
+    generic :: get_n => get_n_dir, get_n_phi
+
+    procedure :: get_padded_dims_phi
+    procedure :: get_padded_dims_dir
+    generic :: get_padded_dims => get_padded_dims_dir, get_padded_dims_phi
+
+    procedure :: get_coordinates
+
+    procedure :: set_sz
+    procedure :: set_padded_dims
+  end type mesh_t
+
+  interface mesh_t
+    module procedure mesh_init
+  end interface mesh_t
+
+contains
+
+  function mesh_init(dims_global, nproc_dir, L_global, BC_x, BC_y, BC_z) &
+    result(mesh)
+    !! Completely initialise the mesh object.
+    !! Upon initialisation the mesh object can be read-only and shouldn't be edited
+    !! Takes as argument global information about the mesh like its length, number of cells and decomposition in each direction
+    integer, dimension(3), intent(in) :: dims_global
+    integer, dimension(3), intent(in) :: nproc_dir ! Number of proc in each direction
+    real(dp), dimension(3), intent(in) :: L_global
+    character(len=*), dimension(2), intent(in) :: BC_x, BC_y, BC_z
+    type(mesh_t) :: mesh
+
+    character(len=20), dimension(3, 2) :: BC_all
+    logical :: is_first_domain, is_last_domain
+    integer :: dir, j
+    integer :: ierr
+
+    allocate (mesh%geo)
+    allocate (mesh%par)
+
+    BC_all(1, 1) = BC_x(1); BC_all(1, 2) = BC_x(2)
+    BC_all(2, 1) = BC_y(1); BC_all(2, 2) = BC_y(2)
+    BC_all(3, 1) = BC_z(1); BC_all(3, 2) = BC_z(2)
+    do dir = 1, 3
+      do j = 1, 2
+        select case (trim(BC_all(dir, j)))
+        case ('periodic')
+          mesh%BCs_global(dir, j) = BC_PERIODIC
+        case ('neumann')
+          mesh%BCs_global(dir, j) = BC_NEUMANN
+        case ('dirichlet')
+          mesh%BCs_global(dir, j) = BC_DIRICHLET
+        case default
+          error stop 'Unknown BC'
+        end select
+      end do
+    end do
+
+    do dir = 1, 3
+      if (any(mesh%BCs_global(dir, :) == BC_PERIODIC) .and. &
+          (.not. all(mesh%BCs_global(dir, :) == BC_PERIODIC))) then
+        error stop 'BCs are incompatible: in a direction make sure to have &
+                    &either both sides periodic or none.'
+      end if
+      mesh%periodic_BC(dir) = all(mesh%BCs_global(dir, :) == BC_PERIODIC)
+    end do
+
+    ! Set global vertex dims
+    mesh%global_vert_dims(:) = dims_global
+
+    ! Set global cell dims
+    do dir = 1, 3
+      if (mesh%periodic_BC(dir)) then
+        mesh%global_cell_dims(dir) = mesh%global_vert_dims(dir)
+      else
+        mesh%global_cell_dims(dir) = mesh%global_vert_dims(dir) - 1
+      end if
+    end do
+
+    ! Geometry
+    mesh%geo%L = L_global
+    mesh%geo%d = mesh%geo%L/mesh%global_cell_dims
+
+    ! Parallel domain decomposition
+    mesh%par%nproc_dir(:) = nproc_dir
+    mesh%par%nproc = product(nproc_dir(:))
+    call MPI_Comm_rank(MPI_COMM_WORLD, mesh%par%nrank, ierr)
+    call MPI_Comm_size(MPI_COMM_WORLD, mesh%par%nproc, ierr)
+    call domain_decomposition(mesh)
+
+    ! Set subdomain BCs
+    do dir = 1, 3
+      is_first_domain = mesh%par%nrank_dir(dir) == 0
+      is_last_domain = mesh%par%nrank_dir(dir) + 1 == mesh%par%nproc_dir(dir)
+      ! subdomain-subdomain boundaries are identical to periodic BCs
+      if (is_first_domain) then
+        mesh%BCs(dir, 1) = mesh%BCs_global(dir, 1)
+        mesh%BCs(dir, 2) = BC_PERIODIC
+      else if (is_last_domain) then
+        mesh%BCs(dir, 1) = BC_PERIODIC
+        mesh%BCs(dir, 2) = mesh%BCs_global(dir, 2)
+      else
+        mesh%BCs(dir, :) = BC_PERIODIC
+      end if
+    end do
+
+    ! Define number of cells and vertices in each direction
+    mesh%vert_dims = mesh%global_vert_dims/mesh%par%nproc_dir
+
+    do dir = 1, 3
+      is_last_domain = (mesh%par%nrank_dir(dir) + 1 == mesh%par%nproc_dir(dir))
+      if (is_last_domain .and. (.not. mesh%periodic_BC(dir))) then
+        mesh%cell_dims(dir) = mesh%vert_dims(dir) - 1
+      else
+        mesh%cell_dims(dir) = mesh%vert_dims(dir)
+      end if
+    end do
+
+    ! Set offset for global indices
+    mesh%par%n_offset(:) = mesh%vert_dims(:)*mesh%par%nrank_dir(:)
+
+    ! Define default values
+    mesh%vert_dims_padded = mesh%vert_dims
+    mesh%sz = 1
+
+  end function mesh_init
+
+  subroutine set_padded_dims(self, vert_dims)
+    class(mesh_t), intent(inout) :: self
+    integer, dimension(3), intent(in) :: vert_dims
+
+    self%vert_dims_padded = vert_dims
+
+  end subroutine
+
+  subroutine set_sz(self, sz)
+    class(mesh_t), intent(inout) :: self
+    integer, intent(in) :: sz
+
+    self%sz = sz
+
+  end subroutine
+
+  subroutine domain_decomposition(mesh)
+    !! Supports 1D, 2D, and 3D domain decomposition.
+    !!
+    !! Current implementation allows only constant sub-domain size across a
+    !! given direction.
+    class(mesh_t), intent(inout) :: mesh
+
+    integer, allocatable, dimension(:, :, :) :: global_ranks
+    integer :: i, nproc_x, nproc_y, nproc_z, nproc
+    integer, dimension(3) :: subd_pos, subd_pos_prev, subd_pos_next
+    integer :: dir
+
+    ! Number of processes on a direction basis
+    nproc_x = mesh%par%nproc_dir(1)
+    nproc_y = mesh%par%nproc_dir(2)
+    nproc_z = mesh%par%nproc_dir(3)
+
+    ! A 3D array corresponding to each region in the global domain
+    allocate (global_ranks(nproc_x, nproc_y, nproc_z))
+
+    ! set the corresponding global rank for each sub-domain
+    global_ranks = reshape([(i, i=0, mesh%par%nproc - 1)], &
+                           shape=[nproc_x, nproc_y, nproc_z])
+
+    ! subdomain position in the global domain
+    subd_pos = findloc(global_ranks, mesh%par%nrank)
+
+    ! local/directional position of the subdomain
+    mesh%par%nrank_dir(:) = subd_pos(:) - 1
+
+    do dir = 1, 3
+      nproc = mesh%par%nproc_dir(dir)
+      subd_pos_prev(:) = subd_pos(:)
+      subd_pos_prev(dir) = modulo(subd_pos(dir) - 2, nproc) + 1
+      mesh%par%pprev(dir) = global_ranks(subd_pos_prev(1), &
+                                         subd_pos_prev(2), &
+                                         subd_pos_prev(3))
+
+      subd_pos_next(:) = subd_pos(:)
+      subd_pos_next(dir) = modulo(subd_pos(dir) - nproc, nproc) + 1
+      mesh%par%pnext(dir) = global_ranks(subd_pos_next(1), &
+                                         subd_pos_next(2), &
+                                         subd_pos_next(3))
+    end do
+
+  end subroutine domain_decomposition
+
+  pure function get_sz(self) result(sz)
+  !! Getter for parameter SZ
+    class(mesh_t), intent(in) :: self
+    integer :: sz
+
+    sz = self%sz
+
+  end function
+
+  pure function get_dims(self, data_loc) result(dims)
+  !! Getter for local domain dimensions
+    class(mesh_t), intent(in) :: self
+    integer, intent(in) :: data_loc
+    integer, dimension(3) :: dims
+
+    dims = get_dims_dataloc(data_loc, self%vert_dims, self%cell_dims)
+  end function
+
+  pure function get_global_dims(self, data_loc) result(dims)
+  !! Getter for local domain dimensions
+    class(mesh_t), intent(in) :: self
+    integer, intent(in) :: data_loc
+    integer, dimension(3) :: dims
+
+    dims = get_dims_dataloc(data_loc, self%global_vert_dims, &
+                            self%global_cell_dims)
+  end function
+
+  pure function get_dims_dataloc(data_loc, vert_dims, cell_dims) result(dims)
+  !! Getter for domain dimensions
+    integer, intent(in) :: data_loc
+    integer, dimension(3), intent(in) :: vert_dims, cell_dims
+    integer, dimension(3) :: dims
+
+    select case (data_loc)
+    case (VERT)
+      dims = vert_dims
+    case (CELL)
+      dims = cell_dims
+    case (X_FACE)
+      dims(1) = vert_dims(1)
+      dims(2:3) = cell_dims(2:3)
+    case (Y_FACE)
+      dims(1) = cell_dims(1)
+      dims(2) = vert_dims(2)
+      dims(3) = cell_dims(3)
+    case (Z_FACE)
+      dims(1:2) = cell_dims(1:2)
+      dims(3) = vert_dims(3)
+    case (X_EDGE)
+      dims(1) = cell_dims(1)
+      dims(2:3) = vert_dims(2:3)
+    case (Y_EDGE)
+      dims(1) = vert_dims(1)
+      dims(2) = cell_dims(2)
+      dims(3) = vert_dims(3)
+    case (Z_EDGE)
+      dims(1:2) = vert_dims(1:2)
+      dims(3) = cell_dims(3)
+    case default
+      error stop "Unknown location in get_dims_dataloc"
+    end select
+  end function get_dims_dataloc
+
+  pure function get_padded_dims_dir(self, dir) result(dims_padded)
+  !! Getter for padded dimensions with structure in `dir` direction
+    class(mesh_t), intent(in) :: self
+    integer, intent(in) :: dir
+    integer, dimension(3) :: dims_padded
+
+    if (dir == DIR_C) then
+      dims_padded = self%vert_dims_padded
+    else
+      dims_padded(1) = self%sz
+      dims_padded(2) = self%vert_dims_padded(dir)
+      dims_padded(3) = self%get_n_groups(dir)
+    end if
+
+  end function
+
+  pure function get_padded_dims_phi(self, phi) result(dims_padded)
+  !! Getter for padded dimensions for field phi
+  !! Gets the field direction from the field itself
+    class(mesh_t), intent(in) :: self
+    class(field_t), intent(in) :: phi
+    integer, dimension(3) :: dims_padded
+
+    dims_padded = self%get_padded_dims(phi%dir)
+
+  end function
+
+  pure function get_n_groups_dir(self, dir) result(n_groups)
+  !! Getter for the number of groups for fields in direction `dir`
+    class(mesh_t), intent(in) :: self
+    integer, intent(in) :: dir
+    integer :: n_groups
+
+    n_groups = (product(self%vert_dims_padded(:))/ &
+                self%vert_dims_padded(dir))/self%sz
+
+  end function
+
+  pure function get_n_groups_phi(self, phi) result(n_groups)
+  !! Getter for the number of groups for fields phi
+    class(mesh_t), intent(in) :: self
+    class(field_t), intent(in) :: phi
+    integer :: n_groups
+
+    n_groups = self%get_n_groups(phi%dir)
+
+  end function
+
+  pure function get_field_dims_phi(self, phi) result(dims)
+  !! Getter for the dimensions of field phi
+    class(mesh_t), intent(in) :: self
+    class(field_t), intent(in) :: phi
+    integer, dimension(3) :: dims
+
+    dims = self%get_field_dims(phi%dir, phi%data_loc)
+
+  end function
+
+  pure function get_field_dims_phi_dataloc(self, phi, data_loc) result(dims)
+  !! Getter for the dimensions of field phi where data is located on `data_loc`
+    class(mesh_t), intent(in) :: self
+    class(field_t), intent(in) :: phi
+    integer, intent(in) :: data_loc
+    integer, dimension(3) :: dims
+
+    dims = self%get_field_dims(phi%dir, data_loc)
+
+  end function
+
+  pure function get_field_dims_dir(self, dir, data_loc) result(dims)
+  !! Getter for the dimensions of an array directed along `dir` where data would be located on `data_loc`
+    class(mesh_t), intent(in) :: self
+    integer, intent(in) :: dir
+    integer, intent(in) :: data_loc
+    integer, dimension(3) :: dims
+
+    if (dir == DIR_C) then
+      dims(1) = self%get_n(DIR_X, data_loc)
+      dims(2) = self%get_n(DIR_Y, data_loc)
+      dims(3) = self%get_n(DIR_Z, data_loc)
+    else
+      dims(1) = self%sz
+      dims(2) = self%get_n(dir, data_loc)
+      dims(3) = self%get_n_groups(dir)
+    end if
+
+  end function
+
+  pure function get_n_phi(self, phi) result(n)
+  !! Getter for the main dimension of field phi
+    class(mesh_t), intent(in) :: self
+    class(field_t), intent(in) :: phi
+    integer :: n
+
+    n = self%get_n(phi%dir, phi%data_loc)
+
+  end function
+
+  pure function get_n_dir(self, dir, data_loc) result(n)
+  !! Getter for the main dimension a field oriented along `dir` with data on `data_loc`
+    class(mesh_t), intent(in) :: self
+    integer, intent(in) :: dir
+    integer, intent(in) :: data_loc
+    integer :: n, n_cell, n_vert
+
+    n_cell = self%cell_dims(dir)
+    n_vert = self%vert_dims(dir)
+
+    ! default to n_vert
+    n = n_vert
+
+    select case (data_loc)
+    case (CELL)
+      n = n_cell
+    case (VERT)
+      n = n_vert
+    case (X_FACE)
+      if (dir /= DIR_X) then
+        n = n_cell
+      end if
+    case (Y_FACE)
+      if (dir /= DIR_Y) then
+        n = n_cell
+      end if
+    case (Z_FACE)
+      if (dir /= DIR_Z) then
+        n = n_cell
+      end if
+    case (X_EDGE)
+      if (dir == DIR_X) then
+        n = n_cell
+      end if
+    case (Y_EDGE)
+      if (dir == DIR_Y) then
+        n = n_cell
+      end if
+    case (Z_EDGE)
+      if (dir == DIR_Z) then
+        n = n_cell
+      end if
+    case default
+      error stop "Unknown direction in get_n_dir"
+    end select
+  end function get_n_dir
+
+  pure function get_coordinates(self, i, j, k) result(xloc)
+  !! Get the physical location of a cell center with i,j,k local indices
+    class(mesh_t), intent(in) :: self
+    integer, intent(in) :: i, j, k
+    real(dp), dimension(3) :: xloc
+
+    xloc(1) = (i - 1 + self%par%n_offset(1))*self%geo%d(1)
+    xloc(2) = (j - 1 + self%par%n_offset(2))*self%geo%d(2)
+    xloc(3) = (k - 1 + self%par%n_offset(3))*self%geo%d(3)
+  end function
+
+  pure function is_root(self) result(is_root_rank)
+  !! Returns whether or not the current rank is the root rank
+    class(parallel_t), intent(in) :: self
+    logical :: is_root_rank
+
+    is_root_rank = (self%nrank == 0)
+
+  end function
+
+end module m_mesh
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/ordering.f90.html b/api/sourcefile/ordering.f90.html new file mode 100644 index 000000000..b61ec4e92 --- /dev/null +++ b/api/sourcefile/ordering.f90.html @@ -0,0 +1,323 @@ + + + + + + + + + + + + + ordering.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

ordering.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_ordering
+
+  use m_common, only: dp, get_dirs_from_rdr, DIR_X, DIR_Y, DIR_Z, DIR_C, &
+                      RDR_X2Y, RDR_X2Z, RDR_Y2X, RDR_Y2Z, RDR_Z2X, RDR_Z2Y
+
+  use m_mesh, only: mesh_t
+
+  implicit none
+  interface get_index_reordering
+    procedure get_index_reordering_rdr, get_index_reordering_dirs
+  end interface
+
+contains
+   !!
+   !! "Application storage" stores spatial data with a directionality for better cache locality
+   !!  This set of functions converts indices from this application storage (_dir) to cartesian indices (_ijk)
+   !!
+
+  pure subroutine get_index_ijk(i, j, k, dir_i, dir_j, dir_k, dir, &
+                                SZ, nx_padded, ny_padded, nz_padded)
+      !! Get cartesian index from application storage directional one
+    integer, intent(out) :: i, j, k                   ! cartesian indices
+    integer, intent(in) :: dir_i, dir_j, dir_k        ! application storage indices
+    integer, intent(in) :: dir                        ! direction of the applicatino storage indices
+    integer, intent(in) :: SZ, nx_padded, ny_padded, nz_padded ! dimensions of the block
+
+    select case (dir)
+    case (DIR_X)
+      i = dir_j
+      j = mod(dir_k - 1, ny_padded/SZ)*SZ + dir_i
+      k = 1 + (dir_k - 1)/(ny_padded/SZ)
+    case (DIR_Y)
+      i = mod(dir_k - 1, nx_padded/SZ)*SZ + dir_i
+      j = dir_j
+      k = 1 + (dir_k - 1)/(nx_padded/SZ)
+    case (DIR_Z)
+      i = mod(dir_k - 1, nx_padded/SZ)*SZ + dir_i
+      j = 1 + (dir_k - 1)/(nx_padded/SZ)
+      k = dir_j
+    case (DIR_C)
+      i = dir_i
+      j = dir_j
+      k = dir_k
+    end select
+
+  end subroutine get_index_ijk
+
+  pure subroutine get_index_dir(dir_i, dir_j, dir_k, i, j, k, dir, &
+                                SZ, nx_padded, ny_padded, nz_padded)
+      !! Get application storage directional index from cartesian index
+    integer, intent(out) :: dir_i, dir_j, dir_k        ! application storage indices
+    integer, intent(in) :: i, j, k                     ! cartesian indices
+    integer, intent(in) :: dir                        ! direction of the application storage indices
+    integer, intent(in) :: SZ, nx_padded, ny_padded, nz_padded ! dimensions of the block
+
+    select case (dir)
+    case (DIR_X)
+      dir_i = mod(j - 1, SZ) + 1
+      dir_j = i
+      dir_k = (ny_padded/SZ)*(k - 1) + 1 + (j - 1)/SZ
+    case (DIR_Y)
+      dir_i = mod(i - 1, SZ) + 1
+      dir_j = j
+      dir_k = (nx_padded/SZ)*(k - 1) + 1 + (i - 1)/SZ
+    case (DIR_Z)
+      dir_i = mod(i - 1, SZ) + 1
+      dir_j = k
+      dir_k = (nx_padded/SZ)*(j - 1) + 1 + (i - 1)/SZ
+    case (DIR_C)
+      dir_i = i
+      dir_j = j
+      dir_k = k
+    end select
+
+  end subroutine get_index_dir
+
+  pure subroutine get_index_reordering_dirs( &
+    out_i, out_j, out_k, in_i, in_j, in_k, dir_from, dir_to, mesh &
+    )
+      !! Converts a set of application storage directional index to an other direction.
+      !! The two directions are defined by the reorder_dir variable, RDR_X2Y will go from storage in X to Y etc.
+    integer, intent(out) :: out_i, out_j, out_k         ! new indices in the application storage
+    integer, intent(in) :: in_i, in_j, in_k             ! original indices
+    integer, intent(in) :: dir_from, dir_to
+    class(mesh_t), intent(in) :: mesh
+    integer :: i, j, k        ! Intermediary cartesian indices
+    integer, dimension(3) :: dims_padded
+
+    dims_padded = mesh%get_padded_dims(DIR_C)
+    call get_index_ijk(i, j, k, in_i, in_j, in_k, dir_from, mesh%get_sz(), &
+                       dims_padded(1), dims_padded(2), dims_padded(3))
+    call get_index_dir(out_i, out_j, out_k, i, j, k, dir_to, mesh%get_sz(), &
+                       dims_padded(1), dims_padded(2), dims_padded(3))
+
+  end subroutine get_index_reordering_dirs
+
+  pure subroutine get_index_reordering_rdr(out_i, out_j, out_k, &
+                                           in_i, in_j, in_k, reorder_dir, mesh)
+    integer, intent(out) :: out_i, out_j, out_k         ! new indices in the application storage
+    integer, intent(in) :: in_i, in_j, in_k             ! original indices
+    integer, intent(in) :: reorder_dir
+    class(mesh_t), intent(in) :: mesh
+    integer :: dir_from, dir_to
+
+    call get_dirs_from_rdr(dir_from, dir_to, reorder_dir)
+    call get_index_reordering(out_i, out_j, out_k, in_i, in_j, in_k, &
+                              dir_from, dir_to, mesh)
+
+  end subroutine get_index_reordering_rdr
+
+end module m_ordering
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/poisson_fft.f90.html b/api/sourcefile/poisson_fft.f90.html new file mode 100644 index 000000000..4a1cdbddc --- /dev/null +++ b/api/sourcefile/poisson_fft.f90.html @@ -0,0 +1,272 @@ + + + + + + + + + + + + + poisson_fft.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

poisson_fft.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_omp_poisson_fft
+  use m_allocator, only: field_t
+  use m_common, only: dp
+  use m_poisson_fft, only: poisson_fft_t
+  use m_tdsops, only: dirps_t
+  use m_mesh, only: mesh_t
+
+  implicit none
+
+  type, extends(poisson_fft_t) :: omp_poisson_fft_t
+      !! FFT based Poisson solver
+      !! It can only handle 1D decompositions along z direction.
+    complex(dp), allocatable, dimension(:, :, :) :: c_x, c_y, c_z
+  contains
+    procedure :: fft_forward => fft_forward_omp
+    procedure :: fft_backward => fft_backward_omp
+    procedure :: fft_postprocess => fft_postprocess_omp
+  end type omp_poisson_fft_t
+
+  interface omp_poisson_fft_t
+    module procedure init
+  end interface omp_poisson_fft_t
+
+  private :: init
+
+contains
+
+  function init(mesh, xdirps, ydirps, zdirps) result(poisson_fft)
+    implicit none
+
+    class(mesh_t), intent(in) :: mesh
+    class(dirps_t), intent(in) :: xdirps, ydirps, zdirps
+
+    type(omp_poisson_fft_t) :: poisson_fft
+
+    call poisson_fft%base_init(mesh, xdirps, ydirps, zdirps)
+
+  end function init
+
+  subroutine fft_forward_omp(self, f_in)
+    implicit none
+
+    class(omp_poisson_fft_t) :: self
+    class(field_t), intent(in) :: f_in
+  end subroutine fft_forward_omp
+
+  subroutine fft_backward_omp(self, f_out)
+    implicit none
+
+    class(omp_poisson_fft_t) :: self
+    class(field_t), intent(inout) :: f_out
+  end subroutine fft_backward_omp
+
+  subroutine fft_postprocess_omp(self)
+    implicit none
+
+    class(omp_poisson_fft_t) :: self
+  end subroutine fft_postprocess_omp
+
+end module m_omp_poisson_fft
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/poisson_fft.f90~2.html b/api/sourcefile/poisson_fft.f90~2.html new file mode 100644 index 000000000..3c80dd2ea --- /dev/null +++ b/api/sourcefile/poisson_fft.f90~2.html @@ -0,0 +1,427 @@ + + + + + + + + + + + + + poisson_fft.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

poisson_fft.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_poisson_fft
+  use iso_c_binding, only: c_loc, c_ptr, c_f_pointer
+  use iso_fortran_env, only: stderr => error_unit
+  use cudafor
+  use cufftXt
+  use cufft
+  use mpi
+
+  use m_allocator, only: field_t
+  use m_common, only: dp, DIR_X, DIR_Y, DIR_Z, CELL
+  use m_mesh, only: mesh_t
+  use m_poisson_fft, only: poisson_fft_t
+  use m_tdsops, only: dirps_t
+
+  use m_cuda_allocator, only: cuda_field_t
+  use m_cuda_spectral, only: process_spectral_div_u
+
+  implicit none
+
+  type, extends(poisson_fft_t) :: cuda_poisson_fft_t
+    !! FFT based Poisson solver
+
+    !> Local domain sized array storing the spectral equivalence constants
+    complex(dp), device, allocatable, dimension(:, :, :) :: waves_dev
+    !> Wave numbers in x, y, and z
+    real(dp), device, allocatable, dimension(:) :: ax_dev, bx_dev, &
+                                                   ay_dev, by_dev, &
+                                                   az_dev, bz_dev
+    !> Forward and backward FFT transform plans
+    integer :: plan3D_fw, plan3D_bw
+
+    !> cuFFTMp object manages decomposition and data storage
+    type(cudaLibXtDesc), pointer :: xtdesc
+  contains
+    procedure :: fft_forward => fft_forward_cuda
+    procedure :: fft_backward => fft_backward_cuda
+    procedure :: fft_postprocess => fft_postprocess_cuda
+  end type cuda_poisson_fft_t
+
+  interface cuda_poisson_fft_t
+    module procedure init
+  end interface cuda_poisson_fft_t
+
+  private :: init
+
+contains
+
+  function init(mesh, xdirps, ydirps, zdirps) result(poisson_fft)
+    implicit none
+
+    class(mesh_t), intent(in) :: mesh
+    type(dirps_t), intent(in) :: xdirps, ydirps, zdirps
+
+    type(cuda_poisson_fft_t) :: poisson_fft
+
+    integer :: nx, ny, nz
+
+    integer :: ierr
+    integer(int_ptr_kind()) :: worksize
+
+    call poisson_fft%base_init(mesh, xdirps, ydirps, zdirps)
+
+    nx = poisson_fft%nx_glob
+    ny = poisson_fft%ny_glob
+    nz = poisson_fft%nz_glob
+
+    allocate (poisson_fft%waves_dev(poisson_fft%nx_spec, &
+                                    poisson_fft%ny_spec, &
+                                    poisson_fft%nz_spec))
+    poisson_fft%waves_dev = poisson_fft%waves
+
+    allocate (poisson_fft%ax_dev(nx), poisson_fft%bx_dev(nx))
+    allocate (poisson_fft%ay_dev(ny), poisson_fft%by_dev(ny))
+    allocate (poisson_fft%az_dev(nz), poisson_fft%bz_dev(nz))
+    poisson_fft%ax_dev = poisson_fft%ax; poisson_fft%bx_dev = poisson_fft%bx
+    poisson_fft%ay_dev = poisson_fft%ay; poisson_fft%by_dev = poisson_fft%by
+    poisson_fft%az_dev = poisson_fft%az; poisson_fft%bz_dev = poisson_fft%bz
+
+    ! 3D plans
+    ierr = cufftCreate(poisson_fft%plan3D_fw)
+    ierr = cufftMpAttachComm(poisson_fft%plan3D_fw, CUFFT_COMM_MPI, &
+                             MPI_COMM_WORLD)
+    ierr = cufftMakePlan3D(poisson_fft%plan3D_fw, nz, ny, nx, CUFFT_D2Z, &
+                           worksize)
+    if (ierr /= 0) then
+      write (stderr, *), 'cuFFT Error Code: ', ierr
+      error stop 'Forward 3D FFT plan generation failed'
+    end if
+
+    ierr = cufftCreate(poisson_fft%plan3D_bw)
+    ierr = cufftMpAttachComm(poisson_fft%plan3D_bw, CUFFT_COMM_MPI, &
+                             MPI_COMM_WORLD)
+    ierr = cufftMakePlan3D(poisson_fft%plan3D_bw, nz, ny, nx, CUFFT_Z2D, &
+                           worksize)
+    if (ierr /= 0) then
+      write (stderr, *), 'cuFFT Error Code: ', ierr
+      error stop 'Backward 3D FFT plan generation failed'
+    end if
+
+    ! allocate storage for cuFFTMp
+    ierr = cufftXtMalloc(poisson_fft%plan3D_fw, poisson_fft%xtdesc, &
+                         CUFFT_XT_FORMAT_INPLACE)
+    if (ierr /= 0) then
+      write (stderr, *), 'cuFFT Error Code: ', ierr
+      error stop 'cufftXtMalloc failed'
+    end if
+
+  end function init
+
+  subroutine fft_forward_cuda(self, f)
+    implicit none
+
+    class(cuda_poisson_fft_t) :: self
+    class(field_t), intent(in) :: f
+
+    real(dp), device, pointer :: flat_dev(:, :), d_dev(:, :, :)
+
+    type(cudaXtDesc), pointer :: descriptor
+
+    integer :: ierr
+
+    select type (f)
+    type is (cuda_field_t)
+      flat_dev(1:self%nx_loc, 1:self%ny_loc*self%nz_loc) => f%data_d
+    end select
+
+    call c_f_pointer(self%xtdesc%descriptor, descriptor)
+    call c_f_pointer(descriptor%data(1), d_dev, &
+                     [self%nx_loc + 2, self%ny_loc*self%nz_loc])
+    ierr = cudaMemcpy2D(d_dev, self%nx_loc + 2, flat_dev, self%nx_loc, &
+                        self%nx_loc, self%ny_loc*self%nz_loc)
+    if (ierr /= 0) then
+      print *, 'cudaMemcpy2D error code: ', ierr
+      error stop 'cudaMemcpy2D failed'
+    end if
+
+    ierr = cufftXtExecDescriptor(self%plan3D_fw, self%xtdesc, self%xtdesc, &
+                                 CUFFT_FORWARD)
+
+    if (ierr /= 0) then
+      write (stderr, *), 'cuFFT Error Code: ', ierr
+      error stop 'Forward 3D FFT execution failed'
+    end if
+
+  end subroutine fft_forward_cuda
+
+  subroutine fft_backward_cuda(self, f)
+    implicit none
+
+    class(cuda_poisson_fft_t) :: self
+    class(field_t), intent(inout) :: f
+
+    real(dp), device, pointer :: flat_dev(:, :), d_dev(:, :, :)
+
+    type(cudaXtDesc), pointer :: descriptor
+
+    integer :: ierr
+
+    ierr = cufftXtExecDescriptor(self%plan3D_bw, self%xtdesc, self%xtdesc, &
+                                 CUFFT_INVERSE)
+    if (ierr /= 0) then
+      write (stderr, *), 'cuFFT Error Code: ', ierr
+      error stop 'Backward 3D FFT execution failed'
+    end if
+
+    select type (f)
+    type is (cuda_field_t)
+      flat_dev(1:self%nx_loc, 1:self%ny_loc*self%nz_loc) => f%data_d
+    end select
+
+    call c_f_pointer(self%xtdesc%descriptor, descriptor)
+    call c_f_pointer(descriptor%data(1), d_dev, &
+                     [self%nx_loc + 2, self%ny_loc*self%nz_loc])
+    ierr = cudaMemcpy2D(flat_dev, self%nx_loc, d_dev, self%nx_loc + 2, &
+                        self%nx_loc, self%ny_loc*self%nz_loc)
+    if (ierr /= 0) then
+      print *, 'cudaMemcpy2D error code: ', ierr
+      error stop 'cudaMemcpy2D failed'
+    end if
+
+  end subroutine fft_backward_cuda
+
+  subroutine fft_postprocess_cuda(self)
+    implicit none
+
+    class(cuda_poisson_fft_t) :: self
+
+    type(cudaXtDesc), pointer :: descriptor
+
+    complex(dp), device, dimension(:, :, :), pointer :: c_dev
+    type(dim3) :: blocks, threads
+    integer :: tsize
+
+    ! obtain a pointer to descriptor so that we can carry out postprocessing
+    call c_f_pointer(self%xtdesc%descriptor, descriptor)
+    call c_f_pointer(descriptor%data(1), c_dev, &
+                     [self%nx_spec, self%ny_spec, self%nz_spec])
+
+    ! tsize is different than SZ, because here we work on a 3D Cartesian
+    ! data structure, and free to specify any suitable thread/block size.
+    tsize = 16
+    blocks = dim3((self%ny_spec - 1)/tsize + 1, self%nz_spec, 1)
+    threads = dim3(tsize, 1, 1)
+
+    ! Postprocess div_u in spectral space
+    call process_spectral_div_u<<<blocks, threads>>>( & !&
+      c_dev, self%waves_dev, self%nx_spec, self%ny_spec, self%y_sp_st, &
+      self%nx_glob, self%ny_glob, self%nz_glob, &
+      self%ax_dev, self%bx_dev, self%ay_dev, self%by_dev, &
+      self%az_dev, self%bz_dev &
+      )
+
+  end subroutine fft_postprocess_cuda
+
+end module m_cuda_poisson_fft
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/poisson_fft.f90~3.html b/api/sourcefile/poisson_fft.f90~3.html new file mode 100644 index 000000000..5b32eae2b --- /dev/null +++ b/api/sourcefile/poisson_fft.f90~3.html @@ -0,0 +1,449 @@ + + + + + + + + + + + + + poisson_fft.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

poisson_fft.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_poisson_fft
+  use m_allocator, only: field_t
+  use m_common, only: dp, pi, CELL
+  use m_tdsops, only: dirps_t
+  use m_mesh, only: mesh_t, geo_t
+
+  implicit none
+
+  type, abstract :: poisson_fft_t
+    !! FFT based Poisson solver
+    !> Global dimensions
+    integer :: nx_glob, ny_glob, nz_glob
+    !> Local dimensions
+    integer :: nx_loc, ny_loc, nz_loc
+    !> Local dimensions in the permuted slabs
+    integer :: nx_perm, ny_perm, nz_perm
+    !> Local dimensions in the permuted slabs in spectral space
+    integer :: nx_spec, ny_spec, nz_spec
+    !> Offset in y direction in the permuted slabs in spectral space
+    integer :: y_sp_st
+    !> Local domain sized array storing the spectral equivalence constants
+    complex(dp), allocatable, dimension(:, :, :) :: waves
+    !> Wave numbers in x, y, and z
+    complex(dp), allocatable, dimension(:) :: ax, bx, ay, by, az, bz
+  contains
+    procedure(fft_forward), deferred :: fft_forward
+    procedure(fft_backward), deferred :: fft_backward
+    procedure(fft_postprocess), deferred :: fft_postprocess
+    procedure :: base_init
+    procedure :: waves_set
+  end type poisson_fft_t
+
+  abstract interface
+    subroutine fft_forward(self, f_in)
+      import :: poisson_fft_t
+      import :: field_t
+      implicit none
+
+      class(poisson_fft_t) :: self
+      class(field_t), intent(in) :: f_in
+    end subroutine fft_forward
+
+    subroutine fft_backward(self, f_out)
+      import :: poisson_fft_t
+      import :: field_t
+      implicit none
+
+      class(poisson_fft_t) :: self
+      class(field_t), intent(inout) :: f_out
+    end subroutine fft_backward
+
+    subroutine fft_postprocess(self)
+      import :: poisson_fft_t
+      implicit none
+
+      class(poisson_fft_t) :: self
+    end subroutine fft_postprocess
+  end interface
+
+contains
+
+  subroutine base_init(self, mesh, xdirps, ydirps, zdirps)
+    implicit none
+
+    class(poisson_fft_t) :: self
+    class(mesh_t), intent(in) :: mesh
+    type(dirps_t), intent(in) :: xdirps, ydirps, zdirps
+
+    integer :: dims(3)
+
+    dims = mesh%get_global_dims(CELL)
+    self%nx_glob = dims(1); self%ny_glob = dims(2); self%nz_glob = dims(3)
+    dims = mesh%get_dims(CELL)
+    self%nx_loc = dims(1); self%ny_loc = dims(2); self%nz_loc = dims(3)
+
+    ! 1D decomposition along Z in real domain, and along Y in spectral space
+    if (mesh%par%nproc_dir(1) /= 1) print *, 'nproc_dir in x-dir must be 1'
+    if (mesh%par%nproc_dir(2) /= 1) print *, 'nproc_dir in y-dir must be 1'
+    self%nx_perm = self%nx_loc/mesh%par%nproc_dir(2)
+    self%ny_perm = self%ny_loc/mesh%par%nproc_dir(3)
+    self%nz_perm = self%nz_glob
+    self%nx_spec = self%nx_loc/2 + 1
+    self%ny_spec = self%ny_perm
+    self%nz_spec = self%nz_perm
+
+    self%y_sp_st = (self%ny_loc/mesh%par%nproc_dir(3))*mesh%par%nrank_dir(3)
+
+    allocate (self%ax(self%nx_glob), self%bx(self%nx_glob))
+    allocate (self%ay(self%ny_glob), self%by(self%ny_glob))
+    allocate (self%az(self%nz_glob), self%bz(self%nz_glob))
+
+    ! cuFFT 3D transform halves the first index.
+    allocate (self%waves(self%nx_spec, self%ny_spec, self%nz_spec))
+
+    ! waves_set requires some of the preprocessed tdsops variables.
+    call self%waves_set(mesh%geo, xdirps, ydirps, zdirps)
+
+  end subroutine base_init
+
+  subroutine waves_set(self, geo, xdirps, ydirps, zdirps)
+    !! Spectral equivalence constants
+    !!
+    !! Ref. JCP 228 (2009), 5989–6015, Sec 4
+    implicit none
+
+    class(poisson_fft_t) :: self
+    type(geo_t), intent(in) :: geo
+    type(dirps_t), intent(in) :: xdirps, ydirps, zdirps
+
+    complex(dp), allocatable, dimension(:) :: xkx, xk2, yky, yk2, zkz, zk2, &
+                                              exs, eys, ezs
+
+    integer :: nx, ny, nz, ix, iy, iz
+    real(dp) :: w, wp, rlexs, rleys, rlezs, xtt, ytt, ztt, xt1, yt1, zt1
+    complex(dp) :: xt2, yt2, zt2, xyzk
+    real(dp) :: d, L
+
+    integer :: i, j, k
+
+    nx = self%nx_glob; ny = self%ny_glob; nz = self%nz_glob
+
+    do i = 1, nx
+      self%ax(i) = sin((i - 1)*pi/nx)
+      self%bx(i) = cos((i - 1)*pi/nx)
+    end do
+
+    do i = 1, ny
+      self%ay(i) = sin((i - 1)*pi/ny)
+      self%by(i) = cos((i - 1)*pi/ny)
+    end do
+
+    do i = 1, nz
+      self%az(i) = sin((i - 1)*pi/nz)
+      self%bz(i) = cos((i - 1)*pi/nz)
+    end do
+
+    ! Now kxyz
+    allocate (xkx(nx), xk2(nx), exs(nx))
+    allocate (yky(ny), yk2(ny), eys(ny))
+    allocate (zkz(nz), zk2(nz), ezs(nz))
+    xkx(:) = 0; xk2(:) = 0; yky(:) = 0; yk2(:) = 0; zkz(:) = 0; zk2(:) = 0
+
+    ! periodic-x
+    d = geo%d(1)
+    L = geo%L(1)
+    do i = 1, nx/2 + 1
+      w = 2*pi*(i - 1)/nx
+      wp = xdirps%stagder_v2p%a*2*d*sin(0.5_dp*w) &
+           + xdirps%stagder_v2p%b*2*d*sin(1.5_dp*w)
+      wp = wp/(1._dp + 2*xdirps%stagder_v2p%alpha*cos(w))
+
+      xkx(i) = cmplx(1._dp, 1._dp, kind=dp)*(nx*wp/L)
+      exs(i) = cmplx(1._dp, 1._dp, kind=dp)*(nx*w/L)
+      xk2(i) = cmplx(1._dp, 1._dp, kind=dp)*(nx*wp/L)**2
+    end do
+    do i = nx/2 + 2, nx
+      xkx(i) = xkx(nx - i + 2)
+      exs(i) = exs(nx - i + 2)
+      xk2(i) = xk2(nx - i + 2)
+    end do
+
+    ! periodic-y
+    d = geo%d(2)
+    L = geo%L(2)
+    do i = 1, ny/2 + 1
+      w = 2*pi*(i - 1)/ny
+      wp = ydirps%stagder_v2p%a*2*d*sin(0.5_dp*w) &
+           + ydirps%stagder_v2p%b*2*d*sin(1.5_dp*w)
+      wp = wp/(1._dp + 2*ydirps%stagder_v2p%alpha*cos(w))
+
+      yky(i) = cmplx(1._dp, 1._dp, kind=dp)*(ny*wp/L)
+      eys(i) = cmplx(1._dp, 1._dp, kind=dp)*(ny*w/L)
+      yk2(i) = cmplx(1._dp, 1._dp, kind=dp)*(ny*wp/L)**2
+    end do
+    do i = ny/2 + 2, ny
+      yky(i) = yky(ny - i + 2)
+      eys(i) = eys(ny - i + 2)
+      yk2(i) = yk2(ny - i + 2)
+    end do
+
+    ! periodic-z
+    d = geo%d(3)
+    L = geo%L(3)
+    do i = 1, nz/2 + 1
+      w = 2*pi*(i - 1)/nz
+      wp = zdirps%stagder_v2p%a*2*d*sin(0.5_dp*w) &
+           + zdirps%stagder_v2p%b*2*d*sin(1.5_dp*w)
+      wp = wp/(1._dp + 2*zdirps%stagder_v2p%alpha*cos(w))
+
+      zkz(i) = cmplx(1._dp, 1._dp, kind=dp)*(nz*wp/L)
+      ezs(i) = cmplx(1._dp, 1._dp, kind=dp)*(nz*w/L)
+      zk2(i) = cmplx(1._dp, 1._dp, kind=dp)*(nz*wp/L)**2
+    end do
+    do i = nz/2 + 2, nz
+      zkz(i) = zkz(nz - i + 2)
+      ezs(i) = ezs(nz - i + 2)
+      zk2(i) = zk2(nz - i + 2)
+    end do
+
+    do i = 1, self%nx_spec
+      do j = 1, self%ny_spec
+        do k = 1, self%nz_spec
+          ix = i; iy = j + self%y_sp_st; iz = k
+          rlexs = real(exs(ix), kind=dp)*geo%d(1)
+          rleys = real(eys(iy), kind=dp)*geo%d(2)
+          rlezs = real(ezs(iz), kind=dp)*geo%d(3)
+
+          xtt = 2*(xdirps%interpl_v2p%a*cos(rlexs*0.5_dp) &
+                   + xdirps%interpl_v2p%b*cos(rlexs*1.5_dp) &
+                   + xdirps%interpl_v2p%c*cos(rlexs*2.5_dp) &
+                   + xdirps%interpl_v2p%d*cos(rlexs*3.5_dp))
+          ytt = 2*(ydirps%interpl_v2p%a*cos(rleys*0.5_dp) &
+                   + ydirps%interpl_v2p%b*cos(rleys*1.5_dp) &
+                   + ydirps%interpl_v2p%c*cos(rleys*2.5_dp) &
+                   + ydirps%interpl_v2p%d*cos(rleys*3.5_dp))
+          ztt = 2*(zdirps%interpl_v2p%a*cos(rlezs*0.5_dp) &
+                   + zdirps%interpl_v2p%b*cos(rlezs*1.5_dp) &
+                   + zdirps%interpl_v2p%c*cos(rlezs*2.5_dp) &
+                   + zdirps%interpl_v2p%d*cos(rlezs*3.5_dp))
+
+          xt1 = 1._dp + 2*xdirps%interpl_v2p%alpha*cos(rlexs)
+          yt1 = 1._dp + 2*ydirps%interpl_v2p%alpha*cos(rleys)
+          zt1 = 1._dp + 2*zdirps%interpl_v2p%alpha*cos(rlezs)
+
+          xt2 = xk2(ix)*(((ytt/yt1)*(ztt/zt1))**2)
+          yt2 = yk2(iy)*(((xtt/xt1)*(ztt/zt1))**2)
+          zt2 = zk2(iz)*(((xtt/xt1)*(ytt/yt1))**2)
+
+          xyzk = xt2 + yt2 + zt2
+          self%waves(i, j, k) = xyzk
+        end do
+      end do
+    end do
+
+  end subroutine waves_set
+
+end module m_poisson_fft
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/reorder.f90.html b/api/sourcefile/reorder.f90.html new file mode 100644 index 000000000..b5530e98d --- /dev/null +++ b/api/sourcefile/reorder.f90.html @@ -0,0 +1,500 @@ + + + + + + + + + + + + + reorder.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

reorder.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_kernels_reorder
+  use cudafor
+
+  use m_common, only: dp
+  use m_cuda_common, only: SZ
+
+contains
+
+  attributes(global) subroutine reorder_c2x(u_x, u_c, nz)
+    implicit none
+
+    real(dp), device, intent(out), dimension(:, :, :) :: u_x
+    real(dp), device, intent(in), dimension(:, :, :) :: u_c
+    integer, value, intent(in) :: nz
+
+    real(dp), shared :: tile(SZ, SZ)
+    integer :: i, j, b_i, b_j, b_k
+
+    i = threadIdx%x; j = threadIdx%y
+    b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z
+
+    ! copy into shared
+    tile(i, j) = u_c(i + (b_i - 1)*SZ, j + (b_j - 1)*SZ, b_k)
+
+    call syncthreads()
+
+    ! copy into output array from shared
+    u_x(i, j + (b_i - 1)*SZ, b_k + (b_j - 1)*nz) = tile(j, i)
+
+  end subroutine reorder_c2x
+
+  attributes(global) subroutine reorder_x2c(u_c, u_x, nz)
+    implicit none
+
+    real(dp), device, intent(out), dimension(:, :, :) :: u_c
+    real(dp), device, intent(in), dimension(:, :, :) :: u_x
+    integer, value, intent(in) :: nz
+
+    real(dp), shared :: tile(SZ, SZ)
+    integer :: i, j, b_i, b_j, b_k
+
+    i = threadIdx%x; j = threadIdx%y
+    b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z
+
+    ! copy into shared
+    tile(i, j) = u_x(i, j + (b_i - 1)*SZ, b_k + (b_j - 1)*nz)
+
+    call syncthreads()
+
+    ! copy into output array from shared
+    u_c(i + (b_i - 1)*SZ, j + (b_j - 1)*SZ, b_k) = tile(j, i)
+
+  end subroutine reorder_x2c
+
+  attributes(global) subroutine reorder_x2y(u_y, u_x, nz)
+    implicit none
+
+    real(dp), device, intent(out), dimension(:, :, :) :: u_y
+    real(dp), device, intent(in), dimension(:, :, :) :: u_x
+    integer, value, intent(in) :: nz
+
+    real(dp), shared :: tile(SZ, SZ)
+    integer :: i, j, b_i, b_j, b_k
+
+    i = threadIdx%x; j = threadIdx%y
+    b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z
+
+    ! copy into shared
+    tile(i, j) = u_x(i, j + (b_i - 1)*SZ, b_j + (b_k - 1)*nz)
+
+    call syncthreads()
+
+    ! copy into output array from shared
+    u_y(i, j + (b_k - 1)*SZ, b_j + (b_i - 1)*nz) = tile(j, i)
+
+  end subroutine reorder_x2y
+
+  attributes(global) subroutine reorder_x2z(u_z, u_x, nz)
+    implicit none
+
+    real(dp), device, intent(out), dimension(:, :, :) :: u_z
+    real(dp), device, intent(in), dimension(:, :, :) :: u_x
+    integer, value, intent(in) :: nz
+
+    integer :: i, j, b_i, b_j, nx
+
+    i = threadIdx%x; b_i = blockIdx%x; b_j = blockIdx%y
+    nx = gridDim%x
+
+    ! Data access pattern for reordering between x and z is quite nice
+    ! thus we don't need to use shared memory for this operation.
+    do j = 1, nz
+      u_z(i, j, b_i + (b_j - 1)*nx) = u_x(i, b_i, j + (b_j - 1)*nz)
+    end do
+
+  end subroutine reorder_x2z
+
+  attributes(global) subroutine reorder_y2x(u_x, u_y, nz)
+    implicit none
+
+    real(dp), device, intent(out), dimension(:, :, :) :: u_x
+    real(dp), device, intent(in), dimension(:, :, :) :: u_y
+    integer, value, intent(in) :: nz
+
+    real(dp), shared :: tile(SZ, SZ)
+    integer :: i, j, b_i, b_j, b_k
+
+    i = threadIdx%x; j = threadIdx%y
+    b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z
+
+    ! copy into shared
+    tile(i, j) = u_y(i, (b_j - 1)*SZ + j, (b_i - 1)*nz + b_k)
+
+    call syncthreads()
+
+    ! copy into output array from shared
+    u_x(i, (b_i - 1)*SZ + j, (b_j - 1)*nz + b_k) = tile(j, i)
+
+  end subroutine reorder_y2x
+
+  attributes(global) subroutine reorder_y2z(u_z, u_y, nx, nz)
+    implicit none
+
+    real(dp), device, intent(out), dimension(:, :, :) :: u_z
+    real(dp), device, intent(in), dimension(:, :, :) :: u_y
+    integer, value, intent(in) :: nx, nz
+
+    real(dp), shared :: tile(SZ, SZ)
+    integer :: i, j, b_i, b_j, b_k
+
+    i = threadIdx%x; j = threadIdx%y
+    b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z
+
+    ! copy into shared
+    tile(i, j) = u_y(i, (b_j - 1)*SZ + j, (b_i - 1)*nz + b_k)
+
+    call syncthreads()
+
+    ! copy into output array from shared
+    u_z(i, b_k, (b_i - 1)*SZ + j + (b_j - 1)*nx) = tile(j, i)
+
+  end subroutine reorder_y2z
+
+  attributes(global) subroutine reorder_z2x(u_x, u_z, nz)
+    implicit none
+
+    real(dp), device, intent(out), dimension(:, :, :) :: u_x
+    real(dp), device, intent(in), dimension(:, :, :) :: u_z
+    integer, value, intent(in) :: nz
+
+    integer :: i, j, b_i, b_j, nx
+
+    i = threadIdx%x; b_i = blockIdx%x; b_j = blockIdx%y
+    nx = gridDim%x
+
+    do j = 1, nz
+      u_x(i, b_i, j + (b_j - 1)*nz) = u_z(i, j, b_i + (b_j - 1)*nx)
+    end do
+
+  end subroutine reorder_z2x
+
+  attributes(global) subroutine reorder_z2y(u_y, u_z, nx, nz)
+    implicit none
+
+    real(dp), device, intent(out), dimension(:, :, :) :: u_y
+    real(dp), device, intent(in), dimension(:, :, :) :: u_z
+    integer, value, intent(in) :: nx, nz
+
+    real(dp), shared :: tile(SZ, SZ)
+    integer :: i, j, b_i, b_j, b_k
+
+    i = threadIdx%x; j = threadIdx%y
+    b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z
+
+    ! copy into shared
+    tile(i, j) = u_z(i, b_k, (b_i - 1)*SZ + j + (b_j - 1)*nx)
+
+    call syncthreads()
+
+    ! copy into output array from shared
+    u_y(i, (b_j - 1)*SZ + j, (b_i - 1)*nz + b_k) = tile(j, i)
+
+  end subroutine reorder_z2y
+
+  attributes(global) subroutine sum_yintox(u_x, u_y, nz)
+    implicit none
+
+    real(dp), device, intent(inout), dimension(:, :, :) :: u_x
+    real(dp), device, intent(in), dimension(:, :, :) :: u_y
+    integer, value, intent(in) :: nz
+
+    real(dp), shared :: tile(SZ, SZ)
+    integer :: i, j, b_i, b_j, b_k
+
+    i = threadIdx%x; j = threadIdx%y
+    b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z
+
+    ! copy into shared
+    tile(i, j) = u_y(i, (b_j - 1)*SZ + j, (b_k) + nz*(b_i - 1))
+
+    call syncthreads()
+
+    ! copy into output array from shared
+    u_x(i, (b_i - 1)*SZ + j, (b_j - 1)*nz + (b_k)) = &
+      u_x(i, (b_i - 1)*SZ + j, (b_j - 1)*nz + (b_k)) + tile(j, i)
+
+  end subroutine sum_yintox
+
+  attributes(global) subroutine sum_zintox(u_x, u_z, nz)
+    implicit none
+
+    ! Arguments
+    real(dp), device, intent(inout), dimension(:, :, :) :: u_x
+    real(dp), device, intent(in), dimension(:, :, :) :: u_z
+    integer, value, intent(in) :: nz
+
+    integer :: i, j, b_i, b_j, nx
+
+    i = threadIdx%x; b_i = blockIdx%x; b_j = blockIdx%y
+    nx = gridDim%x
+
+    do j = 1, nz
+      u_x(i, b_i, j + (b_j - 1)*nz) = u_x(i, b_i, j + (b_j - 1)*nz) &
+                                      + u_z(i, j, b_i + (b_j - 1)*nx)
+    end do
+
+  end subroutine sum_zintox
+
+  attributes(global) subroutine axpby(n, alpha, x, beta, y)
+    implicit none
+
+    integer, value, intent(in) :: n
+    real(dp), value, intent(in) :: alpha, beta
+    real(dp), device, intent(in), dimension(:, :, :) :: x
+    real(dp), device, intent(inout), dimension(:, :, :) :: y
+
+    integer :: i, j, b
+
+    i = threadIdx%x
+    b = blockIdx%x
+
+    do j = 1, n
+      y(i, j, b) = alpha*x(i, j, b) + beta*y(i, j, b)
+    end do
+
+  end subroutine axpby
+
+  attributes(global) subroutine scalar_product(s, x, y, n)
+    implicit none
+
+    real(dp), device, intent(inout) :: s
+    real(dp), device, intent(in), dimension(:, :, :) :: x, y
+    integer, value, intent(in) :: n
+
+    real(dp) :: s_pncl !! pencil sum
+    integer :: i, j, b, ierr
+
+    i = threadIdx%x
+    b = blockIdx%x
+
+    s_pncl = 0._dp
+    do j = 1, n
+      s_pncl = s_pncl + x(i, j, b)*y(i, j, b)
+    end do
+    ierr = atomicadd(s, s_pncl)
+
+  end subroutine scalar_product
+
+  attributes(global) subroutine buffer_copy(u_send_s, u_send_e, u, n, n_halo)
+    implicit none
+
+    real(dp), device, intent(inout), dimension(:, :, :) :: u_send_s, u_send_e
+    real(dp), device, intent(in), dimension(:, :, :) :: u
+    integer, value, intent(in) :: n, n_halo
+
+    integer :: i, j, b
+
+    i = threadIdx%x
+    b = blockIdx%x
+
+    do j = 1, n_halo
+      u_send_s(i, j, b) = u(i, j, b)
+      u_send_e(i, j, b) = u(i, n - n_halo + j, b)
+    end do
+
+  end subroutine buffer_copy
+
+end module m_cuda_kernels_reorder
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/sendrecv.f90.html b/api/sourcefile/sendrecv.f90.html new file mode 100644 index 000000000..11298556d --- /dev/null +++ b/api/sourcefile/sendrecv.f90.html @@ -0,0 +1,306 @@ + + + + + + + + + + + + + sendrecv.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sendrecv.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_sendrecv
+  use cudafor
+  use mpi
+
+  use m_common, only: dp
+
+  implicit none
+
+contains
+
+  subroutine sendrecv_fields(f_recv_s, f_recv_e, f_send_s, f_send_e, &
+                             n_data, nproc, prev, next)
+    implicit none
+
+    real(dp), device, dimension(:, :, :), intent(out) :: f_recv_s, f_recv_e
+    real(dp), device, dimension(:, :, :), intent(in) :: f_send_s, f_send_e
+    integer, intent(in) :: n_data, nproc, prev, next
+
+    integer :: req(4), err(4), ierr, tag = 1234
+
+    if (nproc == 1) then
+      f_recv_s = f_send_e
+      f_recv_e = f_send_s
+    else
+      call MPI_Isend(f_send_s, n_data, MPI_DOUBLE_PRECISION, &
+                     prev, tag, MPI_COMM_WORLD, req(1), err(1))
+      call MPI_Irecv(f_recv_e, n_data, MPI_DOUBLE_PRECISION, &
+                     next, tag, MPI_COMM_WORLD, req(2), err(2))
+      call MPI_Isend(f_send_e, n_data, MPI_DOUBLE_PRECISION, &
+                     next, tag, MPI_COMM_WORLD, req(3), err(3))
+      call MPI_Irecv(f_recv_s, n_data, MPI_DOUBLE_PRECISION, &
+                     prev, tag, MPI_COMM_WORLD, req(4), err(4))
+
+      call MPI_Waitall(4, req, MPI_STATUSES_IGNORE, ierr)
+    end if
+
+  end subroutine sendrecv_fields
+
+  subroutine sendrecv_3fields( &
+    f1_recv_s, f1_recv_e, f2_recv_s, f2_recv_e, f3_recv_s, f3_recv_e, &
+    f1_send_s, f1_send_e, f2_send_s, f2_send_e, f3_send_s, f3_send_e, &
+    n_data, nproc, prev, next &
+    )
+    implicit none
+
+    real(dp), device, dimension(:, :, :), intent(out) :: &
+      f1_recv_s, f1_recv_e, f2_recv_s, f2_recv_e, f3_recv_s, f3_recv_e
+    real(dp), device, dimension(:, :, :), intent(in) :: &
+      f1_send_s, f1_send_e, f2_send_s, f2_send_e, f3_send_s, f3_send_e
+    integer, intent(in) :: n_data, nproc, prev, next
+
+    integer :: req(12), err(12), ierr, tag = 1234
+
+    if (nproc == 1) then
+      f1_recv_s = f1_send_e
+      f1_recv_e = f1_send_s
+      f2_recv_s = f2_send_e
+      f2_recv_e = f2_send_s
+      f3_recv_s = f3_send_e
+      f3_recv_e = f3_send_s
+    else
+      call MPI_Isend(f1_send_s, n_data, MPI_DOUBLE_PRECISION, &
+                     prev, tag, MPI_COMM_WORLD, req(1), err(1))
+      call MPI_Irecv(f1_recv_e, n_data, MPI_DOUBLE_PRECISION, &
+                     next, tag, MPI_COMM_WORLD, req(2), err(2))
+      call MPI_Isend(f1_send_e, n_data, MPI_DOUBLE_PRECISION, &
+                     next, tag, MPI_COMM_WORLD, req(3), err(3))
+      call MPI_Irecv(f1_recv_s, n_data, MPI_DOUBLE_PRECISION, &
+                     prev, tag, MPI_COMM_WORLD, req(4), err(4))
+
+      call MPI_Isend(f2_send_s, n_data, MPI_DOUBLE_PRECISION, &
+                     prev, tag, MPI_COMM_WORLD, req(5), err(5))
+      call MPI_Irecv(f2_recv_e, n_data, MPI_DOUBLE_PRECISION, &
+                     next, tag, MPI_COMM_WORLD, req(6), err(6))
+      call MPI_Isend(f2_send_e, n_data, MPI_DOUBLE_PRECISION, &
+                     next, tag, MPI_COMM_WORLD, req(7), err(7))
+      call MPI_Irecv(f2_recv_s, n_data, MPI_DOUBLE_PRECISION, &
+                     prev, tag, MPI_COMM_WORLD, req(8), err(8))
+
+      call MPI_Isend(f3_send_s, n_data, MPI_DOUBLE_PRECISION, &
+                     prev, tag, MPI_COMM_WORLD, req(9), err(9))
+      call MPI_Irecv(f3_recv_e, n_data, MPI_DOUBLE_PRECISION, &
+                     next, tag, MPI_COMM_WORLD, req(10), err(10))
+      call MPI_Isend(f3_send_e, n_data, MPI_DOUBLE_PRECISION, &
+                     next, tag, MPI_COMM_WORLD, req(11), err(11))
+      call MPI_Irecv(f3_recv_s, n_data, MPI_DOUBLE_PRECISION, &
+                     prev, tag, MPI_COMM_WORLD, req(12), err(12))
+
+      call MPI_Waitall(12, req, MPI_STATUSES_IGNORE, ierr)
+    end if
+
+  end subroutine sendrecv_3fields
+
+end module m_cuda_sendrecv
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/sendrecv.f90~2.html b/api/sourcefile/sendrecv.f90~2.html new file mode 100644 index 000000000..f388eae86 --- /dev/null +++ b/api/sourcefile/sendrecv.f90~2.html @@ -0,0 +1,250 @@ + + + + + + + + + + + + + sendrecv.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

sendrecv.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_omp_sendrecv
+  use mpi
+
+  use m_common, only: dp
+
+  implicit none
+
+contains
+
+  subroutine sendrecv_fields(f_recv_s, f_recv_e, f_send_s, f_send_e, &
+                             n_data, nproc, prev, next)
+    implicit none
+
+    real(dp), dimension(:, :, :), intent(out) :: f_recv_s, f_recv_e
+    real(dp), dimension(:, :, :), intent(in) :: f_send_s, f_send_e
+    integer, intent(in) :: n_data, nproc, prev, next
+
+    integer :: req(4), err(4), ierr, tag = 1234
+
+    if (nproc == 1) then
+      f_recv_s = f_send_e
+      f_recv_e = f_send_s
+    else
+      call MPI_Isend(f_send_s, n_data, MPI_DOUBLE_PRECISION, &
+                     prev, tag, MPI_COMM_WORLD, req(1), err(1))
+      call MPI_Irecv(f_recv_e, n_data, MPI_DOUBLE_PRECISION, &
+                     next, tag, MPI_COMM_WORLD, req(2), err(2))
+      call MPI_Isend(f_send_e, n_data, MPI_DOUBLE_PRECISION, &
+                     next, tag, MPI_COMM_WORLD, req(3), err(3))
+      call MPI_Irecv(f_recv_s, n_data, MPI_DOUBLE_PRECISION, &
+                     prev, tag, MPI_COMM_WORLD, req(4), err(4))
+
+      call MPI_Waitall(4, req, MPI_STATUSES_IGNORE, ierr)
+    end if
+
+  end subroutine sendrecv_fields
+
+end module m_omp_sendrecv
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/solver.f90.html b/api/sourcefile/solver.f90.html new file mode 100644 index 000000000..2c2958476 --- /dev/null +++ b/api/sourcefile/solver.f90.html @@ -0,0 +1,761 @@ + + + + + + + + + + + + + solver.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

solver.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_solver
+  use iso_fortran_env, only: stderr => error_unit
+  use mpi
+
+  use m_allocator, only: allocator_t, field_t
+  use m_base_backend, only: base_backend_t
+  use m_common, only: dp, &
+                      RDR_X2Y, RDR_X2Z, RDR_Y2X, RDR_Y2Z, RDR_Z2X, RDR_Z2Y, &
+                      RDR_Z2C, RDR_C2Z, &
+                      DIR_X, DIR_Y, DIR_Z, DIR_C, VERT, CELL
+  use m_tdsops, only: tdsops_t, dirps_t
+  use m_time_integrator, only: time_intg_t
+  use m_vector_calculus, only: vector_calculus_t
+  use m_mesh, only: mesh_t
+
+  implicit none
+
+  type :: solver_t
+      !! solver class defines the Incompact3D algorithm at a very high level.
+      !!
+      !! Procedures defined here that are part of the Incompact3D algorithm
+      !! are: transeq, divergence, poisson, and gradient.
+      !!
+      !! The operations these high level procedures require are provided by
+      !! the relavant backend implementations.
+      !!
+      !! transeq procedure obtains the derivations in x, y, and z directions
+      !! using the transeq_x, transeq_y, and transeq_z operations provided by
+      !! the backend.
+      !! There are two different algorithms available for this operation, a
+      !! distributed algorithm and the Thomas algorithm. At the solver class
+      !! level it isn't known which algorithm will be executed, that is decided
+      !! at run time and therefore backend implementations are responsible for
+      !! executing the right subroutines.
+      !!
+      !! Allocator is responsible from giving us a field sized array when
+      !! requested. For example, when the derivations in x direction are
+      !! completed and we are ready for the y directional derivatives, we need
+      !! three fields to reorder and store the velocities in y direction. Also,
+      !! we need three more fields for storing the results, and the get_block
+      !! method of the allocator is used to arrange all these memory
+      !! assignments. Later, when a field is no more required, release_block
+      !! method of the allocator can be used to make this field available
+      !! for later use.
+
+    real(dp) :: dt, nu
+    integer :: n_iters, n_output
+    integer :: ngrid
+
+    class(field_t), pointer :: u, v, w
+
+    class(base_backend_t), pointer :: backend
+    class(mesh_t), pointer :: mesh
+    type(time_intg_t) :: time_integrator
+    type(allocator_t), pointer :: host_allocator
+    type(dirps_t), pointer :: xdirps, ydirps, zdirps
+    type(vector_calculus_t) :: vector_calculus
+    procedure(poisson_solver), pointer :: poisson => null()
+  contains
+    procedure :: transeq
+    procedure :: divergence_v2p
+    procedure :: gradient_p2v
+    procedure :: curl
+    procedure :: output
+    procedure :: run
+  end type solver_t
+
+  abstract interface
+    subroutine poisson_solver(self, pressure, div_u)
+      import :: solver_t
+      import :: field_t
+      implicit none
+
+      class(solver_t) :: self
+      class(field_t), intent(inout) :: pressure
+      class(field_t), intent(in) :: div_u
+    end subroutine poisson_solver
+  end interface
+
+  interface solver_t
+    module procedure init
+  end interface solver_t
+
+contains
+
+  function init(backend, mesh, host_allocator) result(solver)
+    implicit none
+
+    class(base_backend_t), target, intent(inout) :: backend
+    type(mesh_t), target, intent(inout) :: mesh
+    type(allocator_t), target, intent(inout) :: host_allocator
+    type(solver_t) :: solver
+
+    class(field_t), pointer :: u_init, v_init, w_init
+
+    character(len=200) :: input_file
+    real(dp) :: Re, dt
+    integer :: n_iters, n_output
+    character(3) :: poisson_solver_type, time_intg
+    character(30) :: der1st_scheme, der2nd_scheme, &
+                     interpl_scheme, stagder_scheme
+    namelist /solver_params/ Re, dt, n_iters, n_output, poisson_solver_type, &
+      time_intg, der1st_scheme, der2nd_scheme, &
+      interpl_scheme, stagder_scheme
+
+    real(dp) :: x, y, z
+    integer :: i, j, k
+    integer, dimension(3) :: dims
+    real(dp), dimension(3) :: xloc
+
+    solver%backend => backend
+    solver%mesh => mesh
+    solver%host_allocator => host_allocator
+
+    allocate (solver%xdirps, solver%ydirps, solver%zdirps)
+    solver%xdirps%dir = DIR_X
+    solver%ydirps%dir = DIR_Y
+    solver%zdirps%dir = DIR_Z
+
+    solver%vector_calculus = vector_calculus_t(solver%backend)
+
+    solver%u => solver%backend%allocator%get_block(DIR_X, VERT)
+    solver%v => solver%backend%allocator%get_block(DIR_X, VERT)
+    solver%w => solver%backend%allocator%get_block(DIR_X, VERT)
+
+    ! set defaults
+    poisson_solver_type = 'FFT'
+    time_intg = 'AB3'
+    der1st_scheme = 'compact6'; der2nd_scheme = 'compact6'
+    interpl_scheme = 'classic'; stagder_scheme = 'compact6'
+
+    if (command_argument_count() >= 1) then
+      call get_command_argument(1, input_file)
+      open (100, file=input_file)
+      read (100, nml=solver_params)
+      close (100)
+    else
+      error stop 'Input file is not provided.'
+    end if
+
+    solver%time_integrator = time_intg_t(solver%backend, &
+                                         solver%backend%allocator, &
+                                         time_intg)
+    if (solver%mesh%par%is_root()) then
+      print *, time_intg//' time integrator instantiated'
+    end if
+
+    solver%dt = dt
+    solver%backend%nu = 1._dp/Re
+    solver%n_iters = n_iters
+    solver%n_output = n_output
+    solver%ngrid = product(solver%mesh%get_global_dims(VERT))
+
+    dims = solver%mesh%get_dims(VERT)
+    u_init => solver%host_allocator%get_block(DIR_C)
+    v_init => solver%host_allocator%get_block(DIR_C)
+    w_init => solver%host_allocator%get_block(DIR_C)
+
+    do k = 1, dims(3)
+      do j = 1, dims(2)
+        do i = 1, dims(1)
+          xloc = solver%mesh%get_coordinates(i, j, k)
+          x = xloc(1)
+          y = xloc(2)
+          z = xloc(3)
+
+          u_init%data(i, j, k) = sin(x)*cos(y)*cos(z)
+          v_init%data(i, j, k) = -cos(x)*sin(y)*cos(z)
+          w_init%data(i, j, k) = 0
+        end do
+      end do
+    end do
+
+    call solver%backend%set_field_data(solver%u, u_init%data)
+    call solver%backend%set_field_data(solver%v, v_init%data)
+    call solver%backend%set_field_data(solver%w, w_init%data)
+
+    call solver%host_allocator%release_block(u_init)
+    call solver%host_allocator%release_block(v_init)
+    call solver%host_allocator%release_block(w_init)
+
+    ! Allocate and set the tdsops
+    call allocate_tdsops(solver%xdirps, solver%backend, &
+                         der1st_scheme, der2nd_scheme, &
+                         interpl_scheme, stagder_scheme)
+    call allocate_tdsops(solver%ydirps, solver%backend, &
+                         der1st_scheme, der2nd_scheme, &
+                         interpl_scheme, stagder_scheme)
+    call allocate_tdsops(solver%zdirps, solver%backend, &
+                         der1st_scheme, der2nd_scheme, &
+                         interpl_scheme, stagder_scheme)
+
+    select case (trim(poisson_solver_type))
+    case ('FFT')
+      if (solver%mesh%par%is_root()) print *, 'Poisson solver: FFT'
+      call solver%backend%init_poisson_fft(solver%mesh, solver%xdirps, &
+                                           solver%ydirps, solver%zdirps)
+      solver%poisson => poisson_fft
+    case ('CG')
+      if (solver%mesh%par%is_root()) &
+        print *, 'Poisson solver: CG, not yet implemented'
+      solver%poisson => poisson_cg
+    case default
+      error stop 'poisson_solver_type is not valid. Use "FFT" or "CG".'
+    end select
+
+  end function init
+
+  subroutine allocate_tdsops(dirps, backend, der1st_scheme, der2nd_scheme, &
+                             interpl_scheme, stagder_scheme)
+    type(dirps_t), intent(inout) :: dirps
+    class(base_backend_t), intent(in) :: backend
+    character(*), intent(in) :: der1st_scheme, der2nd_scheme, &
+                                interpl_scheme, stagder_scheme
+
+    call backend%alloc_tdsops(dirps%der1st, dirps%dir, &
+                              'first-deriv', der1st_scheme)
+    call backend%alloc_tdsops(dirps%der1st_sym, dirps%dir, &
+                              'first-deriv', der1st_scheme)
+    call backend%alloc_tdsops(dirps%der2nd, dirps%dir, &
+                              'second-deriv', der2nd_scheme)
+    call backend%alloc_tdsops(dirps%der2nd_sym, dirps%dir, &
+                              'second-deriv', der2nd_scheme)
+    call backend%alloc_tdsops(dirps%interpl_v2p, dirps%dir, &
+                              'interpolate', interpl_scheme, from_to='v2p')
+    call backend%alloc_tdsops(dirps%interpl_p2v, dirps%dir, &
+                              'interpolate', interpl_scheme, from_to='p2v')
+    call backend%alloc_tdsops(dirps%stagder_v2p, dirps%dir, &
+                              'stag-deriv', stagder_scheme, from_to='v2p')
+    call backend%alloc_tdsops(dirps%stagder_p2v, dirps%dir, &
+                              'stag-deriv', stagder_scheme, from_to='p2v')
+
+  end subroutine
+
+  subroutine transeq(self, du, dv, dw, u, v, w)
+      !! Skew-symmetric form of convection-diffusion terms in the
+      !! incompressible Navier-Stokes momemtum equations, excluding
+      !! pressure terms.
+      !! Inputs from velocity grid and outputs to velocity grid.
+    implicit none
+
+    class(solver_t) :: self
+    class(field_t), intent(inout) :: du, dv, dw
+    class(field_t), intent(in) :: u, v, w
+
+    class(field_t), pointer :: u_y, v_y, w_y, u_z, v_z, w_z, &
+      du_y, dv_y, dw_y, du_z, dv_z, dw_z
+
+    ! -1/2(nabla u curl u + u nabla u) + nu nablasq u
+
+    ! call derivatives in x direction. Based on the run time arguments this
+    ! executes a distributed algorithm or the Thomas algorithm.
+    call self%backend%transeq_x(du, dv, dw, u, v, w, self%xdirps)
+
+    ! request fields from the allocator
+    u_y => self%backend%allocator%get_block(DIR_Y, VERT)
+    v_y => self%backend%allocator%get_block(DIR_Y, VERT)
+    w_y => self%backend%allocator%get_block(DIR_Y, VERT)
+    du_y => self%backend%allocator%get_block(DIR_Y)
+    dv_y => self%backend%allocator%get_block(DIR_Y)
+    dw_y => self%backend%allocator%get_block(DIR_Y)
+
+    ! reorder data from x orientation to y orientation
+    call self%backend%reorder(u_y, u, RDR_X2Y)
+    call self%backend%reorder(v_y, v, RDR_X2Y)
+    call self%backend%reorder(w_y, w, RDR_X2Y)
+
+    ! similar to the x direction, obtain derivatives in y.
+    call self%backend%transeq_y(du_y, dv_y, dw_y, u_y, v_y, w_y, self%ydirps)
+
+    ! we don't need the velocities in y orientation any more, so release
+    ! them to open up space.
+    ! It is important that this doesn't actually deallocate any memory,
+    ! it just makes the corresponding memory space available for use.
+    call self%backend%allocator%release_block(u_y)
+    call self%backend%allocator%release_block(v_y)
+    call self%backend%allocator%release_block(w_y)
+
+    call self%backend%sum_yintox(du, du_y)
+    call self%backend%sum_yintox(dv, dv_y)
+    call self%backend%sum_yintox(dw, dw_y)
+
+    call self%backend%allocator%release_block(du_y)
+    call self%backend%allocator%release_block(dv_y)
+    call self%backend%allocator%release_block(dw_y)
+
+    ! just like in y direction, get some fields for the z derivatives.
+    u_z => self%backend%allocator%get_block(DIR_Z, VERT)
+    v_z => self%backend%allocator%get_block(DIR_Z, VERT)
+    w_z => self%backend%allocator%get_block(DIR_Z, VERT)
+    du_z => self%backend%allocator%get_block(DIR_Z)
+    dv_z => self%backend%allocator%get_block(DIR_Z)
+    dw_z => self%backend%allocator%get_block(DIR_Z)
+
+    ! reorder from x to z
+    call self%backend%reorder(u_z, u, RDR_X2Z)
+    call self%backend%reorder(v_z, v, RDR_X2Z)
+    call self%backend%reorder(w_z, w, RDR_X2Z)
+
+    ! get the derivatives in z
+    call self%backend%transeq_z(du_z, dv_z, dw_z, u_z, v_z, w_z, self%zdirps)
+
+    ! there is no need to keep velocities in z orientation around, so release
+    call self%backend%allocator%release_block(u_z)
+    call self%backend%allocator%release_block(v_z)
+    call self%backend%allocator%release_block(w_z)
+
+    ! gather all the contributions into the x result array
+    call self%backend%sum_zintox(du, du_z)
+    call self%backend%sum_zintox(dv, dv_z)
+    call self%backend%sum_zintox(dw, dw_z)
+
+    ! release all the unnecessary blocks.
+    call self%backend%allocator%release_block(du_z)
+    call self%backend%allocator%release_block(dv_z)
+    call self%backend%allocator%release_block(dw_z)
+
+  end subroutine transeq
+
+  subroutine divergence_v2p(self, div_u, u, v, w)
+    !! Wrapper for divergence_v2p
+    implicit none
+
+    class(solver_t) :: self
+    class(field_t), intent(inout) :: div_u
+    class(field_t), intent(in) :: u, v, w
+
+    call self%vector_calculus%divergence_v2c( &
+      div_u, u, v, w, &
+      self%xdirps%stagder_v2p, self%xdirps%interpl_v2p, &
+      self%ydirps%stagder_v2p, self%ydirps%interpl_v2p, &
+      self%zdirps%stagder_v2p, self%zdirps%interpl_v2p &
+      )
+
+  end subroutine divergence_v2p
+
+  subroutine gradient_p2v(self, dpdx, dpdy, dpdz, pressure)
+    !! Wrapper for gradient_p2v
+    implicit none
+
+    class(solver_t) :: self
+    class(field_t), intent(inout) :: dpdx, dpdy, dpdz
+    class(field_t), intent(in) :: pressure
+
+    call self%vector_calculus%gradient_c2v( &
+      dpdx, dpdy, dpdz, pressure, &
+      self%xdirps%stagder_p2v, self%xdirps%interpl_p2v, &
+      self%ydirps%stagder_p2v, self%ydirps%interpl_p2v, &
+      self%zdirps%stagder_p2v, self%zdirps%interpl_p2v &
+      )
+
+  end subroutine gradient_p2v
+
+  subroutine curl(self, o_i_hat, o_j_hat, o_k_hat, u, v, w)
+    !! Wrapper for curl
+    implicit none
+
+    class(solver_t) :: self
+    !> Vector components of the output vector field Omega
+    class(field_t), intent(inout) :: o_i_hat, o_j_hat, o_k_hat
+    class(field_t), intent(in) :: u, v, w
+
+    call self%vector_calculus%curl( &
+      o_i_hat, o_j_hat, o_k_hat, u, v, w, &
+      self%xdirps%der1st, self%ydirps%der1st, self%zdirps%der1st &
+      )
+
+  end subroutine curl
+
+  subroutine poisson_fft(self, pressure, div_u)
+    implicit none
+
+    class(solver_t) :: self
+    class(field_t), intent(inout) :: pressure
+    class(field_t), intent(in) :: div_u
+
+    class(field_t), pointer :: p_temp
+
+    ! reorder into 3D Cartesian data structure
+    p_temp => self%backend%allocator%get_block(DIR_C, CELL)
+    call self%backend%reorder(p_temp, div_u, RDR_Z2C)
+
+    ! call forward FFT
+    ! output array in spectral space is stored at poisson_fft class
+    call self%backend%poisson_fft%fft_forward(p_temp)
+
+    ! postprocess
+    call self%backend%poisson_fft%fft_postprocess
+
+    ! call backward FFT
+    call self%backend%poisson_fft%fft_backward(p_temp)
+
+    ! reorder back to our specialist data structure from 3D Cartesian
+    call self%backend%reorder(pressure, p_temp, RDR_C2Z)
+
+    call self%backend%allocator%release_block(p_temp)
+
+  end subroutine poisson_fft
+
+  subroutine poisson_cg(self, pressure, div_u)
+    implicit none
+
+    class(solver_t) :: self
+    class(field_t), intent(inout) :: pressure
+    class(field_t), intent(in) :: div_u
+
+  end subroutine poisson_cg
+
+  subroutine output(self, t)
+    implicit none
+
+    class(solver_t), intent(in) :: self
+    real(dp), intent(in) :: t
+
+    class(field_t), pointer :: du, dv, dw, div_u
+    class(field_t), pointer :: u_out
+    real(dp) :: enstrophy, div_u_max, div_u_mean
+    integer :: ierr
+
+    if (self%mesh%par%is_root()) print *, 'time = ', t
+
+    du => self%backend%allocator%get_block(DIR_X, VERT)
+    dv => self%backend%allocator%get_block(DIR_X, VERT)
+    dw => self%backend%allocator%get_block(DIR_X, VERT)
+
+    call self%curl(du, dv, dw, self%u, self%v, self%w)
+    enstrophy = 0.5_dp*(self%backend%scalar_product(du, du) &
+                        + self%backend%scalar_product(dv, dv) &
+                        + self%backend%scalar_product(dw, dw))/self%ngrid
+    if (self%mesh%par%is_root()) print *, 'enstrophy:', enstrophy
+
+    call self%backend%allocator%release_block(du)
+    call self%backend%allocator%release_block(dv)
+    call self%backend%allocator%release_block(dw)
+
+    div_u => self%backend%allocator%get_block(DIR_Z)
+
+    call self%divergence_v2p(div_u, self%u, self%v, self%w)
+
+    u_out => self%host_allocator%get_block(DIR_C)
+    call self%backend%get_field_data(u_out%data, div_u)
+
+    call self%backend%allocator%release_block(div_u)
+
+    div_u_max = maxval(abs(u_out%data))
+    div_u_mean = sum(abs(u_out%data))/self%ngrid
+
+    call self%host_allocator%release_block(u_out)
+
+    call MPI_Allreduce(MPI_IN_PLACE, div_u_max, 1, MPI_DOUBLE_PRECISION, &
+                       MPI_MAX, MPI_COMM_WORLD, ierr)
+    call MPI_Allreduce(MPI_IN_PLACE, div_u_mean, 1, MPI_DOUBLE_PRECISION, &
+                       MPI_SUM, MPI_COMM_WORLD, ierr)
+    if (self%mesh%par%is_root()) &
+      print *, 'div u max mean:', div_u_max, div_u_mean
+
+  end subroutine output
+
+  subroutine run(self)
+    implicit none
+
+    class(solver_t), intent(inout) :: self
+
+    class(field_t), pointer :: du, dv, dw, div_u, pressure, dpdx, dpdy, dpdz
+    class(field_t), pointer :: u_out, v_out, w_out
+
+    real(dp) :: t
+    integer :: i, j
+
+    if (self%mesh%par%is_root()) print *, 'initial conditions'
+    t = 0._dp
+    call self%output(t)
+
+    if (self%mesh%par%is_root()) print *, 'start run'
+
+    do i = 1, self%n_iters
+      do j = 1, self%time_integrator%nstage
+        du => self%backend%allocator%get_block(DIR_X)
+        dv => self%backend%allocator%get_block(DIR_X)
+        dw => self%backend%allocator%get_block(DIR_X)
+
+        call self%transeq(du, dv, dw, self%u, self%v, self%w)
+
+        ! time integration
+        call self%time_integrator%step(self%u, self%v, self%w, &
+                                       du, dv, dw, self%dt)
+
+        call self%backend%allocator%release_block(du)
+        call self%backend%allocator%release_block(dv)
+        call self%backend%allocator%release_block(dw)
+
+        ! pressure
+        div_u => self%backend%allocator%get_block(DIR_Z)
+
+        call self%divergence_v2p(div_u, self%u, self%v, self%w)
+
+        pressure => self%backend%allocator%get_block(DIR_Z, CELL)
+
+        call self%poisson(pressure, div_u)
+
+        call self%backend%allocator%release_block(div_u)
+
+        dpdx => self%backend%allocator%get_block(DIR_X)
+        dpdy => self%backend%allocator%get_block(DIR_X)
+        dpdz => self%backend%allocator%get_block(DIR_X)
+
+        call self%gradient_p2v(dpdx, dpdy, dpdz, pressure)
+
+        call self%backend%allocator%release_block(pressure)
+
+        ! velocity correction
+        call self%backend%vecadd(-1._dp, dpdx, 1._dp, self%u)
+        call self%backend%vecadd(-1._dp, dpdy, 1._dp, self%v)
+        call self%backend%vecadd(-1._dp, dpdz, 1._dp, self%w)
+
+        call self%backend%allocator%release_block(dpdx)
+        call self%backend%allocator%release_block(dpdy)
+        call self%backend%allocator%release_block(dpdz)
+      end do
+
+      if (mod(i, self%n_output) == 0) then
+        t = i*self%dt
+        call self%output(t)
+      end if
+    end do
+
+    if (self%mesh%par%is_root()) print *, 'run end'
+
+    ! Below is for demonstrating purpuses only, to be removed when we have
+    ! proper I/O in place.
+    u_out => self%host_allocator%get_block(DIR_C)
+    v_out => self%host_allocator%get_block(DIR_C)
+    w_out => self%host_allocator%get_block(DIR_C)
+
+    call self%backend%get_field_data(u_out%data, self%u)
+    call self%backend%get_field_data(v_out%data, self%v)
+    call self%backend%get_field_data(w_out%data, self%w)
+
+    if (self%mesh%par%is_root()) then
+      print *, 'norms', norm2(u_out%data), norm2(v_out%data), norm2(w_out%data)
+    end if
+
+    call self%host_allocator%release_block(u_out)
+    call self%host_allocator%release_block(v_out)
+    call self%host_allocator%release_block(w_out)
+
+  end subroutine run
+
+end module m_solver
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/spectral_processing.f90.html b/api/sourcefile/spectral_processing.f90.html new file mode 100644 index 000000000..bdb106ff1 --- /dev/null +++ b/api/sourcefile/spectral_processing.f90.html @@ -0,0 +1,321 @@ + + + + + + + + + + + + + spectral_processing.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

spectral_processing.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_spectral
+  use cudafor
+
+  use m_common, only: dp
+
+  implicit none
+
+contains
+
+  attributes(global) subroutine process_spectral_div_u( &
+    div_u, waves, nx_spec, ny_spec, y_sp_st, nx, ny, nz, &
+    ax, bx, ay, by, az, bz &
+    )
+    !! Post-processes the divergence of velocity in spectral space, including
+    !! scaling w.r.t. grid size.
+    !!
+    !! Ref. JCP 228 (2009), 5989–6015, Sec 4
+    implicit none
+
+    !> Divergence of velocity in spectral space
+    complex(dp), device, intent(inout), dimension(:, :, :) :: div_u
+    !> Spectral equivalence constants
+    complex(dp), device, intent(in), dimension(:, :, :) :: waves
+    real(dp), device, intent(in), dimension(:) :: ax, bx, ay, by, az, bz
+    !> Grid size in spectral space
+    integer, value, intent(in) :: nx_spec, ny_spec
+    !> Offset in y direction in the permuted slabs in spectral space
+    integer, value, intent(in) :: y_sp_st
+    !> Grid size
+    integer, value, intent(in) :: nx, ny, nz
+
+    integer :: i, j, k, ix, iy, iz
+    real(dp) :: tmp_r, tmp_c, div_r, div_c
+
+    j = threadIdx%x + (blockIdx%x - 1)*blockDim%x
+    k = blockIdx%y ! nz_spec
+
+    if (j <= ny_spec) then
+      do i = 1, nx_spec
+        ! normalisation
+        div_r = real(div_u(i, j, k), kind=dp)/(nx*ny*nz)
+        div_c = aimag(div_u(i, j, k))/(nx*ny*nz)
+
+        ix = i; iy = j + y_sp_st; iz = k
+
+        ! post-process forward
+        ! post-process in z
+        tmp_r = div_r
+        tmp_c = div_c
+        div_r = tmp_r*bz(iz) + tmp_c*az(iz)
+        div_c = tmp_c*bz(iz) - tmp_r*az(iz)
+        if (iz > nz/2 + 1) div_r = -div_r
+        if (iz > nz/2 + 1) div_c = -div_c
+
+        ! post-process in y
+        tmp_r = div_r
+        tmp_c = div_c
+        div_r = tmp_r*by(iy) + tmp_c*ay(iy)
+        div_c = tmp_c*by(iy) - tmp_r*ay(iy)
+        if (iy > ny/2 + 1) div_r = -div_r
+        if (iy > ny/2 + 1) div_c = -div_c
+
+        ! post-process in x
+        tmp_r = div_r
+        tmp_c = div_c
+        div_r = tmp_r*bx(ix) + tmp_c*ax(ix)
+        div_c = tmp_c*bx(ix) - tmp_r*ax(ix)
+
+        ! Solve Poisson
+        tmp_r = real(waves(i, j, k), kind=dp)
+        tmp_c = aimag(waves(i, j, k))
+        if ((tmp_r < 1.e-16_dp) .or. (tmp_c < 1.e-16_dp)) then
+          div_r = 0._dp; div_c = 0._dp
+        else
+          div_r = -div_r/tmp_r
+          div_c = -div_c/tmp_c
+        end if
+
+        ! post-process backward
+        ! post-process in z
+        tmp_r = div_r
+        tmp_c = div_c
+        div_r = tmp_r*bz(iz) - tmp_c*az(iz)
+        div_c = -tmp_c*bz(iz) - tmp_r*az(iz)
+        if (iz > nz/2 + 1) div_r = -div_r
+        if (iz > nz/2 + 1) div_c = -div_c
+
+        ! post-process in y
+        tmp_r = div_r
+        tmp_c = div_c
+        div_r = tmp_r*by(iy) + tmp_c*ay(iy)
+        div_c = tmp_c*by(iy) - tmp_r*ay(iy)
+        if (iy > ny/2 + 1) div_r = -div_r
+        if (iy > ny/2 + 1) div_c = -div_c
+
+        ! post-process in x
+        tmp_r = div_r
+        tmp_c = div_c
+        div_r = tmp_r*bx(ix) + tmp_c*ax(ix)
+        div_c = -tmp_c*bx(ix) + tmp_r*ax(ix)
+
+        ! update the entry
+        div_u(i, j, k) = cmplx(div_r, div_c, kind=dp)
+      end do
+    end if
+
+  end subroutine process_spectral_div_u
+
+end module m_cuda_spectral
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/tdsops.f90.html b/api/sourcefile/tdsops.f90.html new file mode 100644 index 000000000..3b6a2c11c --- /dev/null +++ b/api/sourcefile/tdsops.f90.html @@ -0,0 +1,293 @@ + + + + + + + + + + + + + tdsops.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

tdsops.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_tdsops
+  use iso_fortran_env, only: stderr => error_unit
+
+  use m_common, only: dp
+  use m_tdsops, only: tdsops_t, tdsops_init
+
+  implicit none
+
+  type, extends(tdsops_t) :: cuda_tdsops_t
+    !! CUDA extension of the Tridiagonal Solver Operators class.
+    !!
+    !! Regular tdsops_t class is initiated and the coefficient arrays are
+    !! copied into device arrays so that cuda kernels can use them.
+    real(dp), device, allocatable :: dist_fw_dev(:), dist_bw_dev(:), &
+                                     dist_sa_dev(:), dist_sc_dev(:), &
+                                     dist_af_dev(:)
+    real(dp), device, allocatable :: thom_f_dev(:), thom_s_dev(:), &
+                                     thom_w_dev(:), thom_p_dev(:)
+    real(dp), device, allocatable :: coeffs_dev(:), &
+                                     coeffs_s_dev(:, :), coeffs_e_dev(:, :)
+  contains
+  end type cuda_tdsops_t
+
+  interface cuda_tdsops_t
+    module procedure cuda_tdsops_init
+  end interface cuda_tdsops_t
+
+contains
+
+  function cuda_tdsops_init(n, delta, operation, scheme, n_halo, from_to, &
+                            bc_start, bc_end, sym, c_nu, nu0_nu) &
+    result(tdsops)
+      !! Constructor function for the cuda_tdsops_t class.
+      !! See tdsops_t for details.
+    implicit none
+
+    type(cuda_tdsops_t) :: tdsops !! return value of the function
+
+    integer, intent(in) :: n
+    real(dp), intent(in) :: delta
+    character(*), intent(in) :: operation, scheme
+    integer, optional, intent(in) :: n_halo
+    character(*), optional, intent(in) :: from_to, bc_start, bc_end
+    logical, optional, intent(in) :: sym
+    real(dp), optional, intent(in) :: c_nu, nu0_nu
+
+    integer :: n_stencil
+
+    tdsops%tdsops_t = tdsops_init(n, delta, operation, scheme, n_halo, &
+                                  from_to, bc_start, bc_end, sym, &
+                                  c_nu, nu0_nu)
+
+    n_stencil = 2*tdsops%n_halo + 1
+
+    allocate (tdsops%dist_fw_dev(n), tdsops%dist_bw_dev(n))
+    allocate (tdsops%dist_sa_dev(n), tdsops%dist_sc_dev(n))
+    allocate (tdsops%dist_af_dev(n))
+    allocate (tdsops%thom_f_dev(n), tdsops%thom_s_dev(n))
+    allocate (tdsops%thom_w_dev(n), tdsops%thom_p_dev(n))
+    allocate (tdsops%coeffs_dev(n_stencil))
+    allocate (tdsops%coeffs_s_dev(n_stencil, tdsops%n_halo))
+    allocate (tdsops%coeffs_e_dev(n_stencil, tdsops%n_halo))
+
+    tdsops%dist_fw_dev(:) = tdsops%dist_fw(:)
+    tdsops%dist_bw_dev(:) = tdsops%dist_bw(:)
+    tdsops%dist_sa_dev(:) = tdsops%dist_sa(:)
+    tdsops%dist_sc_dev(:) = tdsops%dist_sc(:)
+    tdsops%dist_af_dev(:) = tdsops%dist_af(:)
+
+    tdsops%thom_f_dev(:) = tdsops%thom_f(:)
+    tdsops%thom_s_dev(:) = tdsops%thom_s(:)
+    tdsops%thom_w_dev(:) = tdsops%thom_w(:)
+    tdsops%thom_p_dev(:) = tdsops%thom_p(:)
+
+    tdsops%coeffs_dev(:) = tdsops%coeffs(:)
+    tdsops%coeffs_s_dev(:, :) = tdsops%coeffs_s(:, :)
+    tdsops%coeffs_e_dev(:, :) = tdsops%coeffs_e(:, :)
+
+  end function cuda_tdsops_init
+
+end module m_cuda_tdsops
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/tdsops.f90~2.html b/api/sourcefile/tdsops.f90~2.html new file mode 100644 index 000000000..4e16653b3 --- /dev/null +++ b/api/sourcefile/tdsops.f90~2.html @@ -0,0 +1,1076 @@ + + + + + + + + + + + + + tdsops.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

tdsops.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_tdsops
+  use iso_fortran_env, only: stderr => error_unit
+
+  use m_common, only: dp, pi, VERT, CELL, none
+  use m_mesh, only: mesh_t
+
+  implicit none
+
+  type :: tdsops_t
+      !! Tridiagonal Solver Operators class.
+      !!
+      !! Operator arrays are preprocessed in this class based on the arguments
+      !! provided. dist_fw and dist_bw are used in the first phase of the
+      !! distributed tridiagonal solver algorithm. dist_sa and dist_sc are used
+      !! in the final substitution phase. See the kernels_dist.f90 files in the
+      !! relevant backend folders.
+      !! coeff arrays define the specific rules of building the RHS
+      !! corresponding to the tridiagonal system to be solved, and used only in
+      !! the first phase of the distributed algorithm when building the RHS.
+      !! If a boundary condition is defined then coeffs_s and coeffs_e differ
+      !! from coeffs array and define the RHS rule for the first and last 4
+      !! entries in the tridiagonal system (n_halo = 4).
+      !!
+      !! This class does not know about the current rank or its relative
+      !! location among other ranks. All the operator arrays here are used when
+      !! executing a distributed tridiagonal solver phase one or two.
+    real(dp), allocatable, dimension(:) :: dist_fw, dist_bw, & !! fw/bw phase
+                                           dist_sa, dist_sc, & !! back subs.
+                                           dist_af !! the auxiliary factors
+    real(dp), allocatable, dimension(:) :: thom_f, thom_s, thom_w, thom_p
+    real(dp), allocatable :: coeffs(:), coeffs_s(:, :), coeffs_e(:, :)
+    real(dp) :: alpha, a, b, c = 0._dp, d = 0._dp
+    logical :: periodic
+    integer :: tds_n
+    integer :: dir
+    integer :: n_halo
+  contains
+    procedure :: deriv_1st, deriv_2nd, interpl_mid, stagder_1st
+    procedure :: preprocess_dist, preprocess_thom
+  end type tdsops_t
+
+  interface tdsops_t
+    module procedure tdsops_init
+  end interface tdsops_t
+
+  type :: dirps_t
+    !! Directional tridiagonal solver container.
+    !!
+    !! This class contains the preprocessed tridiagonal solvers for operating
+    !! in each coordinate direction.
+    class(tdsops_t), allocatable :: der1st, der1st_sym, der2nd, der2nd_sym, &
+      stagder_v2p, stagder_p2v, interpl_v2p, interpl_p2v
+    integer :: dir
+  end type dirps_t
+
+contains
+
+  function tdsops_init(tds_n, delta, operation, scheme, n_halo, from_to, &
+                       bc_start, bc_end, sym, c_nu, nu0_nu) result(tdsops)
+      !! Constructor function for the tdsops_t class.
+      !!
+      !! 'n', 'delta', 'operation', and 'scheme' are necessary arguments.
+      !! Number of points 'n', distance between two points 'delta', the
+      !! 'operation' the tridiagonal system defines, and the 'scheme' that
+      !! specifies the exact scheme we choose to apply for the operation.
+      !! The remaining arguments are optional.
+      !! 'from_to' is necessary for interpolation and staggared derivative, and
+      !! it can be 'v2p' or 'p2v'.
+      !! If the specific region the instance is operating is not a boundary
+      !! region, then 'bc_start' and 'bc_end' are either 'null' or not defined.
+      !! 'sym' is relevant when the boundary condition is free-slip. If sym is
+      !! .true. then it means the field we operate on is assumed to be an even
+      !! function (symmetric) accross the boundary. If it is .false. it means
+      !! that the field is assumed to be an odd function (anti-symmetric).
+      !! 'c_nu', 'nu0_nu' are relevant when operation is second order
+      !! derivative and scheme is compact6-hyperviscous.
+    implicit none
+
+    type(tdsops_t) :: tdsops !! return value of the function
+
+    integer, intent(in) :: tds_n
+    real(dp), intent(in) :: delta
+    character(*), intent(in) :: operation, scheme
+    integer, optional, intent(in) :: n_halo !! Number of halo cells
+    character(*), optional, intent(in) :: from_to !! 'v2p' or 'p2v'
+    character(*), optional, intent(in) :: bc_start, bc_end !! Boundary Cond.
+    logical, optional, intent(in) :: sym !! (==npaire), only for Neumann BCs
+    real(dp), optional, intent(in) :: c_nu, nu0_nu !! params for hypervisc.
+
+    integer :: n_stencil
+
+    tdsops%tds_n = tds_n
+
+    if (present(n_halo)) then
+      tdsops%n_halo = n_halo
+      if (n_halo /= 4) then
+        write (stderr, '("Warning: n_halo is set to ", i2, "be careful! &
+                          &The default is 4 and there are quite a few &
+                          &places where things are hardcoded assuming &
+                          &n_halo is 4.")') n_halo
+      end if
+    else
+      tdsops%n_halo = 4
+    end if
+
+    n_stencil = 2*tdsops%n_halo + 1
+
+    ! preprocessed coefficient arrays for the distributed algorithm
+    allocate (tdsops%dist_fw(tds_n), tdsops%dist_bw(tds_n))
+    allocate (tdsops%dist_sa(tds_n), tdsops%dist_sc(tds_n))
+    allocate (tdsops%dist_af(tds_n))
+
+    ! preprocessed coefficient arrays for the Thomas algorithm
+    allocate (tdsops%thom_f(tds_n), tdsops%thom_s(tds_n))
+    allocate (tdsops%thom_w(tds_n), tdsops%thom_p(tds_n))
+
+    ! RHS coefficient arrays
+    allocate (tdsops%coeffs(n_stencil))
+    allocate (tdsops%coeffs_s(n_stencil, tdsops%n_halo))
+    allocate (tdsops%coeffs_e(n_stencil, tdsops%n_halo))
+
+    tdsops%periodic = bc_start == 'periodic' .and. bc_end == 'periodic'
+
+    if (operation == 'first-deriv') then
+      call tdsops%deriv_1st(delta, scheme, bc_start, bc_end, sym)
+    else if (operation == 'second-deriv') then
+      call tdsops%deriv_2nd(delta, scheme, bc_start, bc_end, sym, &
+                            c_nu, nu0_nu)
+    else if (operation == 'interpolate') then
+      call tdsops%interpl_mid(scheme, from_to, bc_start, bc_end, sym)
+    else if (operation == 'stag-deriv') then
+      call tdsops%stagder_1st(delta, scheme, from_to, bc_start, bc_end, sym)
+    else
+      error stop 'operation is not defined'
+    end if
+
+  end function tdsops_init
+
+  pure function get_tds_n(mesh, dir, from_to) result(tds_n)
+  !! Get the tds_n size based on the from_to value (and the mesh)
+    class(mesh_t), intent(in) :: mesh
+    integer, intent(in) :: dir
+    character(*), optional, intent(in) :: from_to
+    integer :: tds_n
+    integer :: data_loc
+
+    data_loc = VERT
+    if (present(from_to)) then
+      if (from_to == "v2p") then
+        data_loc = CELL
+      end if
+    end if
+
+    tds_n = mesh%get_n(dir, data_loc)
+
+  end function
+
+  subroutine deriv_1st(self, delta, scheme, bc_start, bc_end, sym)
+    implicit none
+
+    class(tdsops_t), intent(inout) :: self
+    real(dp), intent(in) :: delta
+    character(*), intent(in) :: scheme
+    character(*), optional, intent(in) :: bc_start, bc_end
+    logical, optional, intent(in) :: sym
+
+    real(dp), allocatable :: dist_b(:)
+    real(dp) :: alpha, afi, bfi
+    integer :: i, n, n_halo
+    logical :: symmetry
+
+    if (self%n_halo < 2) error stop 'First derivative require n_halo >= 2'
+
+    if (present(sym)) then
+      symmetry = sym
+    else
+      symmetry = .false.
+    end if
+
+    ! alpha is alfa
+
+    select case (scheme)
+    case ('compact6')
+      alpha = 1._dp/3._dp
+      afi = 7._dp/9._dp/delta
+      bfi = 1._dp/36._dp/delta
+    case default
+      error stop 'scheme is not defined'
+    end select
+
+    self%alpha = alpha
+    self%a = afi; self%b = bfi
+
+    self%coeffs(:) = [0._dp, 0._dp, -bfi, -afi, &
+                      0._dp, &
+                      afi, bfi, 0._dp, 0._dp]
+
+    do i = 1, self%n_halo
+      self%coeffs_s(:, i) = self%coeffs(:)
+      self%coeffs_e(:, i) = self%coeffs(:)
+    end do
+
+    self%dist_sa(:) = alpha; self%dist_sc(:) = alpha
+
+    n = self%tds_n
+    n_halo = self%n_halo
+
+    allocate (dist_b(n))
+    dist_b(:) = 1._dp
+
+    select case (bc_start)
+    case ('neumann')
+      if (symmetry) then
+        ! sym == .true.; d(uu)/dx, dv/dx, dw/dx
+        !                d(vv)/dy, du/dy, dw/dy
+        !                d(ww)/dz, du/dz, dv/dz
+        self%dist_sa(1) = 0._dp
+        self%dist_sc(1) = 0._dp
+        self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                               0._dp, &
+                               0._dp, 0._dp, 0._dp, 0._dp]
+        self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, -afi, &
+                               -bfi, &
+                               afi, bfi, 0._dp, 0._dp]
+      else
+        ! sym == .false.; d(uv)/dx, d(uw)/dx, du/dx
+        !                 d(vu)/dy, d(vw)/dy, dv/dy
+        !                 d(wu)/dz, d(wv)/dz, dw/dz
+        self%dist_sa(1) = 0._dp
+        self%dist_sc(1) = 2*alpha
+        self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                               0._dp, &
+                               2*afi, 2*bfi, 0._dp, 0._dp]
+        self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, -afi, &
+                               bfi, &
+                               afi, bfi, 0._dp, 0._dp]
+      end if
+    case ('dirichlet')
+      ! first line
+      self%dist_sa(1) = 0._dp
+      self%dist_sc(1) = 2._dp
+      self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                             -2.5_dp, &
+                             2._dp, 0.5_dp, 0._dp, 0._dp]
+      self%coeffs_s(:, 1) = self%coeffs_s(:, 1)/delta
+      ! second line
+      self%dist_sa(2) = 0.25_dp
+      self%dist_sc(2) = 0.25_dp
+      self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, -0.75_dp, &
+                             0._dp, &
+                             0.75_dp, 0._dp, 0._dp, 0._dp]
+      self%coeffs_s(:, 2) = self%coeffs_s(:, 2)/delta
+    end select
+
+    select case (bc_end)
+    case ('neumann')
+      if (symmetry) then
+        ! sym == .true.; d(uu)/dx, dv/dx, dw/dx
+        !                d(vv)/dy, du/dy, dw/dy
+        !                d(ww)/dz, du/dz, dv/dz
+        self%dist_sa(n) = 0._dp
+        self%dist_sc(n) = 0._dp
+        self%coeffs_e(:, n_halo) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                                    0._dp, &
+                                    0._dp, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, n_halo - 1) = [0._dp, 0._dp, -bfi, -afi, &
+                                        bfi, &
+                                        afi, 0._dp, 0._dp, 0._dp]
+      else
+        ! sym == .false.; d(uv)/dx, d(uw)/dx, du/dx
+        !                 d(vu)/dy, d(vw)/dy, dv/dy
+        !                 d(wu)/dz, d(wv)/dz, dw/dz
+        self%dist_sa(n) = 2*alpha
+        self%dist_sc(n) = 0._dp
+        self%coeffs_e(:, n_halo) = [0._dp, 0._dp, -2*bfi, -2*afi, &
+                                    0._dp, &
+                                    0._dp, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, n_halo - 1) = [0._dp, 0._dp, -bfi, -afi, &
+                                        -bfi, &
+                                        afi, 0._dp, 0._dp, 0._dp]
+      end if
+    case ('dirichlet')
+      ! last line
+      self%dist_sa(n) = 2._dp
+      self%dist_sc(n) = 0._dp
+      self%coeffs_e(:, n_halo) = [0._dp, 0._dp, -0.5_dp, -2._dp, &
+                                  2.5_dp, &
+                                  0._dp, 0._dp, 0._dp, 0._dp]
+      self%coeffs_e(:, n_halo) = self%coeffs_e(:, n_halo)/delta
+      ! second last line
+      self%dist_sa(n - 1) = 0.25_dp
+      self%dist_sc(n - 1) = 0.25_dp
+      self%coeffs_e(:, n_halo - 1) = [0._dp, 0._dp, 0._dp, -0.75_dp, &
+                                      0._dp, &
+                                      0.75_dp, 0._dp, 0._dp, 0._dp]
+      self%coeffs_e(:, n_halo - 1) = self%coeffs_e(:, n_halo - 1)/delta
+    end select
+
+    call self%preprocess_thom(dist_b)
+    call self%preprocess_dist(dist_b)
+
+  end subroutine deriv_1st
+
+  subroutine deriv_2nd(self, delta, scheme, bc_start, bc_end, sym, &
+                       c_nu, nu0_nu)
+    implicit none
+
+    class(tdsops_t), intent(inout) :: self
+    real(dp), intent(in) :: delta
+    character(*), intent(in) :: scheme
+    character(*), optional, intent(in) :: bc_start, bc_end
+    logical, optional, intent(in) :: sym
+    real(dp), optional, intent(in) :: c_nu, nu0_nu
+
+    real(dp), allocatable :: dist_b(:)
+    real(dp) :: alpha, asi, bsi, csi, dsi
+    real(dp) :: dpis3, xnpi2, xmpi2, den, d2, temp1, temp2
+    integer :: i, n, n_halo
+    logical :: symmetry
+
+    if (self%n_halo < 4) error stop 'Second derivative require n_halo >= 4'
+
+    if (present(sym)) then
+      symmetry = sym
+    else
+      symmetry = .false.
+    end if
+
+    d2 = delta*delta
+
+    ! alpha is alsa
+
+    select case (scheme)
+    case ('compact6')
+      alpha = 2._dp/11._dp
+      asi = 12._dp/11._dp/d2
+      bsi = 3._dp/44._dp/d2
+      csi = 0._dp
+      dsi = 0._dp
+    case ('compact6-hyperviscous')
+      if (present(c_nu) .and. present(nu0_nu)) then
+        dpis3 = 2._dp*pi/3._dp
+        xnpi2 = pi*pi*(1._dp + nu0_nu)
+        xmpi2 = dpis3*dpis3*(1._dp + c_nu*nu0_nu)
+        den = 405._dp*xnpi2 - 640._dp*xmpi2 + 144._dp
+        alpha = 0.5_dp - (320._dp*xmpi2 - 1296._dp)/den
+        asi = -(4329._dp*xnpi2/8._dp - 32._dp*xmpi2 &
+                - 140._dp*xnpi2*xmpi2 + 286._dp)/den/d2
+        bsi = (2115._dp*xnpi2 - 1792._dp*xmpi2 &
+               - 280._dp*xnpi2*xmpi2 + 1328._dp)/den/(4._dp*d2)
+        csi = -(7695._dp*xnpi2/8._dp + 288._dp*xmpi2 &
+                - 180._dp*xnpi2*xmpi2 - 2574._dp)/den/(9._dp*d2)
+        dsi = (198._dp*xnpi2 + 128._dp*xmpi2 &
+               - 40._dp*xnpi2*xmpi2 - 736._dp)/den/(16._dp*d2)
+      else
+        error stop 'compact6-hyperviscous requires c_nu and nu0_nu'
+      end if
+    case default
+      error stop 'scheme is not defined'
+    end select
+
+    self%alpha = alpha
+    self%a = asi; self%b = bsi; self%c = csi; self%d = dsi
+
+    self%coeffs(:) = [dsi, csi, bsi, asi, &
+                      -2._dp*(asi + bsi + csi + dsi), &
+                      asi, bsi, csi, dsi]
+
+    do i = 1, self%n_halo
+      self%coeffs_s(:, i) = self%coeffs(:)
+      self%coeffs_e(:, i) = self%coeffs(:)
+    end do
+
+    self%dist_sa(:) = alpha; self%dist_sc(:) = alpha
+
+    n = self%tds_n
+    n_halo = self%n_halo
+
+    allocate (dist_b(n))
+    dist_b(:) = 1._dp
+
+    select case (bc_start)
+    case ('neumann')
+      if (symmetry) then
+        ! sym == .true.; d2v/dx2, d2w/dx2
+        !                d2u/dy2, d2w/dy2
+        !                d2u/dz2, d2v/dz2
+        self%dist_sa(1) = 0._dp
+        self%dist_sc(1) = 2*alpha
+        self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                               -2*asi - 2*bsi - 2*csi - 2*dsi, &
+                               2*asi, 2*bsi, 2*csi, 2*dsi]
+        self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, asi, &
+                               -2*asi - bsi - 2*csi - 2*dsi, &
+                               asi + csi, bsi + dsi, csi, dsi]
+        self%coeffs_s(:, 3) = [0._dp, 0._dp, bsi, asi + csi, &
+                               -2*asi - 2*bsi - 2*csi - dsi, &
+                               asi, bsi, csi, dsi]
+        self%coeffs_s(:, 4) = [0._dp, csi, bsi + dsi, asi, &
+                               -2*asi - 2*bsi - 2*csi - 2*dsi, &
+                               asi, bsi, csi, dsi]
+      else
+        ! sym == .false.; d2u/dx2
+        !                 d2v/dy2
+        !                 d2w/dz2
+        self%dist_sa(1) = 0._dp
+        self%dist_sc(1) = 0._dp
+        self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                               0._dp, &
+                               0._dp, 0._dp, 0._dp, 0._dp]
+        self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, asi, &
+                               -2*asi - 3*bsi - 2*csi - 2*dsi, &
+                               asi - csi, bsi - dsi, csi, dsi]
+        self%coeffs_s(:, 3) = [0._dp, 0._dp, bsi, asi - csi, &
+                               -2*asi - 2*bsi - 2*csi - 3*dsi, &
+                               asi, bsi, csi, dsi]
+        self%coeffs_s(:, 4) = [0._dp, -csi, bsi - dsi, asi, &
+                               -2*asi - 2*bsi - 2*csi - 2*dsi, &
+                               asi, bsi, csi, dsi]
+      end if
+    case ('dirichlet')
+      ! first line
+      self%dist_sa(1) = 0._dp
+      self%dist_sc(1) = 11._dp
+      self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                             13._dp/d2, &
+                             -27._dp/d2, 15._dp/d2, -1._dp/d2, 0._dp]
+      ! second line
+      self%dist_sa(2) = 0.1_dp
+      self%dist_sc(2) = 0.1_dp
+      self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, 1.2_dp/d2, &
+                             -2.4_dp/d2, &
+                             1.2_dp/d2, 0._dp, 0._dp, 0._dp]
+      ! third line
+      self%dist_sa(3) = 2._dp/11._dp
+      self%dist_sc(3) = 2._dp/11._dp
+      temp1 = 3._dp/44._dp/d2; temp2 = 12._dp/11._dp/d2
+      self%coeffs_s(:, 3) = [0._dp, 0._dp, temp1, temp2, &
+                             -2._dp*(temp1 + temp2), &
+                             temp2, temp1, 0._dp, 0._dp]
+      ! fourth line is same as third
+      self%dist_sa(4) = 2._dp/11._dp
+      self%dist_sc(4) = 2._dp/11._dp
+      self%coeffs_s(:, 4) = self%coeffs_s(:, 3)
+    end select
+
+    select case (bc_end)
+    case ('neumann')
+      if (symmetry) then
+        ! sym == .true.; d2v/dx2, d2w/dx2
+        !                d2u/dy2, d2w/dy2
+        !                d2u/dz2, d2v/dz2
+        self%dist_sa(n) = 2*alpha
+        self%dist_sc(n) = 0._dp
+        self%coeffs_e(:, 4) = [dsi, csi, bsi, asi, &
+                               -2*asi - 2*bsi - 2*csi - 2*dsi, &
+                               0._dp, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, 3) = [dsi, csi, bsi + dsi, asi + csi, &
+                               -2*asi - bsi - 2*csi - 2*dsi, &
+                               asi, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, 2) = [dsi, csi, bsi, asi, &
+                               -2*asi - 2*bsi - 2*csi - dsi, &
+                               asi + csi, bsi, 0._dp, 0._dp]
+        self%coeffs_e(:, 1) = [dsi, csi, bsi, asi, &
+                               -2*asi - 2*bsi - 2*csi - 2*dsi, &
+                               asi, bsi + dsi, csi, 0._dp]
+      else
+        ! sym == .false.; d2u/dx2
+        !                 d2v/dy2
+        !                 d2w/dz2
+        self%dist_sa(n) = 0._dp
+        self%dist_sc(n) = 0._dp
+        self%coeffs_e(:, 4) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                               0._dp, &
+                               0._dp, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, 3) = [dsi, csi, bsi - dsi, asi - csi, &
+                               -2*asi - 3*bsi - 2*csi - 2*dsi, &
+                               asi, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, 2) = [dsi, csi, bsi, asi, &
+                               -2*asi - 2*bsi - 2*csi - 3*dsi, &
+                               asi - csi, bsi, 0._dp, 0._dp]
+        self%coeffs_e(:, 1) = [dsi, csi, bsi, asi, &
+                               -2*asi - 2*bsi - 2*csi - 2*dsi, &
+                               asi, bsi - dsi, -csi, 0._dp]
+      end if
+    case ('dirichlet')
+      ! last line
+      self%dist_sa(n) = 11._dp
+      self%dist_sc(n) = 0._dp
+      self%coeffs_e(:, 4) = [0._dp, -1._dp/d2, 15._dp/d2, -27._dp/d2, &
+                             13._dp/d2, &
+                             0._dp, 0._dp, 0._dp, 0._dp]
+      ! second last line
+      self%dist_sa(n - 1) = 0.1_dp
+      self%dist_sc(n - 1) = 0.1_dp
+      self%coeffs_e(:, 3) = [0._dp, 0._dp, 0._dp, 1.2_dp/d2, &
+                             -2.4_dp/d2, &
+                             1.2_dp/d2, 0._dp, 0._dp, 0._dp]
+      ! third last line
+      self%dist_sa(n - 2) = 2._dp/11._dp
+      self%dist_sc(n - 2) = 2._dp/11._dp
+      temp1 = 3._dp/44._dp/d2; temp2 = 12._dp/11._dp/d2
+      self%coeffs_e(:, 2) = [0._dp, 0._dp, temp1, temp2, &
+                             -2._dp*(temp1 + temp2), &
+                             temp2, temp1, 0._dp, 0._dp]
+      ! fourth last line is same as third last
+      self%dist_sa(n - 3) = 2._dp/11._dp
+      self%dist_sc(n - 3) = 2._dp/11._dp
+      self%coeffs_e(:, 1) = self%coeffs_e(:, 2)
+    end select
+
+    call self%preprocess_thom(dist_b)
+    call self%preprocess_dist(dist_b)
+
+  end subroutine deriv_2nd
+
+  subroutine interpl_mid(self, scheme, from_to, bc_start, bc_end, sym)
+    implicit none
+
+    class(tdsops_t), intent(inout) :: self
+    character(*), intent(in) :: scheme, from_to
+    character(*), optional, intent(in) :: bc_start, bc_end
+    logical, optional, intent(in) :: sym
+
+    real(dp), allocatable :: dist_b(:)
+    real(dp) :: alpha, aici, bici, cici, dici
+    integer :: i, n, n_halo
+
+    if (self%n_halo < 4) error stop 'Interpolation require n_halo >= 4'
+
+    ! alpha is ailcai
+
+    select case (scheme)
+    case ('classic')
+      alpha = 0.3_dp
+      aici = 0.75_dp
+      bici = 0.05_dp
+      cici = 0._dp
+      dici = 0._dp
+    case ('optimised')
+      alpha = 0.461658_dp
+      dici = 0.00146508_dp
+      aici = (75._dp + 70._dp*alpha - 640._dp*dici)/128._dp
+      bici = (-25._dp + 126._dp*alpha + 2304._dp*dici)/256._dp
+      cici = (3._dp - 10._dp*alpha - 1280._dp*dici)/256._dp
+    case ('aggressive')
+      alpha = 0.49_dp
+      aici = (75._dp + 70._dp*alpha)/128._dp
+      bici = (-25._dp + 126._dp*alpha)/256._dp
+      cici = (3._dp - 10._dp*alpha)/256._dp
+      dici = 0._dp
+    case default
+      error stop 'scheme is not defined'
+    end select
+
+    self%alpha = alpha
+    self%a = aici; self%b = bici; self%c = cici; self%d = dici
+
+    select case (from_to)
+    case ('v2p')
+      self%coeffs(:) = [0._dp, dici, cici, bici, &
+                        aici, &
+                        aici, bici, cici, dici]
+    case ('p2v')
+      self%coeffs(:) = [dici, cici, bici, aici, &
+                        aici, &
+                        bici, cici, dici, 0._dp]
+    end select
+
+    do i = 1, self%n_halo
+      self%coeffs_s(:, i) = self%coeffs(:)
+      self%coeffs_e(:, i) = self%coeffs(:)
+    end do
+
+    self%dist_sa(:) = alpha; self%dist_sc(:) = alpha
+
+    n = self%tds_n
+    n_halo = self%n_halo
+
+    allocate (dist_b(n))
+    dist_b(:) = 1._dp
+
+    if ((bc_start == 'dirichlet') .or. (bc_start == 'neumann')) then
+      self%dist_sa(1) = 0._dp
+
+      select case (from_to)
+      case ('v2p')
+        ! sym is always .true.
+        dist_b(1) = 1._dp + alpha
+        self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                               aici, &
+                               aici + bici, bici + cici, cici + dici, dici]
+        self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, bici, &
+                               aici + cici, &
+                               aici + dici, bici, cici, dici]
+        self%coeffs_s(:, 3) = [0._dp, 0._dp, cici, bici + dici, &
+                               aici, &
+                               aici, bici, cici, dici]
+      case ('p2v')
+        ! sym is always .true.
+        self%dist_sc(1) = 2*alpha
+        self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                               2*aici, &
+                               2*bici, 2*cici, 2*dici, 0._dp]
+        self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, aici + bici, &
+                               aici + cici, &
+                               bici + dici, cici, dici, 0._dp]
+        self%coeffs_s(:, 3) = [0._dp, 0._dp, bici + cici, aici + dici, &
+                               aici, &
+                               bici, cici, dici, 0._dp]
+        self%coeffs_s(:, 4) = [0._dp, cici + dici, bici, aici, &
+                               aici, &
+                               bici, cici, dici, 0._dp]
+      end select
+    end if
+
+    if ((bc_end == 'dirichlet') .or. (bc_end == 'neumann')) then
+      self%dist_sc(n) = 0._dp
+
+      select case (from_to)
+      case ('v2p')
+        ! sym is always .true.
+        dist_b(n) = 1._dp + alpha
+        self%coeffs_e(:, 4) = [0._dp, dici, cici + dici, bici + cici, &
+                               aici + bici, &
+                               aici, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, 3) = [0._dp, dici, cici, bici, &
+                               aici + dici, &
+                               aici + cici, bici, 0._dp, 0._dp]
+        self%coeffs_e(:, 2) = [0._dp, dici, cici, bici, &
+                               aici, &
+                               aici, bici + dici, cici, 0._dp]
+      case ('p2v')
+        ! sym is always .true.
+        self%dist_sa(n) = 2*alpha
+        self%coeffs_e(:, 4) = [2*dici, 2*cici, 2*bici, 2*aici, &
+                               0._dp, &
+                               0._dp, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, 3) = [dici, cici, bici + dici, aici + cici, &
+                               aici + bici, &
+                               0._dp, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, 2) = [dici, cici, bici, aici, &
+                               aici + dici, &
+                               bici + cici, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, 1) = [dici, cici, bici, aici, &
+                               aici, &
+                               bici, cici + dici, 0._dp, 0._dp]
+      end select
+    end if
+
+    call self%preprocess_thom(dist_b)
+    call self%preprocess_dist(dist_b)
+
+  end subroutine interpl_mid
+
+  subroutine stagder_1st(self, delta, scheme, from_to, bc_start, bc_end, sym)
+    implicit none
+
+    class(tdsops_t), intent(inout) :: self
+    real(dp), intent(in) :: delta
+    character(*), intent(in) :: scheme, from_to
+    character(*), optional, intent(in) :: bc_start, bc_end
+    logical, optional, intent(in) :: sym
+
+    real(dp), allocatable :: dist_b(:)
+    real(dp) :: alpha, aci, bci
+    integer :: i, n, n_halo
+
+    if (self%n_halo < 2) error stop 'Staggared deriv require n_halo >= 2'
+
+    ! alpha is alcai
+
+    select case (scheme)
+    case ('compact6')
+      alpha = 9._dp/62._dp
+      aci = 63._dp/62._dp/delta
+      bci = 17._dp/62._dp/3._dp/delta
+    case default
+      error stop 'scheme is not defined'
+    end select
+
+    self%alpha = alpha
+    self%a = aci; self%b = bci
+
+    select case (from_to)
+    case ('v2p')
+      self%coeffs(:) = [0._dp, 0._dp, 0._dp, -bci, &
+                        -aci, &
+                        aci, bci, 0._dp, 0._dp]
+    case ('p2v')
+      self%coeffs(:) = [0._dp, 0._dp, -bci, -aci, &
+                        aci, &
+                        bci, 0._dp, 0._dp, 0._dp]
+    end select
+
+    do i = 1, self%n_halo
+      self%coeffs_s(:, i) = self%coeffs(:)
+      self%coeffs_e(:, i) = self%coeffs(:)
+    end do
+
+    self%dist_sa(:) = alpha; self%dist_sc(:) = alpha
+
+    n = self%tds_n
+    n_halo = self%n_halo
+
+    allocate (dist_b(n))
+    dist_b(:) = 1._dp
+
+    if ((bc_start == 'dirichlet') .or. (bc_start == 'neumann')) then
+      self%dist_sa(1) = 0._dp
+
+      select case (from_to)
+      case ('v2p')
+        ! sym is always .false.
+        dist_b(1) = 1._dp + alpha
+        self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                               -aci - 2*bci, &
+                               aci + bci, bci, 0._dp, 0._dp]
+        self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, -bci, &
+                               -aci, &
+                               aci, bci, 0._dp, 0._dp]
+      case ('p2v')
+        ! sym is always .true.
+        self%dist_sc(1) = 0._dp
+        self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                               0._dp, &
+                               0._dp, 0._dp, 0._dp, 0._dp]
+        self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, -aci - bci, &
+                               aci, &
+                               bci, 0._dp, 0._dp, 0._dp]
+      end select
+    end if
+
+    if ((bc_end == 'dirichlet') .or. (bc_end == 'neumann')) then
+      self%dist_sc(n) = 0._dp
+
+      select case (from_to)
+      case ('v2p')
+        ! sym is always .false.
+        dist_b(n) = 1._dp + alpha
+        self%coeffs_e(:, n_halo) = [0._dp, 0._dp, 0._dp, -bci, &
+                                    -aci - bci, &
+                                    aci + 2*bci, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, n_halo - 1) = [0._dp, 0._dp, 0._dp, -bci, &
+                                        -aci, &
+                                        aci, bci, 0._dp, 0._dp]
+      case ('p2v')
+        ! sym is always .true.
+        self%dist_sa(n) = 0._dp
+        self%coeffs_e(:, n_halo) = [0._dp, 0._dp, 0._dp, 0._dp, &
+                                    0._dp, &
+                                    0._dp, 0._dp, 0._dp, 0._dp]
+        self%coeffs_e(:, n_halo - 1) = [0._dp, 0._dp, -bci, -aci, &
+                                        aci + bci, &
+                                        0._dp, 0._dp, 0._dp, 0._dp]
+      end select
+    end if
+
+    call self%preprocess_thom(dist_b)
+    call self%preprocess_dist(dist_b)
+
+  end subroutine stagder_1st
+
+  subroutine preprocess_dist(self, dist_b)
+    implicit none
+
+    class(tdsops_t), intent(inout) :: self
+
+    real(dp), dimension(:), intent(in) :: dist_b
+
+    integer :: i
+    integer :: n
+
+    n = self%tds_n
+
+    ! Ref DOI: 10.1109/MCSE.2021.3130544
+    ! Algorithm 3 in page 4
+    ! First two lines first
+    do i = 1, 2
+      self%dist_sa(i) = self%dist_sa(i)/dist_b(i)
+      self%dist_sc(i) = self%dist_sc(i)/dist_b(i)
+      self%dist_bw(i) = self%dist_sc(i)
+      self%dist_af(i) = 1._dp/dist_b(i)
+    end do
+
+    ! Then the remaining in the forward pass
+    do i = 3, n
+      ! Algorithm 3 in ref obtains 'r' coeffs on the fly in line 7.
+      ! As we have to solve many RHSs with the same tridiagonal system,
+      ! it is better to do a preprocessing first.
+      ! So lets store 'r' coeff in dist_fw array.
+      self%dist_fw(i) = 1._dp/(dist_b(i) &
+                               - self%dist_sa(i)*self%dist_sc(i - 1))
+      ! dist_af is 'a_i' in line 7 of Algorithm 3 in ref.
+      self%dist_af(i) = self%dist_sa(i)
+      ! We store a_i^* and c_i^* in dist_sa and dist_sc because
+      ! we need them later in the substitution phase.
+      self%dist_sa(i) = -self%dist_fw(i)*self%dist_sa(i) &
+                        *self%dist_sa(i - 1)
+      self%dist_sc(i) = self%dist_fw(i)*self%dist_sc(i)
+    end do
+
+    ! backward pass starting in line 12 of Algorithm 3.
+    do i = n - 2, 2, -1
+      self%dist_sa(i) = self%dist_sa(i) &
+                        - self%dist_sc(i)*self%dist_sa(i + 1)
+      self%dist_bw(i) = self%dist_sc(i)
+      self%dist_sc(i) = -self%dist_sc(i)*self%dist_sc(i + 1)
+    end do
+
+    ! Line 17 and 18 are tricky
+    ! First we have a new 'r', we need it.
+    ! And for 'r' we need c_0^*...
+    ! Now examine closely, c_0^* is set in line 4 and never overwritten!
+    ! So we can use dist_sc(1) as is in place of c_0^*.
+    ! We need to store this new 'r' somewhere ...
+    ! dist_fw(1) is never used, so store this extra 'r' factor here instead
+    self%dist_fw(1) = 1._dp/(1._dp - self%dist_sc(1)*self%dist_sa(2))
+
+    ! Finally Line 19 and 20 in Algorithm 3 in ref.
+    self%dist_sa(1) = self%dist_fw(1)*self%dist_sa(1)
+    self%dist_sc(1) = -self%dist_fw(1)*self%dist_sc(1)*self%dist_sc(2)
+
+  end subroutine preprocess_dist
+
+  subroutine preprocess_thom(self, b)
+    implicit none
+
+    class(tdsops_t), intent(inout) :: self
+    real(dp), dimension(:), intent(in) :: b
+
+    integer :: i, n
+
+    n = self%tds_n
+
+    self%thom_w = b
+    self%thom_f = self%dist_sc
+    if (self%periodic) then
+      self%thom_w(1) = 2._dp
+      self%thom_w(self%tds_n) = 1._dp + self%alpha*self%alpha
+    end if
+
+    self%thom_s(1) = 0._dp
+    do i = 2, n
+      self%thom_s(i) = self%dist_sa(i)/self%thom_w(i - 1)
+      self%thom_w(i) = self%thom_w(i) - self%thom_f(i - 1)*self%thom_s(i)
+    end do
+    do i = 1, n
+      self%thom_w(i) = 1._dp/self%thom_w(i)
+    end do
+
+    self%thom_p = [-1._dp, (0._dp, i=2, self%tds_n - 1), self%alpha]
+    do i = 2, n
+      self%thom_p(i) = self%thom_p(i) - self%thom_p(i - 1)*self%thom_s(i)
+    end do
+    self%thom_p(n) = self%thom_p(n)*self%thom_w(n)
+    do i = n - 1, 1, -1
+      self%thom_p(i) = self%thom_w(i)*(self%thom_p(i) &
+                                       - self%thom_f(i)*self%thom_p(i + 1))
+    end do
+
+  end subroutine preprocess_thom
+
+end module m_tdsops
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/thomas.f90.html b/api/sourcefile/thomas.f90.html new file mode 100644 index 000000000..83f7420ef --- /dev/null +++ b/api/sourcefile/thomas.f90.html @@ -0,0 +1,389 @@ + + + + + + + + + + + + + thomas.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

thomas.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_cuda_kernels_thom
+  use cudafor
+
+  use m_common, only: dp
+
+  implicit none
+
+contains
+
+  attributes(global) subroutine der_univ_thom( &
+    du, u, coeffs_s, coeffs_e, coeffs, n, thom_f, thom_s, thom_w &
+    )
+    implicit none
+
+    real(dp), device, intent(out), dimension(:, :, :) :: du
+    real(dp), device, intent(in), dimension(:, :, :) :: u
+    real(dp), device, intent(in), dimension(:, :) :: coeffs_s, coeffs_e
+    real(dp), device, intent(in), dimension(:) :: coeffs
+    integer, value, intent(in) :: n
+    real(dp), device, intent(in), dimension(:) :: thom_f, thom_s, thom_w
+
+    integer :: i, j, b
+
+    real(dp) :: c_m4, c_m3, c_m2, c_m1, c_j, c_p1, c_p2, c_p3, c_p4, temp_du
+
+    i = threadIdx%x
+    b = blockIdx%x
+
+    ! store bulk coeffs in the registers
+    c_m4 = coeffs(1); c_m3 = coeffs(2); c_m2 = coeffs(3); c_m1 = coeffs(4)
+    c_j = coeffs(5)
+    c_p1 = coeffs(6); c_p2 = coeffs(7); c_p3 = coeffs(8); c_p4 = coeffs(9)
+
+    du(i, 1, b) = coeffs_s(5, 1)*u(i, 1, b) &
+                  + coeffs_s(6, 1)*u(i, 2, b) &
+                  + coeffs_s(7, 1)*u(i, 3, b) &
+                  + coeffs_s(8, 1)*u(i, 4, b) &
+                  + coeffs_s(9, 1)*u(i, 5, b)
+    du(i, 1, b) = du(i, 1, b)
+    du(i, 2, b) = coeffs_s(4, 2)*u(i, 1, b) &
+                  + coeffs_s(5, 2)*u(i, 2, b) &
+                  + coeffs_s(6, 2)*u(i, 3, b) &
+                  + coeffs_s(7, 2)*u(i, 4, b) &
+                  + coeffs_s(8, 2)*u(i, 5, b) &
+                  + coeffs_s(9, 2)*u(i, 6, b)
+    du(i, 2, b) = du(i, 2, b) - du(i, 1, b)*thom_s(2)
+    du(i, 3, b) = coeffs_s(3, 3)*u(i, 1, b) &
+                  + coeffs_s(4, 3)*u(i, 2, b) &
+                  + coeffs_s(5, 3)*u(i, 3, b) &
+                  + coeffs_s(6, 3)*u(i, 4, b) &
+                  + coeffs_s(7, 3)*u(i, 5, b) &
+                  + coeffs_s(8, 3)*u(i, 6, b) &
+                  + coeffs_s(9, 3)*u(i, 7, b)
+    du(i, 3, b) = du(i, 3, b) - du(i, 2, b)*thom_s(3)
+    du(i, 4, b) = coeffs_s(2, 4)*u(i, 1, b) &
+                  + coeffs_s(3, 4)*u(i, 2, b) &
+                  + coeffs_s(4, 4)*u(i, 3, b) &
+                  + coeffs_s(5, 4)*u(i, 4, b) &
+                  + coeffs_s(6, 4)*u(i, 5, b) &
+                  + coeffs_s(7, 4)*u(i, 6, b) &
+                  + coeffs_s(8, 4)*u(i, 7, b) &
+                  + coeffs_s(9, 4)*u(i, 8, b)
+    du(i, 4, b) = du(i, 4, b) - du(i, 3, b)*thom_s(4)
+
+    do j = 5, n - 4
+      temp_du = c_m4*u(i, j - 4, b) + c_m3*u(i, j - 3, b) &
+                + c_m2*u(i, j - 2, b) + c_m1*u(i, j - 1, b) &
+                + c_j*u(i, j, b) &
+                + c_p1*u(i, j + 1, b) + c_p2*u(i, j + 2, b) &
+                + c_p3*u(i, j + 3, b) + c_p4*u(i, j + 4, b)
+      du(i, j, b) = temp_du - du(i, j - 1, b)*thom_s(j)
+    end do
+
+    j = n - 3
+    du(i, j, b) = coeffs_e(1, 1)*u(i, j - 4, b) &
+                  + coeffs_e(2, 1)*u(i, j - 3, b) &
+                  + coeffs_e(3, 1)*u(i, j - 2, b) &
+                  + coeffs_e(4, 1)*u(i, j - 1, b) &
+                  + coeffs_e(5, 1)*u(i, j, b) &
+                  + coeffs_e(6, 1)*u(i, j + 1, b) &
+                  + coeffs_e(7, 1)*u(i, j + 2, b) &
+                  + coeffs_e(8, 1)*u(i, j + 3, b)
+    du(i, j, b) = du(i, j, b) - du(i, j - 1, b)*thom_s(j)
+    j = n - 2
+    du(i, j, b) = coeffs_e(1, 2)*u(i, j - 4, b) &
+                  + coeffs_e(2, 2)*u(i, j - 3, b) &
+                  + coeffs_e(3, 2)*u(i, j - 2, b) &
+                  + coeffs_e(4, 2)*u(i, j - 1, b) &
+                  + coeffs_e(5, 2)*u(i, j, b) &
+                  + coeffs_e(6, 2)*u(i, j + 1, b) &
+                  + coeffs_e(7, 2)*u(i, j + 2, b)
+    du(i, j, b) = du(i, j, b) - du(i, j - 1, b)*thom_s(j)
+    j = n - 1
+    du(i, j, b) = coeffs_e(1, 3)*u(i, j - 4, b) &
+                  + coeffs_e(2, 3)*u(i, j - 3, b) &
+                  + coeffs_e(3, 3)*u(i, j - 2, b) &
+                  + coeffs_e(4, 3)*u(i, j - 1, b) &
+                  + coeffs_e(5, 3)*u(i, j, b) &
+                  + coeffs_e(6, 3)*u(i, j + 1, b)
+    du(i, j, b) = du(i, j, b) - du(i, j - 1, b)*thom_s(j)
+    j = n
+    du(i, j, b) = coeffs_e(1, 4)*u(i, j - 4, b) &
+                  + coeffs_e(2, 4)*u(i, j - 3, b) &
+                  + coeffs_e(3, 4)*u(i, j - 2, b) &
+                  + coeffs_e(4, 4)*u(i, j - 1, b) &
+                  + coeffs_e(5, 4)*u(i, j, b)
+    du(i, j, b) = du(i, j, b) - du(i, j - 1, b)*thom_s(j)
+
+    ! Backward pass of the Thomas algorithm
+    du(i, n, b) = du(i, n, b)*thom_w(n)
+    do j = n - 1, 1, -1
+      du(i, j, b) = (du(i, j, b) - thom_f(j)*du(i, j + 1, b))*thom_w(j)
+    end do
+
+  end subroutine der_univ_thom
+
+  attributes(global) subroutine der_univ_thom_per( &
+    du, u, coeffs, n, alpha, thom_f, thom_s, thom_w, thom_p &
+    )
+    implicit none
+
+    real(dp), device, intent(out), dimension(:, :, :) :: du
+    real(dp), device, intent(in), dimension(:, :, :) :: u
+    real(dp), device, intent(in), dimension(:) :: coeffs
+    integer, value, intent(in) :: n
+    real(dp), value, intent(in) :: alpha
+    real(dp), device, intent(in), dimension(:) :: thom_f, thom_s, thom_w, &
+                                                  thom_p
+
+    integer :: i, j, b
+    integer :: jm4, jm3, jm2, jm1, jp1, jp2, jp3, jp4
+
+    real(dp) :: c_m4, c_m3, c_m2, c_m1, c_j, c_p1, c_p2, c_p3, c_p4
+    real(dp) :: temp_du, ss
+
+    i = threadIdx%x
+    b = blockIdx%x
+
+    ! store bulk coeffs in the registers
+    c_m4 = coeffs(1); c_m3 = coeffs(2); c_m2 = coeffs(3); c_m1 = coeffs(4)
+    c_j = coeffs(5)
+    c_p1 = coeffs(6); c_p2 = coeffs(7); c_p3 = coeffs(8); c_p4 = coeffs(9)
+
+    do j = 1, n
+      jm4 = modulo(j - 5, n) + 1
+      jm3 = modulo(j - 4, n) + 1
+      jm2 = modulo(j - 3, n) + 1
+      jm1 = modulo(j - 2, n) + 1
+      jp1 = modulo(j - n, n) + 1
+      jp2 = modulo(j - n + 1, n) + 1
+      jp3 = modulo(j - n + 2, n) + 1
+      jp4 = modulo(j - n + 3, n) + 1
+
+      temp_du = c_m4*u(i, jm4, b) + c_m3*u(i, jm3, b) &
+                + c_m2*u(i, jm2, b) + c_m1*u(i, jm1, b) &
+                + c_j*u(i, j, b) &
+                + c_p1*u(i, jp1, b) + c_p2*u(i, jp2, b) &
+                + c_p3*u(i, jp3, b) + c_p4*u(i, jp4, b)
+      du(i, j, b) = temp_du - du(i, jm1, b)*thom_s(j)
+    end do
+
+    ! Backward pass of the Thomas algorithm
+    du(i, n, b) = du(i, n, b)*thom_w(n)
+    do j = n - 1, 1, -1
+      du(i, j, b) = (du(i, j, b) - thom_f(j)*du(i, j + 1, b))*thom_w(j)
+    end do
+
+    ! Periodic final pass
+    ss = (du(i, 1, b) - alpha*du(i, n, b)) &
+         /(1._dp + thom_p(1) - alpha*thom_p(n))
+    do j = 1, n
+      du(i, j, b) = du(i, j, b) - ss*thom_p(j)
+    end do
+
+  end subroutine der_univ_thom_per
+
+end module m_cuda_kernels_thom
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/time_integrator.f90.html b/api/sourcefile/time_integrator.f90.html new file mode 100644 index 000000000..57ce1a71c --- /dev/null +++ b/api/sourcefile/time_integrator.f90.html @@ -0,0 +1,528 @@ + + + + + + + + + + + + + time_integrator.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

time_integrator.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_time_integrator
+  use m_allocator, only: allocator_t, field_t, flist_t
+  use m_base_backend, only: base_backend_t
+  use m_common, only: dp, DIR_X
+
+  implicit none
+
+  private adams_bashforth, runge_kutta
+
+  type :: time_intg_t
+    integer :: method, istep, istage, order, nstep, nstage, nvars, nolds
+    real(dp) :: coeffs(4, 4)
+    real(dp) :: rk_b(4, 4)
+    real(dp) :: rk_a(3, 3, 4)
+    character(len=3) :: sname
+    type(flist_t), allocatable :: olds(:, :)
+    type(flist_t), allocatable :: curr(:)
+    type(flist_t), allocatable :: deriv(:)
+    class(base_backend_t), pointer :: backend
+    class(allocator_t), pointer :: allocator
+    procedure(stepper_func), pointer :: stepper => null()
+  contains
+    procedure :: finalize
+    procedure :: step
+    procedure :: runge_kutta
+    procedure :: adams_bashforth
+  end type time_intg_t
+
+  interface time_intg_t
+    module procedure init
+  end interface time_intg_t
+
+  abstract interface
+    subroutine stepper_func(self, dt)
+      import :: time_intg_t
+      import :: dp
+      implicit none
+
+      class(time_intg_t), intent(inout) :: self
+      real(dp), intent(in) :: dt
+    end subroutine stepper_func
+  end interface
+
+contains
+
+  subroutine finalize(self)
+    implicit none
+
+    !type(time_intg_t), intent(inout) :: self
+    class(time_intg_t), intent(inout) :: self
+
+    integer :: i, j
+
+    ! Release all the storage for old timesteps
+    do i = 1, self%nvars
+      do j = 1, self%nolds
+        call self%allocator%release_block(self%olds(i, j)%ptr)
+      end do
+    end do
+
+    ! deallocate memory
+    deallocate (self%olds)
+    deallocate (self%curr)
+    deallocate (self%deriv)
+
+    print *, self%sname, ' time integrator deallocated'
+
+  end subroutine finalize
+
+  function init(backend, allocator, method, nvars)
+    implicit none
+
+    type(time_intg_t) :: init
+    class(base_backend_t), pointer :: backend
+    class(allocator_t), pointer :: allocator
+    character(3), intent(in) :: method
+    integer, intent(in), optional :: nvars
+
+    integer :: i, j, stat
+
+    ! initialize Runge-Kutta coefficients
+    ! rk1
+    init%rk_a(:, 1, 1) = [0.0_dp, 0._dp, 0._dp]
+    init%rk_a(:, 2, 1) = [0.0_dp, 0._dp, 0._dp]
+    init%rk_a(:, 3, 1) = [0.0_dp, 0._dp, 0._dp]
+    init%rk_b(:, 1) = [1._dp, 0._dp, 0._dp, 0._dp]
+
+    ! rk2
+    init%rk_a(:, 1, 2) = [0.5_dp, 0._dp, 0._dp]
+    init%rk_a(:, 2, 2) = [0.0_dp, 0._dp, 0._dp]
+    init%rk_a(:, 3, 2) = [0.0_dp, 0._dp, 0._dp]
+    init%rk_b(:, 2) = [0._dp, 1._dp, 0._dp, 0._dp]
+
+    ! rk3
+    init%rk_a(:, 1, 3) = [0.5_dp, 0._dp, 0._dp]
+    init%rk_a(:, 2, 3) = [0.0_dp, 3._dp/4._dp, 0._dp]
+    init%rk_a(:, 3, 3) = [0.0_dp, 0._dp, 0._dp]
+    init%rk_b(:, 3) = &
+      [2._dp/9.0_dp, 1._dp/3._dp, 4._dp/9._dp, 0._dp]
+
+    ! rk4
+    init%rk_a(:, 1, 4) = [0.5_dp, 0._dp, 0._dp]
+    init%rk_a(:, 2, 4) = [0._dp, 0.5_dp, 0._dp]
+    init%rk_a(:, 3, 4) = [0._dp, 0._dp, 1._dp]
+    init%rk_b(:, 4) = &
+      [1._dp/6._dp, 1._dp/3._dp, 1._dp/3._dp, 1._dp/6._dp]
+
+    ! initialize Adams-Bashforth coefficients
+    ! ab1
+    init%coeffs(:, 1) = [1._dp, 0._dp, 0._dp, 0._dp]
+    ! ab2
+    init%coeffs(:, 2) = [1.5_dp, -0.5_dp, 0._dp, 0._dp]
+    ! ab3
+    init%coeffs(:, 3) = &
+      [23._dp/12._dp, -4._dp/3._dp, 5._dp/12._dp, 0._dp]
+    ! ab4
+    init%coeffs(:, 4) = &
+      [55._dp/24._dp, -59._dp/24._dp, 37._dp/24._dp, -3._dp/8._dp]
+
+    ! set variables
+    init%backend => backend
+    init%allocator => allocator
+    init%sname = method
+
+    if (init%sname(1:2) == 'AB') then
+      read (init%sname(3:3), *, iostat=stat) init%order
+      if (stat /= 0) error stop 'Error reading AB integration order'
+      if (init%order >= 5) error stop 'Integration order >4 is not supported'
+      init%nstep = init%order
+      init%nstage = 1
+      init%nolds = init%nstep - 1
+      init%stepper => adams_bashforth
+    else if (init%sname(1:2) == 'RK') then
+      read (init%sname(3:3), *, iostat=stat) init%order
+      if (stat /= 0) error stop 'Error reading RK integration order'
+      if (init%order >= 5) error stop 'Integration order >4 is not supported'
+      init%nstep = 1
+      init%nstage = init%order
+      init%nolds = init%nstage
+      init%stepper => runge_kutta
+    else
+      print *, 'Integration method '//init%sname//' is not defined'
+      error stop
+    end if
+
+    if (present(nvars)) then
+      init%nvars = nvars
+    else
+      init%nvars = 3
+    end if
+
+    init%istep = 1
+    init%istage = 1
+
+    ! allocate memory
+    allocate (init%olds(init%nvars, init%nolds))
+    allocate (init%curr(init%nvars))
+    allocate (init%deriv(init%nvars))
+
+    ! Request all the storage for old timesteps
+    do i = 1, init%nvars
+      do j = 1, init%nolds
+        init%olds(i, j)%ptr => allocator%get_block(DIR_X)
+      end do
+    end do
+
+  end function init
+
+  subroutine step(self, u, v, w, du, dv, dw, dt)
+    implicit none
+
+    class(time_intg_t), intent(inout) :: self
+    class(field_t), target, intent(inout) :: u, v, w
+    class(field_t), target, intent(in) :: du, dv, dw
+
+    real(dp), intent(in) :: dt
+
+    ! assign pointer to variables
+    self%curr(1)%ptr => u
+    self%curr(2)%ptr => v
+    self%curr(3)%ptr => w
+
+    ! assign pointer to variables
+    self%deriv(1)%ptr => du
+    self%deriv(2)%ptr => dv
+    self%deriv(3)%ptr => dw
+
+    call self%stepper(dt)
+
+  end subroutine step
+
+  subroutine runge_kutta(self, dt)
+    implicit none
+
+    class(time_intg_t), intent(inout) :: self
+    real(dp), intent(in) :: dt
+
+    integer :: i, j
+
+    ! update solution
+    if (self%istage == self%nstage) then
+      do i = 1, self%nvars
+        ! update step solution from stage derivative
+        if (self%nstage > 1) then
+          call self%backend%vecadd(1.0_dp, self%olds(i, 1)%ptr, 0._dp, &
+                                   self%curr(i)%ptr)
+        end if
+
+        do j = 1, self%nstage - 1
+          call self%backend%vecadd(self%rk_b(j, self%nstage)*dt, &
+                                   self%olds(i, j + 1)%ptr, &
+                                   1._dp, self%curr(i)%ptr)
+        end do
+        call self%backend%vecadd(self%rk_b(self%nstage, self%nstage)*dt, &
+                                 self%deriv(i)%ptr, &
+                                 1._dp, self%curr(i)%ptr)
+
+      end do
+
+      ! reset stage counter
+      self%istage = 1
+    else
+      do i = 1, self%nvars
+        ! save step initial condition
+        if (self%istage == 1) then
+          call self%backend%vecadd(1.0_dp, self%curr(i)%ptr, 0._dp, &
+                                   self%olds(i, 1)%ptr)
+        end if
+
+        ! save stage derivative
+        call self%backend%vecadd(1.0_dp, self%deriv(i)%ptr, 0._dp, &
+                                 self%olds(i, self%istage + 1)%ptr)
+
+        ! update stage solution
+        if (self%istage > 1) then
+          call self%backend%vecadd(1.0_dp, self%olds(i, 1)%ptr, 0._dp, &
+                                   self%curr(i)%ptr)
+        end if
+        do j = 1, self%istage
+          call self%backend%vecadd(self%rk_a(j, self%istage, self%nstage)*dt, &
+                                   self%olds(i, j + 1)%ptr, &
+                                   1._dp, self%curr(i)%ptr)
+        end do
+      end do
+
+      ! increment stage counter
+      self%istage = self%istage + 1
+    end if
+
+  end subroutine runge_kutta
+
+  subroutine adams_bashforth(self, dt)
+    implicit none
+
+    class(time_intg_t), intent(inout) :: self
+    real(dp), intent(in) :: dt
+
+    integer :: i, j
+    integer :: nstep
+
+    nstep = min(self%istep, self%nstep)
+    do i = 1, self%nvars
+      ! update solution
+      call self%backend%vecadd(self%coeffs(1, nstep)*dt, &
+                               self%deriv(i)%ptr, &
+                               1._dp, self%curr(i)%ptr)
+      do j = 2, nstep
+        call self%backend%vecadd(self%coeffs(j, nstep)*dt, &
+                                 self%olds(i, j - 1)%ptr, &
+                                 1._dp, self%curr(i)%ptr)
+      end do
+
+      ! rotate pointers
+      if (nstep < self%nstep) then
+        ! for startup
+        if (self%istep > 1) then
+          call rotate(self%olds(i, :), nstep)
+        end if
+      else
+        ! after startup
+        if (self%nstep > 2) then
+          call rotate(self%olds(i, :), nstep - 1)
+        end if
+      end if
+
+      ! update olds(1) with new derivative
+      if (self%nstep > 1) then
+        call self%backend%vecadd(1.0_dp, self%deriv(i)%ptr, 0._dp, &
+                                 self%olds(i, 1)%ptr)
+      end if
+    end do
+
+    ! increment step counter
+    self%istep = self%istep + 1
+
+  end subroutine adams_bashforth
+
+  subroutine rotate(sol, n)
+    implicit none
+
+    type(flist_t), intent(inout) :: sol(:)
+    integer, intent(in) :: n
+
+    integer :: i
+    class(field_t), pointer :: ptr
+
+    ! rotate pointer
+    ptr => sol(n)%ptr
+    do i = n, 2, -1
+      sol(i)%ptr => sol(i - 1)%ptr
+    end do
+    sol(1)%ptr => ptr
+
+  end subroutine rotate
+
+end module m_time_integrator
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/vector_calculus.f90.html b/api/sourcefile/vector_calculus.f90.html new file mode 100644 index 000000000..fada20925 --- /dev/null +++ b/api/sourcefile/vector_calculus.f90.html @@ -0,0 +1,606 @@ + + + + + + + + + + + + + vector_calculus.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

vector_calculus.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
module m_vector_calculus
+  use iso_fortran_env, only: stderr => error_unit
+
+  use m_allocator, only: allocator_t, field_t
+  use m_base_backend, only: base_backend_t
+  use m_common, only: dp, DIR_X, DIR_Y, DIR_Z, &
+                      RDR_X2Y, RDR_X2Z, RDR_Y2X, RDR_Y2Z, RDR_Z2X, RDR_Z2Y
+  use m_tdsops, only: tdsops_t
+
+  implicit none
+
+  type :: vector_calculus_t
+    !! Defines vector calculus operators
+    class(base_backend_t), pointer :: backend
+  contains
+    procedure :: curl
+    procedure :: divergence_v2c
+    procedure :: gradient_c2v
+    procedure :: laplacian
+  end type vector_calculus_t
+
+  interface vector_calculus_t
+    module procedure init
+  end interface vector_calculus_t
+
+contains
+
+  function init(backend) result(vector_calculus)
+    implicit none
+
+    class(base_backend_t), target, intent(inout) :: backend
+    type(vector_calculus_t) :: vector_calculus
+
+    vector_calculus%backend => backend
+
+  end function init
+
+  subroutine curl(self, o_i_hat, o_j_hat, o_k_hat, u, v, w, &
+                  x_der1st, y_der1st, z_der1st)
+    !! Curl of a vector field (u, v, w).
+    !!
+    !! Evaluated at the data_loc defined by u, v, w fields.
+    !!
+    !! All the input and output fields are in DIR_X layout.
+    implicit none
+
+    class(vector_calculus_t) :: self
+    !> Vector components of the output vector field Omega
+    class(field_t), intent(inout) :: o_i_hat, o_j_hat, o_k_hat
+    class(field_t), intent(in) :: u, v, w
+    class(tdsops_t), intent(in) :: x_der1st, y_der1st, z_der1st
+
+    class(field_t), pointer :: u_y, u_z, v_z, w_y, dwdy_y, dvdz_z, dvdz_x, &
+      dudz_z, dudz_x, dudy_y, dudy_x
+
+    if (o_i_hat%dir /= DIR_X .or. o_j_hat%dir /= DIR_X &
+        .or. o_k_hat%dir /= DIR_X .or. u%dir /= DIR_X .or. v%dir /= DIR_X &
+        .or. w%dir /= DIR_X) then
+      error stop 'Error in curl input/output field %dirs: &
+                  &outputs and inputs must be in DIR_X layout.'
+    end if
+
+    ! omega_i_hat = dw/dy - dv/dz
+    ! omega_j_hat = du/dz - dw/dx
+    ! omega_k_hat = dv/dx - du/dy
+
+    ! omega_i_hat
+    ! dw/dy
+    w_y => self%backend%allocator%get_block(DIR_Y)
+    dwdy_y => self%backend%allocator%get_block(DIR_Y)
+    call self%backend%reorder(w_y, w, RDR_X2Y)
+    call self%backend%tds_solve(dwdy_y, w_y, y_der1st)
+
+    call self%backend%reorder(o_i_hat, dwdy_y, RDR_Y2X)
+
+    call self%backend%allocator%release_block(w_y)
+    call self%backend%allocator%release_block(dwdy_y)
+
+    ! dv/dz
+    v_z => self%backend%allocator%get_block(DIR_Z)
+    dvdz_z => self%backend%allocator%get_block(DIR_Z)
+    call self%backend%reorder(v_z, v, RDR_X2Z)
+    call self%backend%tds_solve(dvdz_z, v_z, z_der1st)
+
+    dvdz_x => self%backend%allocator%get_block(DIR_X)
+    call self%backend%reorder(dvdz_x, dvdz_z, RDR_Z2X)
+
+    call self%backend%allocator%release_block(v_z)
+    call self%backend%allocator%release_block(dvdz_z)
+
+    ! omega_i_hat = dw/dy - dv/dz
+    call self%backend%vecadd(-1._dp, dvdz_x, 1._dp, o_i_hat)
+
+    call self%backend%allocator%release_block(dvdz_x)
+
+    ! omega_j_hat
+    ! du/dz
+    u_z => self%backend%allocator%get_block(DIR_Z)
+    dudz_z => self%backend%allocator%get_block(DIR_Z)
+    call self%backend%reorder(u_z, u, RDR_X2Z)
+    call self%backend%tds_solve(dudz_z, u_z, z_der1st)
+
+    dudz_x => self%backend%allocator%get_block(DIR_X)
+    call self%backend%reorder(dudz_x, dudz_z, RDR_Z2X)
+
+    call self%backend%allocator%release_block(u_z)
+    call self%backend%allocator%release_block(dudz_z)
+
+    ! dw/dx
+    call self%backend%tds_solve(o_j_hat, w, x_der1st)
+
+    ! omega_j_hat = du/dz - dw/dx
+    call self%backend%vecadd(1._dp, dudz_x, -1._dp, o_j_hat)
+
+    call self%backend%allocator%release_block(dudz_x)
+
+    ! omega_k_hat
+    ! dv/dx
+    call self%backend%tds_solve(o_k_hat, v, x_der1st)
+
+    ! du/dy
+    u_y => self%backend%allocator%get_block(DIR_Y)
+    dudy_y => self%backend%allocator%get_block(DIR_Y)
+    call self%backend%reorder(u_y, u, RDR_X2Y)
+    call self%backend%tds_solve(dudy_y, u_y, y_der1st)
+
+    dudy_x => self%backend%allocator%get_block(DIR_X)
+    call self%backend%reorder(dudy_x, dudy_y, RDR_Y2X)
+
+    call self%backend%allocator%release_block(u_y)
+    call self%backend%allocator%release_block(dudy_y)
+
+    ! omega_k_hat = dv/dx - du/dy
+    call self%backend%vecadd(-1._dp, dudy_x, 1._dp, o_k_hat)
+
+    call self%backend%allocator%release_block(dudy_x)
+
+  end subroutine curl
+
+  subroutine divergence_v2c(self, div_u, u, v, w, &
+                            x_stagder_v2c, x_interpl_v2c, &
+                            y_stagder_v2c, y_interpl_v2c, &
+                            z_stagder_v2c, z_interpl_v2c)
+    !! Divergence of a vector field (u, v, w).
+    !!
+    !! Evaluated at the cell centers (data_loc=CELL)
+    !! Input fields are at vertices (data_loc=VERT)
+    !!
+    !! Input fields are in DIR_X data layout.
+    !! Output field is in DIR_Z data layout.
+    implicit none
+
+    class(vector_calculus_t) :: self
+    class(field_t), intent(inout) :: div_u
+    class(field_t), intent(in) :: u, v, w
+    class(tdsops_t), intent(in) :: x_stagder_v2c, x_interpl_v2c, &
+      y_stagder_v2c, y_interpl_v2c, &
+      z_stagder_v2c, z_interpl_v2c
+
+    class(field_t), pointer :: du_x, dv_x, dw_x, &
+      u_y, v_y, w_y, du_y, dv_y, dw_y, &
+      u_z, w_z, dw_z
+
+    if (div_u%dir /= DIR_Z .or. u%dir /= DIR_X .or. v%dir /= DIR_X &
+        .or. w%dir /= DIR_X) then
+      error stop 'Error in divergence_v2c input/output field dirs: &
+                  &output must be in DIR_Z, inputs must be in DIR_X layout.'
+    end if
+
+    du_x => self%backend%allocator%get_block(DIR_X)
+    dv_x => self%backend%allocator%get_block(DIR_X)
+    dw_x => self%backend%allocator%get_block(DIR_X)
+
+    ! Staggared der for u field in x
+    ! Interpolation for v field in x
+    ! Interpolation for w field in x
+    call self%backend%tds_solve(du_x, u, x_stagder_v2c)
+    call self%backend%tds_solve(dv_x, v, x_interpl_v2c)
+    call self%backend%tds_solve(dw_x, w, x_interpl_v2c)
+
+    ! request fields from the allocator
+    u_y => self%backend%allocator%get_block(DIR_Y)
+    v_y => self%backend%allocator%get_block(DIR_Y)
+    w_y => self%backend%allocator%get_block(DIR_Y)
+
+    ! reorder data from x orientation to y orientation
+    call self%backend%reorder(u_y, du_x, RDR_X2Y)
+    call self%backend%reorder(v_y, dv_x, RDR_X2Y)
+    call self%backend%reorder(w_y, dw_x, RDR_X2Y)
+
+    call self%backend%allocator%release_block(du_x)
+    call self%backend%allocator%release_block(dv_x)
+    call self%backend%allocator%release_block(dw_x)
+
+    du_y => self%backend%allocator%get_block(DIR_Y)
+    dv_y => self%backend%allocator%get_block(DIR_Y)
+    dw_y => self%backend%allocator%get_block(DIR_Y)
+
+    ! similar to the x direction, obtain derivatives in y.
+    call self%backend%tds_solve(du_y, u_y, y_interpl_v2c)
+    call self%backend%tds_solve(dv_y, v_y, y_stagder_v2c)
+    call self%backend%tds_solve(dw_y, w_y, y_interpl_v2c)
+
+    ! we don't need the velocities in y orientation any more, so release
+    ! them to open up space.
+    ! It is important that this doesn't actually deallocate any memory,
+    ! it just makes the corresponding memory space available for use.
+    call self%backend%allocator%release_block(u_y)
+    call self%backend%allocator%release_block(v_y)
+    call self%backend%allocator%release_block(w_y)
+
+    ! just like in y direction, get some fields for the z derivatives.
+    u_z => self%backend%allocator%get_block(DIR_Z)
+    w_z => self%backend%allocator%get_block(DIR_Z)
+
+    ! du_y = dv_y + du_y
+    call self%backend%vecadd(1._dp, dv_y, 1._dp, du_y)
+
+    ! reorder from y to z
+    call self%backend%reorder(u_z, du_y, RDR_Y2Z)
+    call self%backend%reorder(w_z, dw_y, RDR_Y2Z)
+
+    ! release all the unnecessary blocks.
+    call self%backend%allocator%release_block(du_y)
+    call self%backend%allocator%release_block(dv_y)
+    call self%backend%allocator%release_block(dw_y)
+
+    dw_z => self%backend%allocator%get_block(DIR_Z)
+
+    ! get the derivatives in z
+    call self%backend%tds_solve(div_u, u_z, z_interpl_v2c)
+    call self%backend%tds_solve(dw_z, w_z, z_stagder_v2c)
+
+    ! div_u = div_u + dw_z
+    call self%backend%vecadd(1._dp, dw_z, 1._dp, div_u)
+
+    ! div_u array is in z orientation
+
+    ! there is no need to keep velocities in z orientation around, so release
+    call self%backend%allocator%release_block(u_z)
+    call self%backend%allocator%release_block(w_z)
+    call self%backend%allocator%release_block(dw_z)
+
+  end subroutine divergence_v2c
+
+  subroutine gradient_c2v(self, dpdx, dpdy, dpdz, p, &
+                          x_stagder_c2v, x_interpl_c2v, &
+                          y_stagder_c2v, y_interpl_c2v, &
+                          z_stagder_c2v, z_interpl_c2v)
+    !! Gradient of a scalar field 'p'.
+    !!
+    !! Evaluated at the vertices (data_loc=VERT)
+    !! Input field is at cell centers (data_loc=CELL)
+    !!
+    !! Input field is in DIR_Z data layout.
+    !! Output fields (dpdx, dpdy, dpdz) are in DIR_X data layout.
+    implicit none
+
+    class(vector_calculus_t) :: self
+    class(field_t), intent(inout) :: dpdx, dpdy, dpdz
+    class(field_t), intent(in) :: p
+    class(tdsops_t), intent(in) :: x_stagder_c2v, x_interpl_c2v, &
+      y_stagder_c2v, y_interpl_c2v, &
+      z_stagder_c2v, z_interpl_c2v
+
+    class(field_t), pointer :: p_sxy_z, dpdz_sxy_z, &
+      p_sxy_y, dpdz_sxy_y, &
+      p_sx_y, dpdy_sx_y, dpdz_sx_y, &
+      p_sx_x, dpdy_sx_x, dpdz_sx_x
+
+    if (dpdx%dir /= DIR_X .or. dpdy%dir /= DIR_X .or. dpdz%dir /= DIR_X &
+        .or. p%dir /= DIR_Z) then
+      error stop 'Error in gradient_c2v input/output field dirs: &
+                  &outputs must be in DIR_X, input must be in DIR_Z layout.'
+    end if
+
+    p_sxy_z => self%backend%allocator%get_block(DIR_Z)
+    dpdz_sxy_z => self%backend%allocator%get_block(DIR_Z)
+
+    ! Staggared der for p field in z
+    ! Interpolation for p field in z
+    call self%backend%tds_solve(p_sxy_z, p, z_interpl_c2v)
+    call self%backend%tds_solve(dpdz_sxy_z, p, z_stagder_c2v)
+
+    ! request fields from the allocator
+    p_sxy_y => self%backend%allocator%get_block(DIR_Y)
+    dpdz_sxy_y => self%backend%allocator%get_block(DIR_Y)
+
+    ! reorder data from z orientation to y orientation
+    call self%backend%reorder(p_sxy_y, p_sxy_z, RDR_Z2Y)
+    call self%backend%reorder(dpdz_sxy_y, dpdz_sxy_z, RDR_Z2Y)
+
+    call self%backend%allocator%release_block(p_sxy_z)
+    call self%backend%allocator%release_block(dpdz_sxy_z)
+
+    p_sx_y => self%backend%allocator%get_block(DIR_Y)
+    dpdy_sx_y => self%backend%allocator%get_block(DIR_Y)
+    dpdz_sx_y => self%backend%allocator%get_block(DIR_Y)
+
+    ! similar to the z direction, obtain derivatives in y.
+    call self%backend%tds_solve(p_sx_y, p_sxy_y, y_interpl_c2v)
+    call self%backend%tds_solve(dpdy_sx_y, p_sxy_y, y_stagder_c2v)
+    call self%backend%tds_solve(dpdz_sx_y, dpdz_sxy_y, y_interpl_c2v)
+
+    ! release memory
+    call self%backend%allocator%release_block(p_sxy_y)
+    call self%backend%allocator%release_block(dpdz_sxy_y)
+
+    ! just like in y direction, get some fields for the x derivatives.
+    p_sx_x => self%backend%allocator%get_block(DIR_X)
+    dpdy_sx_x => self%backend%allocator%get_block(DIR_X)
+    dpdz_sx_x => self%backend%allocator%get_block(DIR_X)
+
+    ! reorder from y to x
+    call self%backend%reorder(p_sx_x, p_sx_y, RDR_Y2X)
+    call self%backend%reorder(dpdy_sx_x, dpdy_sx_y, RDR_Y2X)
+    call self%backend%reorder(dpdz_sx_x, dpdz_sx_y, RDR_Y2X)
+
+    ! release all the y directional fields.
+    call self%backend%allocator%release_block(p_sx_y)
+    call self%backend%allocator%release_block(dpdy_sx_y)
+    call self%backend%allocator%release_block(dpdz_sx_y)
+
+    ! get the derivatives in x
+    call self%backend%tds_solve(dpdx, p_sx_x, x_stagder_c2v)
+    call self%backend%tds_solve(dpdy, dpdy_sx_x, x_interpl_c2v)
+    call self%backend%tds_solve(dpdz, dpdz_sx_x, x_interpl_c2v)
+
+    ! release temporary x fields
+    call self%backend%allocator%release_block(p_sx_x)
+    call self%backend%allocator%release_block(dpdy_sx_x)
+    call self%backend%allocator%release_block(dpdz_sx_x)
+
+  end subroutine gradient_c2v
+
+  subroutine laplacian(self, lapl_u, u, x_der2nd, y_der2nd, z_der2nd)
+    !! Laplacian of a scalar field 'u'.
+    !!
+    !! Evaluated at the data_loc defined by the input u field
+    !!
+    !! Input and output fields are in DIR_X layout.
+    implicit none
+
+    class(vector_calculus_t) :: self
+    class(field_t), intent(inout) :: lapl_u
+    class(field_t), intent(in) :: u
+
+    class(tdsops_t), intent(in) :: x_der2nd, y_der2nd, z_der2nd
+
+    class(field_t), pointer :: u_y, d2u_y, u_z, d2u_z
+
+    if (u%dir /= DIR_X .or. lapl_u%dir /= DIR_X) then
+      error stop 'Error in laplacian input/output field %dirs: &
+                  &outputs and inputs must be in DIR_X layout.'
+    end if
+
+    ! d2u/dx2
+    call self%backend%tds_solve(lapl_u, u, x_der2nd)
+
+    ! y directional temporary fields
+    u_y => self%backend%allocator%get_block(DIR_Y)
+    d2u_y => self%backend%allocator%get_block(DIR_Y)
+
+    call self%backend%reorder(u_y, u, RDR_X2Y)
+
+    ! d2u/dy2
+    call self%backend%tds_solve(d2u_y, u_y, y_der2nd)
+
+    ! add the y derivative component into the result field
+    call self%backend%sum_yintox(lapl_u, d2u_y)
+
+    ! release y directional fields
+    call self%backend%allocator%release_block(u_y)
+    call self%backend%allocator%release_block(d2u_y)
+
+    ! z directional temporary fields
+    u_z => self%backend%allocator%get_block(DIR_Z)
+    d2u_z => self%backend%allocator%get_block(DIR_Z)
+
+    call self%backend%reorder(u_z, u, RDR_X2Z)
+
+    ! d2u/dz2
+    call self%backend%tds_solve(d2u_z, u_z, z_der2nd)
+
+    ! add the z derivative component into the result field
+    call self%backend%sum_zintox(lapl_u, d2u_z)
+
+    ! release z directional fields
+    call self%backend%allocator%release_block(u_z)
+    call self%backend%allocator%release_block(d2u_z)
+
+  end subroutine laplacian
+
+end module m_vector_calculus
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/sourcefile/xcompact.f90.html b/api/sourcefile/xcompact.f90.html new file mode 100644 index 000000000..94a3356ab --- /dev/null +++ b/api/sourcefile/xcompact.f90.html @@ -0,0 +1,327 @@ + + + + + + + + + + + + + xcompact.f90 – x3d2 + + + + + + + + + + + + + + +
+ +
+ +
+
+

xcompact.f90 + Source File + +

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ +
+ +
+

Source Code

+
program xcompact
+  use mpi
+
+  use m_allocator
+  use m_base_backend
+  use m_common, only: pi
+  use m_solver, only: solver_t
+  use m_tdsops, only: tdsops_t
+  use m_mesh
+
+#ifdef CUDA
+  use m_cuda_allocator
+  use m_cuda_backend
+  use m_cuda_common, only: SZ
+  use m_cuda_tdsops, only: cuda_tdsops_t
+#else
+  use m_omp_backend
+  use m_omp_common, only: SZ
+#endif
+
+  implicit none
+
+  class(base_backend_t), pointer :: backend
+  class(allocator_t), pointer :: allocator
+  type(mesh_t) :: mesh
+  type(allocator_t), pointer :: host_allocator
+  type(solver_t) :: solver
+
+#ifdef CUDA
+  type(cuda_backend_t), target :: cuda_backend
+  type(cuda_allocator_t), target :: cuda_allocator
+  integer :: ndevs, devnum
+#else
+  type(omp_backend_t), target :: omp_backend
+#endif
+
+  type(allocator_t), target :: omp_allocator
+
+  real(dp) :: t_start, t_end
+
+  character(len=200) :: input_file
+  character(len=20) :: BC_x(2), BC_y(2), BC_z(2)
+  integer, dimension(3) :: dims_global
+  integer, dimension(3) :: nproc_dir = 0
+  real(dp), dimension(3) :: L_global
+  integer :: nrank, nproc, ierr
+
+  namelist /domain_params/ L_global, dims_global, nproc_dir, BC_x, BC_y, BC_z
+
+  call MPI_Init(ierr)
+  call MPI_Comm_rank(MPI_COMM_WORLD, nrank, ierr)
+  call MPI_Comm_size(MPI_COMM_WORLD, nproc, ierr)
+
+  if (nrank == 0) print *, 'Parallel run with', nproc, 'ranks'
+
+#ifdef CUDA
+  ierr = cudaGetDeviceCount(ndevs)
+  ierr = cudaSetDevice(mod(nrank, ndevs)) ! round-robin
+  ierr = cudaGetDevice(devnum)
+#endif
+
+  if (command_argument_count() >= 1) then
+    call get_command_argument(1, input_file)
+    open (100, file=input_file)
+    read (100, nml=domain_params)
+    close (100)
+  else
+    error stop 'Input file is not provided.'
+  end if
+
+  if (product(nproc_dir) /= nproc) then
+    if (nrank == 0) print *, 'nproc_dir specified in the input file does &
+                              &not match the total number of ranks, falling &
+                              &back to a 1D decomposition along Z-dir instead.'
+    nproc_dir = [1, 1, nproc]
+  end if
+
+  mesh = mesh_t(dims_global, nproc_dir, L_global, BC_x, BC_y, BC_z)
+
+#ifdef CUDA
+  cuda_allocator = cuda_allocator_t(mesh, SZ)
+  allocator => cuda_allocator
+  if (nrank == 0) print *, 'CUDA allocator instantiated'
+
+  omp_allocator = allocator_t(mesh, SZ)
+  host_allocator => omp_allocator
+
+  cuda_backend = cuda_backend_t(mesh, allocator)
+  backend => cuda_backend
+  if (nrank == 0) print *, 'CUDA backend instantiated'
+#else
+  omp_allocator = allocator_t(mesh, SZ)
+  allocator => omp_allocator
+  host_allocator => omp_allocator
+  if (nrank == 0) print *, 'OpenMP allocator instantiated'
+
+  omp_backend = omp_backend_t(mesh, allocator)
+  backend => omp_backend
+  if (nrank == 0) print *, 'OpenMP backend instantiated'
+#endif
+
+  solver = solver_t(backend, mesh, host_allocator)
+  if (nrank == 0) print *, 'solver instantiated'
+
+  call cpu_time(t_start)
+
+  call solver%run()
+
+  call cpu_time(t_end)
+
+  if (nrank == 0) print *, 'Time: ', t_end - t_start
+
+  call MPI_Finalize(ierr)
+
+end program xcompact
+
+ +
+
+
+ +
+
+
+
+
+
+

+ x3d2 + was developed by Xcompact3d team
© 2024 +

+
+
+

+ Documentation generated by + FORD +

+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/src/allocator.f90 b/api/src/allocator.f90 new file mode 100644 index 000000000..607a54c9b --- /dev/null +++ b/api/src/allocator.f90 @@ -0,0 +1,207 @@ +module m_allocator + use iso_fortran_env, only: stderr => error_unit + + use m_common, only: dp, DIR_X, DIR_Y, DIR_Z, DIR_C, none, VERT + use m_mesh, only: mesh_t + use m_field, only: field_t + + implicit none + + type :: allocator_t + !! An instance of type allocator_t is responsible for the + !! maintenance of a linked list of instances of equal size + !! [[m_allocator(module):field_t(type)]] objects: + !! + !! ``` + !! ---- ---- ---- ---- ---- ---- + !! ...-->|id=1|data|next|-->|id=0|data|next|-->null() + !! ---- ---- ---- ---- ---- ---- + !! ``` + !! + !! the last block's `next` pointer being non associated. + !! + !! User code can request access to a memory block by using the + !! type bound procedure + !! [[m_allocator(module):get_block(function)]]. If the list is + !! not empty, a pointer to the first block on the list is + !! returned and the block is detached from the list. If the list + !! is empty (i.e. all initially allocated blocks are currently + !! referenced to) then a new block is allocated before a pointer + !! to it is returned. + !! + !! In order to reuse memory it is important that user code + !! release blocks when they are not needed anymore. This is done + !! by calling the type bound procedure + !! [[m_allocator(module):release_block(subroutine)]]. The + !! released block is then pushed in front of the block list. + + integer :: ngrid + !> The id for the next allocated block. This counter is + !> incremented each time a new block is allocated. + integer :: next_id = 0 + !> The pointer to the first block on the list. Non associated if + !> the list is empty + ! TODO: Rename first to head + class(mesh_t), pointer :: mesh + class(field_t), pointer :: first => null() + contains + procedure :: get_block + procedure :: release_block + procedure :: create_block + procedure :: get_block_ids + procedure :: destroy + procedure :: compute_padded_dims + end type allocator_t + + interface allocator_t + module procedure allocator_init + end interface allocator_t + + type :: flist_t + class(field_t), pointer :: ptr + end type flist_t + +contains + + function allocator_init(mesh, sz) result(allocator) + type(mesh_t), target, intent(inout) :: mesh + integer, intent(in) :: sz + type(allocator_t) :: allocator + + allocator%mesh => mesh + call allocator%compute_padded_dims(sz) + allocator%ngrid = product(allocator%mesh%get_padded_dims(DIR_C)) + + end function allocator_init + + subroutine compute_padded_dims(self, sz) + class(allocator_t), intent(inout) :: self + integer, intent(in) :: sz + integer, dimension(3) :: cdims + integer :: nx, ny, nz, nx_padded, ny_padded, nz_padded + + cdims = self%mesh%get_dims(VERT) + nx = cdims(1) + ny = cdims(2) + nz = cdims(3) + + ! Apply padding based on sz + nx_padded = nx - 1 + mod(-(nx - 1), sz) + sz + ny_padded = ny - 1 + mod(-(ny - 1), sz) + sz + ! Current reorder functions do not require a padding in z-direction. + nz_padded = nz + cdims = [nx_padded, ny_padded, nz_padded] + + call self%mesh%set_sz(sz) + call self%mesh%set_padded_dims(cdims) + + end subroutine + + function create_block(self, next) result(ptr) + !! Allocate memory for a new block and return a pointer to a new + !! [[m_allocator(module):field_t(type)]] object. + class(allocator_t), intent(inout) :: self + type(field_t), pointer, intent(in) :: next + type(field_t), pointer :: newblock + class(field_t), pointer :: ptr + self%next_id = self%next_id + 1 + allocate (newblock) + newblock = field_t(self%ngrid, next, id=self%next_id) + ptr => newblock + end function create_block + + function get_block(self, dir, data_loc) result(handle) + !! Return a pointer to the first available memory block, i.e. the + !! current head of the block list. If the list is empty, allocate + !! a new block with [[m_allocator(module):create_block(function)]] + !! first. + !! + !! Example + !! ``` + !! f%data => get_block() + !! ``` + class(allocator_t), intent(inout) :: self + class(field_t), pointer :: handle + integer, intent(in) :: dir + integer, intent(in), optional :: data_loc + integer :: dims(3) + ! If the list is empty, allocate a new block before returning a + ! pointer to it. + if (.not. associated(self%first)) then + ! Construct a field_t. This effectively allocates + ! storage space. + self%first => self%create_block(next=self%first) + end if + handle => self%first + self%first => self%first%next ! 2nd block becomes head block + handle%next => null() ! Detach ex-head block from the block list + + ! Store direction info in the field type. + handle%dir = dir + if (present(data_loc)) then + handle%data_loc = data_loc + else + handle%data_loc = none + end if + + ! Set dims based on direction + dims = self%mesh%get_padded_dims(dir) + + ! Apply bounds remapping based on requested direction + call handle%set_shape(dims) + end function get_block + + subroutine release_block(self, handle) + !! Release memory block pointed to by HANDLE to the block list. + !! It is pushed to the front of the block list, in other words it + !! is made the head block. + class(allocator_t), intent(inout) :: self + class(field_t), pointer :: handle + handle%next => self%first + self%first => handle + end subroutine release_block + + subroutine destroy(self) + !! Go through the block list from head to tail, deallocating each + !! memory block in turn. Deallocation of a + !! [[m_allocator(module):field_t(type)]] object automatically + !! deallocates its internal allocatable + !! [[field_t(type):data(variable)]] array. + class(allocator_t), intent(inout) :: self + type(field_t), pointer :: current + do + if (.not. associated(self%first)) exit + current => self%first + self%first => self%first%next + deallocate (current) + self%next_id = self%next_id - 1 + end do + end subroutine destroy + + function get_block_ids(self) + !! Utility function that returns a array made of the `id` of the + !! block currently in the block list. Return the array [0] if + !! block list is empty. + ! TODO: Block indices should start at 1 or return [-1] in case of + ! empty block list. + class(allocator_t), intent(inout) :: self + integer, allocatable :: get_block_ids(:) + class(field_t), pointer :: current + integer :: i + + current => self%first + if (.not. associated(current)) then + get_block_ids = [0] + else + i = current%id + get_block_ids = [current%id] + do + if (.not. associated(current%next)) exit + i = current%next%id + get_block_ids = [get_block_ids, current%next%id] + current => current%next + end do + end if + end function get_block_ids + +end module m_allocator diff --git a/api/src/backend.f90 b/api/src/backend.f90 new file mode 100644 index 000000000..a9e1a0a45 --- /dev/null +++ b/api/src/backend.f90 @@ -0,0 +1,574 @@ +module m_omp_backend + use m_allocator, only: allocator_t, field_t + use m_base_backend, only: base_backend_t + use m_ordering, only: get_index_reordering + use m_common, only: dp, get_dirs_from_rdr, VERT, DIR_X, DIR_Y, DIR_Z, DIR_C + use m_tdsops, only: dirps_t, tdsops_t, get_tds_n + use m_omp_exec_dist, only: exec_dist_tds_compact, exec_dist_transeq_compact + use m_omp_sendrecv, only: sendrecv_fields + + use m_omp_common, only: SZ + use m_omp_poisson_fft, only: omp_poisson_fft_t + use m_mesh, only: mesh_t + + implicit none + + private :: transeq_halo_exchange, transeq_dist_component + + type, extends(base_backend_t) :: omp_backend_t + !character(len=*), parameter :: name = 'omp' + integer :: MPI_FP_PREC = dp + real(dp), allocatable, dimension(:, :, :) :: & + u_recv_s, u_recv_e, u_send_s, u_send_e, & + v_recv_s, v_recv_e, v_send_s, v_send_e, & + w_recv_s, w_recv_e, w_send_s, w_send_e, & + du_send_s, du_send_e, du_recv_s, du_recv_e, & + dud_send_s, dud_send_e, dud_recv_s, dud_recv_e, & + d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e + contains + procedure :: alloc_tdsops => alloc_omp_tdsops + procedure :: transeq_x => transeq_x_omp + procedure :: transeq_y => transeq_y_omp + procedure :: transeq_z => transeq_z_omp + procedure :: tds_solve => tds_solve_omp + procedure :: reorder => reorder_omp + procedure :: sum_yintox => sum_yintox_omp + procedure :: sum_zintox => sum_zintox_omp + procedure :: vecadd => vecadd_omp + procedure :: scalar_product => scalar_product_omp + procedure :: copy_data_to_f => copy_data_to_f_omp + procedure :: copy_f_to_data => copy_f_to_data_omp + procedure :: init_poisson_fft => init_omp_poisson_fft + procedure :: transeq_omp_dist + end type omp_backend_t + + interface omp_backend_t + module procedure init + end interface omp_backend_t + +contains + + function init(mesh, allocator) result(backend) + implicit none + + class(mesh_t), target, intent(inout) :: mesh + class(allocator_t), target, intent(inout) :: allocator + type(omp_backend_t) :: backend + + integer :: n_halo, n_groups + + call backend%base_init() + + select type (allocator) + type is (allocator_t) + ! class level access to the allocator + backend%allocator => allocator + end select + + n_halo = 4 + backend%mesh => mesh + n_groups = maxval([backend%mesh%get_n_groups(DIR_X), & + backend%mesh%get_n_groups(DIR_Y), & + backend%mesh%get_n_groups(DIR_Z)]) + + allocate (backend%u_send_s(SZ, n_halo, n_groups)) + allocate (backend%u_send_e(SZ, n_halo, n_groups)) + allocate (backend%u_recv_s(SZ, n_halo, n_groups)) + allocate (backend%u_recv_e(SZ, n_halo, n_groups)) + allocate (backend%v_send_s(SZ, n_halo, n_groups)) + allocate (backend%v_send_e(SZ, n_halo, n_groups)) + allocate (backend%v_recv_s(SZ, n_halo, n_groups)) + allocate (backend%v_recv_e(SZ, n_halo, n_groups)) + allocate (backend%w_send_s(SZ, n_halo, n_groups)) + allocate (backend%w_send_e(SZ, n_halo, n_groups)) + allocate (backend%w_recv_s(SZ, n_halo, n_groups)) + allocate (backend%w_recv_e(SZ, n_halo, n_groups)) + + allocate (backend%du_send_s(SZ, 1, n_groups)) + allocate (backend%du_send_e(SZ, 1, n_groups)) + allocate (backend%du_recv_s(SZ, 1, n_groups)) + allocate (backend%du_recv_e(SZ, 1, n_groups)) + allocate (backend%dud_send_s(SZ, 1, n_groups)) + allocate (backend%dud_send_e(SZ, 1, n_groups)) + allocate (backend%dud_recv_s(SZ, 1, n_groups)) + allocate (backend%dud_recv_e(SZ, 1, n_groups)) + allocate (backend%d2u_send_s(SZ, 1, n_groups)) + allocate (backend%d2u_send_e(SZ, 1, n_groups)) + allocate (backend%d2u_recv_s(SZ, 1, n_groups)) + allocate (backend%d2u_recv_e(SZ, 1, n_groups)) + + end function init + + subroutine alloc_omp_tdsops( & + self, tdsops, dir, operation, scheme, & + n_halo, from_to, bc_start, bc_end, sym, c_nu, nu0_nu & + ) + implicit none + + class(omp_backend_t) :: self + class(tdsops_t), allocatable, intent(inout) :: tdsops + integer, intent(in) :: dir + character(*), intent(in) :: operation, scheme + integer, optional, intent(in) :: n_halo + character(*), optional, intent(in) :: from_to, bc_start, bc_end + logical, optional, intent(in) :: sym + real(dp), optional, intent(in) :: c_nu, nu0_nu + integer :: tds_n + real(dp) :: delta + + allocate (tdsops_t :: tdsops) + + select type (tdsops) + type is (tdsops_t) + tds_n = get_tds_n(self%mesh, dir, from_to) + delta = self%mesh%geo%d(dir) + tdsops = tdsops_t(tds_n, delta, operation, scheme, n_halo, from_to, & + bc_start, bc_end, sym, c_nu, nu0_nu) + end select + + end subroutine alloc_omp_tdsops + + subroutine transeq_x_omp(self, du, dv, dw, u, v, w, dirps) + implicit none + + class(omp_backend_t) :: self + class(field_t), intent(inout) :: du, dv, dw + class(field_t), intent(in) :: u, v, w + type(dirps_t), intent(in) :: dirps + + call self%transeq_omp_dist(du, dv, dw, u, v, w, dirps) + + end subroutine transeq_x_omp + + subroutine transeq_y_omp(self, du, dv, dw, u, v, w, dirps) + implicit none + + class(omp_backend_t) :: self + class(field_t), intent(inout) :: du, dv, dw + class(field_t), intent(in) :: u, v, w + type(dirps_t), intent(in) :: dirps + + ! u, v, w is reordered so that we pass v, u, w + call self%transeq_omp_dist(dv, du, dw, v, u, w, dirps) + + end subroutine transeq_y_omp + + subroutine transeq_z_omp(self, du, dv, dw, u, v, w, dirps) + implicit none + + class(omp_backend_t) :: self + class(field_t), intent(inout) :: du, dv, dw + class(field_t), intent(in) :: u, v, w + type(dirps_t), intent(in) :: dirps + + ! u, v, w is reordered so that we pass w, u, v + call self%transeq_omp_dist(dw, du, dv, w, u, v, dirps) + + end subroutine transeq_z_omp + + subroutine transeq_omp_dist(self, du, dv, dw, u, v, w, dirps) + implicit none + + class(omp_backend_t) :: self + class(field_t), intent(inout) :: du, dv, dw + class(field_t), intent(in) :: u, v, w + type(dirps_t), intent(in) :: dirps + + call transeq_halo_exchange(self, u, v, w, dirps%dir) + + call transeq_dist_component(self, du, u, u, & + self%u_recv_s, self%u_recv_e, & + self%u_recv_s, self%u_recv_e, & + dirps%der1st, dirps%der1st_sym, & + dirps%der2nd, dirps%dir) + call transeq_dist_component(self, dv, v, u, & + self%v_recv_s, self%v_recv_e, & + self%u_recv_s, self%u_recv_e, & + dirps%der1st_sym, dirps%der1st, & + dirps%der2nd_sym, dirps%dir) + call transeq_dist_component(self, dw, w, u, & + self%w_recv_s, self%w_recv_e, & + self%u_recv_s, self%u_recv_e, & + dirps%der1st_sym, dirps%der1st, & + dirps%der2nd_sym, dirps%dir) + + end subroutine transeq_omp_dist + + subroutine transeq_halo_exchange(self, u, v, w, dir) + class(omp_backend_t) :: self + class(field_t), intent(in) :: u, v, w + integer, intent(in) :: dir + integer :: n_halo, n, nproc_dir, pprev, pnext + integer :: n_groups + + ! TODO: don't hardcode n_halo + n_halo = 4 + n_groups = self%mesh%get_n_groups(dir) + n = self%mesh%get_n(u) + nproc_dir = self%mesh%par%nproc_dir(dir) + pprev = self%mesh%par%pprev(dir) + pnext = self%mesh%par%pnext(dir) + + call copy_into_buffers(self%u_send_s, self%u_send_e, u%data, & + n, n_groups) + call copy_into_buffers(self%v_send_s, self%v_send_e, v%data, & + n, n_groups) + call copy_into_buffers(self%w_send_s, self%w_send_e, w%data, & + n, n_groups) + + call sendrecv_fields(self%u_recv_s, self%u_recv_e, & + self%u_send_s, self%u_send_e, & + SZ*n_halo*n_groups, & + nproc_dir, pprev, pnext) + call sendrecv_fields(self%v_recv_s, self%v_recv_e, & + self%v_send_s, self%v_send_e, & + SZ*n_halo*n_groups, & + nproc_dir, pprev, pnext) + call sendrecv_fields(self%w_recv_s, self%w_recv_e, & + self%w_send_s, self%w_send_e, & + SZ*n_halo*n_groups, & + nproc_dir, pprev, pnext) + + end subroutine transeq_halo_exchange + + subroutine transeq_dist_component(self, rhs, u, conv, & + u_recv_s, u_recv_e, & + conv_recv_s, conv_recv_e, & + tdsops_du, tdsops_dud, tdsops_d2u, dir) + !! Computes RHS_x^u following: + !! + !! rhs_x^u = -0.5*(conv*du/dx + d(u*conv)/dx) + nu*d2u/dx2 + class(omp_backend_t) :: self + class(field_t), intent(inout) :: rhs + class(field_t), intent(in) :: u, conv + real(dp), dimension(:, :, :), intent(in) :: u_recv_s, u_recv_e, & + conv_recv_s, conv_recv_e + class(tdsops_t), intent(in) :: tdsops_du + class(tdsops_t), intent(in) :: tdsops_dud + class(tdsops_t), intent(in) :: tdsops_d2u + integer, intent(in) :: dir + class(field_t), pointer :: du, d2u, dud + + du => self%allocator%get_block(dir, VERT) + dud => self%allocator%get_block(dir, VERT) + d2u => self%allocator%get_block(dir, VERT) + + call exec_dist_transeq_compact( & + rhs%data, du%data, dud%data, d2u%data, & + self%du_send_s, self%du_send_e, self%du_recv_s, self%du_recv_e, & + self%dud_send_s, self%dud_send_e, self%dud_recv_s, self%dud_recv_e, & + self%d2u_send_s, self%d2u_send_e, self%d2u_recv_s, self%d2u_recv_e, & + u%data, u_recv_s, u_recv_e, & + conv%data, conv_recv_s, conv_recv_e, & + tdsops_du, tdsops_dud, tdsops_d2u, self%nu, & + self%mesh%par%nproc_dir(dir), self%mesh%par%pprev(dir), & + self%mesh%par%pnext(dir), self%mesh%get_n_groups(dir)) + + call self%allocator%release_block(du) + call self%allocator%release_block(dud) + call self%allocator%release_block(d2u) + + end subroutine transeq_dist_component + + subroutine tds_solve_omp(self, du, u, tdsops) + implicit none + + class(omp_backend_t) :: self + class(field_t), intent(inout) :: du + class(field_t), intent(in) :: u + class(tdsops_t), intent(in) :: tdsops + + ! Check if direction matches for both in/out fields + if (u%dir /= du%dir) then + error stop 'DIR mismatch between fields in tds_solve.' + end if + + call tds_solve_dist(self, du, u, tdsops) + + end subroutine tds_solve_omp + + subroutine tds_solve_dist(self, du, u, tdsops) + implicit none + + class(omp_backend_t) :: self + class(field_t), intent(inout) :: du + class(field_t), intent(in) :: u + class(tdsops_t), intent(in) :: tdsops + integer :: n_halo, n_groups, dir + + ! TODO: don't hardcode n_halo + n_halo = 4 + dir = u%dir + n_groups = self%mesh%get_n_groups(u) + + call copy_into_buffers(self%u_send_s, self%u_send_e, u%data, & + tdsops%tds_n, n_groups) + + ! halo exchange + call sendrecv_fields(self%u_recv_s, self%u_recv_e, & + self%u_send_s, self%u_send_e, & + SZ*n_halo*n_groups, & + self%mesh%par%nproc_dir(dir), & + self%mesh%par%pprev(dir), & + self%mesh%par%pnext(dir)) + + call exec_dist_tds_compact( & + du%data, u%data, self%u_recv_s, self%u_recv_e, & + self%du_send_s, self%du_send_e, self%du_recv_s, self%du_recv_e, & + tdsops, self%mesh%par%nproc_dir(dir), & + self%mesh%par%pprev(dir), self%mesh%par%pnext(dir), & + n_groups) + + end subroutine tds_solve_dist + + subroutine reorder_omp(self, u_, u, direction) + implicit none + + class(omp_backend_t) :: self + class(field_t), intent(inout) :: u_ + class(field_t), intent(in) :: u + integer, intent(in) :: direction + integer, dimension(3) :: dims + integer :: i, j, k + integer :: out_i, out_j, out_k + integer :: dir_from, dir_to + + dims = self%mesh%get_padded_dims(u) + call get_dirs_from_rdr(dir_from, dir_to, direction) + + !$omp parallel do private(out_i, out_j, out_k) collapse(2) + do k = 1, dims(3) + do j = 1, dims(2) + do i = 1, dims(1) + call get_index_reordering( & + out_i, out_j, out_k, i, j, k, dir_from, dir_to, self%mesh) + u_%data(out_i, out_j, out_k) = u%data(i, j, k) + end do + end do + end do + !$omp end parallel do + + end subroutine reorder_omp + + subroutine sum_yintox_omp(self, u, u_) + implicit none + + class(omp_backend_t) :: self + class(field_t), intent(inout) :: u + class(field_t), intent(in) :: u_ + + call sum_intox_omp(self, u, u_, DIR_Y) + + end subroutine sum_yintox_omp + + subroutine sum_zintox_omp(self, u, u_) + implicit none + + class(omp_backend_t) :: self + class(field_t), intent(inout) :: u + class(field_t), intent(in) :: u_ + + call sum_intox_omp(self, u, u_, DIR_Z) + + end subroutine sum_zintox_omp + + subroutine sum_intox_omp(self, u, u_, dir_to) + + class(omp_backend_t) :: self + class(field_t), intent(inout) :: u + class(field_t), intent(in) :: u_ + integer, intent(in) :: dir_to + + integer :: dir_from + integer, dimension(3) :: dims + integer :: i, j, k ! Working indices + integer :: ii, jj, kk ! Transpose indices + + dir_from = DIR_X + + dims = self%mesh%get_padded_dims(u) + !$omp parallel do private(i, ii, jj, kk) collapse(2) + do k = 1, dims(3) + do j = 1, dims(2) + do i = 1, dims(1) + call get_index_reordering(ii, jj, kk, i, j, k, & + dir_from, dir_to, self%mesh) + u%data(i, j, k) = u%data(i, j, k) + u_%data(ii, jj, kk) + end do + end do + end do + !$omp end parallel do + + end subroutine sum_intox_omp + + subroutine vecadd_omp(self, a, x, b, y) + implicit none + + class(omp_backend_t) :: self + real(dp), intent(in) :: a + class(field_t), intent(in) :: x + real(dp), intent(in) :: b + class(field_t), intent(inout) :: y + integer, dimension(3) :: dims + integer :: i, j, k, ii + + integer :: nvec, remstart + + if (x%dir /= y%dir) then + error stop "Called vector add with incompatible fields" + end if + + dims = size(x%data) + nvec = dims(1)/SZ + remstart = nvec*SZ + 1 + + !$omp parallel do private(i, ii) collapse(2) + do k = 1, dims(3) + do j = 1, dims(2) + ! Execute inner vectorised loops + do ii = 1, nvec + !$omp simd + do i = 1, SZ + y%data(i + (ii - 1)*SZ, j, k) = & + a*x%data(i + (ii - 1)*SZ, j, k) + & + b*y%data(i + (ii - 1)*SZ, j, k) + end do + !$omp end simd + end do + + ! Remainder loop + do i = remstart, dims(1) + y%data(i, j, k) = a*x%data(i, j, k) + b*y%data(i, j, k) + end do + end do + end do + !$omp end parallel do + + end subroutine vecadd_omp + + real(dp) function scalar_product_omp(self, x, y) result(s) + + use mpi + + use m_common, only: none, get_rdr_from_dirs + + implicit none + + class(omp_backend_t) :: self + class(field_t), intent(in) :: x, y + class(field_t), pointer :: x_, y_ + integer, dimension(3) :: dims + integer :: i, j, k, ii + integer :: nvec, remstart + integer :: ierr + + if ((x%data_loc == none) .or. (y%data_loc == none)) then + error stop "You must set the data_loc before calling scalar product" + end if + if (x%data_loc /= y%data_loc) then + error stop "Called scalar product with incompatible fields" + end if + + ! Reorient data into temporary DIR_C storage + x_ => self%allocator%get_block(DIR_C, x%data_loc) + call self%get_field_data(x_%data, x) + y_ => self%allocator%get_block(DIR_C, y%data_loc) + call self%get_field_data(y_%data, y) + + dims = self%mesh%get_field_dims(x_) + + nvec = dims(1)/SZ + remstart = nvec*SZ + 1 + + s = 0.0_dp + !$omp parallel do reduction(+:s) private(i, ii) collapse(2) + do k = 1, dims(3) + do j = 1, dims(2) + ! Execute inner vectorised loops + do ii = 1, nvec + !$omp simd reduction(+:s) + do i = 1, SZ + s = s + x_%data(i + (ii - 1)*SZ, j, k)* & + y_%data(i + (ii - 1)*SZ, j, k) + end do + !$omp end simd + end do + + ! Remainder loop + do i = remstart, dims(1) + s = s + x_%data(i, j, k)*y_%data(i, j, k) + end do + end do + end do + !$omp end parallel do + + ! Release temporary storage + call self%allocator%release_block(x_) + call self%allocator%release_block(y_) + + ! Reduce the result + call MPI_Allreduce(MPI_IN_PLACE, s, 1, MPI_DOUBLE_PRECISION, & + MPI_SUM, MPI_COMM_WORLD, & + ierr) + + end function scalar_product_omp + + subroutine copy_into_buffers(u_send_s, u_send_e, u, n, n_groups) + implicit none + + real(dp), dimension(:, :, :), intent(out) :: u_send_s, u_send_e + real(dp), dimension(:, :, :), intent(in) :: u + integer, intent(in) :: n + integer, intent(in) :: n_groups + integer :: i, j, k + integer :: n_halo = 4 + + !$omp parallel do + do k = 1, n_groups + do j = 1, n_halo + !$omp simd + do i = 1, SZ + u_send_s(i, j, k) = u(i, j, k) + u_send_e(i, j, k) = u(i, n - n_halo + j, k) + end do + !$omp end simd + end do + end do + !$omp end parallel do + + end subroutine copy_into_buffers + + subroutine copy_data_to_f_omp(self, f, data) + class(omp_backend_t), intent(inout) :: self + class(field_t), intent(inout) :: f + real(dp), dimension(:, :, :), intent(in) :: data + + f%data = data + end subroutine copy_data_to_f_omp + + subroutine copy_f_to_data_omp(self, data, f) + class(omp_backend_t), intent(inout) :: self + real(dp), dimension(:, :, :), intent(out) :: data + class(field_t), intent(in) :: f + + data = f%data + end subroutine copy_f_to_data_omp + + subroutine init_omp_poisson_fft(self, mesh, xdirps, ydirps, zdirps) + implicit none + + class(omp_backend_t) :: self + class(mesh_t), intent(in) :: mesh + type(dirps_t), intent(in) :: xdirps, ydirps, zdirps + + allocate (omp_poisson_fft_t :: self%poisson_fft) + + select type (poisson_fft => self%poisson_fft) + type is (omp_poisson_fft_t) + poisson_fft = omp_poisson_fft_t(mesh, xdirps, ydirps, zdirps) + end select + + end subroutine init_omp_poisson_fft + +end module m_omp_backend + diff --git a/api/src/common.f90 b/api/src/common.f90 new file mode 100644 index 000000000..6d3df179a --- /dev/null +++ b/api/src/common.f90 @@ -0,0 +1,6 @@ +module m_omp_common + implicit none + + integer, parameter :: SZ = 16 + +end module m_omp_common diff --git a/api/src/distributed.f90 b/api/src/distributed.f90 new file mode 100644 index 000000000..c07b5a1b2 --- /dev/null +++ b/api/src/distributed.f90 @@ -0,0 +1,230 @@ +module m_omp_kernels_dist + use omp_lib + + use m_common, only: dp + use m_omp_common, only: SZ + + implicit none + +contains + + subroutine der_univ_dist( & + du, send_u_s, send_u_e, u, u_s, u_e, coeffs_s, coeffs_e, coeffs, n, & + ffr, fbc, faf & + ) + implicit none + + ! Arguments + real(dp), intent(out), dimension(:, :) :: du, send_u_s, send_u_e + real(dp), intent(in), dimension(:, :) :: u, u_s, u_e + real(dp), intent(in), dimension(:, :) :: coeffs_s, coeffs_e ! start/end + real(dp), intent(in), dimension(:) :: coeffs + integer, intent(in) :: n + real(dp), intent(in), dimension(:) :: ffr, fbc, faf + + ! Local variables + integer :: i, j!, b + + real(dp) :: c_m4, c_m3, c_m2, c_m1, c_j, c_p1, c_p2, c_p3, c_p4, & + alpha, last_r + + ! store bulk coeffs in the registers + c_m4 = coeffs(1); c_m3 = coeffs(2); c_m2 = coeffs(3); c_m1 = coeffs(4) + c_j = coeffs(5) + c_p1 = coeffs(6); c_p2 = coeffs(7); c_p3 = coeffs(8); c_p4 = coeffs(9) + last_r = ffr(1) + + !$omp simd + do i = 1, SZ + du(i, 1) = coeffs_s(1, 1)*u_s(i, 1) & + + coeffs_s(2, 1)*u_s(i, 2) & + + coeffs_s(3, 1)*u_s(i, 3) & + + coeffs_s(4, 1)*u_s(i, 4) & + + coeffs_s(5, 1)*u(i, 1) & + + coeffs_s(6, 1)*u(i, 2) & + + coeffs_s(7, 1)*u(i, 3) & + + coeffs_s(8, 1)*u(i, 4) & + + coeffs_s(9, 1)*u(i, 5) + du(i, 1) = du(i, 1)*faf(1) + du(i, 2) = coeffs_s(1, 2)*u_s(i, 2) & + + coeffs_s(2, 2)*u_s(i, 3) & + + coeffs_s(3, 2)*u_s(i, 4) & + + coeffs_s(4, 2)*u(i, 1) & + + coeffs_s(5, 2)*u(i, 2) & + + coeffs_s(6, 2)*u(i, 3) & + + coeffs_s(7, 2)*u(i, 4) & + + coeffs_s(8, 2)*u(i, 5) & + + coeffs_s(9, 2)*u(i, 6) + du(i, 2) = du(i, 2)*faf(2) + du(i, 3) = coeffs_s(1, 3)*u_s(i, 3) & + + coeffs_s(2, 3)*u_s(i, 4) & + + coeffs_s(3, 3)*u(i, 1) & + + coeffs_s(4, 3)*u(i, 2) & + + coeffs_s(5, 3)*u(i, 3) & + + coeffs_s(6, 3)*u(i, 4) & + + coeffs_s(7, 3)*u(i, 5) & + + coeffs_s(8, 3)*u(i, 6) & + + coeffs_s(9, 3)*u(i, 7) + du(i, 3) = ffr(3)*(du(i, 3) - faf(3)*du(i, 2)) + du(i, 4) = coeffs_s(1, 4)*u_s(i, 4) & + + coeffs_s(2, 4)*u(i, 1) & + + coeffs_s(3, 4)*u(i, 2) & + + coeffs_s(4, 4)*u(i, 3) & + + coeffs_s(5, 4)*u(i, 4) & + + coeffs_s(6, 4)*u(i, 5) & + + coeffs_s(7, 4)*u(i, 6) & + + coeffs_s(8, 4)*u(i, 7) & + + coeffs_s(9, 4)*u(i, 8) + du(i, 4) = ffr(4)*(du(i, 4) - faf(4)*du(i, 3)) + end do + !$omp end simd + + ! alpha is always the same in the bulk region for us + alpha = faf(5) + + do j = 5, n - 4 + !$omp simd + do i = 1, SZ + du(i, j) = c_m4*u(i, j - 4) + c_m3*u(i, j - 3) & + + c_m2*u(i, j - 2) + c_m1*u(i, j - 1) & + + c_j*u(i, j) & + + c_p1*u(i, j + 1) + c_p2*u(i, j + 2) & + + c_p3*u(i, j + 3) + c_p4*u(i, j + 4) + du(i, j) = ffr(j)*(du(i, j) - alpha*du(i, j - 1)) + end do + !$omp end simd + end do + + !$omp simd + do i = 1, SZ + j = n - 3 + du(i, j) = coeffs_e(1, 1)*u(i, j - 4) & + + coeffs_e(2, 1)*u(i, j - 3) & + + coeffs_e(3, 1)*u(i, j - 2) & + + coeffs_e(4, 1)*u(i, j - 1) & + + coeffs_e(5, 1)*u(i, j) & + + coeffs_e(6, 1)*u(i, j + 1) & + + coeffs_e(7, 1)*u(i, j + 2) & + + coeffs_e(8, 1)*u(i, j + 3) & + + coeffs_e(9, 1)*u_e(i, 1) + du(i, j) = ffr(j)*(du(i, j) - faf(j)*du(i, j - 1)) + j = n - 2 + du(i, j) = coeffs_e(1, 2)*u(i, j - 4) & + + coeffs_e(2, 2)*u(i, j - 3) & + + coeffs_e(3, 2)*u(i, j - 2) & + + coeffs_e(4, 2)*u(i, j - 1) & + + coeffs_e(5, 2)*u(i, j) & + + coeffs_e(6, 2)*u(i, j + 1) & + + coeffs_e(7, 2)*u(i, j + 2) & + + coeffs_e(8, 2)*u_e(i, 1) & + + coeffs_e(9, 2)*u_e(i, 2) + du(i, j) = ffr(j)*(du(i, j) - faf(j)*du(i, j - 1)) + j = n - 1 + du(i, j) = coeffs_e(1, 3)*u(i, j - 4) & + + coeffs_e(2, 3)*u(i, j - 3) & + + coeffs_e(3, 3)*u(i, j - 2) & + + coeffs_e(4, 3)*u(i, j - 1) & + + coeffs_e(5, 3)*u(i, j) & + + coeffs_e(6, 3)*u(i, j + 1) & + + coeffs_e(7, 3)*u_e(i, 1) & + + coeffs_e(8, 3)*u_e(i, 2) & + + coeffs_e(9, 3)*u_e(i, 3) + du(i, j) = ffr(j)*(du(i, j) - faf(j)*du(i, j - 1)) + j = n + du(i, j) = coeffs_e(1, 4)*u(i, j - 4) & + + coeffs_e(2, 4)*u(i, j - 3) & + + coeffs_e(3, 4)*u(i, j - 2) & + + coeffs_e(4, 4)*u(i, j - 1) & + + coeffs_e(5, 4)*u(i, j) & + + coeffs_e(6, 4)*u_e(i, 1) & + + coeffs_e(7, 4)*u_e(i, 2) & + + coeffs_e(8, 4)*u_e(i, 3) & + + coeffs_e(9, 4)*u_e(i, 4) + du(i, j) = ffr(j)*(du(i, j) - faf(j)*du(i, j - 1)) + end do + !$omp end simd + + !$omp simd + do i = 1, SZ + send_u_e(i, 1) = du(i, n) + end do + !$omp end simd + + ! Backward pass of the hybrid algorithm + do j = n - 2, 2, -1 + !$omp simd + do i = 1, SZ + du(i, j) = du(i, j) - fbc(j)*du(i, j + 1) + end do + !$omp end simd + end do + !$omp simd + do i = 1, SZ + du(i, 1) = last_r*(du(i, 1) - fbc(1)*du(i, 2)) + send_u_s(i, 1) = du(i, 1) + end do + !$omp end simd + + end subroutine der_univ_dist + + subroutine der_univ_subs(du, recv_u_s, recv_u_e, n, dist_sa, dist_sc) + implicit none + + ! Arguments + real(dp), intent(out), dimension(:, :) :: du + real(dp), intent(in), dimension(:, :) :: recv_u_s, recv_u_e + real(dp), intent(in), dimension(:) :: dist_sa, dist_sc + integer, intent(in) :: n + + ! Local variables + integer :: i, j!, b + real(dp) :: ur, bl, recp + real(dp), dimension(SZ) :: du_s, du_e + + !$omp simd + do i = 1, SZ + ! A small trick we do here is valid for symmetric Toeplitz matrices. + ! In our case our matrices satisfy this criteria in the (5:n-4) region + ! and as long as a rank has around at least 20 entries the assumptions + ! we make here are perfectly valid. + + ! bl is the bottom left entry in the 2x2 matrix + ! ur is the upper right entry in the 2x2 matrix + + ! Start + ! At the start we have the 'bl', and assume 'ur' + bl = dist_sa(1) + ur = dist_sa(1) + recp = 1._dp/(1._dp - ur*bl) + du_s(i) = recp*(du(i, 1) - bl*recv_u_s(i, 1)) + + ! End + ! At the end we have the 'ur', and assume 'bl' + bl = dist_sc(n) + ur = dist_sc(n) + recp = 1._dp/(1._dp - ur*bl) + du_e(i) = recp*(du(i, n) - ur*recv_u_e(i, 1)) + end do + !$omp end simd + + !$omp simd + do i = 1, SZ + du(i, 1) = du_s(i) + end do + !$omp end simd + do j = 2, n - 1 + !$omp simd + do i = 1, SZ + du(i, j) = (du(i, j) - dist_sa(j)*du_s(i) - dist_sc(j)*du_e(i)) + end do + !$omp end simd + end do + !$omp simd + do i = 1, SZ + du(i, n) = du_e(i) + end do + !$omp end simd + + end subroutine der_univ_subs + +end module m_omp_kernels_dist diff --git a/api/src/exec_dist.f90 b/api/src/exec_dist.f90 new file mode 100644 index 000000000..b1c39a863 --- /dev/null +++ b/api/src/exec_dist.f90 @@ -0,0 +1,192 @@ +module m_omp_exec_dist + use mpi + + use m_common, only: dp, VERT + use m_omp_common, only: SZ + use m_omp_kernels_dist, only: der_univ_dist, der_univ_subs + use m_tdsops, only: tdsops_t + use m_omp_sendrecv, only: sendrecv_fields + + implicit none + +contains + + subroutine exec_dist_tds_compact( & + du, u, u_recv_s, u_recv_e, du_send_s, du_send_e, du_recv_s, du_recv_e, & + tdsops, nproc, pprev, pnext, n_groups) + implicit none + + ! du = d(u) + real(dp), dimension(:, :, :), intent(out) :: du + real(dp), dimension(:, :, :), intent(in) :: u, u_recv_s, u_recv_e + + ! The ones below are intent(out) just so that we can write data in them, + ! not because we actually need the data they store later where this + ! subroutine is called. We absolutely don't care about the data they pass back + real(dp), dimension(:, :, :), intent(out) :: & + du_send_s, du_send_e, du_recv_s, du_recv_e + + type(tdsops_t), intent(in) :: tdsops + integer, intent(in) :: nproc, pprev, pnext + integer, intent(in) :: n_groups + + integer :: n_data + integer :: k + + n_data = SZ*n_groups + + !$omp parallel do + do k = 1, n_groups + call der_univ_dist( & + du(:, :, k), du_send_s(:, :, k), du_send_e(:, :, k), u(:, :, k), & + u_recv_s(:, :, k), u_recv_e(:, :, k), & + tdsops%coeffs_s, tdsops%coeffs_e, tdsops%coeffs, tdsops%tds_n, & + tdsops%dist_fw, tdsops%dist_bw, tdsops%dist_af & + ) + end do + !$omp end parallel do + + ! halo exchange for 2x2 systems + call sendrecv_fields(du_recv_s, du_recv_e, du_send_s, du_send_e, & + n_data, nproc, pprev, pnext) + + !$omp parallel do + do k = 1, n_groups + call der_univ_subs(du(:, :, k), & + du_recv_s(:, :, k), du_recv_e(:, :, k), & + tdsops%tds_n, tdsops%dist_sa, tdsops%dist_sc) + end do + !$omp end parallel do + + end subroutine exec_dist_tds_compact + + subroutine exec_dist_transeq_compact( & + rhs, du, dud, d2u, & + du_send_s, du_send_e, du_recv_s, du_recv_e, & + dud_send_s, dud_send_e, dud_recv_s, dud_recv_e, & + d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e, & + u, u_recv_s, u_recv_e, & + v, v_recv_s, v_recv_e, & + tdsops_du, tdsops_dud, tdsops_d2u, nu, nproc, pprev, pnext, n_groups) + + implicit none + + ! du = d(u) + real(dp), dimension(:, :, :), intent(out) :: rhs, du, dud, d2u + + ! The ones below are intent(out) just so that we can write data in them, + ! not because we actually need the data they store later where this + ! subroutine is called. We absolutely don't care about the data they pass back + real(dp), dimension(:, :, :), intent(out) :: & + du_send_s, du_send_e, du_recv_s, du_recv_e + real(dp), dimension(:, :, :), intent(out) :: & + dud_send_s, dud_send_e, dud_recv_s, dud_recv_e + real(dp), dimension(:, :, :), intent(out) :: & + d2u_send_s, d2u_send_e, d2u_recv_s, d2u_recv_e + + real(dp), dimension(:, :, :), intent(in) :: u, u_recv_s, u_recv_e + real(dp), dimension(:, :, :), intent(in) :: v, v_recv_s, v_recv_e + + type(tdsops_t), intent(in) :: tdsops_du, tdsops_dud, tdsops_d2u + real(dp), intent(in) :: nu + integer, intent(in) :: nproc, pprev, pnext + integer, intent(in) :: n_groups + + real(dp), dimension(:, :), allocatable :: ud, ud_recv_s, ud_recv_e + + integer :: n_data, n_halo + integer :: k, i, j, n + + ! TODO: don't hardcode n_halo + n_halo = 4 + n = tdsops_du%tds_n + n_data = SZ*n_groups + + allocate (ud(SZ, n)) + allocate (ud_recv_e(SZ, n_halo)) + allocate (ud_recv_s(SZ, n_halo)) + + !$omp parallel do private(ud, ud_recv_e, ud_recv_s) + do k = 1, n_groups + call der_univ_dist( & + du(:, :, k), du_send_s(:, :, k), du_send_e(:, :, k), u(:, :, k), & + u_recv_s(:, :, k), u_recv_e(:, :, k), & + tdsops_du%coeffs_s, tdsops_du%coeffs_e, tdsops_du%coeffs, & + n, tdsops_du%dist_fw, tdsops_du%dist_bw, tdsops_du%dist_af & + ) + + call der_univ_dist( & + d2u(:, :, k), d2u_send_s(:, :, k), d2u_send_e(:, :, k), u(:, :, k), & + u_recv_s(:, :, k), u_recv_e(:, :, k), & + tdsops_d2u%coeffs_s, tdsops_d2u%coeffs_e, tdsops_d2u%coeffs, & + n, tdsops_d2u%dist_fw, tdsops_d2u%dist_bw, & + tdsops_d2u%dist_af & + ) + + ! Handle dud by locally generating u*v + do j = 1, n + !$omp simd + do i = 1, SZ + ud(i, j) = u(i, j, k)*v(i, j, k) + end do + !$omp end simd + end do + + do j = 1, n_halo + !$omp simd + do i = 1, SZ + ud_recv_s(i, j) = u_recv_s(i, j, k)*v_recv_s(i, j, k) + ud_recv_e(i, j) = u_recv_e(i, j, k)*v_recv_e(i, j, k) + end do + !$omp end simd + end do + + call der_univ_dist( & + dud(:, :, k), dud_send_s(:, :, k), dud_send_e(:, :, k), ud(:, :), & + ud_recv_s(:, :), ud_recv_e(:, :), & + tdsops_dud%coeffs_s, tdsops_dud%coeffs_e, tdsops_dud%coeffs, & + n, tdsops_dud%dist_fw, tdsops_dud%dist_bw, & + tdsops_dud%dist_af & + ) + + end do + !$omp end parallel do + + ! halo exchange for 2x2 systems + call sendrecv_fields(du_recv_s, du_recv_e, du_send_s, du_send_e, & + n_data, nproc, pprev, pnext) + call sendrecv_fields(dud_recv_s, dud_recv_e, dud_send_s, dud_send_e, & + n_data, nproc, pprev, pnext) + call sendrecv_fields(d2u_recv_s, d2u_recv_e, d2u_send_s, d2u_send_e, & + n_data, nproc, pprev, pnext) + + !$omp parallel do + do k = 1, n_groups + call der_univ_subs(du(:, :, k), & + du_recv_s(:, :, k), du_recv_e(:, :, k), & + n, tdsops_du%dist_sa, tdsops_du%dist_sc) + + call der_univ_subs(dud(:, :, k), & + dud_recv_s(:, :, k), dud_recv_e(:, :, k), & + n, tdsops_dud%dist_sa, tdsops_dud%dist_sc) + + call der_univ_subs(d2u(:, :, k), & + d2u_recv_s(:, :, k), d2u_recv_e(:, :, k), & + n, tdsops_d2u%dist_sa, tdsops_d2u%dist_sc) + + do j = 1, n + !$omp simd + do i = 1, SZ + rhs(i, j, k) = -0.5_dp*(v(i, j, k)*du(i, j, k) + dud(i, j, k)) & + + nu*d2u(i, j, k) + end do + !$omp end simd + end do + + end do + !$omp end parallel do + + end subroutine exec_dist_transeq_compact + +end module m_omp_exec_dist + diff --git a/api/src/exec_thom.f90 b/api/src/exec_thom.f90 new file mode 100644 index 000000000..6c6421adc --- /dev/null +++ b/api/src/exec_thom.f90 @@ -0,0 +1,37 @@ +module m_cuda_exec_thom + use cudafor + + use m_common, only: dp + use m_cuda_kernels_thom, only: der_univ_thom, der_univ_thom_per + use m_cuda_tdsops, only: cuda_tdsops_t + + implicit none + +contains + + subroutine exec_thom_tds_compact(du, u, tdsops, blocks, threads) + implicit none + + real(dp), device, dimension(:, :, :), intent(out) :: du + real(dp), device, dimension(:, :, :), intent(in) :: u + type(cuda_tdsops_t), intent(in) :: tdsops + type(dim3), intent(in) :: blocks, threads + + if (tdsops%periodic) then + call der_univ_thom_per<<>>( & !& + du, u, tdsops%coeffs_dev, tdsops%tds_n, tdsops%alpha, & + tdsops%thom_f_dev, tdsops%thom_s_dev, & + tdsops%thom_w_dev, tdsops%thom_p_dev & + ) + else + call der_univ_thom<<>>( & !& + du, u, & + tdsops%coeffs_s_dev, tdsops%coeffs_e_dev, tdsops%coeffs_dev, & + tdsops%tds_n, tdsops%thom_f_dev, tdsops%thom_s_dev, & + tdsops%thom_w_dev & + ) + end if + + end subroutine exec_thom_tds_compact + +end module m_cuda_exec_thom diff --git a/api/src/field.f90 b/api/src/field.f90 new file mode 100644 index 000000000..e59d8bc3c --- /dev/null +++ b/api/src/field.f90 @@ -0,0 +1,58 @@ +module m_field + + use m_common, only: dp, DIR_X, DIR_Y, DIR_Z, DIR_C + + type :: field_t + !! Memory block type holding both a data field and a pointer + !! to the next block. The `field_t` type also holds a integer + !! `refcount` that counts the number of references to this + !! field. User code is currently responsible for incrementing + !! the reference count. + class(field_t), pointer :: next + real(dp), pointer, private :: p_data(:) + real(dp), pointer, contiguous :: data(:, :, :) + integer :: dir + integer :: data_loc + integer :: refcount = 0 + integer :: id !! An integer identifying the memory block. + contains + procedure :: set_shape + procedure :: set_data_loc + end type field_t + + interface field_t + module procedure field_init + end interface field_t + +contains + + subroutine set_data_loc(self, data_loc) + class(field_t) :: self + integer, intent(in) :: data_loc + + self%data_loc = data_loc + + end subroutine + + subroutine set_shape(self, dims) + implicit none + + class(field_t) :: self + integer, intent(in) :: dims(3) + + self%data(1:dims(1), 1:dims(2), 1:dims(3)) => self%p_data + + end subroutine set_shape + + function field_init(ngrid, next, id) result(f) + integer, intent(in) :: ngrid, id + type(field_t), pointer, intent(in) :: next + type(field_t) :: f + + allocate (f%p_data(ngrid)) + f%refcount = 0 + f%next => next + f%id = id + end function field_init + +end module m_field diff --git a/api/src/mesh.f90 b/api/src/mesh.f90 new file mode 100644 index 000000000..76dd0360a --- /dev/null +++ b/api/src/mesh.f90 @@ -0,0 +1,481 @@ +module m_mesh + use iso_fortran_env, only: stderr => error_unit + + use mpi + use m_common, only: dp, DIR_X, DIR_Y, DIR_Z, DIR_C, & + CELL, VERT, X_FACE, Y_FACE, Z_FACE, & + X_EDGE, Y_EDGE, Z_EDGE, & + BC_PERIODIC, BC_NEUMANN, BC_DIRICHLET + use m_field, only: field_t + + implicit none + + ! Stores geometry information + type :: geo_t + real(dp), dimension(3) :: d ! size of a cell in each direction (=edge length, distance between centers, distance between vertices) + real(dp), dimension(3) :: L ! Global dimensions of the domain in each direction + end type + + ! Stores parallel domain related information + type :: parallel_t + integer :: nrank ! local rank ID + integer :: nproc ! total number of ranks/proc participating in the domain decomposition + integer, dimension(3) :: nrank_dir ! local rank ID in each direction + integer, dimension(3) :: nproc_dir ! total number of proc in each direction + integer, dimension(3) :: n_offset ! number of cells offset in each direction due to domain decomposition + integer, dimension(3) :: pnext ! rank ID of the previous rank in each direction + integer, dimension(3) :: pprev ! rank ID of the next rank in each direction + contains + procedure :: is_root ! returns if the current rank is the root rank + end type + + ! The mesh class stores all the information about the global and local (due to domain decomposition) mesh + ! It also includes getter functions to access some of its parameters + type :: mesh_t + integer, dimension(3), private :: global_vert_dims ! global number of vertices in each direction without padding (cartesian structure) + integer, dimension(3), private :: global_cell_dims ! global number of cells in each direction without padding (cartesian structure) + + integer, dimension(3), private :: vert_dims_padded ! local domain size including padding (cartesian structure) + integer, dimension(3), private :: vert_dims ! local number of vertices in each direction without padding (cartesian structure) + integer, dimension(3), private :: cell_dims ! local number of cells in each direction without padding (cartesian structure) + logical, dimension(3), private :: periodic_BC ! Whether or not a direction has a periodic BC + integer, dimension(3, 2), private :: BCs_global + integer, dimension(3, 2), private :: BCs + integer, private :: sz + type(geo_t), allocatable :: geo ! object containing geometry information + type(parallel_t), allocatable :: par ! object containing parallel domain decomposition information + contains + procedure :: get_SZ + + procedure :: get_dims + procedure :: get_global_dims + + procedure :: get_n_groups_dir + procedure :: get_n_groups_phi + generic :: get_n_groups => get_n_groups_dir, get_n_groups_phi + + procedure :: get_field_dims_dir + procedure :: get_field_dims_phi + procedure :: get_field_dims_phi_dataloc + generic :: get_field_dims => get_field_dims_dir, get_field_dims_phi, & + get_field_dims_phi_dataloc + + procedure :: get_n_dir + procedure :: get_n_phi + generic :: get_n => get_n_dir, get_n_phi + + procedure :: get_padded_dims_phi + procedure :: get_padded_dims_dir + generic :: get_padded_dims => get_padded_dims_dir, get_padded_dims_phi + + procedure :: get_coordinates + + procedure :: set_sz + procedure :: set_padded_dims + end type mesh_t + + interface mesh_t + module procedure mesh_init + end interface mesh_t + +contains + + function mesh_init(dims_global, nproc_dir, L_global, BC_x, BC_y, BC_z) & + result(mesh) + !! Completely initialise the mesh object. + !! Upon initialisation the mesh object can be read-only and shouldn't be edited + !! Takes as argument global information about the mesh like its length, number of cells and decomposition in each direction + integer, dimension(3), intent(in) :: dims_global + integer, dimension(3), intent(in) :: nproc_dir ! Number of proc in each direction + real(dp), dimension(3), intent(in) :: L_global + character(len=*), dimension(2), intent(in) :: BC_x, BC_y, BC_z + type(mesh_t) :: mesh + + character(len=20), dimension(3, 2) :: BC_all + logical :: is_first_domain, is_last_domain + integer :: dir, j + integer :: ierr + + allocate (mesh%geo) + allocate (mesh%par) + + BC_all(1, 1) = BC_x(1); BC_all(1, 2) = BC_x(2) + BC_all(2, 1) = BC_y(1); BC_all(2, 2) = BC_y(2) + BC_all(3, 1) = BC_z(1); BC_all(3, 2) = BC_z(2) + do dir = 1, 3 + do j = 1, 2 + select case (trim(BC_all(dir, j))) + case ('periodic') + mesh%BCs_global(dir, j) = BC_PERIODIC + case ('neumann') + mesh%BCs_global(dir, j) = BC_NEUMANN + case ('dirichlet') + mesh%BCs_global(dir, j) = BC_DIRICHLET + case default + error stop 'Unknown BC' + end select + end do + end do + + do dir = 1, 3 + if (any(mesh%BCs_global(dir, :) == BC_PERIODIC) .and. & + (.not. all(mesh%BCs_global(dir, :) == BC_PERIODIC))) then + error stop 'BCs are incompatible: in a direction make sure to have & + &either both sides periodic or none.' + end if + mesh%periodic_BC(dir) = all(mesh%BCs_global(dir, :) == BC_PERIODIC) + end do + + ! Set global vertex dims + mesh%global_vert_dims(:) = dims_global + + ! Set global cell dims + do dir = 1, 3 + if (mesh%periodic_BC(dir)) then + mesh%global_cell_dims(dir) = mesh%global_vert_dims(dir) + else + mesh%global_cell_dims(dir) = mesh%global_vert_dims(dir) - 1 + end if + end do + + ! Geometry + mesh%geo%L = L_global + mesh%geo%d = mesh%geo%L/mesh%global_cell_dims + + ! Parallel domain decomposition + mesh%par%nproc_dir(:) = nproc_dir + mesh%par%nproc = product(nproc_dir(:)) + call MPI_Comm_rank(MPI_COMM_WORLD, mesh%par%nrank, ierr) + call MPI_Comm_size(MPI_COMM_WORLD, mesh%par%nproc, ierr) + call domain_decomposition(mesh) + + ! Set subdomain BCs + do dir = 1, 3 + is_first_domain = mesh%par%nrank_dir(dir) == 0 + is_last_domain = mesh%par%nrank_dir(dir) + 1 == mesh%par%nproc_dir(dir) + ! subdomain-subdomain boundaries are identical to periodic BCs + if (is_first_domain) then + mesh%BCs(dir, 1) = mesh%BCs_global(dir, 1) + mesh%BCs(dir, 2) = BC_PERIODIC + else if (is_last_domain) then + mesh%BCs(dir, 1) = BC_PERIODIC + mesh%BCs(dir, 2) = mesh%BCs_global(dir, 2) + else + mesh%BCs(dir, :) = BC_PERIODIC + end if + end do + + ! Define number of cells and vertices in each direction + mesh%vert_dims = mesh%global_vert_dims/mesh%par%nproc_dir + + do dir = 1, 3 + is_last_domain = (mesh%par%nrank_dir(dir) + 1 == mesh%par%nproc_dir(dir)) + if (is_last_domain .and. (.not. mesh%periodic_BC(dir))) then + mesh%cell_dims(dir) = mesh%vert_dims(dir) - 1 + else + mesh%cell_dims(dir) = mesh%vert_dims(dir) + end if + end do + + ! Set offset for global indices + mesh%par%n_offset(:) = mesh%vert_dims(:)*mesh%par%nrank_dir(:) + + ! Define default values + mesh%vert_dims_padded = mesh%vert_dims + mesh%sz = 1 + + end function mesh_init + + subroutine set_padded_dims(self, vert_dims) + class(mesh_t), intent(inout) :: self + integer, dimension(3), intent(in) :: vert_dims + + self%vert_dims_padded = vert_dims + + end subroutine + + subroutine set_sz(self, sz) + class(mesh_t), intent(inout) :: self + integer, intent(in) :: sz + + self%sz = sz + + end subroutine + + subroutine domain_decomposition(mesh) + !! Supports 1D, 2D, and 3D domain decomposition. + !! + !! Current implementation allows only constant sub-domain size across a + !! given direction. + class(mesh_t), intent(inout) :: mesh + + integer, allocatable, dimension(:, :, :) :: global_ranks + integer :: i, nproc_x, nproc_y, nproc_z, nproc + integer, dimension(3) :: subd_pos, subd_pos_prev, subd_pos_next + integer :: dir + + ! Number of processes on a direction basis + nproc_x = mesh%par%nproc_dir(1) + nproc_y = mesh%par%nproc_dir(2) + nproc_z = mesh%par%nproc_dir(3) + + ! A 3D array corresponding to each region in the global domain + allocate (global_ranks(nproc_x, nproc_y, nproc_z)) + + ! set the corresponding global rank for each sub-domain + global_ranks = reshape([(i, i=0, mesh%par%nproc - 1)], & + shape=[nproc_x, nproc_y, nproc_z]) + + ! subdomain position in the global domain + subd_pos = findloc(global_ranks, mesh%par%nrank) + + ! local/directional position of the subdomain + mesh%par%nrank_dir(:) = subd_pos(:) - 1 + + do dir = 1, 3 + nproc = mesh%par%nproc_dir(dir) + subd_pos_prev(:) = subd_pos(:) + subd_pos_prev(dir) = modulo(subd_pos(dir) - 2, nproc) + 1 + mesh%par%pprev(dir) = global_ranks(subd_pos_prev(1), & + subd_pos_prev(2), & + subd_pos_prev(3)) + + subd_pos_next(:) = subd_pos(:) + subd_pos_next(dir) = modulo(subd_pos(dir) - nproc, nproc) + 1 + mesh%par%pnext(dir) = global_ranks(subd_pos_next(1), & + subd_pos_next(2), & + subd_pos_next(3)) + end do + + end subroutine domain_decomposition + + pure function get_sz(self) result(sz) + !! Getter for parameter SZ + class(mesh_t), intent(in) :: self + integer :: sz + + sz = self%sz + + end function + + pure function get_dims(self, data_loc) result(dims) + !! Getter for local domain dimensions + class(mesh_t), intent(in) :: self + integer, intent(in) :: data_loc + integer, dimension(3) :: dims + + dims = get_dims_dataloc(data_loc, self%vert_dims, self%cell_dims) + end function + + pure function get_global_dims(self, data_loc) result(dims) + !! Getter for local domain dimensions + class(mesh_t), intent(in) :: self + integer, intent(in) :: data_loc + integer, dimension(3) :: dims + + dims = get_dims_dataloc(data_loc, self%global_vert_dims, & + self%global_cell_dims) + end function + + pure function get_dims_dataloc(data_loc, vert_dims, cell_dims) result(dims) + !! Getter for domain dimensions + integer, intent(in) :: data_loc + integer, dimension(3), intent(in) :: vert_dims, cell_dims + integer, dimension(3) :: dims + + select case (data_loc) + case (VERT) + dims = vert_dims + case (CELL) + dims = cell_dims + case (X_FACE) + dims(1) = vert_dims(1) + dims(2:3) = cell_dims(2:3) + case (Y_FACE) + dims(1) = cell_dims(1) + dims(2) = vert_dims(2) + dims(3) = cell_dims(3) + case (Z_FACE) + dims(1:2) = cell_dims(1:2) + dims(3) = vert_dims(3) + case (X_EDGE) + dims(1) = cell_dims(1) + dims(2:3) = vert_dims(2:3) + case (Y_EDGE) + dims(1) = vert_dims(1) + dims(2) = cell_dims(2) + dims(3) = vert_dims(3) + case (Z_EDGE) + dims(1:2) = vert_dims(1:2) + dims(3) = cell_dims(3) + case default + error stop "Unknown location in get_dims_dataloc" + end select + end function get_dims_dataloc + + pure function get_padded_dims_dir(self, dir) result(dims_padded) + !! Getter for padded dimensions with structure in `dir` direction + class(mesh_t), intent(in) :: self + integer, intent(in) :: dir + integer, dimension(3) :: dims_padded + + if (dir == DIR_C) then + dims_padded = self%vert_dims_padded + else + dims_padded(1) = self%sz + dims_padded(2) = self%vert_dims_padded(dir) + dims_padded(3) = self%get_n_groups(dir) + end if + + end function + + pure function get_padded_dims_phi(self, phi) result(dims_padded) + !! Getter for padded dimensions for field phi + !! Gets the field direction from the field itself + class(mesh_t), intent(in) :: self + class(field_t), intent(in) :: phi + integer, dimension(3) :: dims_padded + + dims_padded = self%get_padded_dims(phi%dir) + + end function + + pure function get_n_groups_dir(self, dir) result(n_groups) + !! Getter for the number of groups for fields in direction `dir` + class(mesh_t), intent(in) :: self + integer, intent(in) :: dir + integer :: n_groups + + n_groups = (product(self%vert_dims_padded(:))/ & + self%vert_dims_padded(dir))/self%sz + + end function + + pure function get_n_groups_phi(self, phi) result(n_groups) + !! Getter for the number of groups for fields phi + class(mesh_t), intent(in) :: self + class(field_t), intent(in) :: phi + integer :: n_groups + + n_groups = self%get_n_groups(phi%dir) + + end function + + pure function get_field_dims_phi(self, phi) result(dims) + !! Getter for the dimensions of field phi + class(mesh_t), intent(in) :: self + class(field_t), intent(in) :: phi + integer, dimension(3) :: dims + + dims = self%get_field_dims(phi%dir, phi%data_loc) + + end function + + pure function get_field_dims_phi_dataloc(self, phi, data_loc) result(dims) + !! Getter for the dimensions of field phi where data is located on `data_loc` + class(mesh_t), intent(in) :: self + class(field_t), intent(in) :: phi + integer, intent(in) :: data_loc + integer, dimension(3) :: dims + + dims = self%get_field_dims(phi%dir, data_loc) + + end function + + pure function get_field_dims_dir(self, dir, data_loc) result(dims) + !! Getter for the dimensions of an array directed along `dir` where data would be located on `data_loc` + class(mesh_t), intent(in) :: self + integer, intent(in) :: dir + integer, intent(in) :: data_loc + integer, dimension(3) :: dims + + if (dir == DIR_C) then + dims(1) = self%get_n(DIR_X, data_loc) + dims(2) = self%get_n(DIR_Y, data_loc) + dims(3) = self%get_n(DIR_Z, data_loc) + else + dims(1) = self%sz + dims(2) = self%get_n(dir, data_loc) + dims(3) = self%get_n_groups(dir) + end if + + end function + + pure function get_n_phi(self, phi) result(n) + !! Getter for the main dimension of field phi + class(mesh_t), intent(in) :: self + class(field_t), intent(in) :: phi + integer :: n + + n = self%get_n(phi%dir, phi%data_loc) + + end function + + pure function get_n_dir(self, dir, data_loc) result(n) + !! Getter for the main dimension a field oriented along `dir` with data on `data_loc` + class(mesh_t), intent(in) :: self + integer, intent(in) :: dir + integer, intent(in) :: data_loc + integer :: n, n_cell, n_vert + + n_cell = self%cell_dims(dir) + n_vert = self%vert_dims(dir) + + ! default to n_vert + n = n_vert + + select case (data_loc) + case (CELL) + n = n_cell + case (VERT) + n = n_vert + case (X_FACE) + if (dir /= DIR_X) then + n = n_cell + end if + case (Y_FACE) + if (dir /= DIR_Y) then + n = n_cell + end if + case (Z_FACE) + if (dir /= DIR_Z) then + n = n_cell + end if + case (X_EDGE) + if (dir == DIR_X) then + n = n_cell + end if + case (Y_EDGE) + if (dir == DIR_Y) then + n = n_cell + end if + case (Z_EDGE) + if (dir == DIR_Z) then + n = n_cell + end if + case default + error stop "Unknown direction in get_n_dir" + end select + end function get_n_dir + + pure function get_coordinates(self, i, j, k) result(xloc) + !! Get the physical location of a cell center with i,j,k local indices + class(mesh_t), intent(in) :: self + integer, intent(in) :: i, j, k + real(dp), dimension(3) :: xloc + + xloc(1) = (i - 1 + self%par%n_offset(1))*self%geo%d(1) + xloc(2) = (j - 1 + self%par%n_offset(2))*self%geo%d(2) + xloc(3) = (k - 1 + self%par%n_offset(3))*self%geo%d(3) + end function + + pure function is_root(self) result(is_root_rank) + !! Returns whether or not the current rank is the root rank + class(parallel_t), intent(in) :: self + logical :: is_root_rank + + is_root_rank = (self%nrank == 0) + + end function + +end module m_mesh diff --git a/api/src/ordering.f90 b/api/src/ordering.f90 new file mode 100644 index 000000000..f4e797cbc --- /dev/null +++ b/api/src/ordering.f90 @@ -0,0 +1,111 @@ +module m_ordering + + use m_common, only: dp, get_dirs_from_rdr, DIR_X, DIR_Y, DIR_Z, DIR_C, & + RDR_X2Y, RDR_X2Z, RDR_Y2X, RDR_Y2Z, RDR_Z2X, RDR_Z2Y + + use m_mesh, only: mesh_t + + implicit none + interface get_index_reordering + procedure get_index_reordering_rdr, get_index_reordering_dirs + end interface + +contains + !! + !! "Application storage" stores spatial data with a directionality for better cache locality + !! This set of functions converts indices from this application storage (_dir) to cartesian indices (_ijk) + !! + + pure subroutine get_index_ijk(i, j, k, dir_i, dir_j, dir_k, dir, & + SZ, nx_padded, ny_padded, nz_padded) + !! Get cartesian index from application storage directional one + integer, intent(out) :: i, j, k ! cartesian indices + integer, intent(in) :: dir_i, dir_j, dir_k ! application storage indices + integer, intent(in) :: dir ! direction of the applicatino storage indices + integer, intent(in) :: SZ, nx_padded, ny_padded, nz_padded ! dimensions of the block + + select case (dir) + case (DIR_X) + i = dir_j + j = mod(dir_k - 1, ny_padded/SZ)*SZ + dir_i + k = 1 + (dir_k - 1)/(ny_padded/SZ) + case (DIR_Y) + i = mod(dir_k - 1, nx_padded/SZ)*SZ + dir_i + j = dir_j + k = 1 + (dir_k - 1)/(nx_padded/SZ) + case (DIR_Z) + i = mod(dir_k - 1, nx_padded/SZ)*SZ + dir_i + j = 1 + (dir_k - 1)/(nx_padded/SZ) + k = dir_j + case (DIR_C) + i = dir_i + j = dir_j + k = dir_k + end select + + end subroutine get_index_ijk + + pure subroutine get_index_dir(dir_i, dir_j, dir_k, i, j, k, dir, & + SZ, nx_padded, ny_padded, nz_padded) + !! Get application storage directional index from cartesian index + integer, intent(out) :: dir_i, dir_j, dir_k ! application storage indices + integer, intent(in) :: i, j, k ! cartesian indices + integer, intent(in) :: dir ! direction of the application storage indices + integer, intent(in) :: SZ, nx_padded, ny_padded, nz_padded ! dimensions of the block + + select case (dir) + case (DIR_X) + dir_i = mod(j - 1, SZ) + 1 + dir_j = i + dir_k = (ny_padded/SZ)*(k - 1) + 1 + (j - 1)/SZ + case (DIR_Y) + dir_i = mod(i - 1, SZ) + 1 + dir_j = j + dir_k = (nx_padded/SZ)*(k - 1) + 1 + (i - 1)/SZ + case (DIR_Z) + dir_i = mod(i - 1, SZ) + 1 + dir_j = k + dir_k = (nx_padded/SZ)*(j - 1) + 1 + (i - 1)/SZ + case (DIR_C) + dir_i = i + dir_j = j + dir_k = k + end select + + end subroutine get_index_dir + + pure subroutine get_index_reordering_dirs( & + out_i, out_j, out_k, in_i, in_j, in_k, dir_from, dir_to, mesh & + ) + !! Converts a set of application storage directional index to an other direction. + !! The two directions are defined by the reorder_dir variable, RDR_X2Y will go from storage in X to Y etc. + integer, intent(out) :: out_i, out_j, out_k ! new indices in the application storage + integer, intent(in) :: in_i, in_j, in_k ! original indices + integer, intent(in) :: dir_from, dir_to + class(mesh_t), intent(in) :: mesh + integer :: i, j, k ! Intermediary cartesian indices + integer, dimension(3) :: dims_padded + + dims_padded = mesh%get_padded_dims(DIR_C) + call get_index_ijk(i, j, k, in_i, in_j, in_k, dir_from, mesh%get_sz(), & + dims_padded(1), dims_padded(2), dims_padded(3)) + call get_index_dir(out_i, out_j, out_k, i, j, k, dir_to, mesh%get_sz(), & + dims_padded(1), dims_padded(2), dims_padded(3)) + + end subroutine get_index_reordering_dirs + + pure subroutine get_index_reordering_rdr(out_i, out_j, out_k, & + in_i, in_j, in_k, reorder_dir, mesh) + integer, intent(out) :: out_i, out_j, out_k ! new indices in the application storage + integer, intent(in) :: in_i, in_j, in_k ! original indices + integer, intent(in) :: reorder_dir + class(mesh_t), intent(in) :: mesh + integer :: dir_from, dir_to + + call get_dirs_from_rdr(dir_from, dir_to, reorder_dir) + call get_index_reordering(out_i, out_j, out_k, in_i, in_j, in_k, & + dir_from, dir_to, mesh) + + end subroutine get_index_reordering_rdr + +end module m_ordering diff --git a/api/src/poisson_fft.f90 b/api/src/poisson_fft.f90 new file mode 100644 index 000000000..ca16f3375 --- /dev/null +++ b/api/src/poisson_fft.f90 @@ -0,0 +1,237 @@ +module m_poisson_fft + use m_allocator, only: field_t + use m_common, only: dp, pi, CELL + use m_tdsops, only: dirps_t + use m_mesh, only: mesh_t, geo_t + + implicit none + + type, abstract :: poisson_fft_t + !! FFT based Poisson solver + !> Global dimensions + integer :: nx_glob, ny_glob, nz_glob + !> Local dimensions + integer :: nx_loc, ny_loc, nz_loc + !> Local dimensions in the permuted slabs + integer :: nx_perm, ny_perm, nz_perm + !> Local dimensions in the permuted slabs in spectral space + integer :: nx_spec, ny_spec, nz_spec + !> Offset in y direction in the permuted slabs in spectral space + integer :: y_sp_st + !> Local domain sized array storing the spectral equivalence constants + complex(dp), allocatable, dimension(:, :, :) :: waves + !> Wave numbers in x, y, and z + complex(dp), allocatable, dimension(:) :: ax, bx, ay, by, az, bz + contains + procedure(fft_forward), deferred :: fft_forward + procedure(fft_backward), deferred :: fft_backward + procedure(fft_postprocess), deferred :: fft_postprocess + procedure :: base_init + procedure :: waves_set + end type poisson_fft_t + + abstract interface + subroutine fft_forward(self, f_in) + import :: poisson_fft_t + import :: field_t + implicit none + + class(poisson_fft_t) :: self + class(field_t), intent(in) :: f_in + end subroutine fft_forward + + subroutine fft_backward(self, f_out) + import :: poisson_fft_t + import :: field_t + implicit none + + class(poisson_fft_t) :: self + class(field_t), intent(inout) :: f_out + end subroutine fft_backward + + subroutine fft_postprocess(self) + import :: poisson_fft_t + implicit none + + class(poisson_fft_t) :: self + end subroutine fft_postprocess + end interface + +contains + + subroutine base_init(self, mesh, xdirps, ydirps, zdirps) + implicit none + + class(poisson_fft_t) :: self + class(mesh_t), intent(in) :: mesh + type(dirps_t), intent(in) :: xdirps, ydirps, zdirps + + integer :: dims(3) + + dims = mesh%get_global_dims(CELL) + self%nx_glob = dims(1); self%ny_glob = dims(2); self%nz_glob = dims(3) + dims = mesh%get_dims(CELL) + self%nx_loc = dims(1); self%ny_loc = dims(2); self%nz_loc = dims(3) + + ! 1D decomposition along Z in real domain, and along Y in spectral space + if (mesh%par%nproc_dir(1) /= 1) print *, 'nproc_dir in x-dir must be 1' + if (mesh%par%nproc_dir(2) /= 1) print *, 'nproc_dir in y-dir must be 1' + self%nx_perm = self%nx_loc/mesh%par%nproc_dir(2) + self%ny_perm = self%ny_loc/mesh%par%nproc_dir(3) + self%nz_perm = self%nz_glob + self%nx_spec = self%nx_loc/2 + 1 + self%ny_spec = self%ny_perm + self%nz_spec = self%nz_perm + + self%y_sp_st = (self%ny_loc/mesh%par%nproc_dir(3))*mesh%par%nrank_dir(3) + + allocate (self%ax(self%nx_glob), self%bx(self%nx_glob)) + allocate (self%ay(self%ny_glob), self%by(self%ny_glob)) + allocate (self%az(self%nz_glob), self%bz(self%nz_glob)) + + ! cuFFT 3D transform halves the first index. + allocate (self%waves(self%nx_spec, self%ny_spec, self%nz_spec)) + + ! waves_set requires some of the preprocessed tdsops variables. + call self%waves_set(mesh%geo, xdirps, ydirps, zdirps) + + end subroutine base_init + + subroutine waves_set(self, geo, xdirps, ydirps, zdirps) + !! Spectral equivalence constants + !! + !! Ref. JCP 228 (2009), 5989–6015, Sec 4 + implicit none + + class(poisson_fft_t) :: self + type(geo_t), intent(in) :: geo + type(dirps_t), intent(in) :: xdirps, ydirps, zdirps + + complex(dp), allocatable, dimension(:) :: xkx, xk2, yky, yk2, zkz, zk2, & + exs, eys, ezs + + integer :: nx, ny, nz, ix, iy, iz + real(dp) :: w, wp, rlexs, rleys, rlezs, xtt, ytt, ztt, xt1, yt1, zt1 + complex(dp) :: xt2, yt2, zt2, xyzk + real(dp) :: d, L + + integer :: i, j, k + + nx = self%nx_glob; ny = self%ny_glob; nz = self%nz_glob + + do i = 1, nx + self%ax(i) = sin((i - 1)*pi/nx) + self%bx(i) = cos((i - 1)*pi/nx) + end do + + do i = 1, ny + self%ay(i) = sin((i - 1)*pi/ny) + self%by(i) = cos((i - 1)*pi/ny) + end do + + do i = 1, nz + self%az(i) = sin((i - 1)*pi/nz) + self%bz(i) = cos((i - 1)*pi/nz) + end do + + ! Now kxyz + allocate (xkx(nx), xk2(nx), exs(nx)) + allocate (yky(ny), yk2(ny), eys(ny)) + allocate (zkz(nz), zk2(nz), ezs(nz)) + xkx(:) = 0; xk2(:) = 0; yky(:) = 0; yk2(:) = 0; zkz(:) = 0; zk2(:) = 0 + + ! periodic-x + d = geo%d(1) + L = geo%L(1) + do i = 1, nx/2 + 1 + w = 2*pi*(i - 1)/nx + wp = xdirps%stagder_v2p%a*2*d*sin(0.5_dp*w) & + + xdirps%stagder_v2p%b*2*d*sin(1.5_dp*w) + wp = wp/(1._dp + 2*xdirps%stagder_v2p%alpha*cos(w)) + + xkx(i) = cmplx(1._dp, 1._dp, kind=dp)*(nx*wp/L) + exs(i) = cmplx(1._dp, 1._dp, kind=dp)*(nx*w/L) + xk2(i) = cmplx(1._dp, 1._dp, kind=dp)*(nx*wp/L)**2 + end do + do i = nx/2 + 2, nx + xkx(i) = xkx(nx - i + 2) + exs(i) = exs(nx - i + 2) + xk2(i) = xk2(nx - i + 2) + end do + + ! periodic-y + d = geo%d(2) + L = geo%L(2) + do i = 1, ny/2 + 1 + w = 2*pi*(i - 1)/ny + wp = ydirps%stagder_v2p%a*2*d*sin(0.5_dp*w) & + + ydirps%stagder_v2p%b*2*d*sin(1.5_dp*w) + wp = wp/(1._dp + 2*ydirps%stagder_v2p%alpha*cos(w)) + + yky(i) = cmplx(1._dp, 1._dp, kind=dp)*(ny*wp/L) + eys(i) = cmplx(1._dp, 1._dp, kind=dp)*(ny*w/L) + yk2(i) = cmplx(1._dp, 1._dp, kind=dp)*(ny*wp/L)**2 + end do + do i = ny/2 + 2, ny + yky(i) = yky(ny - i + 2) + eys(i) = eys(ny - i + 2) + yk2(i) = yk2(ny - i + 2) + end do + + ! periodic-z + d = geo%d(3) + L = geo%L(3) + do i = 1, nz/2 + 1 + w = 2*pi*(i - 1)/nz + wp = zdirps%stagder_v2p%a*2*d*sin(0.5_dp*w) & + + zdirps%stagder_v2p%b*2*d*sin(1.5_dp*w) + wp = wp/(1._dp + 2*zdirps%stagder_v2p%alpha*cos(w)) + + zkz(i) = cmplx(1._dp, 1._dp, kind=dp)*(nz*wp/L) + ezs(i) = cmplx(1._dp, 1._dp, kind=dp)*(nz*w/L) + zk2(i) = cmplx(1._dp, 1._dp, kind=dp)*(nz*wp/L)**2 + end do + do i = nz/2 + 2, nz + zkz(i) = zkz(nz - i + 2) + ezs(i) = ezs(nz - i + 2) + zk2(i) = zk2(nz - i + 2) + end do + + do i = 1, self%nx_spec + do j = 1, self%ny_spec + do k = 1, self%nz_spec + ix = i; iy = j + self%y_sp_st; iz = k + rlexs = real(exs(ix), kind=dp)*geo%d(1) + rleys = real(eys(iy), kind=dp)*geo%d(2) + rlezs = real(ezs(iz), kind=dp)*geo%d(3) + + xtt = 2*(xdirps%interpl_v2p%a*cos(rlexs*0.5_dp) & + + xdirps%interpl_v2p%b*cos(rlexs*1.5_dp) & + + xdirps%interpl_v2p%c*cos(rlexs*2.5_dp) & + + xdirps%interpl_v2p%d*cos(rlexs*3.5_dp)) + ytt = 2*(ydirps%interpl_v2p%a*cos(rleys*0.5_dp) & + + ydirps%interpl_v2p%b*cos(rleys*1.5_dp) & + + ydirps%interpl_v2p%c*cos(rleys*2.5_dp) & + + ydirps%interpl_v2p%d*cos(rleys*3.5_dp)) + ztt = 2*(zdirps%interpl_v2p%a*cos(rlezs*0.5_dp) & + + zdirps%interpl_v2p%b*cos(rlezs*1.5_dp) & + + zdirps%interpl_v2p%c*cos(rlezs*2.5_dp) & + + zdirps%interpl_v2p%d*cos(rlezs*3.5_dp)) + + xt1 = 1._dp + 2*xdirps%interpl_v2p%alpha*cos(rlexs) + yt1 = 1._dp + 2*ydirps%interpl_v2p%alpha*cos(rleys) + zt1 = 1._dp + 2*zdirps%interpl_v2p%alpha*cos(rlezs) + + xt2 = xk2(ix)*(((ytt/yt1)*(ztt/zt1))**2) + yt2 = yk2(iy)*(((xtt/xt1)*(ztt/zt1))**2) + zt2 = zk2(iz)*(((xtt/xt1)*(ytt/yt1))**2) + + xyzk = xt2 + yt2 + zt2 + self%waves(i, j, k) = xyzk + end do + end do + end do + + end subroutine waves_set + +end module m_poisson_fft diff --git a/api/src/reorder.f90 b/api/src/reorder.f90 new file mode 100644 index 000000000..2a77e05de --- /dev/null +++ b/api/src/reorder.f90 @@ -0,0 +1,288 @@ +module m_cuda_kernels_reorder + use cudafor + + use m_common, only: dp + use m_cuda_common, only: SZ + +contains + + attributes(global) subroutine reorder_c2x(u_x, u_c, nz) + implicit none + + real(dp), device, intent(out), dimension(:, :, :) :: u_x + real(dp), device, intent(in), dimension(:, :, :) :: u_c + integer, value, intent(in) :: nz + + real(dp), shared :: tile(SZ, SZ) + integer :: i, j, b_i, b_j, b_k + + i = threadIdx%x; j = threadIdx%y + b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z + + ! copy into shared + tile(i, j) = u_c(i + (b_i - 1)*SZ, j + (b_j - 1)*SZ, b_k) + + call syncthreads() + + ! copy into output array from shared + u_x(i, j + (b_i - 1)*SZ, b_k + (b_j - 1)*nz) = tile(j, i) + + end subroutine reorder_c2x + + attributes(global) subroutine reorder_x2c(u_c, u_x, nz) + implicit none + + real(dp), device, intent(out), dimension(:, :, :) :: u_c + real(dp), device, intent(in), dimension(:, :, :) :: u_x + integer, value, intent(in) :: nz + + real(dp), shared :: tile(SZ, SZ) + integer :: i, j, b_i, b_j, b_k + + i = threadIdx%x; j = threadIdx%y + b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z + + ! copy into shared + tile(i, j) = u_x(i, j + (b_i - 1)*SZ, b_k + (b_j - 1)*nz) + + call syncthreads() + + ! copy into output array from shared + u_c(i + (b_i - 1)*SZ, j + (b_j - 1)*SZ, b_k) = tile(j, i) + + end subroutine reorder_x2c + + attributes(global) subroutine reorder_x2y(u_y, u_x, nz) + implicit none + + real(dp), device, intent(out), dimension(:, :, :) :: u_y + real(dp), device, intent(in), dimension(:, :, :) :: u_x + integer, value, intent(in) :: nz + + real(dp), shared :: tile(SZ, SZ) + integer :: i, j, b_i, b_j, b_k + + i = threadIdx%x; j = threadIdx%y + b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z + + ! copy into shared + tile(i, j) = u_x(i, j + (b_i - 1)*SZ, b_j + (b_k - 1)*nz) + + call syncthreads() + + ! copy into output array from shared + u_y(i, j + (b_k - 1)*SZ, b_j + (b_i - 1)*nz) = tile(j, i) + + end subroutine reorder_x2y + + attributes(global) subroutine reorder_x2z(u_z, u_x, nz) + implicit none + + real(dp), device, intent(out), dimension(:, :, :) :: u_z + real(dp), device, intent(in), dimension(:, :, :) :: u_x + integer, value, intent(in) :: nz + + integer :: i, j, b_i, b_j, nx + + i = threadIdx%x; b_i = blockIdx%x; b_j = blockIdx%y + nx = gridDim%x + + ! Data access pattern for reordering between x and z is quite nice + ! thus we don't need to use shared memory for this operation. + do j = 1, nz + u_z(i, j, b_i + (b_j - 1)*nx) = u_x(i, b_i, j + (b_j - 1)*nz) + end do + + end subroutine reorder_x2z + + attributes(global) subroutine reorder_y2x(u_x, u_y, nz) + implicit none + + real(dp), device, intent(out), dimension(:, :, :) :: u_x + real(dp), device, intent(in), dimension(:, :, :) :: u_y + integer, value, intent(in) :: nz + + real(dp), shared :: tile(SZ, SZ) + integer :: i, j, b_i, b_j, b_k + + i = threadIdx%x; j = threadIdx%y + b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z + + ! copy into shared + tile(i, j) = u_y(i, (b_j - 1)*SZ + j, (b_i - 1)*nz + b_k) + + call syncthreads() + + ! copy into output array from shared + u_x(i, (b_i - 1)*SZ + j, (b_j - 1)*nz + b_k) = tile(j, i) + + end subroutine reorder_y2x + + attributes(global) subroutine reorder_y2z(u_z, u_y, nx, nz) + implicit none + + real(dp), device, intent(out), dimension(:, :, :) :: u_z + real(dp), device, intent(in), dimension(:, :, :) :: u_y + integer, value, intent(in) :: nx, nz + + real(dp), shared :: tile(SZ, SZ) + integer :: i, j, b_i, b_j, b_k + + i = threadIdx%x; j = threadIdx%y + b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z + + ! copy into shared + tile(i, j) = u_y(i, (b_j - 1)*SZ + j, (b_i - 1)*nz + b_k) + + call syncthreads() + + ! copy into output array from shared + u_z(i, b_k, (b_i - 1)*SZ + j + (b_j - 1)*nx) = tile(j, i) + + end subroutine reorder_y2z + + attributes(global) subroutine reorder_z2x(u_x, u_z, nz) + implicit none + + real(dp), device, intent(out), dimension(:, :, :) :: u_x + real(dp), device, intent(in), dimension(:, :, :) :: u_z + integer, value, intent(in) :: nz + + integer :: i, j, b_i, b_j, nx + + i = threadIdx%x; b_i = blockIdx%x; b_j = blockIdx%y + nx = gridDim%x + + do j = 1, nz + u_x(i, b_i, j + (b_j - 1)*nz) = u_z(i, j, b_i + (b_j - 1)*nx) + end do + + end subroutine reorder_z2x + + attributes(global) subroutine reorder_z2y(u_y, u_z, nx, nz) + implicit none + + real(dp), device, intent(out), dimension(:, :, :) :: u_y + real(dp), device, intent(in), dimension(:, :, :) :: u_z + integer, value, intent(in) :: nx, nz + + real(dp), shared :: tile(SZ, SZ) + integer :: i, j, b_i, b_j, b_k + + i = threadIdx%x; j = threadIdx%y + b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z + + ! copy into shared + tile(i, j) = u_z(i, b_k, (b_i - 1)*SZ + j + (b_j - 1)*nx) + + call syncthreads() + + ! copy into output array from shared + u_y(i, (b_j - 1)*SZ + j, (b_i - 1)*nz + b_k) = tile(j, i) + + end subroutine reorder_z2y + + attributes(global) subroutine sum_yintox(u_x, u_y, nz) + implicit none + + real(dp), device, intent(inout), dimension(:, :, :) :: u_x + real(dp), device, intent(in), dimension(:, :, :) :: u_y + integer, value, intent(in) :: nz + + real(dp), shared :: tile(SZ, SZ) + integer :: i, j, b_i, b_j, b_k + + i = threadIdx%x; j = threadIdx%y + b_i = blockIdx%x; b_j = blockIdx%y; b_k = blockIdx%z + + ! copy into shared + tile(i, j) = u_y(i, (b_j - 1)*SZ + j, (b_k) + nz*(b_i - 1)) + + call syncthreads() + + ! copy into output array from shared + u_x(i, (b_i - 1)*SZ + j, (b_j - 1)*nz + (b_k)) = & + u_x(i, (b_i - 1)*SZ + j, (b_j - 1)*nz + (b_k)) + tile(j, i) + + end subroutine sum_yintox + + attributes(global) subroutine sum_zintox(u_x, u_z, nz) + implicit none + + ! Arguments + real(dp), device, intent(inout), dimension(:, :, :) :: u_x + real(dp), device, intent(in), dimension(:, :, :) :: u_z + integer, value, intent(in) :: nz + + integer :: i, j, b_i, b_j, nx + + i = threadIdx%x; b_i = blockIdx%x; b_j = blockIdx%y + nx = gridDim%x + + do j = 1, nz + u_x(i, b_i, j + (b_j - 1)*nz) = u_x(i, b_i, j + (b_j - 1)*nz) & + + u_z(i, j, b_i + (b_j - 1)*nx) + end do + + end subroutine sum_zintox + + attributes(global) subroutine axpby(n, alpha, x, beta, y) + implicit none + + integer, value, intent(in) :: n + real(dp), value, intent(in) :: alpha, beta + real(dp), device, intent(in), dimension(:, :, :) :: x + real(dp), device, intent(inout), dimension(:, :, :) :: y + + integer :: i, j, b + + i = threadIdx%x + b = blockIdx%x + + do j = 1, n + y(i, j, b) = alpha*x(i, j, b) + beta*y(i, j, b) + end do + + end subroutine axpby + + attributes(global) subroutine scalar_product(s, x, y, n) + implicit none + + real(dp), device, intent(inout) :: s + real(dp), device, intent(in), dimension(:, :, :) :: x, y + integer, value, intent(in) :: n + + real(dp) :: s_pncl !! pencil sum + integer :: i, j, b, ierr + + i = threadIdx%x + b = blockIdx%x + + s_pncl = 0._dp + do j = 1, n + s_pncl = s_pncl + x(i, j, b)*y(i, j, b) + end do + ierr = atomicadd(s, s_pncl) + + end subroutine scalar_product + + attributes(global) subroutine buffer_copy(u_send_s, u_send_e, u, n, n_halo) + implicit none + + real(dp), device, intent(inout), dimension(:, :, :) :: u_send_s, u_send_e + real(dp), device, intent(in), dimension(:, :, :) :: u + integer, value, intent(in) :: n, n_halo + + integer :: i, j, b + + i = threadIdx%x + b = blockIdx%x + + do j = 1, n_halo + u_send_s(i, j, b) = u(i, j, b) + u_send_e(i, j, b) = u(i, n - n_halo + j, b) + end do + + end subroutine buffer_copy + +end module m_cuda_kernels_reorder diff --git a/api/src/sendrecv.f90 b/api/src/sendrecv.f90 new file mode 100644 index 000000000..d055e4766 --- /dev/null +++ b/api/src/sendrecv.f90 @@ -0,0 +1,38 @@ +module m_omp_sendrecv + use mpi + + use m_common, only: dp + + implicit none + +contains + + subroutine sendrecv_fields(f_recv_s, f_recv_e, f_send_s, f_send_e, & + n_data, nproc, prev, next) + implicit none + + real(dp), dimension(:, :, :), intent(out) :: f_recv_s, f_recv_e + real(dp), dimension(:, :, :), intent(in) :: f_send_s, f_send_e + integer, intent(in) :: n_data, nproc, prev, next + + integer :: req(4), err(4), ierr, tag = 1234 + + if (nproc == 1) then + f_recv_s = f_send_e + f_recv_e = f_send_s + else + call MPI_Isend(f_send_s, n_data, MPI_DOUBLE_PRECISION, & + prev, tag, MPI_COMM_WORLD, req(1), err(1)) + call MPI_Irecv(f_recv_e, n_data, MPI_DOUBLE_PRECISION, & + next, tag, MPI_COMM_WORLD, req(2), err(2)) + call MPI_Isend(f_send_e, n_data, MPI_DOUBLE_PRECISION, & + next, tag, MPI_COMM_WORLD, req(3), err(3)) + call MPI_Irecv(f_recv_s, n_data, MPI_DOUBLE_PRECISION, & + prev, tag, MPI_COMM_WORLD, req(4), err(4)) + + call MPI_Waitall(4, req, MPI_STATUSES_IGNORE, ierr) + end if + + end subroutine sendrecv_fields + +end module m_omp_sendrecv diff --git a/api/src/solver.f90 b/api/src/solver.f90 new file mode 100644 index 000000000..4f9547b3f --- /dev/null +++ b/api/src/solver.f90 @@ -0,0 +1,549 @@ +module m_solver + use iso_fortran_env, only: stderr => error_unit + use mpi + + use m_allocator, only: allocator_t, field_t + use m_base_backend, only: base_backend_t + use m_common, only: dp, & + RDR_X2Y, RDR_X2Z, RDR_Y2X, RDR_Y2Z, RDR_Z2X, RDR_Z2Y, & + RDR_Z2C, RDR_C2Z, & + DIR_X, DIR_Y, DIR_Z, DIR_C, VERT, CELL + use m_tdsops, only: tdsops_t, dirps_t + use m_time_integrator, only: time_intg_t + use m_vector_calculus, only: vector_calculus_t + use m_mesh, only: mesh_t + + implicit none + + type :: solver_t + !! solver class defines the Incompact3D algorithm at a very high level. + !! + !! Procedures defined here that are part of the Incompact3D algorithm + !! are: transeq, divergence, poisson, and gradient. + !! + !! The operations these high level procedures require are provided by + !! the relavant backend implementations. + !! + !! transeq procedure obtains the derivations in x, y, and z directions + !! using the transeq_x, transeq_y, and transeq_z operations provided by + !! the backend. + !! There are two different algorithms available for this operation, a + !! distributed algorithm and the Thomas algorithm. At the solver class + !! level it isn't known which algorithm will be executed, that is decided + !! at run time and therefore backend implementations are responsible for + !! executing the right subroutines. + !! + !! Allocator is responsible from giving us a field sized array when + !! requested. For example, when the derivations in x direction are + !! completed and we are ready for the y directional derivatives, we need + !! three fields to reorder and store the velocities in y direction. Also, + !! we need three more fields for storing the results, and the get_block + !! method of the allocator is used to arrange all these memory + !! assignments. Later, when a field is no more required, release_block + !! method of the allocator can be used to make this field available + !! for later use. + + real(dp) :: dt, nu + integer :: n_iters, n_output + integer :: ngrid + + class(field_t), pointer :: u, v, w + + class(base_backend_t), pointer :: backend + class(mesh_t), pointer :: mesh + type(time_intg_t) :: time_integrator + type(allocator_t), pointer :: host_allocator + type(dirps_t), pointer :: xdirps, ydirps, zdirps + type(vector_calculus_t) :: vector_calculus + procedure(poisson_solver), pointer :: poisson => null() + contains + procedure :: transeq + procedure :: divergence_v2p + procedure :: gradient_p2v + procedure :: curl + procedure :: output + procedure :: run + end type solver_t + + abstract interface + subroutine poisson_solver(self, pressure, div_u) + import :: solver_t + import :: field_t + implicit none + + class(solver_t) :: self + class(field_t), intent(inout) :: pressure + class(field_t), intent(in) :: div_u + end subroutine poisson_solver + end interface + + interface solver_t + module procedure init + end interface solver_t + +contains + + function init(backend, mesh, host_allocator) result(solver) + implicit none + + class(base_backend_t), target, intent(inout) :: backend + type(mesh_t), target, intent(inout) :: mesh + type(allocator_t), target, intent(inout) :: host_allocator + type(solver_t) :: solver + + class(field_t), pointer :: u_init, v_init, w_init + + character(len=200) :: input_file + real(dp) :: Re, dt + integer :: n_iters, n_output + character(3) :: poisson_solver_type, time_intg + character(30) :: der1st_scheme, der2nd_scheme, & + interpl_scheme, stagder_scheme + namelist /solver_params/ Re, dt, n_iters, n_output, poisson_solver_type, & + time_intg, der1st_scheme, der2nd_scheme, & + interpl_scheme, stagder_scheme + + real(dp) :: x, y, z + integer :: i, j, k + integer, dimension(3) :: dims + real(dp), dimension(3) :: xloc + + solver%backend => backend + solver%mesh => mesh + solver%host_allocator => host_allocator + + allocate (solver%xdirps, solver%ydirps, solver%zdirps) + solver%xdirps%dir = DIR_X + solver%ydirps%dir = DIR_Y + solver%zdirps%dir = DIR_Z + + solver%vector_calculus = vector_calculus_t(solver%backend) + + solver%u => solver%backend%allocator%get_block(DIR_X, VERT) + solver%v => solver%backend%allocator%get_block(DIR_X, VERT) + solver%w => solver%backend%allocator%get_block(DIR_X, VERT) + + ! set defaults + poisson_solver_type = 'FFT' + time_intg = 'AB3' + der1st_scheme = 'compact6'; der2nd_scheme = 'compact6' + interpl_scheme = 'classic'; stagder_scheme = 'compact6' + + if (command_argument_count() >= 1) then + call get_command_argument(1, input_file) + open (100, file=input_file) + read (100, nml=solver_params) + close (100) + else + error stop 'Input file is not provided.' + end if + + solver%time_integrator = time_intg_t(solver%backend, & + solver%backend%allocator, & + time_intg) + if (solver%mesh%par%is_root()) then + print *, time_intg//' time integrator instantiated' + end if + + solver%dt = dt + solver%backend%nu = 1._dp/Re + solver%n_iters = n_iters + solver%n_output = n_output + solver%ngrid = product(solver%mesh%get_global_dims(VERT)) + + dims = solver%mesh%get_dims(VERT) + u_init => solver%host_allocator%get_block(DIR_C) + v_init => solver%host_allocator%get_block(DIR_C) + w_init => solver%host_allocator%get_block(DIR_C) + + do k = 1, dims(3) + do j = 1, dims(2) + do i = 1, dims(1) + xloc = solver%mesh%get_coordinates(i, j, k) + x = xloc(1) + y = xloc(2) + z = xloc(3) + + u_init%data(i, j, k) = sin(x)*cos(y)*cos(z) + v_init%data(i, j, k) = -cos(x)*sin(y)*cos(z) + w_init%data(i, j, k) = 0 + end do + end do + end do + + call solver%backend%set_field_data(solver%u, u_init%data) + call solver%backend%set_field_data(solver%v, v_init%data) + call solver%backend%set_field_data(solver%w, w_init%data) + + call solver%host_allocator%release_block(u_init) + call solver%host_allocator%release_block(v_init) + call solver%host_allocator%release_block(w_init) + + ! Allocate and set the tdsops + call allocate_tdsops(solver%xdirps, solver%backend, & + der1st_scheme, der2nd_scheme, & + interpl_scheme, stagder_scheme) + call allocate_tdsops(solver%ydirps, solver%backend, & + der1st_scheme, der2nd_scheme, & + interpl_scheme, stagder_scheme) + call allocate_tdsops(solver%zdirps, solver%backend, & + der1st_scheme, der2nd_scheme, & + interpl_scheme, stagder_scheme) + + select case (trim(poisson_solver_type)) + case ('FFT') + if (solver%mesh%par%is_root()) print *, 'Poisson solver: FFT' + call solver%backend%init_poisson_fft(solver%mesh, solver%xdirps, & + solver%ydirps, solver%zdirps) + solver%poisson => poisson_fft + case ('CG') + if (solver%mesh%par%is_root()) & + print *, 'Poisson solver: CG, not yet implemented' + solver%poisson => poisson_cg + case default + error stop 'poisson_solver_type is not valid. Use "FFT" or "CG".' + end select + + end function init + + subroutine allocate_tdsops(dirps, backend, der1st_scheme, der2nd_scheme, & + interpl_scheme, stagder_scheme) + type(dirps_t), intent(inout) :: dirps + class(base_backend_t), intent(in) :: backend + character(*), intent(in) :: der1st_scheme, der2nd_scheme, & + interpl_scheme, stagder_scheme + + call backend%alloc_tdsops(dirps%der1st, dirps%dir, & + 'first-deriv', der1st_scheme) + call backend%alloc_tdsops(dirps%der1st_sym, dirps%dir, & + 'first-deriv', der1st_scheme) + call backend%alloc_tdsops(dirps%der2nd, dirps%dir, & + 'second-deriv', der2nd_scheme) + call backend%alloc_tdsops(dirps%der2nd_sym, dirps%dir, & + 'second-deriv', der2nd_scheme) + call backend%alloc_tdsops(dirps%interpl_v2p, dirps%dir, & + 'interpolate', interpl_scheme, from_to='v2p') + call backend%alloc_tdsops(dirps%interpl_p2v, dirps%dir, & + 'interpolate', interpl_scheme, from_to='p2v') + call backend%alloc_tdsops(dirps%stagder_v2p, dirps%dir, & + 'stag-deriv', stagder_scheme, from_to='v2p') + call backend%alloc_tdsops(dirps%stagder_p2v, dirps%dir, & + 'stag-deriv', stagder_scheme, from_to='p2v') + + end subroutine + + subroutine transeq(self, du, dv, dw, u, v, w) + !! Skew-symmetric form of convection-diffusion terms in the + !! incompressible Navier-Stokes momemtum equations, excluding + !! pressure terms. + !! Inputs from velocity grid and outputs to velocity grid. + implicit none + + class(solver_t) :: self + class(field_t), intent(inout) :: du, dv, dw + class(field_t), intent(in) :: u, v, w + + class(field_t), pointer :: u_y, v_y, w_y, u_z, v_z, w_z, & + du_y, dv_y, dw_y, du_z, dv_z, dw_z + + ! -1/2(nabla u curl u + u nabla u) + nu nablasq u + + ! call derivatives in x direction. Based on the run time arguments this + ! executes a distributed algorithm or the Thomas algorithm. + call self%backend%transeq_x(du, dv, dw, u, v, w, self%xdirps) + + ! request fields from the allocator + u_y => self%backend%allocator%get_block(DIR_Y, VERT) + v_y => self%backend%allocator%get_block(DIR_Y, VERT) + w_y => self%backend%allocator%get_block(DIR_Y, VERT) + du_y => self%backend%allocator%get_block(DIR_Y) + dv_y => self%backend%allocator%get_block(DIR_Y) + dw_y => self%backend%allocator%get_block(DIR_Y) + + ! reorder data from x orientation to y orientation + call self%backend%reorder(u_y, u, RDR_X2Y) + call self%backend%reorder(v_y, v, RDR_X2Y) + call self%backend%reorder(w_y, w, RDR_X2Y) + + ! similar to the x direction, obtain derivatives in y. + call self%backend%transeq_y(du_y, dv_y, dw_y, u_y, v_y, w_y, self%ydirps) + + ! we don't need the velocities in y orientation any more, so release + ! them to open up space. + ! It is important that this doesn't actually deallocate any memory, + ! it just makes the corresponding memory space available for use. + call self%backend%allocator%release_block(u_y) + call self%backend%allocator%release_block(v_y) + call self%backend%allocator%release_block(w_y) + + call self%backend%sum_yintox(du, du_y) + call self%backend%sum_yintox(dv, dv_y) + call self%backend%sum_yintox(dw, dw_y) + + call self%backend%allocator%release_block(du_y) + call self%backend%allocator%release_block(dv_y) + call self%backend%allocator%release_block(dw_y) + + ! just like in y direction, get some fields for the z derivatives. + u_z => self%backend%allocator%get_block(DIR_Z, VERT) + v_z => self%backend%allocator%get_block(DIR_Z, VERT) + w_z => self%backend%allocator%get_block(DIR_Z, VERT) + du_z => self%backend%allocator%get_block(DIR_Z) + dv_z => self%backend%allocator%get_block(DIR_Z) + dw_z => self%backend%allocator%get_block(DIR_Z) + + ! reorder from x to z + call self%backend%reorder(u_z, u, RDR_X2Z) + call self%backend%reorder(v_z, v, RDR_X2Z) + call self%backend%reorder(w_z, w, RDR_X2Z) + + ! get the derivatives in z + call self%backend%transeq_z(du_z, dv_z, dw_z, u_z, v_z, w_z, self%zdirps) + + ! there is no need to keep velocities in z orientation around, so release + call self%backend%allocator%release_block(u_z) + call self%backend%allocator%release_block(v_z) + call self%backend%allocator%release_block(w_z) + + ! gather all the contributions into the x result array + call self%backend%sum_zintox(du, du_z) + call self%backend%sum_zintox(dv, dv_z) + call self%backend%sum_zintox(dw, dw_z) + + ! release all the unnecessary blocks. + call self%backend%allocator%release_block(du_z) + call self%backend%allocator%release_block(dv_z) + call self%backend%allocator%release_block(dw_z) + + end subroutine transeq + + subroutine divergence_v2p(self, div_u, u, v, w) + !! Wrapper for divergence_v2p + implicit none + + class(solver_t) :: self + class(field_t), intent(inout) :: div_u + class(field_t), intent(in) :: u, v, w + + call self%vector_calculus%divergence_v2c( & + div_u, u, v, w, & + self%xdirps%stagder_v2p, self%xdirps%interpl_v2p, & + self%ydirps%stagder_v2p, self%ydirps%interpl_v2p, & + self%zdirps%stagder_v2p, self%zdirps%interpl_v2p & + ) + + end subroutine divergence_v2p + + subroutine gradient_p2v(self, dpdx, dpdy, dpdz, pressure) + !! Wrapper for gradient_p2v + implicit none + + class(solver_t) :: self + class(field_t), intent(inout) :: dpdx, dpdy, dpdz + class(field_t), intent(in) :: pressure + + call self%vector_calculus%gradient_c2v( & + dpdx, dpdy, dpdz, pressure, & + self%xdirps%stagder_p2v, self%xdirps%interpl_p2v, & + self%ydirps%stagder_p2v, self%ydirps%interpl_p2v, & + self%zdirps%stagder_p2v, self%zdirps%interpl_p2v & + ) + + end subroutine gradient_p2v + + subroutine curl(self, o_i_hat, o_j_hat, o_k_hat, u, v, w) + !! Wrapper for curl + implicit none + + class(solver_t) :: self + !> Vector components of the output vector field Omega + class(field_t), intent(inout) :: o_i_hat, o_j_hat, o_k_hat + class(field_t), intent(in) :: u, v, w + + call self%vector_calculus%curl( & + o_i_hat, o_j_hat, o_k_hat, u, v, w, & + self%xdirps%der1st, self%ydirps%der1st, self%zdirps%der1st & + ) + + end subroutine curl + + subroutine poisson_fft(self, pressure, div_u) + implicit none + + class(solver_t) :: self + class(field_t), intent(inout) :: pressure + class(field_t), intent(in) :: div_u + + class(field_t), pointer :: p_temp + + ! reorder into 3D Cartesian data structure + p_temp => self%backend%allocator%get_block(DIR_C, CELL) + call self%backend%reorder(p_temp, div_u, RDR_Z2C) + + ! call forward FFT + ! output array in spectral space is stored at poisson_fft class + call self%backend%poisson_fft%fft_forward(p_temp) + + ! postprocess + call self%backend%poisson_fft%fft_postprocess + + ! call backward FFT + call self%backend%poisson_fft%fft_backward(p_temp) + + ! reorder back to our specialist data structure from 3D Cartesian + call self%backend%reorder(pressure, p_temp, RDR_C2Z) + + call self%backend%allocator%release_block(p_temp) + + end subroutine poisson_fft + + subroutine poisson_cg(self, pressure, div_u) + implicit none + + class(solver_t) :: self + class(field_t), intent(inout) :: pressure + class(field_t), intent(in) :: div_u + + end subroutine poisson_cg + + subroutine output(self, t) + implicit none + + class(solver_t), intent(in) :: self + real(dp), intent(in) :: t + + class(field_t), pointer :: du, dv, dw, div_u + class(field_t), pointer :: u_out + real(dp) :: enstrophy, div_u_max, div_u_mean + integer :: ierr + + if (self%mesh%par%is_root()) print *, 'time = ', t + + du => self%backend%allocator%get_block(DIR_X, VERT) + dv => self%backend%allocator%get_block(DIR_X, VERT) + dw => self%backend%allocator%get_block(DIR_X, VERT) + + call self%curl(du, dv, dw, self%u, self%v, self%w) + enstrophy = 0.5_dp*(self%backend%scalar_product(du, du) & + + self%backend%scalar_product(dv, dv) & + + self%backend%scalar_product(dw, dw))/self%ngrid + if (self%mesh%par%is_root()) print *, 'enstrophy:', enstrophy + + call self%backend%allocator%release_block(du) + call self%backend%allocator%release_block(dv) + call self%backend%allocator%release_block(dw) + + div_u => self%backend%allocator%get_block(DIR_Z) + + call self%divergence_v2p(div_u, self%u, self%v, self%w) + + u_out => self%host_allocator%get_block(DIR_C) + call self%backend%get_field_data(u_out%data, div_u) + + call self%backend%allocator%release_block(div_u) + + div_u_max = maxval(abs(u_out%data)) + div_u_mean = sum(abs(u_out%data))/self%ngrid + + call self%host_allocator%release_block(u_out) + + call MPI_Allreduce(MPI_IN_PLACE, div_u_max, 1, MPI_DOUBLE_PRECISION, & + MPI_MAX, MPI_COMM_WORLD, ierr) + call MPI_Allreduce(MPI_IN_PLACE, div_u_mean, 1, MPI_DOUBLE_PRECISION, & + MPI_SUM, MPI_COMM_WORLD, ierr) + if (self%mesh%par%is_root()) & + print *, 'div u max mean:', div_u_max, div_u_mean + + end subroutine output + + subroutine run(self) + implicit none + + class(solver_t), intent(inout) :: self + + class(field_t), pointer :: du, dv, dw, div_u, pressure, dpdx, dpdy, dpdz + class(field_t), pointer :: u_out, v_out, w_out + + real(dp) :: t + integer :: i, j + + if (self%mesh%par%is_root()) print *, 'initial conditions' + t = 0._dp + call self%output(t) + + if (self%mesh%par%is_root()) print *, 'start run' + + do i = 1, self%n_iters + do j = 1, self%time_integrator%nstage + du => self%backend%allocator%get_block(DIR_X) + dv => self%backend%allocator%get_block(DIR_X) + dw => self%backend%allocator%get_block(DIR_X) + + call self%transeq(du, dv, dw, self%u, self%v, self%w) + + ! time integration + call self%time_integrator%step(self%u, self%v, self%w, & + du, dv, dw, self%dt) + + call self%backend%allocator%release_block(du) + call self%backend%allocator%release_block(dv) + call self%backend%allocator%release_block(dw) + + ! pressure + div_u => self%backend%allocator%get_block(DIR_Z) + + call self%divergence_v2p(div_u, self%u, self%v, self%w) + + pressure => self%backend%allocator%get_block(DIR_Z, CELL) + + call self%poisson(pressure, div_u) + + call self%backend%allocator%release_block(div_u) + + dpdx => self%backend%allocator%get_block(DIR_X) + dpdy => self%backend%allocator%get_block(DIR_X) + dpdz => self%backend%allocator%get_block(DIR_X) + + call self%gradient_p2v(dpdx, dpdy, dpdz, pressure) + + call self%backend%allocator%release_block(pressure) + + ! velocity correction + call self%backend%vecadd(-1._dp, dpdx, 1._dp, self%u) + call self%backend%vecadd(-1._dp, dpdy, 1._dp, self%v) + call self%backend%vecadd(-1._dp, dpdz, 1._dp, self%w) + + call self%backend%allocator%release_block(dpdx) + call self%backend%allocator%release_block(dpdy) + call self%backend%allocator%release_block(dpdz) + end do + + if (mod(i, self%n_output) == 0) then + t = i*self%dt + call self%output(t) + end if + end do + + if (self%mesh%par%is_root()) print *, 'run end' + + ! Below is for demonstrating purpuses only, to be removed when we have + ! proper I/O in place. + u_out => self%host_allocator%get_block(DIR_C) + v_out => self%host_allocator%get_block(DIR_C) + w_out => self%host_allocator%get_block(DIR_C) + + call self%backend%get_field_data(u_out%data, self%u) + call self%backend%get_field_data(v_out%data, self%v) + call self%backend%get_field_data(w_out%data, self%w) + + if (self%mesh%par%is_root()) then + print *, 'norms', norm2(u_out%data), norm2(v_out%data), norm2(w_out%data) + end if + + call self%host_allocator%release_block(u_out) + call self%host_allocator%release_block(v_out) + call self%host_allocator%release_block(w_out) + + end subroutine run + +end module m_solver diff --git a/api/src/spectral_processing.f90 b/api/src/spectral_processing.f90 new file mode 100644 index 000000000..5c8c6629a --- /dev/null +++ b/api/src/spectral_processing.f90 @@ -0,0 +1,109 @@ +module m_cuda_spectral + use cudafor + + use m_common, only: dp + + implicit none + +contains + + attributes(global) subroutine process_spectral_div_u( & + div_u, waves, nx_spec, ny_spec, y_sp_st, nx, ny, nz, & + ax, bx, ay, by, az, bz & + ) + !! Post-processes the divergence of velocity in spectral space, including + !! scaling w.r.t. grid size. + !! + !! Ref. JCP 228 (2009), 5989–6015, Sec 4 + implicit none + + !> Divergence of velocity in spectral space + complex(dp), device, intent(inout), dimension(:, :, :) :: div_u + !> Spectral equivalence constants + complex(dp), device, intent(in), dimension(:, :, :) :: waves + real(dp), device, intent(in), dimension(:) :: ax, bx, ay, by, az, bz + !> Grid size in spectral space + integer, value, intent(in) :: nx_spec, ny_spec + !> Offset in y direction in the permuted slabs in spectral space + integer, value, intent(in) :: y_sp_st + !> Grid size + integer, value, intent(in) :: nx, ny, nz + + integer :: i, j, k, ix, iy, iz + real(dp) :: tmp_r, tmp_c, div_r, div_c + + j = threadIdx%x + (blockIdx%x - 1)*blockDim%x + k = blockIdx%y ! nz_spec + + if (j <= ny_spec) then + do i = 1, nx_spec + ! normalisation + div_r = real(div_u(i, j, k), kind=dp)/(nx*ny*nz) + div_c = aimag(div_u(i, j, k))/(nx*ny*nz) + + ix = i; iy = j + y_sp_st; iz = k + + ! post-process forward + ! post-process in z + tmp_r = div_r + tmp_c = div_c + div_r = tmp_r*bz(iz) + tmp_c*az(iz) + div_c = tmp_c*bz(iz) - tmp_r*az(iz) + if (iz > nz/2 + 1) div_r = -div_r + if (iz > nz/2 + 1) div_c = -div_c + + ! post-process in y + tmp_r = div_r + tmp_c = div_c + div_r = tmp_r*by(iy) + tmp_c*ay(iy) + div_c = tmp_c*by(iy) - tmp_r*ay(iy) + if (iy > ny/2 + 1) div_r = -div_r + if (iy > ny/2 + 1) div_c = -div_c + + ! post-process in x + tmp_r = div_r + tmp_c = div_c + div_r = tmp_r*bx(ix) + tmp_c*ax(ix) + div_c = tmp_c*bx(ix) - tmp_r*ax(ix) + + ! Solve Poisson + tmp_r = real(waves(i, j, k), kind=dp) + tmp_c = aimag(waves(i, j, k)) + if ((tmp_r < 1.e-16_dp) .or. (tmp_c < 1.e-16_dp)) then + div_r = 0._dp; div_c = 0._dp + else + div_r = -div_r/tmp_r + div_c = -div_c/tmp_c + end if + + ! post-process backward + ! post-process in z + tmp_r = div_r + tmp_c = div_c + div_r = tmp_r*bz(iz) - tmp_c*az(iz) + div_c = -tmp_c*bz(iz) - tmp_r*az(iz) + if (iz > nz/2 + 1) div_r = -div_r + if (iz > nz/2 + 1) div_c = -div_c + + ! post-process in y + tmp_r = div_r + tmp_c = div_c + div_r = tmp_r*by(iy) + tmp_c*ay(iy) + div_c = tmp_c*by(iy) - tmp_r*ay(iy) + if (iy > ny/2 + 1) div_r = -div_r + if (iy > ny/2 + 1) div_c = -div_c + + ! post-process in x + tmp_r = div_r + tmp_c = div_c + div_r = tmp_r*bx(ix) + tmp_c*ax(ix) + div_c = -tmp_c*bx(ix) + tmp_r*ax(ix) + + ! update the entry + div_u(i, j, k) = cmplx(div_r, div_c, kind=dp) + end do + end if + + end subroutine process_spectral_div_u + +end module m_cuda_spectral diff --git a/api/src/tdsops.f90 b/api/src/tdsops.f90 new file mode 100644 index 000000000..c4327f3fe --- /dev/null +++ b/api/src/tdsops.f90 @@ -0,0 +1,865 @@ +module m_tdsops + use iso_fortran_env, only: stderr => error_unit + + use m_common, only: dp, pi, VERT, CELL, none + use m_mesh, only: mesh_t + + implicit none + + type :: tdsops_t + !! Tridiagonal Solver Operators class. + !! + !! Operator arrays are preprocessed in this class based on the arguments + !! provided. dist_fw and dist_bw are used in the first phase of the + !! distributed tridiagonal solver algorithm. dist_sa and dist_sc are used + !! in the final substitution phase. See the kernels_dist.f90 files in the + !! relevant backend folders. + !! coeff arrays define the specific rules of building the RHS + !! corresponding to the tridiagonal system to be solved, and used only in + !! the first phase of the distributed algorithm when building the RHS. + !! If a boundary condition is defined then coeffs_s and coeffs_e differ + !! from coeffs array and define the RHS rule for the first and last 4 + !! entries in the tridiagonal system (n_halo = 4). + !! + !! This class does not know about the current rank or its relative + !! location among other ranks. All the operator arrays here are used when + !! executing a distributed tridiagonal solver phase one or two. + real(dp), allocatable, dimension(:) :: dist_fw, dist_bw, & !! fw/bw phase + dist_sa, dist_sc, & !! back subs. + dist_af !! the auxiliary factors + real(dp), allocatable, dimension(:) :: thom_f, thom_s, thom_w, thom_p + real(dp), allocatable :: coeffs(:), coeffs_s(:, :), coeffs_e(:, :) + real(dp) :: alpha, a, b, c = 0._dp, d = 0._dp + logical :: periodic + integer :: tds_n + integer :: dir + integer :: n_halo + contains + procedure :: deriv_1st, deriv_2nd, interpl_mid, stagder_1st + procedure :: preprocess_dist, preprocess_thom + end type tdsops_t + + interface tdsops_t + module procedure tdsops_init + end interface tdsops_t + + type :: dirps_t + !! Directional tridiagonal solver container. + !! + !! This class contains the preprocessed tridiagonal solvers for operating + !! in each coordinate direction. + class(tdsops_t), allocatable :: der1st, der1st_sym, der2nd, der2nd_sym, & + stagder_v2p, stagder_p2v, interpl_v2p, interpl_p2v + integer :: dir + end type dirps_t + +contains + + function tdsops_init(tds_n, delta, operation, scheme, n_halo, from_to, & + bc_start, bc_end, sym, c_nu, nu0_nu) result(tdsops) + !! Constructor function for the tdsops_t class. + !! + !! 'n', 'delta', 'operation', and 'scheme' are necessary arguments. + !! Number of points 'n', distance between two points 'delta', the + !! 'operation' the tridiagonal system defines, and the 'scheme' that + !! specifies the exact scheme we choose to apply for the operation. + !! The remaining arguments are optional. + !! 'from_to' is necessary for interpolation and staggared derivative, and + !! it can be 'v2p' or 'p2v'. + !! If the specific region the instance is operating is not a boundary + !! region, then 'bc_start' and 'bc_end' are either 'null' or not defined. + !! 'sym' is relevant when the boundary condition is free-slip. If sym is + !! .true. then it means the field we operate on is assumed to be an even + !! function (symmetric) accross the boundary. If it is .false. it means + !! that the field is assumed to be an odd function (anti-symmetric). + !! 'c_nu', 'nu0_nu' are relevant when operation is second order + !! derivative and scheme is compact6-hyperviscous. + implicit none + + type(tdsops_t) :: tdsops !! return value of the function + + integer, intent(in) :: tds_n + real(dp), intent(in) :: delta + character(*), intent(in) :: operation, scheme + integer, optional, intent(in) :: n_halo !! Number of halo cells + character(*), optional, intent(in) :: from_to !! 'v2p' or 'p2v' + character(*), optional, intent(in) :: bc_start, bc_end !! Boundary Cond. + logical, optional, intent(in) :: sym !! (==npaire), only for Neumann BCs + real(dp), optional, intent(in) :: c_nu, nu0_nu !! params for hypervisc. + + integer :: n_stencil + + tdsops%tds_n = tds_n + + if (present(n_halo)) then + tdsops%n_halo = n_halo + if (n_halo /= 4) then + write (stderr, '("Warning: n_halo is set to ", i2, "be careful! & + &The default is 4 and there are quite a few & + &places where things are hardcoded assuming & + &n_halo is 4.")') n_halo + end if + else + tdsops%n_halo = 4 + end if + + n_stencil = 2*tdsops%n_halo + 1 + + ! preprocessed coefficient arrays for the distributed algorithm + allocate (tdsops%dist_fw(tds_n), tdsops%dist_bw(tds_n)) + allocate (tdsops%dist_sa(tds_n), tdsops%dist_sc(tds_n)) + allocate (tdsops%dist_af(tds_n)) + + ! preprocessed coefficient arrays for the Thomas algorithm + allocate (tdsops%thom_f(tds_n), tdsops%thom_s(tds_n)) + allocate (tdsops%thom_w(tds_n), tdsops%thom_p(tds_n)) + + ! RHS coefficient arrays + allocate (tdsops%coeffs(n_stencil)) + allocate (tdsops%coeffs_s(n_stencil, tdsops%n_halo)) + allocate (tdsops%coeffs_e(n_stencil, tdsops%n_halo)) + + tdsops%periodic = bc_start == 'periodic' .and. bc_end == 'periodic' + + if (operation == 'first-deriv') then + call tdsops%deriv_1st(delta, scheme, bc_start, bc_end, sym) + else if (operation == 'second-deriv') then + call tdsops%deriv_2nd(delta, scheme, bc_start, bc_end, sym, & + c_nu, nu0_nu) + else if (operation == 'interpolate') then + call tdsops%interpl_mid(scheme, from_to, bc_start, bc_end, sym) + else if (operation == 'stag-deriv') then + call tdsops%stagder_1st(delta, scheme, from_to, bc_start, bc_end, sym) + else + error stop 'operation is not defined' + end if + + end function tdsops_init + + pure function get_tds_n(mesh, dir, from_to) result(tds_n) + !! Get the tds_n size based on the from_to value (and the mesh) + class(mesh_t), intent(in) :: mesh + integer, intent(in) :: dir + character(*), optional, intent(in) :: from_to + integer :: tds_n + integer :: data_loc + + data_loc = VERT + if (present(from_to)) then + if (from_to == "v2p") then + data_loc = CELL + end if + end if + + tds_n = mesh%get_n(dir, data_loc) + + end function + + subroutine deriv_1st(self, delta, scheme, bc_start, bc_end, sym) + implicit none + + class(tdsops_t), intent(inout) :: self + real(dp), intent(in) :: delta + character(*), intent(in) :: scheme + character(*), optional, intent(in) :: bc_start, bc_end + logical, optional, intent(in) :: sym + + real(dp), allocatable :: dist_b(:) + real(dp) :: alpha, afi, bfi + integer :: i, n, n_halo + logical :: symmetry + + if (self%n_halo < 2) error stop 'First derivative require n_halo >= 2' + + if (present(sym)) then + symmetry = sym + else + symmetry = .false. + end if + + ! alpha is alfa + + select case (scheme) + case ('compact6') + alpha = 1._dp/3._dp + afi = 7._dp/9._dp/delta + bfi = 1._dp/36._dp/delta + case default + error stop 'scheme is not defined' + end select + + self%alpha = alpha + self%a = afi; self%b = bfi + + self%coeffs(:) = [0._dp, 0._dp, -bfi, -afi, & + 0._dp, & + afi, bfi, 0._dp, 0._dp] + + do i = 1, self%n_halo + self%coeffs_s(:, i) = self%coeffs(:) + self%coeffs_e(:, i) = self%coeffs(:) + end do + + self%dist_sa(:) = alpha; self%dist_sc(:) = alpha + + n = self%tds_n + n_halo = self%n_halo + + allocate (dist_b(n)) + dist_b(:) = 1._dp + + select case (bc_start) + case ('neumann') + if (symmetry) then + ! sym == .true.; d(uu)/dx, dv/dx, dw/dx + ! d(vv)/dy, du/dy, dw/dy + ! d(ww)/dz, du/dz, dv/dz + self%dist_sa(1) = 0._dp + self%dist_sc(1) = 0._dp + self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, & + 0._dp, & + 0._dp, 0._dp, 0._dp, 0._dp] + self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, -afi, & + -bfi, & + afi, bfi, 0._dp, 0._dp] + else + ! sym == .false.; d(uv)/dx, d(uw)/dx, du/dx + ! d(vu)/dy, d(vw)/dy, dv/dy + ! d(wu)/dz, d(wv)/dz, dw/dz + self%dist_sa(1) = 0._dp + self%dist_sc(1) = 2*alpha + self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, & + 0._dp, & + 2*afi, 2*bfi, 0._dp, 0._dp] + self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, -afi, & + bfi, & + afi, bfi, 0._dp, 0._dp] + end if + case ('dirichlet') + ! first line + self%dist_sa(1) = 0._dp + self%dist_sc(1) = 2._dp + self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, & + -2.5_dp, & + 2._dp, 0.5_dp, 0._dp, 0._dp] + self%coeffs_s(:, 1) = self%coeffs_s(:, 1)/delta + ! second line + self%dist_sa(2) = 0.25_dp + self%dist_sc(2) = 0.25_dp + self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, -0.75_dp, & + 0._dp, & + 0.75_dp, 0._dp, 0._dp, 0._dp] + self%coeffs_s(:, 2) = self%coeffs_s(:, 2)/delta + end select + + select case (bc_end) + case ('neumann') + if (symmetry) then + ! sym == .true.; d(uu)/dx, dv/dx, dw/dx + ! d(vv)/dy, du/dy, dw/dy + ! d(ww)/dz, du/dz, dv/dz + self%dist_sa(n) = 0._dp + self%dist_sc(n) = 0._dp + self%coeffs_e(:, n_halo) = [0._dp, 0._dp, 0._dp, 0._dp, & + 0._dp, & + 0._dp, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, n_halo - 1) = [0._dp, 0._dp, -bfi, -afi, & + bfi, & + afi, 0._dp, 0._dp, 0._dp] + else + ! sym == .false.; d(uv)/dx, d(uw)/dx, du/dx + ! d(vu)/dy, d(vw)/dy, dv/dy + ! d(wu)/dz, d(wv)/dz, dw/dz + self%dist_sa(n) = 2*alpha + self%dist_sc(n) = 0._dp + self%coeffs_e(:, n_halo) = [0._dp, 0._dp, -2*bfi, -2*afi, & + 0._dp, & + 0._dp, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, n_halo - 1) = [0._dp, 0._dp, -bfi, -afi, & + -bfi, & + afi, 0._dp, 0._dp, 0._dp] + end if + case ('dirichlet') + ! last line + self%dist_sa(n) = 2._dp + self%dist_sc(n) = 0._dp + self%coeffs_e(:, n_halo) = [0._dp, 0._dp, -0.5_dp, -2._dp, & + 2.5_dp, & + 0._dp, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, n_halo) = self%coeffs_e(:, n_halo)/delta + ! second last line + self%dist_sa(n - 1) = 0.25_dp + self%dist_sc(n - 1) = 0.25_dp + self%coeffs_e(:, n_halo - 1) = [0._dp, 0._dp, 0._dp, -0.75_dp, & + 0._dp, & + 0.75_dp, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, n_halo - 1) = self%coeffs_e(:, n_halo - 1)/delta + end select + + call self%preprocess_thom(dist_b) + call self%preprocess_dist(dist_b) + + end subroutine deriv_1st + + subroutine deriv_2nd(self, delta, scheme, bc_start, bc_end, sym, & + c_nu, nu0_nu) + implicit none + + class(tdsops_t), intent(inout) :: self + real(dp), intent(in) :: delta + character(*), intent(in) :: scheme + character(*), optional, intent(in) :: bc_start, bc_end + logical, optional, intent(in) :: sym + real(dp), optional, intent(in) :: c_nu, nu0_nu + + real(dp), allocatable :: dist_b(:) + real(dp) :: alpha, asi, bsi, csi, dsi + real(dp) :: dpis3, xnpi2, xmpi2, den, d2, temp1, temp2 + integer :: i, n, n_halo + logical :: symmetry + + if (self%n_halo < 4) error stop 'Second derivative require n_halo >= 4' + + if (present(sym)) then + symmetry = sym + else + symmetry = .false. + end if + + d2 = delta*delta + + ! alpha is alsa + + select case (scheme) + case ('compact6') + alpha = 2._dp/11._dp + asi = 12._dp/11._dp/d2 + bsi = 3._dp/44._dp/d2 + csi = 0._dp + dsi = 0._dp + case ('compact6-hyperviscous') + if (present(c_nu) .and. present(nu0_nu)) then + dpis3 = 2._dp*pi/3._dp + xnpi2 = pi*pi*(1._dp + nu0_nu) + xmpi2 = dpis3*dpis3*(1._dp + c_nu*nu0_nu) + den = 405._dp*xnpi2 - 640._dp*xmpi2 + 144._dp + alpha = 0.5_dp - (320._dp*xmpi2 - 1296._dp)/den + asi = -(4329._dp*xnpi2/8._dp - 32._dp*xmpi2 & + - 140._dp*xnpi2*xmpi2 + 286._dp)/den/d2 + bsi = (2115._dp*xnpi2 - 1792._dp*xmpi2 & + - 280._dp*xnpi2*xmpi2 + 1328._dp)/den/(4._dp*d2) + csi = -(7695._dp*xnpi2/8._dp + 288._dp*xmpi2 & + - 180._dp*xnpi2*xmpi2 - 2574._dp)/den/(9._dp*d2) + dsi = (198._dp*xnpi2 + 128._dp*xmpi2 & + - 40._dp*xnpi2*xmpi2 - 736._dp)/den/(16._dp*d2) + else + error stop 'compact6-hyperviscous requires c_nu and nu0_nu' + end if + case default + error stop 'scheme is not defined' + end select + + self%alpha = alpha + self%a = asi; self%b = bsi; self%c = csi; self%d = dsi + + self%coeffs(:) = [dsi, csi, bsi, asi, & + -2._dp*(asi + bsi + csi + dsi), & + asi, bsi, csi, dsi] + + do i = 1, self%n_halo + self%coeffs_s(:, i) = self%coeffs(:) + self%coeffs_e(:, i) = self%coeffs(:) + end do + + self%dist_sa(:) = alpha; self%dist_sc(:) = alpha + + n = self%tds_n + n_halo = self%n_halo + + allocate (dist_b(n)) + dist_b(:) = 1._dp + + select case (bc_start) + case ('neumann') + if (symmetry) then + ! sym == .true.; d2v/dx2, d2w/dx2 + ! d2u/dy2, d2w/dy2 + ! d2u/dz2, d2v/dz2 + self%dist_sa(1) = 0._dp + self%dist_sc(1) = 2*alpha + self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, & + -2*asi - 2*bsi - 2*csi - 2*dsi, & + 2*asi, 2*bsi, 2*csi, 2*dsi] + self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, asi, & + -2*asi - bsi - 2*csi - 2*dsi, & + asi + csi, bsi + dsi, csi, dsi] + self%coeffs_s(:, 3) = [0._dp, 0._dp, bsi, asi + csi, & + -2*asi - 2*bsi - 2*csi - dsi, & + asi, bsi, csi, dsi] + self%coeffs_s(:, 4) = [0._dp, csi, bsi + dsi, asi, & + -2*asi - 2*bsi - 2*csi - 2*dsi, & + asi, bsi, csi, dsi] + else + ! sym == .false.; d2u/dx2 + ! d2v/dy2 + ! d2w/dz2 + self%dist_sa(1) = 0._dp + self%dist_sc(1) = 0._dp + self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, & + 0._dp, & + 0._dp, 0._dp, 0._dp, 0._dp] + self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, asi, & + -2*asi - 3*bsi - 2*csi - 2*dsi, & + asi - csi, bsi - dsi, csi, dsi] + self%coeffs_s(:, 3) = [0._dp, 0._dp, bsi, asi - csi, & + -2*asi - 2*bsi - 2*csi - 3*dsi, & + asi, bsi, csi, dsi] + self%coeffs_s(:, 4) = [0._dp, -csi, bsi - dsi, asi, & + -2*asi - 2*bsi - 2*csi - 2*dsi, & + asi, bsi, csi, dsi] + end if + case ('dirichlet') + ! first line + self%dist_sa(1) = 0._dp + self%dist_sc(1) = 11._dp + self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, & + 13._dp/d2, & + -27._dp/d2, 15._dp/d2, -1._dp/d2, 0._dp] + ! second line + self%dist_sa(2) = 0.1_dp + self%dist_sc(2) = 0.1_dp + self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, 1.2_dp/d2, & + -2.4_dp/d2, & + 1.2_dp/d2, 0._dp, 0._dp, 0._dp] + ! third line + self%dist_sa(3) = 2._dp/11._dp + self%dist_sc(3) = 2._dp/11._dp + temp1 = 3._dp/44._dp/d2; temp2 = 12._dp/11._dp/d2 + self%coeffs_s(:, 3) = [0._dp, 0._dp, temp1, temp2, & + -2._dp*(temp1 + temp2), & + temp2, temp1, 0._dp, 0._dp] + ! fourth line is same as third + self%dist_sa(4) = 2._dp/11._dp + self%dist_sc(4) = 2._dp/11._dp + self%coeffs_s(:, 4) = self%coeffs_s(:, 3) + end select + + select case (bc_end) + case ('neumann') + if (symmetry) then + ! sym == .true.; d2v/dx2, d2w/dx2 + ! d2u/dy2, d2w/dy2 + ! d2u/dz2, d2v/dz2 + self%dist_sa(n) = 2*alpha + self%dist_sc(n) = 0._dp + self%coeffs_e(:, 4) = [dsi, csi, bsi, asi, & + -2*asi - 2*bsi - 2*csi - 2*dsi, & + 0._dp, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, 3) = [dsi, csi, bsi + dsi, asi + csi, & + -2*asi - bsi - 2*csi - 2*dsi, & + asi, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, 2) = [dsi, csi, bsi, asi, & + -2*asi - 2*bsi - 2*csi - dsi, & + asi + csi, bsi, 0._dp, 0._dp] + self%coeffs_e(:, 1) = [dsi, csi, bsi, asi, & + -2*asi - 2*bsi - 2*csi - 2*dsi, & + asi, bsi + dsi, csi, 0._dp] + else + ! sym == .false.; d2u/dx2 + ! d2v/dy2 + ! d2w/dz2 + self%dist_sa(n) = 0._dp + self%dist_sc(n) = 0._dp + self%coeffs_e(:, 4) = [0._dp, 0._dp, 0._dp, 0._dp, & + 0._dp, & + 0._dp, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, 3) = [dsi, csi, bsi - dsi, asi - csi, & + -2*asi - 3*bsi - 2*csi - 2*dsi, & + asi, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, 2) = [dsi, csi, bsi, asi, & + -2*asi - 2*bsi - 2*csi - 3*dsi, & + asi - csi, bsi, 0._dp, 0._dp] + self%coeffs_e(:, 1) = [dsi, csi, bsi, asi, & + -2*asi - 2*bsi - 2*csi - 2*dsi, & + asi, bsi - dsi, -csi, 0._dp] + end if + case ('dirichlet') + ! last line + self%dist_sa(n) = 11._dp + self%dist_sc(n) = 0._dp + self%coeffs_e(:, 4) = [0._dp, -1._dp/d2, 15._dp/d2, -27._dp/d2, & + 13._dp/d2, & + 0._dp, 0._dp, 0._dp, 0._dp] + ! second last line + self%dist_sa(n - 1) = 0.1_dp + self%dist_sc(n - 1) = 0.1_dp + self%coeffs_e(:, 3) = [0._dp, 0._dp, 0._dp, 1.2_dp/d2, & + -2.4_dp/d2, & + 1.2_dp/d2, 0._dp, 0._dp, 0._dp] + ! third last line + self%dist_sa(n - 2) = 2._dp/11._dp + self%dist_sc(n - 2) = 2._dp/11._dp + temp1 = 3._dp/44._dp/d2; temp2 = 12._dp/11._dp/d2 + self%coeffs_e(:, 2) = [0._dp, 0._dp, temp1, temp2, & + -2._dp*(temp1 + temp2), & + temp2, temp1, 0._dp, 0._dp] + ! fourth last line is same as third last + self%dist_sa(n - 3) = 2._dp/11._dp + self%dist_sc(n - 3) = 2._dp/11._dp + self%coeffs_e(:, 1) = self%coeffs_e(:, 2) + end select + + call self%preprocess_thom(dist_b) + call self%preprocess_dist(dist_b) + + end subroutine deriv_2nd + + subroutine interpl_mid(self, scheme, from_to, bc_start, bc_end, sym) + implicit none + + class(tdsops_t), intent(inout) :: self + character(*), intent(in) :: scheme, from_to + character(*), optional, intent(in) :: bc_start, bc_end + logical, optional, intent(in) :: sym + + real(dp), allocatable :: dist_b(:) + real(dp) :: alpha, aici, bici, cici, dici + integer :: i, n, n_halo + + if (self%n_halo < 4) error stop 'Interpolation require n_halo >= 4' + + ! alpha is ailcai + + select case (scheme) + case ('classic') + alpha = 0.3_dp + aici = 0.75_dp + bici = 0.05_dp + cici = 0._dp + dici = 0._dp + case ('optimised') + alpha = 0.461658_dp + dici = 0.00146508_dp + aici = (75._dp + 70._dp*alpha - 640._dp*dici)/128._dp + bici = (-25._dp + 126._dp*alpha + 2304._dp*dici)/256._dp + cici = (3._dp - 10._dp*alpha - 1280._dp*dici)/256._dp + case ('aggressive') + alpha = 0.49_dp + aici = (75._dp + 70._dp*alpha)/128._dp + bici = (-25._dp + 126._dp*alpha)/256._dp + cici = (3._dp - 10._dp*alpha)/256._dp + dici = 0._dp + case default + error stop 'scheme is not defined' + end select + + self%alpha = alpha + self%a = aici; self%b = bici; self%c = cici; self%d = dici + + select case (from_to) + case ('v2p') + self%coeffs(:) = [0._dp, dici, cici, bici, & + aici, & + aici, bici, cici, dici] + case ('p2v') + self%coeffs(:) = [dici, cici, bici, aici, & + aici, & + bici, cici, dici, 0._dp] + end select + + do i = 1, self%n_halo + self%coeffs_s(:, i) = self%coeffs(:) + self%coeffs_e(:, i) = self%coeffs(:) + end do + + self%dist_sa(:) = alpha; self%dist_sc(:) = alpha + + n = self%tds_n + n_halo = self%n_halo + + allocate (dist_b(n)) + dist_b(:) = 1._dp + + if ((bc_start == 'dirichlet') .or. (bc_start == 'neumann')) then + self%dist_sa(1) = 0._dp + + select case (from_to) + case ('v2p') + ! sym is always .true. + dist_b(1) = 1._dp + alpha + self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, & + aici, & + aici + bici, bici + cici, cici + dici, dici] + self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, bici, & + aici + cici, & + aici + dici, bici, cici, dici] + self%coeffs_s(:, 3) = [0._dp, 0._dp, cici, bici + dici, & + aici, & + aici, bici, cici, dici] + case ('p2v') + ! sym is always .true. + self%dist_sc(1) = 2*alpha + self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, & + 2*aici, & + 2*bici, 2*cici, 2*dici, 0._dp] + self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, aici + bici, & + aici + cici, & + bici + dici, cici, dici, 0._dp] + self%coeffs_s(:, 3) = [0._dp, 0._dp, bici + cici, aici + dici, & + aici, & + bici, cici, dici, 0._dp] + self%coeffs_s(:, 4) = [0._dp, cici + dici, bici, aici, & + aici, & + bici, cici, dici, 0._dp] + end select + end if + + if ((bc_end == 'dirichlet') .or. (bc_end == 'neumann')) then + self%dist_sc(n) = 0._dp + + select case (from_to) + case ('v2p') + ! sym is always .true. + dist_b(n) = 1._dp + alpha + self%coeffs_e(:, 4) = [0._dp, dici, cici + dici, bici + cici, & + aici + bici, & + aici, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, 3) = [0._dp, dici, cici, bici, & + aici + dici, & + aici + cici, bici, 0._dp, 0._dp] + self%coeffs_e(:, 2) = [0._dp, dici, cici, bici, & + aici, & + aici, bici + dici, cici, 0._dp] + case ('p2v') + ! sym is always .true. + self%dist_sa(n) = 2*alpha + self%coeffs_e(:, 4) = [2*dici, 2*cici, 2*bici, 2*aici, & + 0._dp, & + 0._dp, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, 3) = [dici, cici, bici + dici, aici + cici, & + aici + bici, & + 0._dp, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, 2) = [dici, cici, bici, aici, & + aici + dici, & + bici + cici, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, 1) = [dici, cici, bici, aici, & + aici, & + bici, cici + dici, 0._dp, 0._dp] + end select + end if + + call self%preprocess_thom(dist_b) + call self%preprocess_dist(dist_b) + + end subroutine interpl_mid + + subroutine stagder_1st(self, delta, scheme, from_to, bc_start, bc_end, sym) + implicit none + + class(tdsops_t), intent(inout) :: self + real(dp), intent(in) :: delta + character(*), intent(in) :: scheme, from_to + character(*), optional, intent(in) :: bc_start, bc_end + logical, optional, intent(in) :: sym + + real(dp), allocatable :: dist_b(:) + real(dp) :: alpha, aci, bci + integer :: i, n, n_halo + + if (self%n_halo < 2) error stop 'Staggared deriv require n_halo >= 2' + + ! alpha is alcai + + select case (scheme) + case ('compact6') + alpha = 9._dp/62._dp + aci = 63._dp/62._dp/delta + bci = 17._dp/62._dp/3._dp/delta + case default + error stop 'scheme is not defined' + end select + + self%alpha = alpha + self%a = aci; self%b = bci + + select case (from_to) + case ('v2p') + self%coeffs(:) = [0._dp, 0._dp, 0._dp, -bci, & + -aci, & + aci, bci, 0._dp, 0._dp] + case ('p2v') + self%coeffs(:) = [0._dp, 0._dp, -bci, -aci, & + aci, & + bci, 0._dp, 0._dp, 0._dp] + end select + + do i = 1, self%n_halo + self%coeffs_s(:, i) = self%coeffs(:) + self%coeffs_e(:, i) = self%coeffs(:) + end do + + self%dist_sa(:) = alpha; self%dist_sc(:) = alpha + + n = self%tds_n + n_halo = self%n_halo + + allocate (dist_b(n)) + dist_b(:) = 1._dp + + if ((bc_start == 'dirichlet') .or. (bc_start == 'neumann')) then + self%dist_sa(1) = 0._dp + + select case (from_to) + case ('v2p') + ! sym is always .false. + dist_b(1) = 1._dp + alpha + self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, & + -aci - 2*bci, & + aci + bci, bci, 0._dp, 0._dp] + self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, -bci, & + -aci, & + aci, bci, 0._dp, 0._dp] + case ('p2v') + ! sym is always .true. + self%dist_sc(1) = 0._dp + self%coeffs_s(:, 1) = [0._dp, 0._dp, 0._dp, 0._dp, & + 0._dp, & + 0._dp, 0._dp, 0._dp, 0._dp] + self%coeffs_s(:, 2) = [0._dp, 0._dp, 0._dp, -aci - bci, & + aci, & + bci, 0._dp, 0._dp, 0._dp] + end select + end if + + if ((bc_end == 'dirichlet') .or. (bc_end == 'neumann')) then + self%dist_sc(n) = 0._dp + + select case (from_to) + case ('v2p') + ! sym is always .false. + dist_b(n) = 1._dp + alpha + self%coeffs_e(:, n_halo) = [0._dp, 0._dp, 0._dp, -bci, & + -aci - bci, & + aci + 2*bci, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, n_halo - 1) = [0._dp, 0._dp, 0._dp, -bci, & + -aci, & + aci, bci, 0._dp, 0._dp] + case ('p2v') + ! sym is always .true. + self%dist_sa(n) = 0._dp + self%coeffs_e(:, n_halo) = [0._dp, 0._dp, 0._dp, 0._dp, & + 0._dp, & + 0._dp, 0._dp, 0._dp, 0._dp] + self%coeffs_e(:, n_halo - 1) = [0._dp, 0._dp, -bci, -aci, & + aci + bci, & + 0._dp, 0._dp, 0._dp, 0._dp] + end select + end if + + call self%preprocess_thom(dist_b) + call self%preprocess_dist(dist_b) + + end subroutine stagder_1st + + subroutine preprocess_dist(self, dist_b) + implicit none + + class(tdsops_t), intent(inout) :: self + + real(dp), dimension(:), intent(in) :: dist_b + + integer :: i + integer :: n + + n = self%tds_n + + ! Ref DOI: 10.1109/MCSE.2021.3130544 + ! Algorithm 3 in page 4 + ! First two lines first + do i = 1, 2 + self%dist_sa(i) = self%dist_sa(i)/dist_b(i) + self%dist_sc(i) = self%dist_sc(i)/dist_b(i) + self%dist_bw(i) = self%dist_sc(i) + self%dist_af(i) = 1._dp/dist_b(i) + end do + + ! Then the remaining in the forward pass + do i = 3, n + ! Algorithm 3 in ref obtains 'r' coeffs on the fly in line 7. + ! As we have to solve many RHSs with the same tridiagonal system, + ! it is better to do a preprocessing first. + ! So lets store 'r' coeff in dist_fw array. + self%dist_fw(i) = 1._dp/(dist_b(i) & + - self%dist_sa(i)*self%dist_sc(i - 1)) + ! dist_af is 'a_i' in line 7 of Algorithm 3 in ref. + self%dist_af(i) = self%dist_sa(i) + ! We store a_i^* and c_i^* in dist_sa and dist_sc because + ! we need them later in the substitution phase. + self%dist_sa(i) = -self%dist_fw(i)*self%dist_sa(i) & + *self%dist_sa(i - 1) + self%dist_sc(i) = self%dist_fw(i)*self%dist_sc(i) + end do + + ! backward pass starting in line 12 of Algorithm 3. + do i = n - 2, 2, -1 + self%dist_sa(i) = self%dist_sa(i) & + - self%dist_sc(i)*self%dist_sa(i + 1) + self%dist_bw(i) = self%dist_sc(i) + self%dist_sc(i) = -self%dist_sc(i)*self%dist_sc(i + 1) + end do + + ! Line 17 and 18 are tricky + ! First we have a new 'r', we need it. + ! And for 'r' we need c_0^*... + ! Now examine closely, c_0^* is set in line 4 and never overwritten! + ! So we can use dist_sc(1) as is in place of c_0^*. + ! We need to store this new 'r' somewhere ... + ! dist_fw(1) is never used, so store this extra 'r' factor here instead + self%dist_fw(1) = 1._dp/(1._dp - self%dist_sc(1)*self%dist_sa(2)) + + ! Finally Line 19 and 20 in Algorithm 3 in ref. + self%dist_sa(1) = self%dist_fw(1)*self%dist_sa(1) + self%dist_sc(1) = -self%dist_fw(1)*self%dist_sc(1)*self%dist_sc(2) + + end subroutine preprocess_dist + + subroutine preprocess_thom(self, b) + implicit none + + class(tdsops_t), intent(inout) :: self + real(dp), dimension(:), intent(in) :: b + + integer :: i, n + + n = self%tds_n + + self%thom_w = b + self%thom_f = self%dist_sc + if (self%periodic) then + self%thom_w(1) = 2._dp + self%thom_w(self%tds_n) = 1._dp + self%alpha*self%alpha + end if + + self%thom_s(1) = 0._dp + do i = 2, n + self%thom_s(i) = self%dist_sa(i)/self%thom_w(i - 1) + self%thom_w(i) = self%thom_w(i) - self%thom_f(i - 1)*self%thom_s(i) + end do + do i = 1, n + self%thom_w(i) = 1._dp/self%thom_w(i) + end do + + self%thom_p = [-1._dp, (0._dp, i=2, self%tds_n - 1), self%alpha] + do i = 2, n + self%thom_p(i) = self%thom_p(i) - self%thom_p(i - 1)*self%thom_s(i) + end do + self%thom_p(n) = self%thom_p(n)*self%thom_w(n) + do i = n - 1, 1, -1 + self%thom_p(i) = self%thom_w(i)*(self%thom_p(i) & + - self%thom_f(i)*self%thom_p(i + 1)) + end do + + end subroutine preprocess_thom + +end module m_tdsops + diff --git a/api/src/thomas.f90 b/api/src/thomas.f90 new file mode 100644 index 000000000..6d00301f6 --- /dev/null +++ b/api/src/thomas.f90 @@ -0,0 +1,177 @@ +module m_cuda_kernels_thom + use cudafor + + use m_common, only: dp + + implicit none + +contains + + attributes(global) subroutine der_univ_thom( & + du, u, coeffs_s, coeffs_e, coeffs, n, thom_f, thom_s, thom_w & + ) + implicit none + + real(dp), device, intent(out), dimension(:, :, :) :: du + real(dp), device, intent(in), dimension(:, :, :) :: u + real(dp), device, intent(in), dimension(:, :) :: coeffs_s, coeffs_e + real(dp), device, intent(in), dimension(:) :: coeffs + integer, value, intent(in) :: n + real(dp), device, intent(in), dimension(:) :: thom_f, thom_s, thom_w + + integer :: i, j, b + + real(dp) :: c_m4, c_m3, c_m2, c_m1, c_j, c_p1, c_p2, c_p3, c_p4, temp_du + + i = threadIdx%x + b = blockIdx%x + + ! store bulk coeffs in the registers + c_m4 = coeffs(1); c_m3 = coeffs(2); c_m2 = coeffs(3); c_m1 = coeffs(4) + c_j = coeffs(5) + c_p1 = coeffs(6); c_p2 = coeffs(7); c_p3 = coeffs(8); c_p4 = coeffs(9) + + du(i, 1, b) = coeffs_s(5, 1)*u(i, 1, b) & + + coeffs_s(6, 1)*u(i, 2, b) & + + coeffs_s(7, 1)*u(i, 3, b) & + + coeffs_s(8, 1)*u(i, 4, b) & + + coeffs_s(9, 1)*u(i, 5, b) + du(i, 1, b) = du(i, 1, b) + du(i, 2, b) = coeffs_s(4, 2)*u(i, 1, b) & + + coeffs_s(5, 2)*u(i, 2, b) & + + coeffs_s(6, 2)*u(i, 3, b) & + + coeffs_s(7, 2)*u(i, 4, b) & + + coeffs_s(8, 2)*u(i, 5, b) & + + coeffs_s(9, 2)*u(i, 6, b) + du(i, 2, b) = du(i, 2, b) - du(i, 1, b)*thom_s(2) + du(i, 3, b) = coeffs_s(3, 3)*u(i, 1, b) & + + coeffs_s(4, 3)*u(i, 2, b) & + + coeffs_s(5, 3)*u(i, 3, b) & + + coeffs_s(6, 3)*u(i, 4, b) & + + coeffs_s(7, 3)*u(i, 5, b) & + + coeffs_s(8, 3)*u(i, 6, b) & + + coeffs_s(9, 3)*u(i, 7, b) + du(i, 3, b) = du(i, 3, b) - du(i, 2, b)*thom_s(3) + du(i, 4, b) = coeffs_s(2, 4)*u(i, 1, b) & + + coeffs_s(3, 4)*u(i, 2, b) & + + coeffs_s(4, 4)*u(i, 3, b) & + + coeffs_s(5, 4)*u(i, 4, b) & + + coeffs_s(6, 4)*u(i, 5, b) & + + coeffs_s(7, 4)*u(i, 6, b) & + + coeffs_s(8, 4)*u(i, 7, b) & + + coeffs_s(9, 4)*u(i, 8, b) + du(i, 4, b) = du(i, 4, b) - du(i, 3, b)*thom_s(4) + + do j = 5, n - 4 + temp_du = c_m4*u(i, j - 4, b) + c_m3*u(i, j - 3, b) & + + c_m2*u(i, j - 2, b) + c_m1*u(i, j - 1, b) & + + c_j*u(i, j, b) & + + c_p1*u(i, j + 1, b) + c_p2*u(i, j + 2, b) & + + c_p3*u(i, j + 3, b) + c_p4*u(i, j + 4, b) + du(i, j, b) = temp_du - du(i, j - 1, b)*thom_s(j) + end do + + j = n - 3 + du(i, j, b) = coeffs_e(1, 1)*u(i, j - 4, b) & + + coeffs_e(2, 1)*u(i, j - 3, b) & + + coeffs_e(3, 1)*u(i, j - 2, b) & + + coeffs_e(4, 1)*u(i, j - 1, b) & + + coeffs_e(5, 1)*u(i, j, b) & + + coeffs_e(6, 1)*u(i, j + 1, b) & + + coeffs_e(7, 1)*u(i, j + 2, b) & + + coeffs_e(8, 1)*u(i, j + 3, b) + du(i, j, b) = du(i, j, b) - du(i, j - 1, b)*thom_s(j) + j = n - 2 + du(i, j, b) = coeffs_e(1, 2)*u(i, j - 4, b) & + + coeffs_e(2, 2)*u(i, j - 3, b) & + + coeffs_e(3, 2)*u(i, j - 2, b) & + + coeffs_e(4, 2)*u(i, j - 1, b) & + + coeffs_e(5, 2)*u(i, j, b) & + + coeffs_e(6, 2)*u(i, j + 1, b) & + + coeffs_e(7, 2)*u(i, j + 2, b) + du(i, j, b) = du(i, j, b) - du(i, j - 1, b)*thom_s(j) + j = n - 1 + du(i, j, b) = coeffs_e(1, 3)*u(i, j - 4, b) & + + coeffs_e(2, 3)*u(i, j - 3, b) & + + coeffs_e(3, 3)*u(i, j - 2, b) & + + coeffs_e(4, 3)*u(i, j - 1, b) & + + coeffs_e(5, 3)*u(i, j, b) & + + coeffs_e(6, 3)*u(i, j + 1, b) + du(i, j, b) = du(i, j, b) - du(i, j - 1, b)*thom_s(j) + j = n + du(i, j, b) = coeffs_e(1, 4)*u(i, j - 4, b) & + + coeffs_e(2, 4)*u(i, j - 3, b) & + + coeffs_e(3, 4)*u(i, j - 2, b) & + + coeffs_e(4, 4)*u(i, j - 1, b) & + + coeffs_e(5, 4)*u(i, j, b) + du(i, j, b) = du(i, j, b) - du(i, j - 1, b)*thom_s(j) + + ! Backward pass of the Thomas algorithm + du(i, n, b) = du(i, n, b)*thom_w(n) + do j = n - 1, 1, -1 + du(i, j, b) = (du(i, j, b) - thom_f(j)*du(i, j + 1, b))*thom_w(j) + end do + + end subroutine der_univ_thom + + attributes(global) subroutine der_univ_thom_per( & + du, u, coeffs, n, alpha, thom_f, thom_s, thom_w, thom_p & + ) + implicit none + + real(dp), device, intent(out), dimension(:, :, :) :: du + real(dp), device, intent(in), dimension(:, :, :) :: u + real(dp), device, intent(in), dimension(:) :: coeffs + integer, value, intent(in) :: n + real(dp), value, intent(in) :: alpha + real(dp), device, intent(in), dimension(:) :: thom_f, thom_s, thom_w, & + thom_p + + integer :: i, j, b + integer :: jm4, jm3, jm2, jm1, jp1, jp2, jp3, jp4 + + real(dp) :: c_m4, c_m3, c_m2, c_m1, c_j, c_p1, c_p2, c_p3, c_p4 + real(dp) :: temp_du, ss + + i = threadIdx%x + b = blockIdx%x + + ! store bulk coeffs in the registers + c_m4 = coeffs(1); c_m3 = coeffs(2); c_m2 = coeffs(3); c_m1 = coeffs(4) + c_j = coeffs(5) + c_p1 = coeffs(6); c_p2 = coeffs(7); c_p3 = coeffs(8); c_p4 = coeffs(9) + + do j = 1, n + jm4 = modulo(j - 5, n) + 1 + jm3 = modulo(j - 4, n) + 1 + jm2 = modulo(j - 3, n) + 1 + jm1 = modulo(j - 2, n) + 1 + jp1 = modulo(j - n, n) + 1 + jp2 = modulo(j - n + 1, n) + 1 + jp3 = modulo(j - n + 2, n) + 1 + jp4 = modulo(j - n + 3, n) + 1 + + temp_du = c_m4*u(i, jm4, b) + c_m3*u(i, jm3, b) & + + c_m2*u(i, jm2, b) + c_m1*u(i, jm1, b) & + + c_j*u(i, j, b) & + + c_p1*u(i, jp1, b) + c_p2*u(i, jp2, b) & + + c_p3*u(i, jp3, b) + c_p4*u(i, jp4, b) + du(i, j, b) = temp_du - du(i, jm1, b)*thom_s(j) + end do + + ! Backward pass of the Thomas algorithm + du(i, n, b) = du(i, n, b)*thom_w(n) + do j = n - 1, 1, -1 + du(i, j, b) = (du(i, j, b) - thom_f(j)*du(i, j + 1, b))*thom_w(j) + end do + + ! Periodic final pass + ss = (du(i, 1, b) - alpha*du(i, n, b)) & + /(1._dp + thom_p(1) - alpha*thom_p(n)) + do j = 1, n + du(i, j, b) = du(i, j, b) - ss*thom_p(j) + end do + + end subroutine der_univ_thom_per + +end module m_cuda_kernels_thom diff --git a/api/src/time_integrator.f90 b/api/src/time_integrator.f90 new file mode 100644 index 000000000..4dd326af6 --- /dev/null +++ b/api/src/time_integrator.f90 @@ -0,0 +1,316 @@ +module m_time_integrator + use m_allocator, only: allocator_t, field_t, flist_t + use m_base_backend, only: base_backend_t + use m_common, only: dp, DIR_X + + implicit none + + private adams_bashforth, runge_kutta + + type :: time_intg_t + integer :: method, istep, istage, order, nstep, nstage, nvars, nolds + real(dp) :: coeffs(4, 4) + real(dp) :: rk_b(4, 4) + real(dp) :: rk_a(3, 3, 4) + character(len=3) :: sname + type(flist_t), allocatable :: olds(:, :) + type(flist_t), allocatable :: curr(:) + type(flist_t), allocatable :: deriv(:) + class(base_backend_t), pointer :: backend + class(allocator_t), pointer :: allocator + procedure(stepper_func), pointer :: stepper => null() + contains + procedure :: finalize + procedure :: step + procedure :: runge_kutta + procedure :: adams_bashforth + end type time_intg_t + + interface time_intg_t + module procedure init + end interface time_intg_t + + abstract interface + subroutine stepper_func(self, dt) + import :: time_intg_t + import :: dp + implicit none + + class(time_intg_t), intent(inout) :: self + real(dp), intent(in) :: dt + end subroutine stepper_func + end interface + +contains + + subroutine finalize(self) + implicit none + + !type(time_intg_t), intent(inout) :: self + class(time_intg_t), intent(inout) :: self + + integer :: i, j + + ! Release all the storage for old timesteps + do i = 1, self%nvars + do j = 1, self%nolds + call self%allocator%release_block(self%olds(i, j)%ptr) + end do + end do + + ! deallocate memory + deallocate (self%olds) + deallocate (self%curr) + deallocate (self%deriv) + + print *, self%sname, ' time integrator deallocated' + + end subroutine finalize + + function init(backend, allocator, method, nvars) + implicit none + + type(time_intg_t) :: init + class(base_backend_t), pointer :: backend + class(allocator_t), pointer :: allocator + character(3), intent(in) :: method + integer, intent(in), optional :: nvars + + integer :: i, j, stat + + ! initialize Runge-Kutta coefficients + ! rk1 + init%rk_a(:, 1, 1) = [0.0_dp, 0._dp, 0._dp] + init%rk_a(:, 2, 1) = [0.0_dp, 0._dp, 0._dp] + init%rk_a(:, 3, 1) = [0.0_dp, 0._dp, 0._dp] + init%rk_b(:, 1) = [1._dp, 0._dp, 0._dp, 0._dp] + + ! rk2 + init%rk_a(:, 1, 2) = [0.5_dp, 0._dp, 0._dp] + init%rk_a(:, 2, 2) = [0.0_dp, 0._dp, 0._dp] + init%rk_a(:, 3, 2) = [0.0_dp, 0._dp, 0._dp] + init%rk_b(:, 2) = [0._dp, 1._dp, 0._dp, 0._dp] + + ! rk3 + init%rk_a(:, 1, 3) = [0.5_dp, 0._dp, 0._dp] + init%rk_a(:, 2, 3) = [0.0_dp, 3._dp/4._dp, 0._dp] + init%rk_a(:, 3, 3) = [0.0_dp, 0._dp, 0._dp] + init%rk_b(:, 3) = & + [2._dp/9.0_dp, 1._dp/3._dp, 4._dp/9._dp, 0._dp] + + ! rk4 + init%rk_a(:, 1, 4) = [0.5_dp, 0._dp, 0._dp] + init%rk_a(:, 2, 4) = [0._dp, 0.5_dp, 0._dp] + init%rk_a(:, 3, 4) = [0._dp, 0._dp, 1._dp] + init%rk_b(:, 4) = & + [1._dp/6._dp, 1._dp/3._dp, 1._dp/3._dp, 1._dp/6._dp] + + ! initialize Adams-Bashforth coefficients + ! ab1 + init%coeffs(:, 1) = [1._dp, 0._dp, 0._dp, 0._dp] + ! ab2 + init%coeffs(:, 2) = [1.5_dp, -0.5_dp, 0._dp, 0._dp] + ! ab3 + init%coeffs(:, 3) = & + [23._dp/12._dp, -4._dp/3._dp, 5._dp/12._dp, 0._dp] + ! ab4 + init%coeffs(:, 4) = & + [55._dp/24._dp, -59._dp/24._dp, 37._dp/24._dp, -3._dp/8._dp] + + ! set variables + init%backend => backend + init%allocator => allocator + init%sname = method + + if (init%sname(1:2) == 'AB') then + read (init%sname(3:3), *, iostat=stat) init%order + if (stat /= 0) error stop 'Error reading AB integration order' + if (init%order >= 5) error stop 'Integration order >4 is not supported' + init%nstep = init%order + init%nstage = 1 + init%nolds = init%nstep - 1 + init%stepper => adams_bashforth + else if (init%sname(1:2) == 'RK') then + read (init%sname(3:3), *, iostat=stat) init%order + if (stat /= 0) error stop 'Error reading RK integration order' + if (init%order >= 5) error stop 'Integration order >4 is not supported' + init%nstep = 1 + init%nstage = init%order + init%nolds = init%nstage + init%stepper => runge_kutta + else + print *, 'Integration method '//init%sname//' is not defined' + error stop + end if + + if (present(nvars)) then + init%nvars = nvars + else + init%nvars = 3 + end if + + init%istep = 1 + init%istage = 1 + + ! allocate memory + allocate (init%olds(init%nvars, init%nolds)) + allocate (init%curr(init%nvars)) + allocate (init%deriv(init%nvars)) + + ! Request all the storage for old timesteps + do i = 1, init%nvars + do j = 1, init%nolds + init%olds(i, j)%ptr => allocator%get_block(DIR_X) + end do + end do + + end function init + + subroutine step(self, u, v, w, du, dv, dw, dt) + implicit none + + class(time_intg_t), intent(inout) :: self + class(field_t), target, intent(inout) :: u, v, w + class(field_t), target, intent(in) :: du, dv, dw + + real(dp), intent(in) :: dt + + ! assign pointer to variables + self%curr(1)%ptr => u + self%curr(2)%ptr => v + self%curr(3)%ptr => w + + ! assign pointer to variables + self%deriv(1)%ptr => du + self%deriv(2)%ptr => dv + self%deriv(3)%ptr => dw + + call self%stepper(dt) + + end subroutine step + + subroutine runge_kutta(self, dt) + implicit none + + class(time_intg_t), intent(inout) :: self + real(dp), intent(in) :: dt + + integer :: i, j + + ! update solution + if (self%istage == self%nstage) then + do i = 1, self%nvars + ! update step solution from stage derivative + if (self%nstage > 1) then + call self%backend%vecadd(1.0_dp, self%olds(i, 1)%ptr, 0._dp, & + self%curr(i)%ptr) + end if + + do j = 1, self%nstage - 1 + call self%backend%vecadd(self%rk_b(j, self%nstage)*dt, & + self%olds(i, j + 1)%ptr, & + 1._dp, self%curr(i)%ptr) + end do + call self%backend%vecadd(self%rk_b(self%nstage, self%nstage)*dt, & + self%deriv(i)%ptr, & + 1._dp, self%curr(i)%ptr) + + end do + + ! reset stage counter + self%istage = 1 + else + do i = 1, self%nvars + ! save step initial condition + if (self%istage == 1) then + call self%backend%vecadd(1.0_dp, self%curr(i)%ptr, 0._dp, & + self%olds(i, 1)%ptr) + end if + + ! save stage derivative + call self%backend%vecadd(1.0_dp, self%deriv(i)%ptr, 0._dp, & + self%olds(i, self%istage + 1)%ptr) + + ! update stage solution + if (self%istage > 1) then + call self%backend%vecadd(1.0_dp, self%olds(i, 1)%ptr, 0._dp, & + self%curr(i)%ptr) + end if + do j = 1, self%istage + call self%backend%vecadd(self%rk_a(j, self%istage, self%nstage)*dt, & + self%olds(i, j + 1)%ptr, & + 1._dp, self%curr(i)%ptr) + end do + end do + + ! increment stage counter + self%istage = self%istage + 1 + end if + + end subroutine runge_kutta + + subroutine adams_bashforth(self, dt) + implicit none + + class(time_intg_t), intent(inout) :: self + real(dp), intent(in) :: dt + + integer :: i, j + integer :: nstep + + nstep = min(self%istep, self%nstep) + do i = 1, self%nvars + ! update solution + call self%backend%vecadd(self%coeffs(1, nstep)*dt, & + self%deriv(i)%ptr, & + 1._dp, self%curr(i)%ptr) + do j = 2, nstep + call self%backend%vecadd(self%coeffs(j, nstep)*dt, & + self%olds(i, j - 1)%ptr, & + 1._dp, self%curr(i)%ptr) + end do + + ! rotate pointers + if (nstep < self%nstep) then + ! for startup + if (self%istep > 1) then + call rotate(self%olds(i, :), nstep) + end if + else + ! after startup + if (self%nstep > 2) then + call rotate(self%olds(i, :), nstep - 1) + end if + end if + + ! update olds(1) with new derivative + if (self%nstep > 1) then + call self%backend%vecadd(1.0_dp, self%deriv(i)%ptr, 0._dp, & + self%olds(i, 1)%ptr) + end if + end do + + ! increment step counter + self%istep = self%istep + 1 + + end subroutine adams_bashforth + + subroutine rotate(sol, n) + implicit none + + type(flist_t), intent(inout) :: sol(:) + integer, intent(in) :: n + + integer :: i + class(field_t), pointer :: ptr + + ! rotate pointer + ptr => sol(n)%ptr + do i = n, 2, -1 + sol(i)%ptr => sol(i - 1)%ptr + end do + sol(1)%ptr => ptr + + end subroutine rotate + +end module m_time_integrator diff --git a/api/src/vector_calculus.f90 b/api/src/vector_calculus.f90 new file mode 100644 index 000000000..f1f9a24c4 --- /dev/null +++ b/api/src/vector_calculus.f90 @@ -0,0 +1,394 @@ +module m_vector_calculus + use iso_fortran_env, only: stderr => error_unit + + use m_allocator, only: allocator_t, field_t + use m_base_backend, only: base_backend_t + use m_common, only: dp, DIR_X, DIR_Y, DIR_Z, & + RDR_X2Y, RDR_X2Z, RDR_Y2X, RDR_Y2Z, RDR_Z2X, RDR_Z2Y + use m_tdsops, only: tdsops_t + + implicit none + + type :: vector_calculus_t + !! Defines vector calculus operators + class(base_backend_t), pointer :: backend + contains + procedure :: curl + procedure :: divergence_v2c + procedure :: gradient_c2v + procedure :: laplacian + end type vector_calculus_t + + interface vector_calculus_t + module procedure init + end interface vector_calculus_t + +contains + + function init(backend) result(vector_calculus) + implicit none + + class(base_backend_t), target, intent(inout) :: backend + type(vector_calculus_t) :: vector_calculus + + vector_calculus%backend => backend + + end function init + + subroutine curl(self, o_i_hat, o_j_hat, o_k_hat, u, v, w, & + x_der1st, y_der1st, z_der1st) + !! Curl of a vector field (u, v, w). + !! + !! Evaluated at the data_loc defined by u, v, w fields. + !! + !! All the input and output fields are in DIR_X layout. + implicit none + + class(vector_calculus_t) :: self + !> Vector components of the output vector field Omega + class(field_t), intent(inout) :: o_i_hat, o_j_hat, o_k_hat + class(field_t), intent(in) :: u, v, w + class(tdsops_t), intent(in) :: x_der1st, y_der1st, z_der1st + + class(field_t), pointer :: u_y, u_z, v_z, w_y, dwdy_y, dvdz_z, dvdz_x, & + dudz_z, dudz_x, dudy_y, dudy_x + + if (o_i_hat%dir /= DIR_X .or. o_j_hat%dir /= DIR_X & + .or. o_k_hat%dir /= DIR_X .or. u%dir /= DIR_X .or. v%dir /= DIR_X & + .or. w%dir /= DIR_X) then + error stop 'Error in curl input/output field %dirs: & + &outputs and inputs must be in DIR_X layout.' + end if + + ! omega_i_hat = dw/dy - dv/dz + ! omega_j_hat = du/dz - dw/dx + ! omega_k_hat = dv/dx - du/dy + + ! omega_i_hat + ! dw/dy + w_y => self%backend%allocator%get_block(DIR_Y) + dwdy_y => self%backend%allocator%get_block(DIR_Y) + call self%backend%reorder(w_y, w, RDR_X2Y) + call self%backend%tds_solve(dwdy_y, w_y, y_der1st) + + call self%backend%reorder(o_i_hat, dwdy_y, RDR_Y2X) + + call self%backend%allocator%release_block(w_y) + call self%backend%allocator%release_block(dwdy_y) + + ! dv/dz + v_z => self%backend%allocator%get_block(DIR_Z) + dvdz_z => self%backend%allocator%get_block(DIR_Z) + call self%backend%reorder(v_z, v, RDR_X2Z) + call self%backend%tds_solve(dvdz_z, v_z, z_der1st) + + dvdz_x => self%backend%allocator%get_block(DIR_X) + call self%backend%reorder(dvdz_x, dvdz_z, RDR_Z2X) + + call self%backend%allocator%release_block(v_z) + call self%backend%allocator%release_block(dvdz_z) + + ! omega_i_hat = dw/dy - dv/dz + call self%backend%vecadd(-1._dp, dvdz_x, 1._dp, o_i_hat) + + call self%backend%allocator%release_block(dvdz_x) + + ! omega_j_hat + ! du/dz + u_z => self%backend%allocator%get_block(DIR_Z) + dudz_z => self%backend%allocator%get_block(DIR_Z) + call self%backend%reorder(u_z, u, RDR_X2Z) + call self%backend%tds_solve(dudz_z, u_z, z_der1st) + + dudz_x => self%backend%allocator%get_block(DIR_X) + call self%backend%reorder(dudz_x, dudz_z, RDR_Z2X) + + call self%backend%allocator%release_block(u_z) + call self%backend%allocator%release_block(dudz_z) + + ! dw/dx + call self%backend%tds_solve(o_j_hat, w, x_der1st) + + ! omega_j_hat = du/dz - dw/dx + call self%backend%vecadd(1._dp, dudz_x, -1._dp, o_j_hat) + + call self%backend%allocator%release_block(dudz_x) + + ! omega_k_hat + ! dv/dx + call self%backend%tds_solve(o_k_hat, v, x_der1st) + + ! du/dy + u_y => self%backend%allocator%get_block(DIR_Y) + dudy_y => self%backend%allocator%get_block(DIR_Y) + call self%backend%reorder(u_y, u, RDR_X2Y) + call self%backend%tds_solve(dudy_y, u_y, y_der1st) + + dudy_x => self%backend%allocator%get_block(DIR_X) + call self%backend%reorder(dudy_x, dudy_y, RDR_Y2X) + + call self%backend%allocator%release_block(u_y) + call self%backend%allocator%release_block(dudy_y) + + ! omega_k_hat = dv/dx - du/dy + call self%backend%vecadd(-1._dp, dudy_x, 1._dp, o_k_hat) + + call self%backend%allocator%release_block(dudy_x) + + end subroutine curl + + subroutine divergence_v2c(self, div_u, u, v, w, & + x_stagder_v2c, x_interpl_v2c, & + y_stagder_v2c, y_interpl_v2c, & + z_stagder_v2c, z_interpl_v2c) + !! Divergence of a vector field (u, v, w). + !! + !! Evaluated at the cell centers (data_loc=CELL) + !! Input fields are at vertices (data_loc=VERT) + !! + !! Input fields are in DIR_X data layout. + !! Output field is in DIR_Z data layout. + implicit none + + class(vector_calculus_t) :: self + class(field_t), intent(inout) :: div_u + class(field_t), intent(in) :: u, v, w + class(tdsops_t), intent(in) :: x_stagder_v2c, x_interpl_v2c, & + y_stagder_v2c, y_interpl_v2c, & + z_stagder_v2c, z_interpl_v2c + + class(field_t), pointer :: du_x, dv_x, dw_x, & + u_y, v_y, w_y, du_y, dv_y, dw_y, & + u_z, w_z, dw_z + + if (div_u%dir /= DIR_Z .or. u%dir /= DIR_X .or. v%dir /= DIR_X & + .or. w%dir /= DIR_X) then + error stop 'Error in divergence_v2c input/output field dirs: & + &output must be in DIR_Z, inputs must be in DIR_X layout.' + end if + + du_x => self%backend%allocator%get_block(DIR_X) + dv_x => self%backend%allocator%get_block(DIR_X) + dw_x => self%backend%allocator%get_block(DIR_X) + + ! Staggared der for u field in x + ! Interpolation for v field in x + ! Interpolation for w field in x + call self%backend%tds_solve(du_x, u, x_stagder_v2c) + call self%backend%tds_solve(dv_x, v, x_interpl_v2c) + call self%backend%tds_solve(dw_x, w, x_interpl_v2c) + + ! request fields from the allocator + u_y => self%backend%allocator%get_block(DIR_Y) + v_y => self%backend%allocator%get_block(DIR_Y) + w_y => self%backend%allocator%get_block(DIR_Y) + + ! reorder data from x orientation to y orientation + call self%backend%reorder(u_y, du_x, RDR_X2Y) + call self%backend%reorder(v_y, dv_x, RDR_X2Y) + call self%backend%reorder(w_y, dw_x, RDR_X2Y) + + call self%backend%allocator%release_block(du_x) + call self%backend%allocator%release_block(dv_x) + call self%backend%allocator%release_block(dw_x) + + du_y => self%backend%allocator%get_block(DIR_Y) + dv_y => self%backend%allocator%get_block(DIR_Y) + dw_y => self%backend%allocator%get_block(DIR_Y) + + ! similar to the x direction, obtain derivatives in y. + call self%backend%tds_solve(du_y, u_y, y_interpl_v2c) + call self%backend%tds_solve(dv_y, v_y, y_stagder_v2c) + call self%backend%tds_solve(dw_y, w_y, y_interpl_v2c) + + ! we don't need the velocities in y orientation any more, so release + ! them to open up space. + ! It is important that this doesn't actually deallocate any memory, + ! it just makes the corresponding memory space available for use. + call self%backend%allocator%release_block(u_y) + call self%backend%allocator%release_block(v_y) + call self%backend%allocator%release_block(w_y) + + ! just like in y direction, get some fields for the z derivatives. + u_z => self%backend%allocator%get_block(DIR_Z) + w_z => self%backend%allocator%get_block(DIR_Z) + + ! du_y = dv_y + du_y + call self%backend%vecadd(1._dp, dv_y, 1._dp, du_y) + + ! reorder from y to z + call self%backend%reorder(u_z, du_y, RDR_Y2Z) + call self%backend%reorder(w_z, dw_y, RDR_Y2Z) + + ! release all the unnecessary blocks. + call self%backend%allocator%release_block(du_y) + call self%backend%allocator%release_block(dv_y) + call self%backend%allocator%release_block(dw_y) + + dw_z => self%backend%allocator%get_block(DIR_Z) + + ! get the derivatives in z + call self%backend%tds_solve(div_u, u_z, z_interpl_v2c) + call self%backend%tds_solve(dw_z, w_z, z_stagder_v2c) + + ! div_u = div_u + dw_z + call self%backend%vecadd(1._dp, dw_z, 1._dp, div_u) + + ! div_u array is in z orientation + + ! there is no need to keep velocities in z orientation around, so release + call self%backend%allocator%release_block(u_z) + call self%backend%allocator%release_block(w_z) + call self%backend%allocator%release_block(dw_z) + + end subroutine divergence_v2c + + subroutine gradient_c2v(self, dpdx, dpdy, dpdz, p, & + x_stagder_c2v, x_interpl_c2v, & + y_stagder_c2v, y_interpl_c2v, & + z_stagder_c2v, z_interpl_c2v) + !! Gradient of a scalar field 'p'. + !! + !! Evaluated at the vertices (data_loc=VERT) + !! Input field is at cell centers (data_loc=CELL) + !! + !! Input field is in DIR_Z data layout. + !! Output fields (dpdx, dpdy, dpdz) are in DIR_X data layout. + implicit none + + class(vector_calculus_t) :: self + class(field_t), intent(inout) :: dpdx, dpdy, dpdz + class(field_t), intent(in) :: p + class(tdsops_t), intent(in) :: x_stagder_c2v, x_interpl_c2v, & + y_stagder_c2v, y_interpl_c2v, & + z_stagder_c2v, z_interpl_c2v + + class(field_t), pointer :: p_sxy_z, dpdz_sxy_z, & + p_sxy_y, dpdz_sxy_y, & + p_sx_y, dpdy_sx_y, dpdz_sx_y, & + p_sx_x, dpdy_sx_x, dpdz_sx_x + + if (dpdx%dir /= DIR_X .or. dpdy%dir /= DIR_X .or. dpdz%dir /= DIR_X & + .or. p%dir /= DIR_Z) then + error stop 'Error in gradient_c2v input/output field dirs: & + &outputs must be in DIR_X, input must be in DIR_Z layout.' + end if + + p_sxy_z => self%backend%allocator%get_block(DIR_Z) + dpdz_sxy_z => self%backend%allocator%get_block(DIR_Z) + + ! Staggared der for p field in z + ! Interpolation for p field in z + call self%backend%tds_solve(p_sxy_z, p, z_interpl_c2v) + call self%backend%tds_solve(dpdz_sxy_z, p, z_stagder_c2v) + + ! request fields from the allocator + p_sxy_y => self%backend%allocator%get_block(DIR_Y) + dpdz_sxy_y => self%backend%allocator%get_block(DIR_Y) + + ! reorder data from z orientation to y orientation + call self%backend%reorder(p_sxy_y, p_sxy_z, RDR_Z2Y) + call self%backend%reorder(dpdz_sxy_y, dpdz_sxy_z, RDR_Z2Y) + + call self%backend%allocator%release_block(p_sxy_z) + call self%backend%allocator%release_block(dpdz_sxy_z) + + p_sx_y => self%backend%allocator%get_block(DIR_Y) + dpdy_sx_y => self%backend%allocator%get_block(DIR_Y) + dpdz_sx_y => self%backend%allocator%get_block(DIR_Y) + + ! similar to the z direction, obtain derivatives in y. + call self%backend%tds_solve(p_sx_y, p_sxy_y, y_interpl_c2v) + call self%backend%tds_solve(dpdy_sx_y, p_sxy_y, y_stagder_c2v) + call self%backend%tds_solve(dpdz_sx_y, dpdz_sxy_y, y_interpl_c2v) + + ! release memory + call self%backend%allocator%release_block(p_sxy_y) + call self%backend%allocator%release_block(dpdz_sxy_y) + + ! just like in y direction, get some fields for the x derivatives. + p_sx_x => self%backend%allocator%get_block(DIR_X) + dpdy_sx_x => self%backend%allocator%get_block(DIR_X) + dpdz_sx_x => self%backend%allocator%get_block(DIR_X) + + ! reorder from y to x + call self%backend%reorder(p_sx_x, p_sx_y, RDR_Y2X) + call self%backend%reorder(dpdy_sx_x, dpdy_sx_y, RDR_Y2X) + call self%backend%reorder(dpdz_sx_x, dpdz_sx_y, RDR_Y2X) + + ! release all the y directional fields. + call self%backend%allocator%release_block(p_sx_y) + call self%backend%allocator%release_block(dpdy_sx_y) + call self%backend%allocator%release_block(dpdz_sx_y) + + ! get the derivatives in x + call self%backend%tds_solve(dpdx, p_sx_x, x_stagder_c2v) + call self%backend%tds_solve(dpdy, dpdy_sx_x, x_interpl_c2v) + call self%backend%tds_solve(dpdz, dpdz_sx_x, x_interpl_c2v) + + ! release temporary x fields + call self%backend%allocator%release_block(p_sx_x) + call self%backend%allocator%release_block(dpdy_sx_x) + call self%backend%allocator%release_block(dpdz_sx_x) + + end subroutine gradient_c2v + + subroutine laplacian(self, lapl_u, u, x_der2nd, y_der2nd, z_der2nd) + !! Laplacian of a scalar field 'u'. + !! + !! Evaluated at the data_loc defined by the input u field + !! + !! Input and output fields are in DIR_X layout. + implicit none + + class(vector_calculus_t) :: self + class(field_t), intent(inout) :: lapl_u + class(field_t), intent(in) :: u + + class(tdsops_t), intent(in) :: x_der2nd, y_der2nd, z_der2nd + + class(field_t), pointer :: u_y, d2u_y, u_z, d2u_z + + if (u%dir /= DIR_X .or. lapl_u%dir /= DIR_X) then + error stop 'Error in laplacian input/output field %dirs: & + &outputs and inputs must be in DIR_X layout.' + end if + + ! d2u/dx2 + call self%backend%tds_solve(lapl_u, u, x_der2nd) + + ! y directional temporary fields + u_y => self%backend%allocator%get_block(DIR_Y) + d2u_y => self%backend%allocator%get_block(DIR_Y) + + call self%backend%reorder(u_y, u, RDR_X2Y) + + ! d2u/dy2 + call self%backend%tds_solve(d2u_y, u_y, y_der2nd) + + ! add the y derivative component into the result field + call self%backend%sum_yintox(lapl_u, d2u_y) + + ! release y directional fields + call self%backend%allocator%release_block(u_y) + call self%backend%allocator%release_block(d2u_y) + + ! z directional temporary fields + u_z => self%backend%allocator%get_block(DIR_Z) + d2u_z => self%backend%allocator%get_block(DIR_Z) + + call self%backend%reorder(u_z, u, RDR_X2Z) + + ! d2u/dz2 + call self%backend%tds_solve(d2u_z, u_z, z_der2nd) + + ! add the z derivative component into the result field + call self%backend%sum_zintox(lapl_u, d2u_z) + + ! release z directional fields + call self%backend%allocator%release_block(u_z) + call self%backend%allocator%release_block(d2u_z) + + end subroutine laplacian + +end module m_vector_calculus diff --git a/api/src/xcompact.f90 b/api/src/xcompact.f90 new file mode 100644 index 000000000..bdcb7bf75 --- /dev/null +++ b/api/src/xcompact.f90 @@ -0,0 +1,115 @@ +program xcompact + use mpi + + use m_allocator + use m_base_backend + use m_common, only: pi + use m_solver, only: solver_t + use m_tdsops, only: tdsops_t + use m_mesh + +#ifdef CUDA + use m_cuda_allocator + use m_cuda_backend + use m_cuda_common, only: SZ + use m_cuda_tdsops, only: cuda_tdsops_t +#else + use m_omp_backend + use m_omp_common, only: SZ +#endif + + implicit none + + class(base_backend_t), pointer :: backend + class(allocator_t), pointer :: allocator + type(mesh_t) :: mesh + type(allocator_t), pointer :: host_allocator + type(solver_t) :: solver + +#ifdef CUDA + type(cuda_backend_t), target :: cuda_backend + type(cuda_allocator_t), target :: cuda_allocator + integer :: ndevs, devnum +#else + type(omp_backend_t), target :: omp_backend +#endif + + type(allocator_t), target :: omp_allocator + + real(dp) :: t_start, t_end + + character(len=200) :: input_file + character(len=20) :: BC_x(2), BC_y(2), BC_z(2) + integer, dimension(3) :: dims_global + integer, dimension(3) :: nproc_dir = 0 + real(dp), dimension(3) :: L_global + integer :: nrank, nproc, ierr + + namelist /domain_params/ L_global, dims_global, nproc_dir, BC_x, BC_y, BC_z + + call MPI_Init(ierr) + call MPI_Comm_rank(MPI_COMM_WORLD, nrank, ierr) + call MPI_Comm_size(MPI_COMM_WORLD, nproc, ierr) + + if (nrank == 0) print *, 'Parallel run with', nproc, 'ranks' + +#ifdef CUDA + ierr = cudaGetDeviceCount(ndevs) + ierr = cudaSetDevice(mod(nrank, ndevs)) ! round-robin + ierr = cudaGetDevice(devnum) +#endif + + if (command_argument_count() >= 1) then + call get_command_argument(1, input_file) + open (100, file=input_file) + read (100, nml=domain_params) + close (100) + else + error stop 'Input file is not provided.' + end if + + if (product(nproc_dir) /= nproc) then + if (nrank == 0) print *, 'nproc_dir specified in the input file does & + ¬ match the total number of ranks, falling & + &back to a 1D decomposition along Z-dir instead.' + nproc_dir = [1, 1, nproc] + end if + + mesh = mesh_t(dims_global, nproc_dir, L_global, BC_x, BC_y, BC_z) + +#ifdef CUDA + cuda_allocator = cuda_allocator_t(mesh, SZ) + allocator => cuda_allocator + if (nrank == 0) print *, 'CUDA allocator instantiated' + + omp_allocator = allocator_t(mesh, SZ) + host_allocator => omp_allocator + + cuda_backend = cuda_backend_t(mesh, allocator) + backend => cuda_backend + if (nrank == 0) print *, 'CUDA backend instantiated' +#else + omp_allocator = allocator_t(mesh, SZ) + allocator => omp_allocator + host_allocator => omp_allocator + if (nrank == 0) print *, 'OpenMP allocator instantiated' + + omp_backend = omp_backend_t(mesh, allocator) + backend => omp_backend + if (nrank == 0) print *, 'OpenMP backend instantiated' +#endif + + solver = solver_t(backend, mesh, host_allocator) + if (nrank == 0) print *, 'solver instantiated' + + call cpu_time(t_start) + + call solver%run() + + call cpu_time(t_end) + + if (nrank == 0) print *, 'Time: ', t_end - t_start + + call MPI_Finalize(ierr) + +end program xcompact diff --git a/api/tipuesearch/.DS_Store b/api/tipuesearch/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c087f26fc1ec03457f07b37e92ca109fcf71ce26 GIT binary patch literal 12292 zcmeHM&2JM&6n`%XWgU`c$rmXpup&|~q*1D*bx^C;n4l7INQ9#%Q37FYPn>NxJ7#yC zl%@uY0~h`P{s4{~dg`7213hx(fW*0%p5e_0POxi`P?1{kMws8UQd~ zD}Mnn1^^Cqh3+vL{-G(m>TRvdYLpQv9+GM?ltudWTd~^a%?f6K8DIvO0cL<1_%AR( z?`+ka4)1+q<&ha+20lmz)bqikt}th@VNgFhXwXLh!X8rVMzX5}LeFB(V#6R=sG$iZ zG@)pZ7-+&t?^(Z`#fCu>4rmV_XlF)yLP0${<@dBZV9p?q%m6dc&VagYx*>oas1)}O zpWnkENvEfuU{`m~k&li#nT*r7u@XdOxtEuF&SdNLjN@e7$Ii&vsc$YX z`@hz2-)We2qrLZ`zom}52Sw}_;wY3+g*3{008%Qh3N;XrQ1>B*5^Td1jR_uwyQ@u9Q~PmIKFIa`aIh0elW2 zjTNdQrP)m%L+HL21{1iNfc_ zs@cg;UAMWHdyn~tG9~>GU89g!f$;pxNPX|w**>s1Be(9%5tTy;X8Ok^x*2Jbe zkkD3ND9LCk7Q2DiTTR8!X(9N9OoJ@}S4H{yfol0ym3)Q|6guhmu@KREB@W7qTU&`p zSJe!qScc(}OoB9!(b^&F+J>Gz6*~*jmZTZg3Kpx%E%`yj31M6O>Y5+NepC_pTh~Ir zD8j`kFVj@+tOittTDhK}Q1weeRGIkhFWN8EG84JajMP-Q&=e<(37}nYnyTf<-w~8& z-sZ_&u~813g9}iA4Y&yncmNOKclZPTglF(Kyuf}O#j|)0C-F=C7IV0OS8*N7C@{hV zQ>>dTU9TPhp3Q0j?*YI6U#EnBF#~U7pxZc%sq_D(&fovvmWJ)j05kAEW&nHU3-fs@ z;MME3wH9^O_Nd>WuA1^|7}Oy&=;Jt{K8_PM|1c!kBUcrZv)C|58fyF@K-0hjGr$Zm h1Iz$3zzi@0%m6dM3@`)C05iZ0Fayj0Gw`o6@Dit0MkoLP literal 0 HcmV?d00001 diff --git a/api/tipuesearch/img/.DS_Store b/api/tipuesearch/img/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ed47b307b4e599558cc6ce0423a93300a81785de GIT binary patch literal 6148 zcmeHKyH3PF3>?E1CupujqS=NPiJE403I#Pkz&+qViU`~W2cS_-fwd-map zlPTg|0J419o&gI0bGjm4HB3$S)hD(WD~e*zxWN-nvBN!XC)wXGDE9y@I;^n8fPdL` zn|0f*xvwvGPCtG#<48Hr%wDnH26wFahzIPxa2n>F3^+#2kP4&%sX!`_3jA{g*t6B< zhmILjfm9$B_*6ju4~4E+1A9mNbuj4hUH4~-Y8=~Lf>>f+1A9mA(8Q@kr%GHg#OYiw zkyitIN2f!qGM^KbC2lBU)wy0Q9a0@LrUI$JSb=??F0}t&(0{o9k4ZU81yX@OrGU(u zt7gehirzZ;IPJBCeog-{=2|+#=n%k(w&J5fUePi0YGChZbgmnnmB4F%o- D170Wt literal 0 HcmV?d00001 diff --git a/api/tipuesearch/img/loader.gif b/api/tipuesearch/img/loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..9c97738a27ad26d4e63494485484f1b5e4a5c73a GIT binary patch literal 4178 zcmd7VSy&VI-Usl>tjSCgCNLmNf(amEL=GZapzSeaVU36gNGn#*C`v(A6;at20Ywn3 z3Z+J5iFjmjK|ItF3@9Kw0*Y2pam9tj9_dk!C-$}PO`rF@>P7#Tb1^f|=lTA=zZq9| z7YD~+KEMZl0e}x5KKS|hy?y(3czF2Lt5>gIzaAYORjE{sjg7OjvoBx1eE05MeSLj( zb@jx=L{CpoQ&ZFQ^mJQW+vCTN6A}_~b8`y|3o9!tTUuIXW@a)nGQR)*`@q0J?LF__ zy*n~8GCn>Y9UWazP_Sjomdwn|j*brCPd$SF7)zoz1;=hu@ON&F-NFwJ4UP+o2v*o^ z-L^Rb5c~=Qxb}I14}_P@ceTXXTV`it3(>TftUtcSM+5-mAux~% zL~x=}R0fBb?EeN}M68iAHUcn)F+C(?h#mq#2oVt#&WRw>=qE9`+J2dn4RU*x|Tx$V;#)Js`YDUo>3FoM*S)X}g=hHYiNvYNvaZH)Md$4MuAbNL7R0Stss6$ap5SrM*1BsfhhHxaM#-Ecdb!!VK0lB9 zR#D~g?->^trvCJ;(W+yCX8hEWoJQf*Sf9}56nN#!y^>SvcD>KcoM@9V@U%i^$v?r= z_YtO=nV0{t=)jGsQ{?_H)8$Cg;IpBcRmbgOoD>UzDAQK|3stH|1HZ=-`lF={3OPr$ z+ZzyV;=VqGC1qq5%jbQ^J{Y=6>1~yM+eRJbP}tJyw(F>=8T-JJKsSGj-c@2W9NN8d z69H@oSr{`Cj29D|e6~a3zEWLXNg>WTv<4>#1gQuorI0>F=}C+>;i}j}Vwk>8&!mPj z*?2x7gTZRj5B5JF>Nlsxm`wDm*t}bhV!;_eRz{ti$bXQ3clTPir)-U;!4Kk1P4l?V z&kyXLoSEX@AAfjEIG~8Y(tLDV;-rKqk$(yPk+5> zn|g8In|)j3U5KQH1}%XDy_1N~7K7x`?ZDojTBxIu=q$HlcQ$xQFb9rf3*!3owDD&Ef3uI|x9=-hBHL`=B)MzceGrE~Y~cje@)aRahT zLeW4bhQSgeS8ec+95KUG0h47I8GH(l-wDpUz&zo9svyX)j0IK82xLzG63HjtMI(z2Rv-tP~Dcq#wr$Z zCS9DQNxWsYAII3G)5dZGKDG$gluZdPny?UpC1FgL>- z+|s@}*S^Buan2ll2eqJLbTjAd&wUa#e@#Z;%1r7F7dh$SNh+hD0uo<{s)gR{!+`FQHDS#r7hIBobJDW+`W)OUF`5fOyMU-nF91nPH}Ybj#Sj|ox;=*ASIvdx+4zy5 zv&HNap2WH`PX@%lBL69>GpZb=9}7d9ty)qZsSft?NNQkKJW;_4LM< z4h>%Fe0kyJn%h#8A|HByVR?@Nlp<#hq_`O=TP+TPoNp3 zoV5K<)Xt`cW%egQ!@TK}rdD4mbxV6sNC*69yV>_1*w8;oqJOrejIj&&M47jBTbP^V ze;tW+nOep8%KTDEHhwhdOs>2lCo}^&{?GV>7gMjKUJj~2+S(OWRW))1>g(_8?CkdJ z9q1=CS6cnR_WQ%5Jx#!xVJRDfo^32j4y9ul3%z*e#~6hC1*3Cqedc8iEQAc`^eFY% zC}dU@wraypHI0T-#-vfN(lp=YvJK6!_k;!jShn|LmW-tqt#)@Sl2#_Cq{r;=Z~uY; zfL9l0KDKAwax4Q8R=s+bBlg`#+pnA8+-`T?Vx_>uDzLTvhH&@@*MWXR@Ao8Pe;vOR zO-oh#r5Hs=x0bN0gSghxG&s~Mx!tdI>ysf6`{$KfH&f^ZtYzy&UqGhk#C36AmH66RpP?6ZINFiC&_sFk-ZpoC@E3a*v6Bw5grXWnD) zkTh1DM`P;HlihXIfL->Ni3gsjX?i^ZCflB@gh2^il8Z-)rZhFWJTaLsgs;%ZWjT?o z>lVo<>(y(R_9=$+zdR%RW)p&Oq0ZAmq;{{H$CO-`X&aJ9p)n1vMx`dx=JUi1gJ=2+ z#*(vv6%T>32a!Pr5KdWAhPHf4MoKquf@@FQCPDHuATn}H^chKoygzqA0ZBn~&mC(; zeVQ7a{qZ*j@hh#GYw^VOj>}TG z>9HS!G)7~*>*YGyHLo92a>+x_f= z5Q&l?47A~7$)sRvtOO4$^bZOsT3u>3r#ugnlFG&nHHzYjD1C;5@2?0?qE$1+5_hFmeJr>cr6^@n{TspZh+GQTsg25w4U$NI$*5sAM|EwPaE7Wtrwo2qooL&krB172sAFxlwl|Ly5SlEk~ z*wACwnVILWUQBJ<@>(_i;8CzandA-2<6?@xfrB9s>0Y>{|L;u0N!q~@PnoXaQ3o|3 z4ZLb+al2(ok|8iVOz$lZHC>#`KL3^YEPo(nHQw3fE0Q*+Emb%ipMAR2uAulE_jeW@ zw_Y%n%L|3UE$6c5`!4k>7M7HuU9Oq9m(u}{oUnm9^B%k4!9{f7^N)Yfzn%XAsSy+_ literal 0 HcmV?d00001 diff --git a/api/tipuesearch/img/search.png b/api/tipuesearch/img/search.png new file mode 100644 index 0000000000000000000000000000000000000000..8c6943d42ab8e861373162f11056f1d198146ebf GIT binary patch literal 368 zcmV-$0gwKPP)7K``wv zphx>2{0_r#5g#iG#&nVEgc=X!FGs?cTA62~5l& zE5XM}PVJ65P%pNIM82yJAK#80@CP~In49nwXo}dj4c`pfmG1%#c=QkCE2g|7f{hjc O0000'); + var t_2 = html.toLowerCase().indexOf('', t_1 + 7); + if (t_1 != -1 && t_2 != -1) + { + var tit = html.slice(t_1 + 7, t_2); + } + else + { + var tit = 'No title'; + } + + tipuesearch_in.pages.push({ + "title": tit, + "text": desc, + "tags": cont, + "loc": tipuesearch_pages[i] + }); + } + ); + } + } + + if (set.mode == 'json') + { + $.getJSON(set.contentLocation, + function(json) + { + tipuesearch_in = $.extend({}, json); + } + ); + } + + if (set.mode == 'static') + { + tipuesearch_in = $.extend({}, tipuesearch); + } + + var tipue_search_w = ''; + if (set.newWindow) + { + tipue_search_w = ' target="_blank"'; + } + + function getURLP(name) + { + return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, '%20')) || null; + } + if (getURLP('q')) + { + $('#tipue_search_input').val(getURLP('q')); + getTipueSearch(0, true); + } + + $(this).keyup(function(event) + { + if(event.keyCode == '13') + { + getTipueSearch(0, true); + } + }); + + function getTipueSearch(start, replace) + { + $('#tipue_search_content').hide(); + var out = ''; + var results = ''; + var show_replace = false; + var show_stop = false; + var standard = true; + var c = 0; + found = new Array(); + + var d = $('#tipue_search_input').val().toLowerCase(); + d = $.trim(d); + + if ((d.match("^\"") && d.match("\"$")) || (d.match("^'") && d.match("'$"))) + { + standard = false; + } + + if (standard) + { + var d_w = d.split(' '); + d = ''; + for (var i = 0; i < d_w.length; i++) + { + var a_w = true; + for (var f = 0; f < tipuesearch_stop_words.length; f++) + { + if (d_w[i] == tipuesearch_stop_words[f]) + { + a_w = false; + show_stop = true; + } + } + if (a_w) + { + d = d + ' ' + d_w[i]; + } + } + d = $.trim(d); + d_w = d.split(' '); + } + else + { + d = d.substring(1, d.length - 1); + } + + if (d.length >= set.minimumLength) + { + if (standard) + { + if (replace) + { + var d_r = d; + for (var i = 0; i < d_w.length; i++) + { + for (var f = 0; f < tipuesearch_replace.words.length; f++) + { + if (d_w[i] == tipuesearch_replace.words[f].word) + { + d = d.replace(d_w[i], tipuesearch_replace.words[f].replace_with); + show_replace = true; + } + } + } + d_w = d.split(' '); + } + + var d_t = d; + for (var i = 0; i < d_w.length; i++) + { + for (var f = 0; f < tipuesearch_stem.words.length; f++) + { + if (d_w[i] == tipuesearch_stem.words[f].word) + { + d_t = d_t + ' ' + tipuesearch_stem.words[f].stem; + } + } + } + d_w = d_t.split(' '); + + for (var i = 0; i < tipuesearch_in.pages.length; i++) + { + var score = 1000000000; + var s_t = tipuesearch_in.pages[i].text; + for (var f = 0; f < d_w.length; f++) + { + var pat = new RegExp(d_w[f], 'i'); + if (tipuesearch_in.pages[i].title.search(pat) != -1) + { + score -= (200000 - i); + } + if (tipuesearch_in.pages[i].text.search(pat) != -1) + { + score -= (150000 - i); + } + + if (set.highlightTerms) + { + if (set.highlightEveryTerm) + { + var patr = new RegExp('(' + d_w[f] + ')', 'gi'); + } + else + { + var patr = new RegExp('(' + d_w[f] + ')', 'i'); + } + s_t = s_t.replace(patr, "$1"); + } + if (tipuesearch_in.pages[i].tags.search(pat) != -1) + { + score -= (100000 - i); + } + + if (d_w[f].match("^-")) + { + pat = new RegExp(d_w[f].substring(1), 'i'); + if (tipuesearch_in.pages[i].title.search(pat) != -1 || tipuesearch_in.pages[i].text.search(pat) != -1 || tipuesearch_in.pages[i].tags.search(pat) != -1) + { + score = 1000000000; + } + } + } + + if (score < 1000000000) + { + found[c++] = score + '^' + tipuesearch_in.pages[i].title + '^' + s_t + '^' + tipuesearch_in.pages[i].loc; + } + } + } + else + { + for (var i = 0; i < tipuesearch_in.pages.length; i++) + { + var score = 1000000000; + var s_t = tipuesearch_in.pages[i].text; + var pat = new RegExp(d, 'i'); + if (tipuesearch_in.pages[i].title.search(pat) != -1) + { + score -= (200000 - i); + } + if (tipuesearch_in.pages[i].text.search(pat) != -1) + { + score -= (150000 - i); + } + + if (set.highlightTerms) + { + if (set.highlightEveryTerm) + { + var patr = new RegExp('(' + d + ')', 'gi'); + } + else + { + var patr = new RegExp('(' + d + ')', 'i'); + } + s_t = s_t.replace(patr, "$1"); + } + if (tipuesearch_in.pages[i].tags.search(pat) != -1) + { + score -= (100000 - i); + } + + if (score < 1000000000) + { + found[c++] = score + '^' + tipuesearch_in.pages[i].title + '^' + s_t + '^' + tipuesearch_in.pages[i].loc; + } + } + } + + if (c != 0) + { + if (show_replace == 1) + { + out += '
Showing results for ' + d + '
'; + out += '
Search instead for ' + d_r + '
'; + } + if (c == 1) + { + out += '
1 result
'; + } + else + { + c_c = c.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + out += '
' + c_c + ' results
'; + } + + found.sort(); + var l_o = 0; + for (var i = 0; i < found.length; i++) + { + var fo = found[i].split('^'); + if (l_o >= start && l_o < set.show + start) + { + out += ''; + + if (set.showURL) + { + out += ''; + } + + var t = fo[2]; + var t_d = ''; + var t_w = t.split(' '); + if (t_w.length < set.descriptiveWords) + { + t_d = t; + } + else + { + for (var f = 0; f < set.descriptiveWords; f++) + { + t_d += t_w[f] + ' '; + } + } + t_d = $.trim(t_d); + if (t_d.charAt(t_d.length - 1) != '.') + { + t_d += ' ...'; + } + out += '
' + t_d + '
'; + } + l_o++; + } + + if (c > set.show) + { + var pages = Math.ceil(c / set.show); + var page = (start / set.show); + out += '
+ +