From fbcf4f3115cc2bd357812cfa32b8a6d799c21807 Mon Sep 17 00:00:00 2001 From: NaokiHori <36466440+NaokiHori@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:38:32 +0200 Subject: [PATCH] [mod] renew documentation --- .github/workflows/ci.yml | 130 +-- .github/workflows/documentation.yml | 76 +- .../documentation_download_artifact.py | 145 ---- .github/workflows/extract_nd.yml | 9 +- README.rst | 69 +- SimpleDecomp | 2 +- SimpleNpyIO | 2 +- docs/source/conf_params/mathjax_params.py | 156 +++- docs/source/equations/eq/internal_energy.rst | 142 ---- docs/source/equations/eq/mass.rst | 73 -- docs/source/equations/eq/momentum.rst | 217 ----- docs/source/equations/eq/tilde_note.rst | 4 - docs/source/equations/main.rst | 620 ++++++++++---- docs/source/equations/nu/heat_flux.rst | 257 ------ .../nu/kinetic_energy_dissipation.rst | 178 ---- .../equations/nu/kinetic_energy_injection.rst | 150 ---- .../nu/thermal_energy_dissipation.rst | 187 ---- docs/source/examples/energy/data/exec_2d.sh | 19 +- docs/source/examples/energy/data/exec_3d.sh | 22 +- docs/source/examples/energy/data/process.py | 17 +- docs/source/examples/energy/main.rst | 72 +- docs/source/examples/gl/data/exec.sh | 41 - docs/source/examples/gl/data/process.py | 47 -- docs/source/examples/gl/main.rst | 41 - docs/source/examples/main.rst | 25 +- docs/source/examples/nu/data/process.py | 37 - docs/source/examples/nu/main.rst | 110 --- .../examples/{nu => nusselt}/data/exec_2d.sh | 30 +- .../examples/{nu => nusselt}/data/exec_3d.sh | 35 +- docs/source/examples/nusselt/data/process.py | 60 ++ docs/source/examples/nusselt/main.rst | 63 ++ .../examples/typical/data/divergence.py | 2 +- docs/source/examples/typical/data/exec_2d.sh | 54 +- docs/source/examples/typical/data/exec_3d.sh | 60 +- .../examples/typical/data/nusselt_time.py | 68 +- .../source/examples/typical/data/nusselt_x.py | 51 +- docs/source/examples/typical/data/std.py | 26 +- docs/source/examples/typical/main.rst | 177 ++-- docs/source/implementation/decide_dt.rst | 99 --- docs/source/implementation/domain.rst | 88 -- docs/source/implementation/fileio.rst | 38 - docs/source/implementation/fluid/boundary.rst | 98 --- .../fluid/compute_potential/dct.rst | 112 --- .../fluid/compute_potential/dft.rst | 125 --- .../fluid/compute_potential/main.rst | 70 -- .../math/governing_equation.rst | 59 -- .../math/orthogonal_decomposition.rst | 323 ------- .../implementation/fluid/correct_velocity.rst | 77 -- docs/source/implementation/fluid/init.rst | 43 - .../fluid/integrate/compute_rhs.rst | 167 ---- .../implementation/fluid/integrate/main.rst | 51 -- .../fluid/integrate/predict_field.rst | 140 --- docs/source/implementation/fluid/main.rst | 81 -- .../implementation/fluid/update_pressure.rst | 92 -- docs/source/implementation/halo/main.rst | 42 - docs/source/implementation/halo/y-halo.rst | 35 - docs/source/implementation/halo/z-halo.rst | 20 - docs/source/implementation/integrate.rst | 78 -- docs/source/implementation/linear_system.rst | 442 ---------- .../implementation/logging/divergence.rst | 53 -- docs/source/implementation/logging/energy.rst | 108 --- docs/source/implementation/logging/main.rst | 47 -- .../implementation/logging/momentum.rst | 38 - .../source/implementation/logging/nusselt.rst | 22 - docs/source/implementation/main.rst | 183 ---- docs/source/implementation/param.rst | 18 - docs/source/implementation/statistics.rst | 79 -- docs/source/implementation/tdm/backward.rst | 74 -- docs/source/implementation/tdm/forward.rst | 169 ---- docs/source/implementation/tdm/main.rst | 17 - .../tdm/method_and_implementation.rst | 130 --- docs/source/implementation/tdm/overview.rst | 114 --- .../implementation/tdm/sherman_morrison.rst | 294 ------- docs/source/index.rst | 2 - docs/source/misc.rst | 18 - .../source/numerical_method/linear_system.rst | 165 ++++ docs/source/numerical_method/main.rst | 27 +- docs/source/numerical_method/poisson.rst | 427 ++++++++++ .../inconsistent_results/example1.rst | 120 --- .../inconsistent_results/example2.rst | 81 -- .../images/adv_convergence.png | Bin 48739 -> 0 bytes .../images/adv_energy.png | Bin 40587 -> 0 bytes .../images/dif_convergence.png | Bin 41454 -> 0 bytes .../appendix/inconsistent_results/main.rst | 22 - .../continuous_domain.rst | 157 ---- .../discrete_domain/governing_equations.rst | 100 --- .../discrete_domain/kinetic_energy.rst | 430 ---------- .../discrete_domain/main.rst | 19 - .../thermal_quadratic_quantity.rst | 173 ---- .../conservation_property/main.rst | 13 - .../nusselt/heat_flux.rst | 148 ---- .../nusselt/injection.rst | 31 - .../conservation_property/nusselt/kinetic.rst | 85 -- .../conservation_property/nusselt/main.rst | 52 -- .../conservation_property/nusselt/thermal.rst | 70 -- .../continuous_governing_equations.rst | 66 -- .../discrete_governing_equations.rst | 124 --- .../strong_conservation_form/main.rst | 19 - .../strong_conservation_form.rst | 334 -------- .../conclusion/incompressibility.rst | 31 - .../conclusion/internal_energy/adv_x.rst | 85 -- .../conclusion/internal_energy/adv_y.rst | 71 -- .../conclusion/internal_energy/adv_z.rst | 71 -- .../conclusion/internal_energy/dif_x.rst | 54 -- .../conclusion/internal_energy/dif_y.rst | 54 -- .../conclusion/internal_energy/dif_z.rst | 54 -- .../conclusion/internal_energy/main.rst | 53 -- .../conclusion/squared_temperature.rst | 58 -- .../conclusion/squared_velocity.rst | 90 -- .../conclusion/x_momentum/adv_x.rst | 71 -- .../conclusion/x_momentum/adv_y.rst | 61 -- .../conclusion/x_momentum/adv_z.rst | 61 -- .../conclusion/x_momentum/dif_x.rst | 54 -- .../conclusion/x_momentum/dif_y.rst | 54 -- .../conclusion/x_momentum/dif_z.rst | 54 -- .../conclusion/x_momentum/main.rst | 57 -- .../conclusion/y_momentum/adv_x.rst | 92 -- .../conclusion/y_momentum/adv_y.rst | 83 -- .../conclusion/y_momentum/adv_z.rst | 83 -- .../conclusion/y_momentum/dif_x.rst | 54 -- .../conclusion/y_momentum/dif_y.rst | 58 -- .../conclusion/y_momentum/dif_z.rst | 58 -- .../conclusion/y_momentum/main.rst | 54 -- .../conclusion/z_momentum/adv_x.rst | 93 -- .../conclusion/z_momentum/adv_y.rst | 77 -- .../conclusion/z_momentum/adv_z.rst | 77 -- .../conclusion/z_momentum/dif_x.rst | 56 -- .../conclusion/z_momentum/dif_y.rst | 54 -- .../conclusion/z_momentum/dif_z.rst | 54 -- .../conclusion/z_momentum/main.rst | 54 -- .../basic_operators/conservative_form.rst | 44 - .../differentiation_and_integral.rst | 167 ---- .../basic_operators/interpolation.rst | 83 -- .../derivation/basic_operators/main.rst | 16 - ...incompressibility_and_poisson_equation.rst | 168 ---- .../advective_terms/divergence_form.rst | 69 -- .../advective_terms/gradient_form.rst | 212 ----- .../advective_terms/main.rst | 14 - .../advective_terms/quadratic_quantities.rst | 173 ---- .../diffusive_terms.rst | 359 -------- .../internal_energy_balance/introduction.rst | 30 - .../internal_energy_balance/main.rst | 31 - .../internal_energy_balance/summary.rst | 28 - .../advective_terms/divergence_form.rst | 136 --- .../advective_terms/gradient_form.rst | 795 ------------------ .../momentum_balance/advective_terms/main.rst | 14 - .../advective_terms/quadratic_quantities.rst | 723 ---------------- .../momentum_balance/diffusive_terms.rst | 534 ------------ .../external_forcing_term.rst | 20 - .../momentum_balance/introduction.rst | 61 -- .../derivation/momentum_balance/main.rst | 33 - .../pressure_gradient_terms.rst | 129 --- .../derivation/momentum_balance/summary.rst | 61 -- .../derivation/nusselt/heat_flux.rst | 386 --------- .../nusselt/kinetic_energy_dissipation.rst | 292 ------- .../nusselt/kinetic_energy_injection.rst | 167 ---- .../derivation/nusselt/main.rst | 28 - .../nusselt/thermal_energy_dissipation.rst | 207 ----- .../figures/plot.gp | 125 +++ .../figures/result.png | Bin 0 -> 13926 bytes .../discrete_governing_equations/main.rst | 97 +-- .../resulting_schemes/main.rst | 174 ++++ .../resulting_schemes/momentum/adv.rst | 97 +++ .../resulting_schemes/momentum/buo.rst | 12 + .../resulting_schemes/momentum/dif.rst | 124 +++ .../resulting_schemes/momentum/pre.rst | 43 + .../resulting_schemes/symbols/average.rst | 92 ++ .../symbols/differentiation.rst | 95 +++ .../resulting_schemes/symbols/summation.rst | 100 +++ .../resulting_schemes/t/adv.rst | 31 + .../resulting_schemes/t/dif.rst | 40 + .../strong_conservation_form.rst | 289 +++++++ .../domain_setup/images/domain.png | Bin 0 -> 33599 bytes .../domain_setup/images/grid1.png | Bin 0 -> 15675 bytes .../domain_setup/images/grid2.png | Bin 0 -> 20810 bytes .../domain_setup/images/grid3.png | Bin 0 -> 17118 bytes .../domain_setup/images/grid4.png | Bin 0 -> 17888 bytes .../domain_setup/images/staggered1.png | Bin 0 -> 26038 bytes .../domain_setup/images/staggered2.png | Bin 0 -> 43546 bytes .../domain_setup/images/staggered3.png | Bin 0 -> 39128 bytes .../domain_setup/images/staggered4.png | Bin 0 -> 22068 bytes .../domain_setup/main.rst | 165 ++-- .../spatial_discretisation/main.rst | 36 +- .../spatial_discretisation/nusselt.rst | 301 +++++++ .../derivation/advective.rst | 308 +++++++ .../derivation/diffusive.rst | 142 ++++ .../derivation/prerequisite/main.rst | 17 + .../derivation/prerequisite/t.rst | 113 +++ .../derivation/prerequisite/x.rst | 101 +++ .../derivation/prerequisite/y.rst | 113 +++ .../derivation/prerequisite/z.rst | 110 +++ .../derivation/pressure_gradient.rst | 72 ++ .../quadratic_quantities/main.rst | 432 ++++++++++ .../squared_temperature.rst | 56 ++ .../quadratic_quantities/squared_velocity.rst | 131 +++ docs/source/numerical_method/tdm.rst | 657 +++++++++++++++ .../temporal_discretisation/derivation/cn.rst | 31 + .../temporal_discretisation/derivation/rk.rst | 124 +++ .../temporal_discretisation/implicit.rst | 413 +++++---- .../temporal_discretisation/main.rst | 18 +- .../temporal_discretisation/momentum.rst | 571 +++++++++++++ .../temporal_discretisation/smac_method.rst | 499 ----------- .../temporal_discretisation/temperature.rst | 255 +++--- .../temporal_discretisation/time_marcher.rst | 490 ++++------- docs/source/references.rst | 6 +- docs/source/references.txt | 28 +- include/array.h | 2 +- include/array_macros/domain/dxc.h | 10 - include/array_macros/domain/dxf.h | 10 - include/array_macros/domain/hxxc.h | 10 + include/array_macros/domain/hxxf.h | 10 + include/array_macros/domain/jdxc.h | 10 + include/array_macros/domain/jdxf.h | 10 + include/array_macros/statistics/adv.h | 18 + include/array_macros/statistics/dif.h | 18 + include/array_macros/statistics/uxt.h | 18 - include/domain.h | 29 +- include/fluid.h | 15 +- include/fluid_solver.h | 29 +- include/runge_kutta.h | 7 +- include/save.h | 9 +- initial_condition/2d.py | 85 -- initial_condition/3d.py | 90 -- initial_condition/main.py | 109 +++ initial_condition/{exec.sh => main.sh} | 2 - src/README.rst | 51 -- src/array.c | 4 +- src/decide_dt.c | 123 +-- src/domain.c | 158 ++-- src/fluid/README.rst | 46 - src/fluid/compute_diffusivity.c | 15 + .../dft.c => compute_potential.c} | 488 ++++++----- src/fluid/compute_potential/dct.c | 656 --------------- src/fluid/compute_potential/internal.h | 25 - src/fluid/compute_potential/main.c | 40 - .../{correct_velocity => correct}/internal.h | 0 .../{correct_velocity => correct}/main.c | 0 src/fluid/correct/ux.c | 63 ++ src/fluid/correct/uy.c | 61 ++ src/fluid/{correct_velocity => correct}/uz.c | 29 +- src/fluid/correct_velocity/ux.c | 64 -- src/fluid/correct_velocity/uy.c | 61 -- src/fluid/init.c | 74 +- src/fluid/integrate/couple_external_force.c | 25 - src/fluid/integrate/predict_field.c | 36 - src/fluid/integrate/t.c | 530 ------------ src/fluid/integrate/ux.c | 616 -------------- src/fluid/integrate/uy.c | 570 ------------- src/fluid/integrate/uz.c | 453 ---------- src/fluid/{integrate => predict}/internal.h | 46 +- .../compute_rhs.c => predict/main.c} | 28 +- src/fluid/predict/t.c | 501 +++++++++++ src/fluid/predict/ux.c | 566 +++++++++++++ src/fluid/predict/uy.c | 525 ++++++++++++ src/fluid/predict/uz.c | 431 ++++++++++ src/fluid/save.c | 4 +- src/fluid/update_pressure.c | 179 ++-- src/integrate.c | 11 +- src/logging/README.rst | 26 - src/logging/dissipated_squared_temperature.c | 161 ++++ src/logging/dissipated_squared_velocity.c | 377 +++++++++ src/logging/divergence.c | 102 --- src/logging/energy.c | 142 ---- src/logging/heat_transfer.c | 82 ++ src/logging/injected_squared_velocity.c | 82 ++ src/logging/internal.h | 37 +- src/logging/main.c | 61 +- src/logging/max_divergence.c | 112 +++ src/logging/momentum.c | 118 --- src/logging/nusselt/README.rst | 6 - src/logging/nusselt/heat_flux.c | 72 -- src/logging/nusselt/internal.h | 29 - .../nusselt/kinetic_energy_dissipation.c | 484 ----------- .../nusselt/kinetic_energy_injection.c | 75 -- src/logging/nusselt/main.c | 52 -- src/logging/nusselt/reference.c | 20 - .../nusselt/thermal_energy_dissipation.c | 199 ----- src/logging/total_energy.c | 120 +++ src/logging/total_momentum.c | 99 +++ src/main.c | 63 +- src/save.c | 50 +- src/statistics.c | 307 +++---- src/tdm.c | 2 +- tools/define_arrays.py | 57 +- 284 files changed, 11248 insertions(+), 21908 deletions(-) delete mode 100644 .github/workflows/documentation_download_artifact.py delete mode 100644 docs/source/equations/eq/internal_energy.rst delete mode 100644 docs/source/equations/eq/mass.rst delete mode 100644 docs/source/equations/eq/momentum.rst delete mode 100644 docs/source/equations/eq/tilde_note.rst delete mode 100644 docs/source/equations/nu/heat_flux.rst delete mode 100644 docs/source/equations/nu/kinetic_energy_dissipation.rst delete mode 100644 docs/source/equations/nu/kinetic_energy_injection.rst delete mode 100644 docs/source/equations/nu/thermal_energy_dissipation.rst delete mode 100644 docs/source/examples/gl/data/exec.sh delete mode 100644 docs/source/examples/gl/data/process.py delete mode 100644 docs/source/examples/gl/main.rst delete mode 100644 docs/source/examples/nu/data/process.py delete mode 100644 docs/source/examples/nu/main.rst rename docs/source/examples/{nu => nusselt}/data/exec_2d.sh (52%) rename docs/source/examples/{nu => nusselt}/data/exec_3d.sh (53%) create mode 100644 docs/source/examples/nusselt/data/process.py create mode 100644 docs/source/examples/nusselt/main.rst delete mode 100644 docs/source/implementation/decide_dt.rst delete mode 100644 docs/source/implementation/domain.rst delete mode 100644 docs/source/implementation/fileio.rst delete mode 100644 docs/source/implementation/fluid/boundary.rst delete mode 100644 docs/source/implementation/fluid/compute_potential/dct.rst delete mode 100644 docs/source/implementation/fluid/compute_potential/dft.rst delete mode 100644 docs/source/implementation/fluid/compute_potential/main.rst delete mode 100644 docs/source/implementation/fluid/compute_potential/math/governing_equation.rst delete mode 100644 docs/source/implementation/fluid/compute_potential/math/orthogonal_decomposition.rst delete mode 100644 docs/source/implementation/fluid/correct_velocity.rst delete mode 100644 docs/source/implementation/fluid/init.rst delete mode 100644 docs/source/implementation/fluid/integrate/compute_rhs.rst delete mode 100644 docs/source/implementation/fluid/integrate/main.rst delete mode 100644 docs/source/implementation/fluid/integrate/predict_field.rst delete mode 100644 docs/source/implementation/fluid/main.rst delete mode 100644 docs/source/implementation/fluid/update_pressure.rst delete mode 100644 docs/source/implementation/halo/main.rst delete mode 100644 docs/source/implementation/halo/y-halo.rst delete mode 100644 docs/source/implementation/halo/z-halo.rst delete mode 100644 docs/source/implementation/integrate.rst delete mode 100644 docs/source/implementation/linear_system.rst delete mode 100644 docs/source/implementation/logging/divergence.rst delete mode 100644 docs/source/implementation/logging/energy.rst delete mode 100644 docs/source/implementation/logging/main.rst delete mode 100644 docs/source/implementation/logging/momentum.rst delete mode 100644 docs/source/implementation/logging/nusselt.rst delete mode 100644 docs/source/implementation/main.rst delete mode 100644 docs/source/implementation/param.rst delete mode 100644 docs/source/implementation/statistics.rst delete mode 100644 docs/source/implementation/tdm/backward.rst delete mode 100644 docs/source/implementation/tdm/forward.rst delete mode 100644 docs/source/implementation/tdm/main.rst delete mode 100644 docs/source/implementation/tdm/method_and_implementation.rst delete mode 100644 docs/source/implementation/tdm/overview.rst delete mode 100644 docs/source/implementation/tdm/sherman_morrison.rst delete mode 100644 docs/source/misc.rst create mode 100644 docs/source/numerical_method/linear_system.rst create mode 100644 docs/source/numerical_method/poisson.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/example1.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/example2.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/images/adv_convergence.png delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/images/adv_energy.png delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/images/dif_convergence.png delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/continuous_domain.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/governing_equations.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/kinetic_energy.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/thermal_quadratic_quantity.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/heat_flux.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/injection.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/kinetic.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/thermal.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/continuous_governing_equations.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/discrete_governing_equations.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/strong_conservation_form.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/incompressibility.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_x.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_y.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_z.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_x.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_y.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_z.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/squared_temperature.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/squared_velocity.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_x.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_y.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_z.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_x.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_y.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_z.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_x.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_y.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_z.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_x.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_y.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_z.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_x.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_y.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_z.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_x.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_y.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_z.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/conservative_form.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/differentiation_and_integral.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/interpolation.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/incompressibility_and_poisson_equation.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/divergence_form.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/gradient_form.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/quadratic_quantities.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/diffusive_terms.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/introduction.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/summary.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/divergence_form.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/gradient_form.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/quadratic_quantities.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/diffusive_terms.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/external_forcing_term.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/introduction.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/pressure_gradient_terms.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/summary.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/heat_flux.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/kinetic_energy_dissipation.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/kinetic_energy_injection.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/main.rst delete mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/thermal_energy_dissipation.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/figures/plot.gp create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/figures/result.png create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/main.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/adv.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/buo.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/dif.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/pre.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/average.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/differentiation.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/summation.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/t/adv.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/t/dif.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/strong_conservation_form.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/domain_setup/images/domain.png create mode 100644 docs/source/numerical_method/spatial_discretisation/domain_setup/images/grid1.png create mode 100644 docs/source/numerical_method/spatial_discretisation/domain_setup/images/grid2.png create mode 100644 docs/source/numerical_method/spatial_discretisation/domain_setup/images/grid3.png create mode 100644 docs/source/numerical_method/spatial_discretisation/domain_setup/images/grid4.png create mode 100644 docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered1.png create mode 100644 docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered2.png create mode 100644 docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered3.png create mode 100644 docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered4.png create mode 100644 docs/source/numerical_method/spatial_discretisation/nusselt.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/advective.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/diffusive.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/main.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/t.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/x.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/y.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/z.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/pressure_gradient.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/quadratic_quantities/main.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/quadratic_quantities/squared_temperature.rst create mode 100644 docs/source/numerical_method/spatial_discretisation/quadratic_quantities/squared_velocity.rst create mode 100644 docs/source/numerical_method/tdm.rst create mode 100644 docs/source/numerical_method/temporal_discretisation/derivation/cn.rst create mode 100644 docs/source/numerical_method/temporal_discretisation/derivation/rk.rst create mode 100644 docs/source/numerical_method/temporal_discretisation/momentum.rst delete mode 100644 docs/source/numerical_method/temporal_discretisation/smac_method.rst delete mode 100644 include/array_macros/domain/dxc.h delete mode 100644 include/array_macros/domain/dxf.h create mode 100644 include/array_macros/domain/hxxc.h create mode 100644 include/array_macros/domain/hxxf.h create mode 100644 include/array_macros/domain/jdxc.h create mode 100644 include/array_macros/domain/jdxf.h create mode 100644 include/array_macros/statistics/adv.h create mode 100644 include/array_macros/statistics/dif.h delete mode 100644 include/array_macros/statistics/uxt.h delete mode 100644 initial_condition/2d.py delete mode 100644 initial_condition/3d.py create mode 100644 initial_condition/main.py rename initial_condition/{exec.sh => main.sh} (73%) delete mode 100644 src/README.rst delete mode 100644 src/fluid/README.rst create mode 100644 src/fluid/compute_diffusivity.c rename src/fluid/{compute_potential/dft.c => compute_potential.c} (59%) delete mode 100644 src/fluid/compute_potential/dct.c delete mode 100644 src/fluid/compute_potential/internal.h delete mode 100644 src/fluid/compute_potential/main.c rename src/fluid/{correct_velocity => correct}/internal.h (100%) rename src/fluid/{correct_velocity => correct}/main.c (100%) create mode 100644 src/fluid/correct/ux.c create mode 100644 src/fluid/correct/uy.c rename src/fluid/{correct_velocity => correct}/uz.c (50%) delete mode 100644 src/fluid/correct_velocity/ux.c delete mode 100644 src/fluid/correct_velocity/uy.c delete mode 100644 src/fluid/integrate/couple_external_force.c delete mode 100644 src/fluid/integrate/predict_field.c delete mode 100644 src/fluid/integrate/t.c delete mode 100644 src/fluid/integrate/ux.c delete mode 100644 src/fluid/integrate/uy.c delete mode 100644 src/fluid/integrate/uz.c rename src/fluid/{integrate => predict}/internal.h (51%) rename src/fluid/{integrate/compute_rhs.c => predict/main.c} (69%) create mode 100644 src/fluid/predict/t.c create mode 100644 src/fluid/predict/ux.c create mode 100644 src/fluid/predict/uy.c create mode 100644 src/fluid/predict/uz.c delete mode 100644 src/logging/README.rst create mode 100644 src/logging/dissipated_squared_temperature.c create mode 100644 src/logging/dissipated_squared_velocity.c delete mode 100644 src/logging/divergence.c delete mode 100644 src/logging/energy.c create mode 100644 src/logging/heat_transfer.c create mode 100644 src/logging/injected_squared_velocity.c create mode 100644 src/logging/max_divergence.c delete mode 100644 src/logging/momentum.c delete mode 100644 src/logging/nusselt/README.rst delete mode 100644 src/logging/nusselt/heat_flux.c delete mode 100644 src/logging/nusselt/internal.h delete mode 100644 src/logging/nusselt/kinetic_energy_dissipation.c delete mode 100644 src/logging/nusselt/kinetic_energy_injection.c delete mode 100644 src/logging/nusselt/main.c delete mode 100644 src/logging/nusselt/reference.c delete mode 100644 src/logging/nusselt/thermal_energy_dissipation.c create mode 100644 src/logging/total_energy.c create mode 100644 src/logging/total_momentum.c diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9971a57b..90987fd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,6 @@ jobs: - name: Checkout repository uses: actions/checkout@main with: - repository: "NaokiHori/SimpleNSSolver" ref: ${{ matrix.dimension }}d submodules: "recursive" - name: Install dependencies @@ -39,7 +38,7 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@main with: - name: typical-cases-${{ matrix.dimension }}d + name: typical-${{ matrix.dimension }}d path: artifacts check-energy-conservations: @@ -53,7 +52,6 @@ jobs: - name: Checkout repository uses: actions/checkout@main with: - repository: "NaokiHori/SimpleNSSolver" ref: ${{ matrix.dimension }}d submodules: "recursive" - name: Install dependencies @@ -70,7 +68,7 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@main with: - name: check-energy-conservations-${{ matrix.dimension }}d + name: energy-${{ matrix.dimension }}d path: artifacts check-nusselt-agreements: @@ -85,7 +83,6 @@ jobs: - name: Checkout repository uses: actions/checkout@main with: - repository: "NaokiHori/SimpleNSSolver" ref: ${{ matrix.dimension }}d submodules: "recursive" - name: Install dependencies @@ -98,117 +95,40 @@ jobs: pip install numpy matplotlib - name: Pre-process, execute, and post-process run: | - bash docs/source/examples/nu/data/exec_${{ matrix.dimension }}d.sh ${{ matrix.prandtl }} + bash docs/source/examples/nusselt/data/exec_${{ matrix.dimension }}d.sh ${{ matrix.prandtl }} - name: Upload artifacts uses: actions/upload-artifact@main with: - name: check-nusselt-agreements-${{ matrix.prandtl }}-${{ matrix.dimension }}d + name: nusselt-${{ matrix.dimension }}d-${{ matrix.prandtl }} path: artifacts - check-gl-exec: - name: Check GL theory, execution - if: ${{ github.event.workflow_run.conclusion == 'success' }} + unify-and-push-artifacts: + name: Gather artifacts and push them to a branch + permissions: + contents: write runs-on: ubuntu-latest - strategy: - matrix: - rayleigh: ["1.0e+4", "3.1e+4", "1.0e+5", "3.1e+5", "1.0e+6", "3.1e+6", "1.0e+7", "3.1e+7", "1.0e+8"] + needs: [run-typical-cases, check-energy-conservations, check-nusselt-agreements] + env: + BRANCH_NAME: artifacts + DIRECTORY_NAME: artifacts steps: - name: Checkout repository uses: actions/checkout@main - with: - repository: "NaokiHori/SimpleNSSolver" - ref: "2d" - submodules: "recursive" - - name: Install dependencies - run: | - sudo apt-get -y update && \ - sudo apt-get -y install make libopenmpi-dev libfftw3-dev - - name: Install python dependencies for pre-processing - run: | - python -m pip install --upgrade pip - pip install numpy - - name: Pre-process, execute, and post-process - run: | - bash docs/source/examples/gl/data/exec.sh ${{ matrix.rayleigh }} - - name: Upload artifacts - uses: actions/upload-artifact@main - with: - name: gl-${{ matrix.rayleigh }} - path: artifacts - - check-gl-post: - name: Check GL theory, post-process - runs-on: ubuntu-latest - needs: check-gl-exec - steps: - - name: Checkout repository - uses: actions/checkout@main - with: - repository: "NaokiHori/SimpleNSSolver" - ref: "2d" - submodules: "recursive" - - name: Install python dependencies for post-processings - run: | - python -m pip install --upgrade pip - pip install numpy matplotlib - - name: Prepare place for dat files - run: | - mkdir data - name: Download artifacts uses: actions/download-artifact@main with: - name: gl-1.0e+4 - path: data - - name: Download artifacts - uses: actions/download-artifact@main - with: - name: gl-3.1e+4 - path: data - - name: Download artifacts - uses: actions/download-artifact@main - with: - name: gl-1.0e+5 - path: data - - name: Download artifacts - uses: actions/download-artifact@main - with: - name: gl-3.1e+5 - path: data - - name: Download artifacts - uses: actions/download-artifact@main - with: - name: gl-1.0e+6 - path: data - - name: Download artifacts - uses: actions/download-artifact@main - with: - name: gl-3.1e+6 - path: data - - name: Download artifacts - uses: actions/download-artifact@main - with: - name: gl-1.0e+7 - path: data - - name: Download artifacts - uses: actions/download-artifact@main - with: - name: gl-3.1e+7 - path: data - - name: Download artifacts - uses: actions/download-artifact@main - with: - name: gl-1.0e+8 - path: data - - name: Create figure from data + path: ${{ env.DIRECTORY_NAME }} + - name: Check artifacts run: | - mkdir artifacts - python \ - docs/source/examples/gl/data/process.py \ - data \ - artifacts/nu_ra.png - - name: Upload artifacts - uses: actions/upload-artifact@main - with: - name: gl - path: artifacts + ls -R ${{ env.DIRECTORY_NAME }} + - name: Push artifacts + run: | + set -x + set -e + git config --local user.email "36466440+NaokiHori@users.noreply.github.com" + git config --local user.name "NaokiHori" + git switch -c ${{ env.BRANCH_NAME }} + git add ${{ env.DIRECTORY_NAME }} + git commit -m "Update artifacts" -a || true + git push -f origin ${{ env.BRANCH_NAME }} diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index efcad33b..6e348774 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -3,65 +3,15 @@ name: Documentation on: workflow_run: - workflows: [CI] + workflows: [ExtractND] types: - completed + workflow_dispatch: jobs: - build-doc: - name: Build documentation - if: ${{ github.event.workflow_run.conclusion == 'success' }} - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@main - with: - repository: "NaokiHori/SimpleNSSolver" - ref: "main" - - name: Install python dependencies for post-processings - run: | - python -m pip install --upgrade pip - pip install requests numpy matplotlib - - name: Download artifacts - run: | - python \ - .github/workflows/documentation_download_artifact.py \ - ${{ secrets.GITHUB_TOKEN }} - - name: Draw figures - run: | - for fname in $(find docs/source -type f -name "draw_figure_*.py"); do - python ${fname}; - done - - name: Check dead links - run: | - docker run \ - --rm \ - --volume ${PWD}:/project \ - --workdir /project \ - sphinxdoc/sphinx:latest \ - sphinx-build -b linkcheck docs/source . - log=output.txt - if [ -e ${log} ]; then - wc -l ${log} - cat ${log} - fi - - name: Build documentation using Sphinx - run: | - docker run \ - --rm \ - --volume ${PWD}:/project \ - --workdir /project \ - sphinxdoc/sphinx:latest \ - sphinx-build -b html docs/source docs/build - - name: Upload HTML artifacts - uses: actions/upload-artifact@main - with: - name: DocHTML - path: docs/build - - deploy-doc: - name: Deploy documentation + build-and-deploy-doc: + name: Build and deploy documentation permissions: contents: read pages: write @@ -73,19 +23,25 @@ jobs: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest - needs: [build-doc] steps: - - name: Download HTML artifacts - uses: actions/download-artifact@main + - name: Checkout repository + uses: actions/checkout@main with: - name: DocHTML - path: artifacts + ref: ${{ github.ref_name }} + - name: Build documentation using Sphinx + run: | + docker run \ + --rm \ + --volume ${PWD}:/project \ + --workdir /project \ + sphinxdoc/sphinx:latest \ + sphinx-build docs/source docs/build - name: Setup GitHub Pages uses: actions/configure-pages@main - name: Upload HTML uses: actions/upload-pages-artifact@main with: - path: artifacts + path: docs/build - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@main diff --git a/.github/workflows/documentation_download_artifact.py b/.github/workflows/documentation_download_artifact.py deleted file mode 100644 index 2da4df84..00000000 --- a/.github/workflows/documentation_download_artifact.py +++ /dev/null @@ -1,145 +0,0 @@ -import sys -import datetime -import zipfile -import io -import requests - -def set_params(): - # list all all artifacts (name and destination) - artifacts = ( - { - "name": "typical-cases-2d", - "path": "docs/source/examples/typical/data/", - }, - { - "name": "typical-cases-3d", - "path": "docs/source/examples/typical/data/", - }, - { - "name": "check-energy-conservations-2d", - "path": "docs/source/examples/energy/data/", - }, - { - "name": "check-energy-conservations-3d", - "path": "docs/source/examples/energy/data/", - }, - { - "name": "check-nusselt-agreements-1.e-1-2d", - "path": "docs/source/examples/nu/data/", - }, - { - "name": "check-nusselt-agreements-1.e+0-2d", - "path": "docs/source/examples/nu/data/", - }, - { - "name": "check-nusselt-agreements-1.e+1-2d", - "path": "docs/source/examples/nu/data/", - }, - { - "name": "check-nusselt-agreements-1.e-1-3d", - "path": "docs/source/examples/nu/data/", - }, - { - "name": "check-nusselt-agreements-1.e+0-3d", - "path": "docs/source/examples/nu/data/", - }, - { - "name": "check-nusselt-agreements-1.e+1-3d", - "path": "docs/source/examples/nu/data/", - }, - { - "name": "gl", - "path": "docs/source/examples/gl/data/", - }, - ) - return artifacts - -def prepare_curl_header(token): - headers = { - "Accept": "application/vnd.github+json", - "Authorization": f"Bearer {token}", - "X-GitHub-Api-Version": "2022-11-28", - } - return headers - -def kernel_get(url, headers): - r = requests.get( - url=url, - headers=headers - ) - if not r.ok: - msg = f"request failed: {url}" - raise RuntimeError(msg) - return r - -def list_artifacts(token): - r = kernel_get( - url="https://api.github.com/repos/NaokiHori/SimpleNSSolver/actions/artifacts", - headers=prepare_curl_header(token) - ) - artifacts = r.json() - return artifacts - -def get_and_save_artifact(token, param, artifacts): - def convert_time(strtime): - # from string to datetime.datetime - # string: e.g. '2023-04-02T11:50:06Z' - keywords = dict() - # year - year = strtime[:4] - keywords["year"] = int(year) - strtime = strtime.removeprefix(f"{year}-") - # month - month = strtime[:2] - keywords["month"] = int(month) - strtime = strtime.removeprefix(f"{month}-") - # day - day = strtime[:2] - keywords["day"] = int(day) - strtime = strtime.removeprefix(f"{day}T") - # hour - hour = strtime[:2] - keywords["hour"] = int(hour) - strtime = strtime.removeprefix(f"{hour}:") - # minute - minute = strtime[:2] - keywords["minute"] = int(minute) - strtime = strtime.removeprefix(f"{minute}:") - # second - second = strtime[:2] - keywords["second"] = int(second) - return datetime.datetime(**keywords) - # initialise with N/A data - result = { - "url": "", - "time": datetime.datetime(year=datetime.MINYEAR, month=1, day=1) - } - # check all artifacts and take the latest one - for a in artifacts["artifacts"]: - if a["name"] == param["name"]: - time = a["created_at"] - time = convert_time(time) - if time > result["time"]: - result["url"] = a["archive_download_url"] - result["time"] = time - if None == result["url"]: - msg = "artifact '{}' is not found".format(param["name"]) - raise RuntimeError(msg) - # get and save to the desired place - r = kernel_get( - url=result["url"], - headers=prepare_curl_header(token) - ) - zipfile.ZipFile(io.BytesIO(r.content)).extractall(param["path"]) - -def main(token): - params = set_params() - artifacts = list_artifacts(token) - for param in params: - get_and_save_artifact(token, param, artifacts) - -if __name__ == "__main__": - argv = sys.argv - assert(2 == len(argv)) - main(argv[1]) - diff --git a/.github/workflows/extract_nd.yml b/.github/workflows/extract_nd.yml index 5fb2a6c0..e8f9a568 100644 --- a/.github/workflows/extract_nd.yml +++ b/.github/workflows/extract_nd.yml @@ -5,6 +5,10 @@ on: push: branches: - main + paths: + - src/** + - include/** + workflow_dispatch: jobs: @@ -45,11 +49,6 @@ jobs: git switch -c ${{ matrix.dimension }}d git config --local user.email "36466440+NaokiHori@users.noreply.github.com" git config --local user.name "NaokiHori" - # let IC generator of the given dimension as main.py - cd initial_condition - find . -type f -name "*d.py" | grep -v ${{ matrix.dimension }}d.py | xargs git rm - git mv ${{ matrix.dimension }}d.py main.py - cd .. # add, commit, and push git add Makefile git add src diff --git a/README.rst b/README.rst index 74871d57..6e6b9d71 100644 --- a/README.rst +++ b/README.rst @@ -29,7 +29,6 @@ Simple Navier-Stokes Solver .. shortcuts -.. _implementations: https://naokihori.github.io/SimpleNSSolver/implementation/main.html .. _theories: https://naokihori.github.io/SimpleNSSolver/equations/main.html .. _numerics: https://naokihori.github.io/SimpleNSSolver/numerical_method/main.html .. _examples: https://naokihori.github.io/SimpleNSSolver/examples/main.html @@ -41,20 +40,16 @@ Overview This library numerically solves the incompressible Navier-Stokes equations (coupled with a temperature field) in two- and three-dimensional Cartesian domains using the finite-difference method. -The main objective is to develop a library where the `implementations`_ and their background knowledge (`theories`_ and `numerics`_) are closely linked via a `documentation`_ and various `examples`_, so that users can understand *how* and *why* things are treated. +The main objective is to develop a library where the implementation and the background knowledge are closely linked via a `documentation`_ and various `examples`_, so that users can understand *how* and *why* things are treated. ******** Features ******** -* Strong link between `theories`_, `numerics`_, `implementations`_ and `examples`_ through a `documentation`_. -* Automatic code validation (`examples`_) using `GitHub Actions `_. -* `Numerically perfect mass and momentum balances `_. -* `Numerically perfect Nusselt number agreements `_. -* `Conservation of quadratic quantities (squared velocity and temperature) for inviscid fluids `_ and resulting extreme stability. -* `Pencil-based MPI parallelisation `_, from single process to O(10^4) processes. -* `Efficient FFT-based direct Poisson solver `_. -* `Explicit/implicit treatment of diffusive terms in all spatial directions `_ are easily switchable. +* An energy-consistent treatment of advective, pressure-gradient, and diffusive terms, correctly replicating properties of the conservation laws. +* `MPI parallelisation `_. +* Efficient FFT-based direct Poisson solver. +* Explicit / implicit treatments of diffusive terms in all spatial directions. Please refer to the `documentation`_ for details. @@ -105,9 +100,9 @@ Please consider to use `Windows Subsystem for Linux `_ using `SimpleNpyIO `_. When ``Python3`` with ``NumPy`` and ``Matplotlib`` is installed, one can easily visualise the flow fields: -.. image:: https://naokihori.github.io/SimpleNSSolver/_images/snapshot_2d.png - :width: 50% - -Also it is trivial to extract statistics. -For example, this plot shows the fluctuations of the flow quantities: - -.. image:: https://naokihori.github.io/SimpleNSSolver/_images/std_2d.png - :width: 50% - -or the mean heat flux: - -.. image:: https://naokihori.github.io/SimpleNSSolver/_images/nusselt_x_2d.png +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-2d/snapshot.png :width: 50% -By varying the parameter (in particular the Rayleigh number ``Ra``), one can observe a famous scaling law: +or statistics (e.g., mean advective and diffusive heat transfer): -.. image:: https://naokihori.github.io/SimpleNSSolver/_images/nu_ra.png +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-2d/nusselt_x.png :width: 50% Note that all the results shown here are automatically updated to maintain / improve the code quality, and all scripts to produce the above figures are available in the `examples`_. @@ -260,19 +243,19 @@ See the `documentation`_ for more details. ************* By default, this project simulates two-dimensional cases because they are easy to test and thus can be a good starting point. -When the three-dimensional counterpart is needed, checkout the ``3d`` branch. -Note that the ``main`` branch contains both dimensions, which is for the developers to maintain both cases at the same time. +When a three-dimensional version is needed, checkout ``3d`` branch. +Note that the ``main`` branch contains both dimensions, which is to maintain both cases at the same time (mainly for personal use). Please refer to the `examples`_, where several small-scale 3D simulations are attempted as a part of the continuous integration. -.. image:: https://naokihori.github.io/SimpleNSSolver/_images/snapshot_3d.png +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-3d/snapshot.png :width: 50% ************ Contributing ************ -Feel free to ask questions, to report bugs, or to suggest new features at `issues `_. +Feel free to ask questions, report bugs, suggest new features, polish documentation at `issues `_. **************** Acknowledgements diff --git a/SimpleDecomp b/SimpleDecomp index 1398accf..7e10fd0c 160000 --- a/SimpleDecomp +++ b/SimpleDecomp @@ -1 +1 @@ -Subproject commit 1398accf95ed0a8e5a16d431152c0d26749072e6 +Subproject commit 7e10fd0cbfc592e6ceaf04bf91107b27c1de1b0d diff --git a/SimpleNpyIO b/SimpleNpyIO index ce1078b9..03384133 160000 --- a/SimpleNpyIO +++ b/SimpleNpyIO @@ -1 +1 @@ -Subproject commit ce1078b9786dbfa8c4b698367e9a65bbd4eff234 +Subproject commit 03384133f7fefd10b702ae0d659f9c49311fd4d8 diff --git a/docs/source/conf_params/mathjax_params.py b/docs/source/conf_params/mathjax_params.py index 3cc8c12e..a8373b76 100644 --- a/docs/source/conf_params/mathjax_params.py +++ b/docs/source/conf_params/mathjax_params.py @@ -2,15 +2,38 @@ macros = dict() +# new symbols +macros["gcs"] = ["{\\xi^{#1}}", 1] +macros["vel"] = ["{u_{#1}}", 1] +macros["gvel"] = ["{u^{\\gcs{#1}}}", 1] +macros["pder"] = ["{\\frac{\\partial #1}{\\partial #2}}", 2] +macros["sfact"] = ["{h_{\\gcs{#1}}}", 1] +macros["basis"] = ["{\\vec{e}_{#1}}", 1] +macros["gbasis"] = ["{\\vec{e}_{\\gcs{#1}}}", 1] +macros["ave"] = ["{\\overline{#1}^{#2}}", 2] +macros["dif"] = ["{\\delta_{#2} {#1}}", 2] +macros["ngp"] = ["{N_{#1}}", 1] +macros["sumxf"] = "{\\sum_{i = \\frac{1}{2}}^{\\ngp{1} + \\frac{1}{2}}}" +macros["sumxc"] = "{\\sum_{i = 1}^{\\ngp{1}}}" +macros["sumyf"] = "{\\sum_{j = \\frac{1}{2}}^{\\ngp{2} - \\frac{1}{2}}}" +macros["sumyc"] = "{\\sum_{j = 1}^{\\ngp{2}}}" +macros["sumzf"] = "{\\sum_{k = \\frac{1}{2}}^{\\ngp{3} - \\frac{1}{2}}}" +macros["sumzc"] = "{\\sum_{k = 1}^{\\ngp{3}}}" +macros["heattransfer"] = "{\\mathcal{Q}}" + +macros["cmidx"] = ["{#1 - \\frac{1}{2}}", 1] +macros["ccidx"] = ["{#1}", 1] +macros["cpidx"] = ["{#1 + \\frac{1}{2}}", 1] + # general coordinates macros["gx"] = "{\\xi}" macros["gy"] = "{\\eta}" macros["gz"] = "{\\zeta}" # velocity -macros["ux"] = "{u_x}" -macros["uy"] = "{u_y}" -macros["uz"] = "{u_z}" +macros["ux"] = "{\\vel{x}}" +macros["uy"] = "{\\vel{y}}" +macros["uz"] = "{\\vel{z}}" # cell centers macros["pimm"] = "i-1 " @@ -94,12 +117,133 @@ # value at macros["vat"] = ["{\\left. {#1} \\right|_{#2}}", 2] -# average -macros["ave"] = ["{\\left\\langle {#1} \\right\\rangle_{#2}}", 2] - # interpolations, arithmetic, volume, unknown macros["dintrpa"] = ["{\\overline {#1}^{\\left( A,#2 \\right)}}", 2] macros["dintrpv"] = ["{\\overline {#1}^{\\left( V,#2 \\right)}}", 2] macros["dintrpu"] = ["{\\overline {#1}^{\\left( U,#2 \\right)}}", 2] +macros["ddiv"] = [ + "\\frac{1}{J}" + "\\dif{}{\\gcs{#1}}" + "\\left(" + " \\frac{J}{\\sfact{#1}}" + " \\vel{#1}" + "\\right)" + , 1 +] + +macros["dmomadv"] = [ + "\\frac{1}{J}" + "\\ave{" + " \\ave{" + " \\frac{J}{\\sfact{#2}}" + " \\vel{#2}" + " }{\\gcs{#1}}" + " \\dif{\\vel{#1}}{\\gcs{#2}}" + "}{\\gcs{#2}}" + , 2 +] +macros["dmompre"] = [ + "\\frac{1}{\\sfact{#1}}" + "\\dif{p}{\\gcs{#1}}" + , 1 +] +macros["dmomdif"] = [ + "\\frac{\\sqrt{Pr}}{\\sqrt{Ra}}" + "\\frac{1}{J}" + "\\dif{}{\\gcs{#1}}" + "\\left(" + " \\frac{J}{\\sfact{#1}}" + " \\frac{1}{\\sfact{#1}}" + " \\dif{\\vel{#2}}{\\gcs{#1}}" + "\\right)" + , 2 +] +macros["dmombuo"] = "{\\ave{T}{\\gcs{1}}}" + +macros["dtempadv"] = [ + "\\frac{1}{J}" + "\\ave{" + " \\frac{J}{\\sfact{#1}}" + " \\vel{#1}" + " \\dif{T}{\\gcs{#1}}" + "}{\\gcs{#1}}" + , 1 +] +macros["dtempdif"] = [ + "\\frac{1}{\\sqrt{Pr} \\sqrt{Ra}}" + "\\frac{1}{J}" + "\\dif{}{\\gcs{#1}}" + "\\left(" + " \\frac{J}{\\sfact{#1}}" + " \\frac{1}{\\sfact{#1}}" + " \\dif{T}{\\gcs{#1}}" + "\\right)" + , 1 +] + +macros["dkdis"] = [ + "\\frac{\\sqrt{Pr}}{\\sqrt{Ra}}" + "J" + "\\left(" + " \\frac{1}{\\sfact{#1}}" + " \\dif{\\vel{#2}}{\\gcs{#1}}" + "\\right)^2" + , 2 +] + +macros["dhdis"] = [ + "\\frac{1}{\\sqrt{Pr} \\sqrt{Ra}}" + "J" + "\\left(" + " \\frac{1}{\\sfact{#1}}" + " \\dif{T}{\\gcs{#1}}" + "\\right)^2" + , 1 +] + +macros["dhinjall"] = [ + "-" + "\\sumzc" + "\\sumyc" + "\\frac{1}{\\sqrt{Pr} \\sqrt{Ra}}" + "\\vat{" + " \\left(" + " \\frac{J}{\\sfact{1}}" + " T" + " \\frac{1}{\\sfact{1}}" + " \\dif{T}{\\gcs{1}}" + " \\right)" + "}{\\frac{1}{2}}" + "+" + "\\sumzc" + "\\sumyc" + "\\frac{1}{\\sqrt{Pr} \\sqrt{Ra}}" + "\\vat{" + " \\left(" + " \\frac{J}{\\sfact{1}}" + " T" + " \\frac{1}{\\sfact{1}}" + " \\dif{T}{\\gcs{1}}" + " \\right)" + "}{\\ngp{1} + \\frac{1}{2}}" +] + +macros["dhdisall"] = [ + "\\sumzc" + "\\sumyc" + "\\sumxf" + "\\dhdis{1}" + "+" + "\\sumzc" + "\\sumyf" + "\\sumxc" + "\\dhdis{2}" + "+" + "\\sumzf" + "\\sumyc" + "\\sumxc" + "\\dhdis{3}" +] + mathjax3_config = {"TeX": {"Macros": macros}} diff --git a/docs/source/equations/eq/internal_energy.rst b/docs/source/equations/eq/internal_energy.rst deleted file mode 100644 index 7c6c4991..00000000 --- a/docs/source/equations/eq/internal_energy.rst +++ /dev/null @@ -1,142 +0,0 @@ -################################### -Derivation: internal energy balance -################################### - -.. include:: tilde_note.rst - -*************** -Internal energy -*************** - -The internal energy of the liquid in a control volume leads - -.. math:: - - \int_{V} \rho c T dV \,\, \left[ M L^2 T^{-2} \right], - -where :math:`c` is the specific heat capacity of the liquid. - -The balance of this quantity is written as - -.. math:: - - \der{}{t} \int_{V} \rho c T dV - = - - - \int_{V} \der{u_i \rho c T}{x_i} dV - - - \int_{\partial V} q_i n_i dS, \,\, \left[ M L^2 T^{-3} \right] - -where :math:`q_i` is the heat flux given by - -.. math:: - - q_i - = - - - k - \der{T}{x_i} - -using the `Fourier's law `_, where :math:`k` is the thermal conductivity. -Also, using the Gauss theorem, the diffusive term leads to - -.. math:: - - \int_{V} \der{}{x_i} \left( k \der{T}{x_i} \right) dV. - -Assuming that the physical properties are constant, I obtain the internal energy balance: - -.. math:: - - \der{T}{t} - + - u_i \der{T}{x_i} - = - \kappa \der{}{x_i} \der{T}{x_i}, - -where :math:`\kappa` is the thermal diffusivity defined as :math:`k / \rho / c`. - -******************* -Squared temperature -******************* - -I consider to multiply the equation of the internal energy by :math:`T`. - -* Temporal derivative - - .. math:: - - T \der{T}{t} - = - \der{T^2}{t} - - - \der{T}{t} T, - - giving - - .. math:: - - T \der{T}{t} - = - \frac{1}{2} \der{T^2}{t}. - -* Advective terms - - .. math:: - - T u_i \der{T}{x_i} - = - u_i \der{T^2}{x_i} - - - u_i \der{T}{x_i} T, - - giving - - .. math:: - - T u_i \der{T}{x_i} - = - \frac{1}{2} u_i \der{T^2}{x_i}. - -* Diffusive terms - - .. math:: - - T \kappa \der{}{x_i} \der{T}{x_i} - = - \kappa \der{}{x_i} \left( T \der{T}{x_i} \right) - - - \kappa \der{T}{x_i} \der{T}{x_i}. - - .. note:: - - The first term in the right-hand side yields - - .. math:: - - \kappa \der{}{x_i} \left\{ \der{}{x_i} \left( T^2 \right) \right\} - - - \kappa \der{}{x_i} \left( \der{T}{x_i} T \right), - - and thus I notice - - .. math:: - - \kappa \der{}{x_i} \left\{ \der{}{x_i} \left( \frac{1}{2} T^2 \right) \right\}, - - which describes the diffusive effect of the squared temperature. - -Thus, I have - -.. math:: - - \der{h}{t} - + - u_i \der{h}{x_i} - = - \kappa \der{}{x_i} \left( T \der{T}{x_i} \right) - - - \kappa \der{T}{x_i} \der{T}{x_i}, - -where :math:`h \equiv \frac{1}{2} T^2`. - diff --git a/docs/source/equations/eq/mass.rst b/docs/source/equations/eq/mass.rst deleted file mode 100644 index 10392f0b..00000000 --- a/docs/source/equations/eq/mass.rst +++ /dev/null @@ -1,73 +0,0 @@ -######################## -Derivation: mass balance -######################## - -.. include:: tilde_note.rst - -I consider a closed control volume :math:`V`, whose surface is denoted by :math:`\partial V` or :math:`S` and its outward normal is given by :math:`n_i`. - -By assuming there is no mass source nor sink, the mass balance inside this control volume leads to - -.. math:: - - \der{}{t} \int_{V} \rho dV - + - \int_{\partial V} \rho u_i n_i dS - = - 0, - -i.e. the increase or decrease of the mass is determined by the mass flux. -Here, :math:`\rho` and :math:`u_i` denote the density and the velocity of the liquid, which are functions of space :math:`x_i` and time :math:`t`. - -Assuming the temporal derivative and the spatial integral are commutative, I have - -.. math:: - - \int_{V} \der{\rho}{t} dV - + - \int_{\partial V} \rho u_i n_i dS - = - 0, - -which yields - -.. math:: - - \int_{V} \left( \der{\rho}{t} - + - \der{\rho u_i}{x_i} \right) dV - = - 0 - -using the Gauss theorem. - -Since this relation should be satisfied everywhere (for any control volume), I request the integrand to be zero: - -.. math:: - - \der{\rho}{t} - + - \der{\rho u_i}{x_i} - = - 0. - -In this project, I assume that the liquid density is constant across the domain, i.e. - -.. math:: - - \der{\rho}{t} - = - 0, - - \der{\rho}{x_i} - = - 0_i. - -Using these relations, I am left with the incompressibility constraint - -.. math:: - - \der{u_i}{x_i} - = - 0. - diff --git a/docs/source/equations/eq/momentum.rst b/docs/source/equations/eq/momentum.rst deleted file mode 100644 index 42a513bf..00000000 --- a/docs/source/equations/eq/momentum.rst +++ /dev/null @@ -1,217 +0,0 @@ -############################ -Derivation: momentum balance -############################ - -.. include:: tilde_note.rst - -******** -Momentum -******** - -The momentum of the liquid in a control volume leads - -.. math:: - - \int_{V} \rho u_i dV \,\, \left[ M L T^{-1} \right]. - -The balance of this quantity is written as - -.. math:: - - \der{}{t} \int_{V} \rho u_i dV - = - - - \int_{V} \der{u_j \rho u_i}{x_j} dV - + - \int_{\partial V} \sigma_{ij} n_j dS - + - \int_{V} \rho g_i dV, \,\, \left[ M L T^{-2} \right] - -indicating that changes in the momentum are caused by - -* the convection, - -* the normal and the tangential surface forces, - -* the body force. - -Using the divergence theorem, the surface force leads to - -.. math:: - - \int_{V} \der{\sigma_{ij}}{x_j} dV, - -and thus - -.. math:: - - \der{\rho u_i}{t} - + - \der{u_j \rho u_i}{x_j} - = - \der{\sigma_{ij}}{x_j} - + - \rho g_i, - -where I assume the temporal derivative and the spatial integrals are commutative, and request the integrand to be zero. - -Here, :math:`\sigma_{ij}` is the Cauchy stress tensor, which is regarded as - -.. math:: - - \sigma_{ij} - = - - - p \delta_{ij} - + - 2 \mu e_{ij}, - -where the incompressibility - -.. math:: - - \der{u_i}{x_i} - = - 0 - -is assumed and :math:`\mu` is the dynamic viscosity of the liquid. -Also, :math:`p` is the reduced pressure and :math:`e_{ij}` is the symmetric part of the strain-rate tensor: - -.. math:: - - e_{ij} - = - \frac{1}{2} \left( \der{u_i}{x_j} + \der{u_j}{x_i} \right) - -to close the equation. - -Note that, using the incompressibility, I can write the advective term as - -.. math:: - - u_j \der{\rho u_i}{x_j}. - -Finally, assuming that the physical properties are constant, I obtain - -.. math:: - - \der{u_i}{t} - + - u_j \der{u_i}{x_j} - = - - - \frac{1}{\rho} \der{p}{x_i} - + - \nu \der{}{x_j} \der{u_i}{x_j} - + - g_i, - -where :math:`\nu` is the kinematic viscosity defined as :math:`\mu / \rho`. - -.. seealso:: - - * `Navier-Stokes equations `_ - * `Newtonian fluid `_ - -**************** -Squared velocity -**************** - -I consider to take the inner product of the velocity vector :math:`u_i` and the momentum balance derived above. - -* Temporal derivative - - .. math:: - - u_i \der{u_i}{t} - = - \der{u_i u_i}{t} - - - \der{u_i}{t} u_i, - - giving - - .. math:: - - u_i \der{u_i}{t} - = - \frac{1}{2} \der{u_i u_i}{t}. - -* Advective terms - - .. math:: - - u_i u_j \der{u_i}{x_j} - = - u_j \der{u_i u_i}{x_j} - - - u_j \der{u_i}{t} u_i, - - giving - - .. math:: - - u_i u_j \der{u_i}{x_j} - = - \frac{1}{2} u_j \der{u_i u_i}{x_j}. - -* Pressure-gradient term - - .. math:: - - - - u_i \frac{1}{\rho} \der{p}{x_i}. - -* Diffusive terms - - .. math:: - - u_i \nu \der{}{x_j} \der{u_i}{x_j} - = - \nu \der{}{x_j} \left( u_i \der{u_i}{x_j} \right) - - - \nu \der{u_i}{x_j} \der{u_i}{x_j}. - - .. note:: - - The first term in the right-hand side yields - - .. math:: - - \nu \der{}{x_j} \left\{ \der{}{x_j} \left( u_i u_i \right) \right\} - - - \nu \der{}{x_j} \left( \der{u_i}{x_j} u_i \right), - - and thus I notice - - .. math:: - - \nu \der{}{x_j} \left\{ \der{}{x_j} \left( \frac{1}{2} u_i u_i \right) \right\}, - - which describes the diffusive effect of the squared velocity. - -* Body force term - - .. math:: - - u_i g_i. - -Thus, I have - -.. math:: - - \der{k}{t} - + - u_i \der{k}{x_i} - = - - - u_i \frac{1}{\rho} \der{p}{x_i} - + - \nu \der{}{x_j} \left( u_i \der{u_i}{x_j} \right) - - - \nu \der{u_i}{x_j} \der{u_i}{x_j} - + - u_i g_i, - -where :math:`k \equiv \frac{1}{2} u_i u_i`. - diff --git a/docs/source/equations/eq/tilde_note.rst b/docs/source/equations/eq/tilde_note.rst deleted file mode 100644 index d818981b..00000000 --- a/docs/source/equations/eq/tilde_note.rst +++ /dev/null @@ -1,4 +0,0 @@ -.. note:: - - In this part, :math:`\tilde{q}` is dropped for notational convenience. - diff --git a/docs/source/equations/main.rst b/docs/source/equations/main.rst index 6acac52d..8e7190fa 100644 --- a/docs/source/equations/main.rst +++ b/docs/source/equations/main.rst @@ -5,238 +5,548 @@ Equations ######### -******************* -Governing equations -******************* - -================ +**************** Dimensional form -================ +**************** -In this project, I consider the conservation laws of the mass, the momentum, and the internal energy, which are governed by +In this project, we consider the conservation laws of the mass, the momentum, and the internal energy, which are governed by .. math:: - \der{\tilde{u}_i}{\tilde{x}_i} - = - 0, + \pder{\tilde{u}_i}{\tilde{x}_i} + = + 0, .. math:: - \der{\tilde{u}_i}{\tilde{t}} - + - \tilde{u}_j \der{\tilde{u}_i}{\tilde{x}_j} - = - - - \frac{1}{\tilde{\rho}} \der{\tilde{p}}{\tilde{x}_i} - + - \tilde{\nu} \der{}{\tilde{x}_j} \der{\tilde{u}_i}{\tilde{x}_j} - + - \tilde{g}_i, - -and + \pder{\tilde{u}_i}{\tilde{t}} + + + \tilde{u}_j \pder{\tilde{u}_i}{\tilde{x}_j} + = + - + \frac{1}{\tilde{\rho}} \pder{\tilde{p}}{\tilde{x}_i} + + + \tilde{\nu} \pder{}{\tilde{x}_j} \pder{\tilde{u}_i}{\tilde{x}_j} + + + \tilde{g}_i, .. math:: - \der{\tilde{T}}{\tilde{t}} - + - \tilde{u}_i \der{\tilde{T}}{\tilde{x}_i} - = - \tilde{\kappa} \der{}{\tilde{x}_i} \der{\tilde{T}}{\tilde{x}_i}, + \pder{\tilde{T}}{\tilde{t}} + + + \tilde{u}_j \pder{\tilde{T}}{\tilde{x}_j} + = + \tilde{\kappa} \pder{}{\tilde{x}_j} \pder{\tilde{T}}{\tilde{x}_j}, -respectively. +respectively, where the summation rule is applied. .. note:: - Physical properties (e.g. the density :math:`\tilde{\rho}`, the kinematic viscosity :math:`\tilde{\nu}`, the thermal diffusivity :math:`\tilde{\kappa}`) are assumed to be constant in time and in space. + * :math:`\tilde{q}` implies that the quantity :math:`q` is dimensional (i.e., before normalised). - :math:`\tilde{q}` implies that the quantity :math:`q` is dimensional (i.e. before normalised). + * We assume the physical properties (e.g., the density :math:`\tilde{\rho}`, the dynamic and kinematic viscosities :math:`\tilde{\mu}, \tilde{\nu}`, the thermal diffusivity :math:`\tilde{\kappa}`) to be constant. + +******************** +Non-dimensional form +******************** -Also, by combining the mass balance and the momentum balance, the equation of the squared velocity +In this project, we focus on `Rayleigh-Bénard convection `_, which is an excellent model problem to shed light on the conservation properties. +By adopting `Boussinesq approximation `_ and normalise the equations with proper scales, we obtain the following non-dimensional equations which play the central role in this project. + +.. _eq_mass: .. math:: - \der{\tilde{k}}{\tilde{t}} - + - \tilde{u}_i \der{\tilde{k}}{\tilde{x}_i} - = - - - \tilde{u}_i \frac{1}{\tilde{\rho}} \der{\tilde{p}}{\tilde{x}_i} - + - \tilde{\nu} \der{}{\tilde{x}_j} \left( \tilde{u}_i \der{\tilde{u}_i}{\tilde{x}_j} \right) - - - \tilde{\nu} \der{\tilde{u}_i}{\tilde{x}_j} \der{\tilde{u}_i}{\tilde{x}_j} - + - \tilde{u}_i \tilde{g}_i + \pder{u_i}{x_i} + = + 0. -comes out, where +.. _eq_momentum: .. math:: - \tilde{k} - \equiv - \frac{1}{2} - \tilde{u}_i \tilde{u}_i. + \pder{u_i}{t} + + + u_j \pder{u_i}{x_j} + = + - + \pder{p}{x_i} + + + \frac{\sqrt{Pr}}{\sqrt{Ra}} \pder{}{x_j} \pder{u_i}{x_j} + + + T \delta_{ix}. -Similarly I obtain the equation of the squared temperature +.. _eq_temperature: .. math:: - \der{\tilde{h}}{\tilde{t}} - + - \tilde{u}_i \der{\tilde{h}}{\tilde{x}_i} - = - \tilde{\kappa} \der{}{\tilde{x}_i} \left( \tilde{T} \der{\tilde{T}}{\tilde{x}_i} \right) - - - \tilde{\kappa} \der{\tilde{T}}{\tilde{x}_i} \der{\tilde{T}}{\tilde{x}_i} + \pder{T}{t} + + + u_j \pder{T}{x_j} + = + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \pder{}{x_j} \pder{T}{x_j}. -using the internal energy balance, where +Here Rayleigh number :math:`Ra` and Prandtl number :math:`Pr` are dimensionless parameters given by .. math:: - \tilde{h} - \equiv - \frac{1}{2} - \tilde{T} \tilde{T}. + Ra & = \frac{\tilde{\beta} \tilde{g} {\tilde{l_x}}^3 \left( \Delta \tilde{T} \right)}{\tilde{\nu} \tilde{\kappa}}, \\ + Pr & = \frac{\tilde{\nu}}{\tilde{\kappa}}, -These additional equations also play important roles in the following discussion. +where :math:`\tilde{\beta}`, :math:`\tilde{g}`, :math:`\tilde{l_x}`, and :math:`\Delta \tilde{T} = \tilde{T}_{H} - \tilde{T}_{L}` are the thermal expansion coefficient :math:`\left[ K^{-1} \right]`, the gravitational acceleration :math:`\left[ L T^{-2} \right]`, the distance between the walls :math:`\left[ L \right]`, and the temperature difference :math:`\left[ K \right]`, respectively. -Although the derivations can be found everywhere, they are briefly discussed here for the sake of completeness. +Also, by taking the inner product of the momentum balance and the velocity vector, a relation with respect to the squared velocity is obtained: -.. toctree:: - :maxdepth: 1 +.. _eq_squared_velocity: - eq/mass - eq/momentum - eq/internal_energy +.. math:: -==================== -Non-dimensional form -==================== + \pder{k}{t} + + + u_j \pder{k}{x_j} + = + - + u_j \pder{p}{x_j} + + + \frac{\sqrt{Pr}}{\sqrt{Ra}} \pder{}{x_j} \left( u_i \pder{u_i}{x_j} \right) + - + \frac{\sqrt{Pr}}{\sqrt{Ra}} \pder{u_i}{x_j} \pder{u_i}{x_j} + + + u_i T \delta_{ix}, + +where -In this project, I focus on `Rayleigh-Bénard convection `_, which is an excellent model problem to shed light on the conservation properties. -By adopting `Boussinesq approximation `_ and normalise the equations with proper scales, I obtain the following non-dimensional equations which play the central role in this project. +.. math:: -.. _eq_mass: + \tilde{k} + \equiv + \frac{1}{2} + \tilde{u}_i \tilde{u}_i. -* Mass balance +Similarly, by multiplying the temperature with the internal energy balance, we obtain the relation with respect to the squared temperature: - .. math:: +.. _eq_squared_temperature: - \der{u_i}{x_i} - = - 0. +.. math:: -.. _eq_momentum: + \pder{h}{t} + + + u_j \pder{h}{x_j} + = + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \pder{}{x_j} \left( T \pder{T}{x_j} \right) + - + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \pder{T}{x_j} \pder{T}{x_j}, -* Momentum balance +where - .. math:: +.. math:: - \der{u_i}{t} - + - u_j \der{u_i}{x_j} - = - - - \der{p}{x_i} - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \der{}{x_j} \der{u_i}{x_j} - + - T \delta_{ix}. + \tilde{h} + \equiv + \frac{1}{2} + \tilde{T} \tilde{T}. -.. _eq_temperature: +.. image:: image/schematic.png + :align: center + :width: 400 -* Internal energy balance +Periodic boundary conditions are imposed in the homogeneous directions :math:`y` and :math:`z`. +The boundary conditions in the :math:`x` (wall-normal) direction are listed here: - .. math:: +* :math:`\ux = 0`: Dirichlet condition, impermeable walls. - \der{T}{t} - + - u_i \der{T}{x_i} - = - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{}{x_i} \der{T}{x_i}. +* :math:`\uy = \uz = 0`: Dirichlet condition, no-slip and stationary walls. -.. _eq_squared_velocity: +* :math:`\partial p / \partial x = 0`: Neumann condition. -* Equation of the squared velocity +* :math:`\vat{T}{x = 0}, \vat{T}{x = l_x \equiv 1}`: Dirichlet condition, fixed temperature satisfying :math:`\vat{T}{x = 0} - \vat{T}{x = 1} = 1`. - .. math:: +.. note:: - \der{k}{t} + u_i \der{k}{x_i} - = - - - u_i \der{p}{x_i} - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \der{}{x_j} \left( u_i \der{u_i}{x_j} \right) - - - \frac{\sqrt{Pr}}{\sqrt{Ra}} \der{u_i}{x_j} \der{u_i}{x_j} - + - u_i T \delta_{ix}. + * Without loss of generality, :math:`\tilde{\beta}`, :math:`\tilde{g}`, :math:`\tilde{l_x}`, and :math:`\Delta \tilde{T}` are fixed to unity. -.. _eq_squared_temperature: + * The reference velocity scale :math:`\tilde{U} \left[ L T^{-1} \right]` is defined by the other parameters :math:`\tilde{U} = \sqrt{\tilde{\beta} \tilde{g} \tilde{l_x} \left( \Delta \tilde{T} \right)} \left( = 1 \right)`, which is often called as the free-fall velocity. -* Equation of the squared temperature +.. _continuous_quadratic_quantities: - .. math:: +******************** +Quadratic Quantities +******************** - \der{h}{t} - + - u_i \der{h}{x_i} - = - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{}{x_i} \left( T \der{T}{x_i} \right) - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{T}{x_i} \der{T}{x_i}. +We investigate the properties of the mentioned relations with respect to the quadratic quantities :math:`k` and :math:`h`. +In particular, we focus on how the net amount of them: -Here Rayleigh number :math:`Ra` and Prandtl number :math:`Pr` are dimensionless parameters given by +.. math:: + + & + \int + \int + \int + k + dx + dy + dz, + + & + \int + \int + \int + h + dx + dy + dz, + +behave, which follow .. math:: - Ra & = \frac{\tilde{\beta} \tilde{g} {\tilde{l_x}}^3 \left( \Delta \tilde{T} \right)}{\tilde{\nu} \tilde{\kappa}}, \\ - Pr & = \frac{\tilde{\nu}}{\tilde{\kappa}}, + & + \int + \int + \int + \pder{k}{t} + dx + dy + dz + = + \int + \int + \int + \left\{ + - + u_j \pder{k}{x_j} + - + u_i \pder{p}{x_i} + + + \frac{\sqrt{Pr}}{\sqrt{Ra}} \pder{}{x_j} \left( u_i \pder{u_i}{x_j} \right) + - + \frac{\sqrt{Pr}}{\sqrt{Ra}} \pder{u_i}{x_j} \pder{u_i}{x_j} + + + u_i T \delta_{ix} + \right\} + dx + dy + dz, + + & + \int + \int + \int + \pder{h}{t} + dx + dy + dz + = + \int + \int + \int + \left\{ + - + u_j \pder{h}{x_j} + + + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \pder{}{x_j} \left( T \pder{T}{x_j} \right) + - + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \pder{T}{x_j} \pder{T}{x_j} + \right\} + dx + dy + dz, + +giving + +.. _quadratic_quantity_balance: -where :math:`\tilde{\beta}`, :math:`\tilde{g}`, :math:`\tilde{l_x}`, and :math:`\Delta \tilde{T} = \tilde{T}_{H} - \tilde{T}_{L}` are the thermal expansion coefficient :math:`\left[ K^{-1} \right]`, the gravitational acceleration :math:`\left[ L T^{-2} \right]`, the distance between the walls :math:`\left[ L \right]`, and the temperature difference :math:`\left[ K \right]`, respectively. +.. math:: -.. image:: image/schematic.png - :align: center - :width: 400 + & + \int + \int + \int + \pder{k}{t} + dx + dy + dz + = + \int + \int + \int + u_x T + dx + dy + dz + - + \int + \int + \int + \frac{\sqrt{Pr}}{\sqrt{Ra}} \pder{u_i}{x_j} \pder{u_i}{x_j} + dx + dy + dz, + + & + \int + \int + \int + \pder{h}{t} + dx + dy + dz + = + \vat{ + T + }{x = 1} + \int + \int + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \vat{ + \pder{T}{x} + }{x = 1} + dy + dz + - + \vat{ + T + }{x = 0} + \int + \int + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \vat{ + \pder{T}{x} + }{x = 0} + dy + dz + - + \int + \int + \int + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \pder{T}{x_j} \pder{T}{x_j} + dx + dy + dz. + +In this project, we aim at faithfully replicating these relations from a numerical standpoint. + +Derivation is given below, focusing on the individual components. + +=============== +Advective terms +=============== + +We have -Periodic boundary conditions are imposed in the homogeneous directions :math:`y` and :math:`z`. -The boundary conditions in the :math:`x` (wall-normal) direction are listed here: +.. math:: -* :math:`\ux = 0`: Dirichlet condition, impermeable walls. + - + \int + \int + \int + u_j \pder{k}{x_j} + dx + dy + dz + = + - + \int + \int + \int + \left( + \pder{u_j k}{x_j} + - + k + \pder{u_j}{x_j} + \right) + dx + dy + dz, + +which is, due to the incompressibility: -* :math:`\uy = \uz = 0`: Dirichlet condition, no-slip and stationary walls. +.. math:: -* :math:`\partial p / \partial x = 0`: Neumann condition. + - + \int + \int + \int + \pder{u_j k}{x_j} + dx + dy + dz + = + - + \int + \int + \vat{ + \left( + u_x k + \right) + }{x = 1} + dy + dz + + + \int + \int + \vat{ + \left( + u_x k + \right) + }{x = 0} + dy + dz, + +where the divergence theorem and the periodic boundary conditions are utilized. +Since the walls are impermeable, :math:`u_x \equiv 0` on the walls and thus this leads to 0, indicating that the advective terms do not affect the total amount of :math:`k`. + +Note that exactly the same statement holds for the advective terms of :math:`h`. + +======================= +Pressure-gradient terms +======================= + +We consider -* :math:`\vat{T}{x = 0}, \vat{T}{x = l_x \equiv 1}`: Dirichlet condition, fixed temperature satisfying :math:`\vat{T}{x = 0} - \vat{T}{x = 1} = 1`. +.. math:: -.. note:: + - + \int + \int + \int + u_i \pder{p}{x_i} + dx + dy + dz. + +By following the identical algebra we adopted to investigate the advective terms, we find that the pressure-gradient terms do not affect the net amount of :math:`k` either. + +============================ +Diffusive terms - conduction +============================ - * Without loss of generality, I can fix :math:`\tilde{\beta}`, :math:`\tilde{g}`, :math:`\tilde{l_x}`, and :math:`\Delta \tilde{T}` to unity, which is assumed in this project. +We have - * The reference velocity scale :math:`\tilde{U} \left[ L T^{-1} \right]` is defined by the other parameters :math:`\tilde{U} = \sqrt{\tilde{\beta} \tilde{g} \tilde{l_x} \left( \Delta \tilde{T} \right)} \left( = 1 \right)`, which is often called as the free-fall velocity. +.. math:: + + \int + \int + \int + \frac{\sqrt{Pr}}{\sqrt{Ra}} + \pder{}{x_j} + \left( + u_i + \pder{u_i}{x_j} + \right) + dx + dy + dz. + +By utilizing the divergence theorem, this leads to + +.. math:: + + \int + \int + \frac{\sqrt{Pr}}{\sqrt{Ra}} + \vat{ + \left( + u_i + \pder{u_i}{x} + \right) + }{x = 1} + dy + dz + - + \int + \int + \frac{\sqrt{Pr}}{\sqrt{Ra}} + \vat{ + \left( + u_i + \pder{u_i}{x} + \right) + }{x = 0} + dy + dz, + +where periodic boundary conditions are assumed in the :math:`y` and :math:`z` directions. +Since we assume that the walls are impermeable and fixed (i.e., :math:`u_i \equiv 0_i` on the walls), the integrands are all zero for all directions, indicating that the conductive terms do not alter the total amount of :math:`k`. + +Regarding the conductive terms with respect to :math:`h`, we also have an analogous relation: + +.. math:: + + \int + \int + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \vat{ + \left( + T + \pder{T}{x} + \right) + }{x = 1} + dy + dz + - + \int + \int + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \vat{ + \left( + T + \pder{T}{x} + \right) + }{x = 0} + dy + dz. + +Since they are non-zero in general, we find that the conduction plays a role in the budget of :math:`h`. + +============================= +Diffusive terms - dissipation +============================= + +We have - * Although it is numerically trivial to let the walls move in the homogeneous directions, the following discussion assumes the walls are at rest. - In particular, I need to modify :ref:`the Nusselt number based on the kinetic energy dissipation ` if the walls move. +.. math:: + + - + \int + \int + \int + \frac{\sqrt{Pr}}{\sqrt{Ra}} \pder{u_i}{x_j} \pder{u_i}{x_j} + dx + dy + dz + +and -************** -Nusselt number -************** +.. math:: + + - + \int + \int + \int + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \pder{T}{x_j} \pder{T}{x_j} + dx + dy + dz, -One of the most important features of the Rayleigh-Bénard convections is the theoretical relationships of the Nusselt number :math:`Nu`, which measures how much the heat transfer between the two walls is enhanced by the convective effects. -In this part, I mathematically define the heat flux and the Nusselt number, which is followed by the derivations of several :math:`Nu` relationships in the continuous domain. +which are always non-positive and dissipate :math:`k` and :math:`h`. -.. toctree:: - :maxdepth: 1 +=============== +Body force term +=============== - nu/heat_flux - nu/kinetic_energy_injection - nu/kinetic_energy_dissipation - nu/thermal_energy_dissipation +We have + +.. math:: -.. seealso:: + \int + \int + \int + u_x + T + dx + dy + dz, - Although all :math:`Nu` should give the identical result in the continuous domain, it is non-trivial to satisfy these relations umerically (discrete domain). - This is discussed in detail in :ref:`the numerical method `. +which works as a source term and alters the net amount of :math:`k`. diff --git a/docs/source/equations/nu/heat_flux.rst b/docs/source/equations/nu/heat_flux.rst deleted file mode 100644 index 5ddb8db3..00000000 --- a/docs/source/equations/nu/heat_flux.rst +++ /dev/null @@ -1,257 +0,0 @@ - -.. _nu_heat_flux: - -############################ -Heat flux and Nusselt number -############################ - -To quantitatively discuss the enhancement and to define :math:`Nu` eventually, I need an equation describing the heat flux between two walls (how much the thermal energy is transported). - -********* -Heat flux -********* - -To begin with, I look at the equation of the (non-dimensional) internal energy (temperature): - -.. math:: - - \der{T}{t} - + - u_i \der{T}{x_i} - = - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{}{x_i} \der{T}{x_i}, - -and integrate this equation in the homogeneous directions (:math:`y` and :math:`z`), since I am now interested in the total heat flux. - -Since I am interested in systems which are in statistically-steady states, I first consider to average the equation in time: - -.. math:: - - \ave{q}{t} - \equiv - \frac{1}{t_{max} - t_{min}} - \int_{\tau = t_{min}}^{\tau = t_{max}} q d \tau, - -giving - -.. math:: - - \ave{u_i \der{T}{x_i}}{t} - = - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{}{x_i} \der{T}{x_i}}{t}. - -.. note:: - - Hereafter I assume that operations in time and in space are commutative, which is in general not accepted but hold in the current context. - -By using the incompressibility constraint, the advective terms lead to - -.. math:: - - \der{u_i T}{x_i} - = - \der{\ux T}{x} - + - \der{\uy T}{y} - + - \der{\uz T}{z}. - -Since they are conservative, the last two terms vanish because of the periodic boundary condition, e.g. - -.. math:: - - \int_{y = 0}^{y = l_y} \der{\uy T}{y} dy - = - \left[ \uy T \right]_{y = l_y} - - - \left[ \uy T \right]_{y = 0}, - -which is zero because of the periodic boundary condition: - -.. math:: - - \vat{q}{y = l_y} - = - \vat{q}{y = 0}. - -The second-order derivatives in the :math:`y` and :math:`z` directions, which reflect the diffusive effect, are also zero for the same reason, and thus I am left with - -.. math:: - - \int_{z} \int_{y} - \left( - \ave{\der{\ux T}{x}}{t} - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{}{x} \der{T}{x}}{t} - \right) - dy dz - = - 0. - -Then, I further integrate this equation in the wall-normal direction from one place :math:`x = x_0` to the other :math:`x = x_1`: - -.. math:: - - \int_{x = x_0}^{x = x_1} \int_{z} \int_{y} - \left( - \ave{\der{\ux T}{x}}{t} - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{}{x} \der{T}{x}}{t} - \right) - dy dz dx - = - 0, - -or equivalently - -.. math:: - - \int_{z} \int_{y} \left[ \ave{\ux T}{t} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{T}{x}}{t} \right]_{x = x_0} dy dz - = - \int_{z} \int_{y} \left[ \ave{\ux T}{t} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{T}{x}}{t} \right]_{x = x_1} dy dz, - -where :math:`x_0` and :math:`x_1` are arbitrary as long as :math:`\in \left[ 0, l_x \equiv 1 \right]`. -This gives the definition of the time-averaged net heat flux :math:`J_{T} \left( x \right)`: - -.. _eq_heat_flux: - -.. math:: - - J_T \left( x \right) - \equiv - \int_{z} \int_{y} \left[ - \ave{\ux T}{t} - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{T}{x}}{t} - \right] dy dz - = - const. - -Two important takeaways are as follows. - -* The net heat flux is consisted of the advective effect (the first term) and the diffusive effect (the second term). - -* The net heat flux in the wall-normal direction (heat flux going through an arbitrary :math:`y-z` plane inside the domain) is constant. - -.. note:: - - The integrand is the time-averaged and non-dimensional `differential form of Fourier's law `_ if the advective term is absent: - - .. math:: - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{T}{x}}{t}. - -************** -Nusselt number -************** - -Recall that I am interested in quantifying the heat transfer enhancement by the convective effect. -To do so, I define :math:`Nu` as - -.. math:: - - Nu - \equiv - \frac{J_{T}}{J_{T,ref}}, - -where the denominator is the *reference* net heat flux, which is the heat flux if the convective effect is missing. - -If the convection is absent (:math:`u_i \equiv 0`), the time-averaged equation of the internal energy is simply - -.. math:: - - \der{}{x} \der{\ave{T}{t}}{x} - = - 0, - -with the boundary conditions - -.. math:: - - \vat{T}{x = 0} - - - \vat{T}{x = 1} - = - 1, - -which have an analytical solution: - -.. math:: - - T \left( x \right) - = - const. - x - \,\, \rightarrow \,\, - \der{T}{x} - = - -1, - -and thus - -.. math:: - - J_{T,ref} - = - \int_{z} \int_{y} \left( - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \times \left( -1 \right) \right) dy dz - = - \int_{z} \int_{y} \frac{1}{\sqrt{Pr} \sqrt{Ra}} dy dz. - -In summary, I have - -.. _eq_nu_definition: - -.. math:: - - Nu - \equiv - \frac{J_{T} \left( x \right)}{J_{T,ref}} - = - \frac{ - \int_{z} \int_{y} \left( - \ave{\ux T}{t} - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{T}{x}}{t} - \right) dy dz - }{ - \int_{z} \int_{y} \frac{1}{\sqrt{Pr} \sqrt{Ra}} dy dz - }, - -which I regard as the **definition** of the Nusselt number in this project. - -Note that the numerator :math:`J_{T} \left( x \right)` is constant at any :math:`x` and thus can be evaluated at any :math:`x`. -However, the most intuitive way would be to compute on the walls, i.e. - -.. math:: - - J_{T} \left( x = 0 \right) - = - \int_{z} \int_{y} \left( - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \vat{\ave{\der{T}{x}}{t}}{x = 0} \right) dy dz - -at :math:`x = 0`, or - -.. math:: - - J_{T} \left( x = 1 \right) - = - \int_{z} \int_{y} \left( - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \vat{\ave{\der{T}{x}}{t}}{x = 1} \right) dy dz - -at :math:`x = l_x \equiv 1`, where the advective term is dropped because of the impermeable condition :math:`\vat{\ux}{x = 0} = \vat{\ux}{x = 1} \equiv 0`. - -Finally I obtain the following famous relation: - -.. math:: - - Nu - = - \frac{J_{T} \left( x = wall \right)}{J_{T,ref}} - = - \frac{ - \int_{z} \int_{y} \left( - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \vat{\ave{\der{T}{x}}{t}}{wall} \right) dy dz - }{ - \int_{z} \int_{y} \frac{1}{\sqrt{Pr} \sqrt{Ra}} dy dz - }. - -.. seealso:: - - :ref:`Discrete counterpart `. - diff --git a/docs/source/equations/nu/kinetic_energy_dissipation.rst b/docs/source/equations/nu/kinetic_energy_dissipation.rst deleted file mode 100644 index 09a902bb..00000000 --- a/docs/source/equations/nu/kinetic_energy_dissipation.rst +++ /dev/null @@ -1,178 +0,0 @@ - -.. _nu_kinetic_energy_dissipation: - -###################################################### -Nusselt number based on the kinetic energy dissipation -###################################################### - -I consider to integrate :ref:`the equation of the squared velocity ` in the whole volume. - -* Temporal derivative - - .. math:: - - \int_V \der{\ave{k}{t}}{t} dV - - is dropped because of the definition of the statistically-steady state. - -* Advective and pressure-gradient contributions - - .. math:: - - \int_V \ave{u_i \der{}{x_i} \left( - k - p \right)}{t} dV, - - which is - - .. math:: - - \int_V \ave{\der{}{x_i} \left[ u_i \left( - k - p \right) \right]}{t} dV - - - \int_V \ave{\left( - k - p \right) \der{u_i}{x_i}}{t} dV, - - which also disappear because the first term is conservative and the second term includes the incompressibility. - -* Diffusive contribution - - .. math:: - - \int_V \frac{\sqrt{Pr}}{\sqrt{Ra}} \ave{\der{}{x_j} \left( u_i \der{u_i}{x_j} \right)}{t} dV - - - \int_V \frac{\sqrt{Pr}}{\sqrt{Ra}} \ave{\der{u_i}{x_j} \der{u_i}{x_j}}{t} dV, - - where the second term remains. - Regarding the first term, the :math:`y` and :math:`z` derivatives are conservative and thus zero, whilst the :math:`x` derivative yields - - .. math:: - - \int_V \frac{\sqrt{Pr}}{\sqrt{Ra}} \ave{\der{}{x} \left( u_i \der{u_i}{x} \right)}{t} dV - & = - \int_z \int_y \frac{\sqrt{Pr}}{\sqrt{Ra}} \ave{\left[ u_i \der{u_i}{x} \right]_{x = 1}}{t} dy dz \\ - & - - \int_z \int_y \frac{\sqrt{Pr}}{\sqrt{Ra}} \ave{\left[ u_i \der{u_i}{x} \right]_{x = 0}}{t} dy dz. - - The integrands are all zero because :math:`u_i \equiv 0`, i.e. - - * :math:`\ux`: the walls are impermeable, - - * :math:`\uy`, :math:`\uz`: the walls are at rest. - - .. note:: - - The second term gives the dissipation of the squared velocity (sink term). - Since I can write the first term as - - .. math:: - - \int_V \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{}{x_i} \left( \der{k}{x_i} \right)}{t} dV, - - I notice that this term tells the diffusive process of :math:`k`. - In particular, this term is responsible for the transportation of :math:`k` between the walls and the fluid. - -* Energy injection - - .. math:: - - \int_V \ave{u_i T \delta_{ix}}{t} dV - = - \int_V \ave{\ux T}{t} dV. - -In summary, I obtain - -.. math:: - - 0 - = - - - \int_V \frac{\sqrt{Pr}}{\sqrt{Ra}} \ave{\der{u_i}{x_j} \der{u_i}{x_j}}{t} dV - + - \int_V \ave{\ux T}{t} dV. - -As derived in :ref:`the previous page `, I know - -.. math:: - - \int_V \ave{\ux T}{t} dV - = - J_{T,ref} \left( Nu - 1 \right). - -Thus, I finally notice - -.. math:: - - \int_V \frac{\sqrt{Pr}}{\sqrt{Ra}} \ave{\der{u_i}{x_j} \der{u_i}{x_j}}{t} dV - = - J_{T,ref} \left( Nu - 1 \right), - -or - -.. math:: - - Nu - = - \frac{1}{J_{T,ref}} \int_V \frac{\sqrt{Pr}}{\sqrt{Ra}} \ave{\der{u_i}{x_j} \der{u_i}{x_j}}{t} dV - + - 1. - -By introducing the local kinetic energy dissipation - -.. math:: - - \epsilon_k \left( x, y, z, t \right) - \equiv - \frac{\sqrt{Pr}}{\sqrt{Ra}} \der{u_i}{x_j} \der{u_i}{x_j}, - -I obtain the conclusive equation of this page: - -.. math:: - - Nu - = - \frac{1}{J_{T,ref}} \int_V \ave{\epsilon_k}{t} dV - + - 1. - -.. note:: - - Since - - .. math:: - - J_{T,ref} - = - \int_z \int_y \frac{1}{\sqrt{Pr} \sqrt{Ra}} dy dz - = - \int_V \frac{1}{\sqrt{Pr} \sqrt{Ra}} dV, - - I have - - .. math:: - - Nu - & = - \frac{ - \int_V \ave{\epsilon_k}{t} dV - }{ - \int_V \frac{1}{\sqrt{Pr} \sqrt{Ra}} dV - } - + - 1 \\ - & = - \sqrt{Pr} \sqrt{Ra} - \frac{ - \int_V \ave{\epsilon_k}{t} dV - }{ - \int_V dV - } - + - 1 \\ - & = - \sqrt{Pr} \sqrt{Ra} \ave{\epsilon_k}{V,t} - + - 1, - - which is more generally used. - -.. seealso:: - - :ref:`Discrete counterpart `. - diff --git a/docs/source/equations/nu/kinetic_energy_injection.rst b/docs/source/equations/nu/kinetic_energy_injection.rst deleted file mode 100644 index 3541cc84..00000000 --- a/docs/source/equations/nu/kinetic_energy_injection.rst +++ /dev/null @@ -1,150 +0,0 @@ - -.. _nu_kinetic_energy_injection: - -#################################################### -Nusselt number based on the kinetic energy injection -#################################################### - -Recall that :ref:`the definition of the Nusselt number ` is - -.. math:: - - J_{T} \left( x \right) - = - Nu - \times - J_{T,ref}. - -Now I consider to integrate :ref:`the equation of the heat flux ` from the bottom wall :math:`x = 0` to the top wall :math:`x = l_x \equiv 1`, yielding - -.. math:: - - \int_{x = 0}^{x = 1} - J_{T} \left( x \right) - dx - = - \int_{x = 0}^{x = 1} Nu J_{T,ref} dx, - -or - -.. math:: - - \int_x \int_z \int_y - \left[ - \ave{\ux T}{t} - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{\der{T}{x}}{t} - \right] - dy dz dx - = - Nu J_{T,ref}. - -The diffusive terms yield - -.. math:: - - \int_z \int_y - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \left( - - - \vat{\ave{T}{t}}{x = 1} - + - \vat{\ave{T}{t}}{x = 0} - \right) - dy dz - = - \int_z \int_y - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - dy dz - = - J_{T,ref}, - -since I impose - -.. math:: - - \vat{\ave{T}{t}}{x = 0} - - - \vat{\ave{T}{t}}{x = 1} - = - 1 - -as the boundary conditions. - -Thus I notice - -.. math:: - - \int_x \int_z \int_y - \ave{\ux T}{t} - dy dz dx - + - J_{T,ref} - = - Nu J_{T,ref}, - -or equivalently - -.. math:: - - Nu - = - \frac{1}{J_{T,ref}} - \int_V - \ave{\ux T}{t} - dV - + - 1. - -Notice that the first term represents the enhancement of the heat transport by the convective effect. -This term appears in :ref:`the equation of the squared velocity ` as the buoyancy body force, which I interpret as the kinetic energy injection since the squared velocity has a close relation with the kinetic energy. -This is the reason why I call this equation as the Nusselt number based on the kinetic energy injection. - -.. note:: - - Since I have - - .. math:: - - J_{T,ref} - & = - \int_z \int_y - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - dy dz \\ - & = - \int_x \int_z \int_y - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - dy dz dx \,\, \left( \because l_x \equiv 1 \right) \\ - & = - \int_V - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - dV, - - the result is equal to - - .. math:: - - Nu - & = - \sqrt{Pr} \sqrt{Ra} - \frac{ - \int_V - \ave{\ux T}{t} - dV - }{ - \int_V dV - } - + - 1 \\ - & = - \sqrt{Pr} \sqrt{Ra} \ave{\ux T}{x,y,z,t} - + - 1, - - which might be more general. - -.. seealso:: - - :ref:`Discrete counterpart `. - diff --git a/docs/source/equations/nu/thermal_energy_dissipation.rst b/docs/source/equations/nu/thermal_energy_dissipation.rst deleted file mode 100644 index 6ba5b1f9..00000000 --- a/docs/source/equations/nu/thermal_energy_dissipation.rst +++ /dev/null @@ -1,187 +0,0 @@ - -.. _nu_thermal_energy_dissipation: - -###################################################### -Nusselt number based on the thermal energy dissipation -###################################################### - -I consider to integrate :ref:`the equation of the squared temperature ` in the whole volume. - -* Temporal derivative - - .. math:: - - \int_V \der{\ave{h}{t}}{t} dV - - is dropped because of the definition of the statistically-steady state. - -* Advective contribution - - .. math:: - - \int_V \ave{u_i \der{h}{x_i}}{t} dV, - - which is - - .. math:: - - \int_V \ave{\der{}{x_i} \left( - u_i - h \right)}{t} dV - - - \int_V \ave{\left( - h \right) \der{u_i}{x_i}}{t} dV, - - and also disappears because the first term is conservative and the second term includes the incompressibility. - -* Diffusive contribution - - .. math:: - - \int_V \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{}{x_i} \left( T \der{T}{x_i} \right)}{t} dV - - - \int_V \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{T}{x_i} \der{T}{x_i}}{t} dV, - - where the second term remains as it is. - Regarding the first term, the :math:`y` and :math:`z` derivatives are conservative and thus zero, whilst the :math:`x` component yields - - .. math:: - - & - \int_z \int_y \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left[ \ave{T \der{T}{x}}{t} \right]_{x = 1} dy dz - - - \int_z \int_y \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left[ \ave{T \der{T}{x}}{t} \right]_{x = 0} dy dz \\ - & = - \vat{\ave{T}{t}}{x = 1} \int_z \int_y \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left[ \ave{\der{T}{x}}{t} \right]_{x = 1} dy dz \\ - & - - \vat{\ave{T}{t}}{x = 0} \int_z \int_y \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left[ \ave{\der{T}{x}}{t} \right]_{x = 0} dy dz, - - where the integrands are :ref:`the heat fluxes ` evaluated at :math:`x = 1` and at :math:`x = 0`, respectively, and thus - - .. math:: - - - - \vat{\ave{T}{t}}{x = 1} J_{T} \left( x = 1 \right) - + - \vat{\ave{T}{t}}{x = 0} J_{T} \left( x = 0 \right) - = - \left[ - - - \vat{\ave{T}{t}}{x = 1} - + - \vat{\ave{T}{t}}{x = 0} - \right] - J_{T}. - - Since I know - - .. math:: - - \vat{\ave{T}{t}}{x = 0} - - - \vat{\ave{T}{t}}{x = 1} - \equiv - 1 - - because of the boundary conditions and - - .. math:: - - J_{T} \left( x \right) - = - J_{T} - = - Nu - J_{T,ref} - - because of :ref:`the defnition of the Nusselt number `, I finally find that the diffusive term yields - - .. math:: - - Nu J_{T,ref} - - - \int_V \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{T}{x_i} \der{T}{x_i}}{t} dV. - - .. note:: - - The second term gives the dissipation of the squared temperature (sink term). - Since I can write the first term as - - .. math:: - - \int_V \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{}{x_i} \left( \der{h}{x_i} \right)}{t} dV, - - I notice that this term tells the diffusive process of :math:`h`. - In particular, this term is responsible for the transportation of :math:`h` between the walls and the fluid. - -In summary, I obtain - -.. math:: - - 0 - = - Nu J_{T,ref} - - - \int_V \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{T}{x_i} \der{T}{x_i}}{t} dV, - -or - -.. math:: - - Nu - = - \frac{1}{J_{T,ref}} - \int_V \frac{1}{\sqrt{Pr} \sqrt{Ra}} \ave{\der{T}{x_i} \der{T}{x_i}}{t} dV. - -By introducing the local thermal energy dissipation - -.. math:: - - \epsilon_h \left( x, y, z, t \right) - \equiv - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{T}{x_i} \der{T}{x_i}, - -I obtain the conclusive equation of this page: - -.. math:: - - Nu - = - \frac{1}{J_{T,ref}} \int_V \ave{\epsilon_h}{t} dV. - -.. note:: - - Since - - .. math:: - - J_{T,ref} - = - \int_z \int_y \frac{1}{\sqrt{Pr} \sqrt{Ra}} dy dz - = - \int_V \frac{1}{\sqrt{Pr} \sqrt{Ra}} dV, - - I have - - .. math:: - - Nu - & = - \frac{ - \int_V \epsilon_h dV - }{ - \int_V \frac{1}{\sqrt{Pr} \sqrt{Ra}} dV - } \\ - & = - \sqrt{Pr} \sqrt{Ra} - \frac{ - \int_V \ave{\epsilon_h}{t} dV - }{ - \int_V dV - } \\ - & = - \sqrt{Pr} \sqrt{Ra} \ave{\epsilon_h}{V,t}, - - which is more generally used. - -.. seealso:: - - :ref:`Discrete counterpart `. - diff --git a/docs/source/examples/energy/data/exec_2d.sh b/docs/source/examples/energy/data/exec_2d.sh index 26b3db08..e86a47e5 100644 --- a/docs/source/examples/energy/data/exec_2d.sh +++ b/docs/source/examples/energy/data/exec_2d.sh @@ -10,7 +10,6 @@ lx=1.0e+0 \ ly=1.0e+0 \ glisize=32 \ gljsize=32 \ -uniformx=false \ python main.py output cd .. @@ -33,18 +32,18 @@ mpirun -n 2 --oversubscribe ./a.out initial_condition/output # stash last flow field dirname_ic=$(find output/save -type d | sort | tail -n 1) -mv ${dirname_ic} dirname_ic +mkdir dirname_ic +mv ${dirname_ic}/*.npy dirname_ic/ # check decays for different time steps -for factor_adv in 0.1 0.2 0.4 0.8 -do +for factor_adv in 0.1 0.2 0.4 0.8; do sed -i "s/param_add_buoyancy.*$/param_add_buoyancy = false;/g" src/param/buoyancy.c && cat src/param/buoyancy.c make datadel && make all && make output timemax=6.0e+1 \ log_rate=1.0e-1 \ coef_dt_adv=${factor_adv} \ mpirun -n 2 --oversubscribe ./a.out dirname_ic - mv output/log/energy.dat ./energy-${factor_adv}.dat + mv output/log/total_energy.dat ./energy-${factor_adv}.dat done # post process @@ -52,9 +51,9 @@ mkdir artifacts python \ docs/source/examples/energy/data/process.py \ . \ - artifacts/energy1_2d.png \ - artifacts/energy2_2d.png -echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci_2d.txt -echo "Date :" $(date) >> artifacts/ci_2d.txt -echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci_2d.txt + artifacts/energy1.png \ + artifacts/energy2.png +echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci.txt +echo "Date :" $(date) >> artifacts/ci.txt +echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci.txt diff --git a/docs/source/examples/energy/data/exec_3d.sh b/docs/source/examples/energy/data/exec_3d.sh index d1f7fe07..2fa45093 100644 --- a/docs/source/examples/energy/data/exec_3d.sh +++ b/docs/source/examples/energy/data/exec_3d.sh @@ -12,12 +12,9 @@ lz=1.0e+0 \ glisize=24 \ gljsize=24 \ glksize=24 \ -uniformx=false \ python main.py output cd .. -sed -i "s/DNDIMS=2/DNDIMS=3/g" Makefile - # configure export wtimemax=3.0e+2 export save_rate=1.0e+3 @@ -37,18 +34,18 @@ mpirun -n 2 --oversubscribe ./a.out initial_condition/output # stash last flow field dirname_ic=$(find output/save -type d | sort | tail -n 1) -mv ${dirname_ic} dirname_ic +mkdir dirname_ic +mv ${dirname_ic}/*.npy dirname_ic/ # check decays for different time steps -for factor_adv in 0.1 0.2 0.4 0.8 -do +for factor_adv in 0.1 0.2 0.4 0.8; do sed -i "s/param_add_buoyancy.*$/param_add_buoyancy = false;/g" src/param/buoyancy.c && cat src/param/buoyancy.c make datadel && make all && make output timemax=6.0e+1 \ log_rate=1.0e-1 \ coef_dt_adv=${factor_adv} \ mpirun -n 2 --oversubscribe ./a.out dirname_ic - mv output/log/energy.dat ./energy-${factor_adv}.dat + mv output/log/total_energy.dat ./energy-${factor_adv}.dat done # post process @@ -56,9 +53,10 @@ mkdir artifacts python \ docs/source/examples/energy/data/process.py \ . \ - artifacts/energy1_3d.png \ - artifacts/energy2_3d.png -echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci_3d.txt -echo "Date :" $(date) >> artifacts/ci_3d.txt -echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci_3d.txt + artifacts/energy1.png \ + artifacts/energy2.png + +echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci.txt +echo "Date :" $(date) >> artifacts/ci.txt +echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci.txt diff --git a/docs/source/examples/energy/data/process.py b/docs/source/examples/energy/data/process.py index 0cc5856b..9a824841 100644 --- a/docs/source/examples/energy/data/process.py +++ b/docs/source/examples/energy/data/process.py @@ -5,7 +5,7 @@ matplotlib.use("Agg") from matplotlib import pyplot -def load(dname, is3d): +def load(dname): fnames = [f"{dname}/{fname}" for fname in os.listdir(dname) if fname.startswith("energy") and fname.endswith(".dat")] fnames = sorted(fnames) ls = list() @@ -14,6 +14,7 @@ def load(dname, is3d): y2s = list() for fname in fnames: data = np.loadtxt(fname) + is3d = 5 == len(data[0]) t = data[:, 0] if is3d: kx = data[:, 1] @@ -37,23 +38,17 @@ def load(dname, is3d): return ls, xs, y1s, y2s if __name__ == "__main__": - np.random.seed(seed=16384) argv = sys.argv - assert(4 == len(argv)) + assert 4 == len(argv) idname = argv[1] ofname1 = argv[2] ofname2 = argv[3] - is3d = "3d" in ofname1 - ls, xs, y1s, y2s = load(idname, is3d) + ls, xs, y1s, y2s = load(idname) + colors = pyplot.rcParams["axes.prop_cycle"].by_key()["color"] # fig = pyplot.figure() ax = fig.add_subplot(111) - for l, x, y1, y2 in zip(ls, xs, y1s, y2s): - color = "#{:02X}{:02X}{:02X}".format( - np.random.randint(low=0, high=255), - np.random.randint(low=0, high=255), - np.random.randint(low=0, high=255), - ) + for l, x, y1, y2, color in zip(ls, xs, y1s, y2s, colors): ax.plot(x, -y1, color=color, linestyle="-", label=l) ax.plot(x, -y2, color=color, linestyle="--") kwrds = { diff --git a/docs/source/examples/energy/main.rst b/docs/source/examples/energy/main.rst index 535799d1..40f2dc76 100644 --- a/docs/source/examples/energy/main.rst +++ b/docs/source/examples/energy/main.rst @@ -4,83 +4,55 @@ .. include:: /references.txt #################################### -Conservation of quadratic quantities +Conservation of Quadratic Quantities #################################### -In this section, I check the conservations of the volume-integrated quadratic quantities :math:`K` and :math:`H`. +In this section, we check the conservations of the volume-integrated quadratic quantities. See :ref:`the governing equations ` for the definitions of these quantities. -.. mydetails:: Signatures - - .. literalinclude:: data/ci_2d.txt - :language: text - - .. literalinclude:: data/ci_3d.txt - :language: text - ************* Configuration ************* -First, I simulate for :math:`50` time units to get a random velocity field and a random temperature field. -:math:`Ra` is set to an extremely high value to mimic the inviscid condition. +First, we simulate for :math:`50` time units to get a random flow. +:math:`Ra` is set to an extremely high value to mimic an inviscid condition. This is followed by another run for :math:`10` time units, restarted from the previous simulation without the buoyancy force, so that the quadratic quantities should be conserved. -To see the effect of the time step size, I consider four different safety factors :math:`0.1`, :math:`0.2`, :math:`0.4`, :math:`0.8`, which are multiplied to the maximum time step size computed in :ref:`src/decide_dt.c `. - -.. mydetails:: Configurations - - .. literalinclude:: data/exec_2d.sh - :language: sh - - .. literalinclude:: data/exec_3d.sh - :language: sh +To see the effect of the time step size, I consider four different :ref:`Courant numbers ` :math:`0.1`, :math:`0.2`, :math:`0.4`, :math:`0.8`. ******* Results ******* -As derived in :ref:`the discrete momentum balance ` and :ref:`the discrete internal energy balance `, they should be constant in time. -This is not the case in this project, since I adopt an :ref:`explicit Runge-Kutta scheme ` to integrate the advective terms in time, which is dissipative (see |MORINISHI1998|, |COPPOLA2019|). -Thus these two quantities should decrease monotonically over time, which is unfavourable but advantageous from a stability point of view. +As derived in :ref:`the governing equaations `, the quadratic quantities should be constant over time. +This is not the case in this project, since we adopt an :ref:`explicit Runge-Kutta scheme ` to integrate the advective terms in time, which is dissipative (see |MORINISHI1998|, |COPPOLA2019|). +Thus these two quantities decrease monotonically over time, which is unfavourable but advantageous from a stability point of view. The following graphs show the quadratic quantities as a function of time: -* 2D +2D: - .. image:: data/energy1_2d.png - :width: 600 +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/energy-2d/energy1.png + :width: 600 -* 3D +3D: - .. image:: data/energy1_3d.png - :width: 600 +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/energy-3d/energy1.png + :width: 600 Here four different time step sizes are considered. Also, the decays of :math:`K` and :math:`H` (:math:`t = 50` and :math:`60` are compared) are displayed. -* 2D - - .. image:: data/energy2_2d.png - :width: 600 - -* 3D - - .. image:: data/energy2_3d.png - :width: 600 - -.. mydetails:: Script +2D: - .. literalinclude:: data/process.py - :language: python - :linenos: +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/energy-2d/energy2.png + :width: 600 -Here the third-order convergence is observed, which is also reported by the previous works (e.g. |MORINISHI1998|, |HAM2002| and |COPPOLA2019|). -This indicates that the decay, which is a numerical artifact, is converged relatively quickly. +3D: -.. seealso:: +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/energy-3d/energy2.png + :width: 600 - The conservations of :math:`K` and :math:`H` depend strongly on the discretisation of the advective terms. - Also the third-order temporal convergence is not expected if the spatial treatment is inconsistent. - See :ref:`here ` for details. +Here the third-order convergence is observed, which is also reported by the previous works (e.g., |MORINISHI1998|, |HAM2002| and |COPPOLA2019|). +This indicates that the decay, which is a numerical artifact, is properly converged. diff --git a/docs/source/examples/gl/data/exec.sh b/docs/source/examples/gl/data/exec.sh deleted file mode 100644 index daa975a4..00000000 --- a/docs/source/examples/gl/data/exec.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -set -x -set -e - -Ra=${1} - -# set initial condition -cd initial_condition -make output -lx=1.0e+0 \ -ly=2.0e+0 \ -glisize=128 \ -gljsize=256 \ -uniformx=false \ -python main.py output -cd .. - -# build and execute -sed -i "s/false/true/g" src/param/implicit.c && cat src/param/implicit.c -make all && make output -timemax=2.0e+2 \ -wtimemax=3.0e+2 \ -log_rate=5.0e-1 \ -save_rate=1.0e+3 \ -save_after=1.0e+3 \ -stat_rate=1.0e+0 \ -stat_after=1.0e+3 \ -coef_dt_adv=0.95 \ -coef_dt_dif=0.95 \ -Ra=${Ra} \ -Pr=7.0e+0 \ -mpirun -n 2 --oversubscribe ./a.out initial_condition/output - -# post process -mkdir artifacts -cp output/log/nusselt.dat artifacts/nusselt_${Ra}.dat -echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci_${Ra}.txt -echo "Date :" $(date) >> artifacts/ci_${Ra}.txt -echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci_${Ra}.txt - diff --git a/docs/source/examples/gl/data/process.py b/docs/source/examples/gl/data/process.py deleted file mode 100644 index adbf2d3e..00000000 --- a/docs/source/examples/gl/data/process.py +++ /dev/null @@ -1,47 +0,0 @@ -import os -import sys -import numpy as np -import matplotlib -matplotlib.use("Agg") -from matplotlib import pyplot - -def load(dname, ras, xmin): - xs = list() - ys = list() - for ra in ras: - fname = f"{dname}/nusselt_{ra}.dat" - data = np.loadtxt(fname, usecols=[0, 1]) - x = data[:, 0] - y = data[:, 1] - y = y[x > xmin] - xs.append(float(ra)) - ys.append(np.average(y)) - return xs, ys - -if __name__ == "__main__": - argv = sys.argv - assert(3 == len(argv)) - ras = ["1.0e+4", "3.1e+4", "1.0e+5", "3.1e+5", "1.0e+6", "3.1e+6", "1.0e+7", "3.1e+7", "1.0e+8"] - xmin = 100. - idname = argv[1] - ofname = argv[2] - x, y = load(idname, ras, xmin) - fig = pyplot.figure() - ax = fig.add_subplot(111) - ax.plot(x, y, color="#FF0000", marker=".") - # Kooloth et al., PRF, 2021 - ax.plot(x, 0.1 * np.power(x, 0.28), color="#000000", linestyle="--") - kwrds = { - "title": "", - "xlim": [0.5 * np.min(x), 2. * np.max(x)], - "xlabel": "Ra", - "ylabel": "Nu", - "xticks": x, - "yticks": [1, 2, 4, 8, 16, 32], - "xscale": "log", - "yscale": "log", - } - ax.set(**kwrds) - pyplot.savefig(ofname) - pyplot.close() - diff --git a/docs/source/examples/gl/main.rst b/docs/source/examples/gl/main.rst deleted file mode 100644 index af5bcd1e..00000000 --- a/docs/source/examples/gl/main.rst +++ /dev/null @@ -1,41 +0,0 @@ - -.. include:: /references.txt - -###################### -Grossmann-Lohse theory -###################### - -In this section, I investigate the relation between :math:`Ra` and :math:`Nu`, which is known as the Grossmann-Lohse theory (|GROSSMANN2000|). - -************* -Configuration -************* - -The Prandtl number is fixed to :math:`0.7`, and the Rayleigh number is varied from :math:`10^4` to :math:`10^8` to see the relation between :math:`Ra` and :math:`Nu`. - -.. mydetails:: Details - - .. literalinclude:: data/exec.sh - :language: sh - -****** -Result -****** - -.. image:: data/nu_ra.png - :width: 600 - -The black-dashed line shows - -.. math:: - - Nu \propto Ra^{0.28}, - -which is reported by e.g. |KOOLOTH2021|. - -.. mydetails:: Script - - .. literalinclude:: data/process.py - :language: python - :linenos: - diff --git a/docs/source/examples/main.rst b/docs/source/examples/main.rst index 75a06025..5e15f5f8 100644 --- a/docs/source/examples/main.rst +++ b/docs/source/examples/main.rst @@ -1,26 +1,21 @@ + +.. _examples: + ######## Examples ######## -This section presents several examples and their analyses. - -To check the code quality regularly and automatically, this section is partially updated by `the GitHub Actions `_ when the ``main`` branch is updated. -The workflow file is `here `_. +Several example cases are displayed in this section. .. note:: - To reduce the computational effort, I restrict the duration of the simulation ``timemax`` to relatively small values. - As a result, - - * I might start to collect statistics even though the system has not reached the statistically-stationary state yet, and / or - - * statistics which are described in the following pages might not be converged. + To reduce the computational effort, ``timemax`` is set to relatively small values. + As a result, the statistics might be collected before the system reaches statistically-stationary state. .. toctree:: - :maxdepth: 1 + :maxdepth: 1 - typical/main - energy/main - nu/main - gl/main + typical/main + energy/main + nusselt/main diff --git a/docs/source/examples/nu/data/process.py b/docs/source/examples/nu/data/process.py deleted file mode 100644 index d7c70780..00000000 --- a/docs/source/examples/nu/data/process.py +++ /dev/null @@ -1,37 +0,0 @@ -import sys -import numpy as np -import matplotlib -matplotlib.use("Agg") -from matplotlib import pyplot - -def load(fname): - data = np.loadtxt(fname) - x = data[:, 0] - y0 = data[:, 1] - y1 = data[:, 2] - y2 = data[:, 3] - y3 = data[:, 4] - return x, y0, y1, y2, y3 - -if __name__ == "__main__": - argv = sys.argv - assert(3 == len(argv)) - ifname = argv[1] - ofname = argv[2] - x, y0, y1, y2, y3 = load(ifname) - fig = pyplot.figure() - ax = fig.add_subplot(111) - ax.plot(x, np.abs(y1 - y0), color="#FF0000") - ax.plot(x, np.abs(y2 - y0), color="#0000FF") - ax.plot(x, np.abs(y3 - y0), color="#33AA00") - kwrds = { - "title": "", - "xlabel": "time", - "ylabel": "deviations", - "yticks": [1.e-16, 1.e-12, 1.e-8, 1.e-4, 1.e+0], - "yscale": "log", - } - ax.set(**kwrds) - pyplot.savefig(ofname) - pyplot.close() - diff --git a/docs/source/examples/nu/main.rst b/docs/source/examples/nu/main.rst deleted file mode 100644 index a9c90bae..00000000 --- a/docs/source/examples/nu/main.rst +++ /dev/null @@ -1,110 +0,0 @@ - -.. _example_nu_agreement: - -######################### -Nusselt number agreements -######################### - -Here, I investigate the perfect :math:`Nu` matches. -To eliminate oscillations, relatively low Rayleigh numbers are considered. -In particular a lower Rayleigh number is employed for the three-dimensional cases to stabilise the thermal plumes. - -.. mydetails:: Signatures - - .. literalinclude:: data/ci_1.e-1_2d.txt - :language: text - - .. literalinclude:: data/ci_1.e+0_2d.txt - :language: text - - .. literalinclude:: data/ci_1.e+1_2d.txt - :language: text - - .. literalinclude:: data/ci_1.e-1_3d.txt - :language: text - - .. literalinclude:: data/ci_1.e+0_3d.txt - :language: text - - .. literalinclude:: data/ci_1.e+1_3d.txt - :language: text - -************* -Configuration -************* - -I consider three Prandtl numbers: :math:`Pr = 10^{-1}, 10^0, 10^1`. -The other parameters are listed below. - -.. mydetails:: Details - - .. literalinclude:: data/exec_2d.sh - :language: sh - - .. literalinclude:: data/exec_3d.sh - :language: sh - -****** -Result -****** - -:math:`Nu` calculated by four different formulae are considered: - - * reference: heat flux on the walls - - * red: energy injection - - * blue: kinetic energy dissipation - - * green: thermal energy dissipation - -.. seealso:: - - :ref:`Nusselt number relations ` for the derivations. - -Their deviations from the reference value :math:`Nu_{wall}` are plotted to highlight the difference. -The reason why the lines are partly discontinuous is that the discrepancies are smaller than :math:`10^{-15}`. - -* 2D, :math:`Pr = 10^{-1}` - - .. image:: data/nusselt_1.e-1_2d.png - :width: 600 - -* 2D, :math:`Pr = 10^{ 0}` - - .. image:: data/nusselt_1.e+0_2d.png - :width: 600 - -* 2D, :math:`Pr = 10^{ 1}` - - .. image:: data/nusselt_1.e+1_2d.png - :width: 600 - -* 3D, :math:`Pr = 10^{-1}` - - .. image:: data/nusselt_1.e-1_3d.png - :width: 600 - -* 3D, :math:`Pr = 10^{ 0}` - - .. image:: data/nusselt_1.e+0_3d.png - :width: 600 - -* 3D, :math:`Pr = 10^{ 1}` - - .. image:: data/nusselt_1.e+1_3d.png - :width: 600 - -The deviations should be small enough (around the rounding error). - -.. mydetails:: Script - - .. literalinclude:: data/process.py - :language: python - :linenos: - -.. seealso:: - - The :math:`Nu` consistency discussed here depends to a large extent on how I compute the dissipations. - See a more detailed analysis :ref:`here `. - diff --git a/docs/source/examples/nu/data/exec_2d.sh b/docs/source/examples/nusselt/data/exec_2d.sh similarity index 52% rename from docs/source/examples/nu/data/exec_2d.sh rename to docs/source/examples/nusselt/data/exec_2d.sh index 55d5cda4..43affc99 100644 --- a/docs/source/examples/nu/data/exec_2d.sh +++ b/docs/source/examples/nusselt/data/exec_2d.sh @@ -3,16 +3,18 @@ set -x set -e +lx=1.0e+0 +ly=2.0e+0 +Ra=1.0e+4 Pr=${1} # set initial condition cd initial_condition make output -lx=1.0e+0 \ -ly=2.0e+0 \ +lx=${lx} \ +ly=${ly} \ glisize=32 \ gljsize=64 \ -uniformx=false \ python main.py output cd .. @@ -27,17 +29,25 @@ stat_rate=1.0e+0 \ stat_after=1.0e+3 \ coef_dt_adv=0.95 \ coef_dt_dif=0.95 \ -Ra=1.0e+4 \ +Ra=${Ra} \ Pr=${Pr} \ mpirun -n 2 --oversubscribe ./a.out initial_condition/output # post process mkdir artifacts + +ly=${ly} \ +Ra=${Ra} \ +Pr=${Pr} \ python \ - docs/source/examples/nu/data/process.py \ - output/log/nusselt.dat \ - artifacts/nusselt_${Pr}_2d.png -echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci_${Pr}_2d.txt -echo "Date :" $(date) >> artifacts/ci_${Pr}_2d.txt -echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci_${Pr}_2d.txt + docs/source/examples/nusselt/data/process.py \ + output/log/heat_transfer.dat \ + output/log/injected_squared_velocity.dat \ + output/log/dissipated_squared_velocity.dat \ + output/log/dissipated_squared_temperature.dat \ + artifacts/nusselt.png + +echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci_${Pr}.txt +echo "Date :" $(date) >> artifacts/ci_${Pr}.txt +echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci_${Pr}.txt diff --git a/docs/source/examples/nu/data/exec_3d.sh b/docs/source/examples/nusselt/data/exec_3d.sh similarity index 53% rename from docs/source/examples/nu/data/exec_3d.sh rename to docs/source/examples/nusselt/data/exec_3d.sh index 26bff281..b5c30a0b 100644 --- a/docs/source/examples/nu/data/exec_3d.sh +++ b/docs/source/examples/nusselt/data/exec_3d.sh @@ -3,23 +3,25 @@ set -x set -e +lx=1.0e+0 +ly=1.0e+0 +lz=1.0e+0 +Ra=5.0e+3 Pr=${1} # set initial condition cd initial_condition make output -lx=1.0e+0 \ -ly=1.0e+0 \ -lz=1.0e+0 \ +lx=${lx} \ +ly=${ly} \ +lz=${lz} \ glisize=16 \ gljsize=16 \ glksize=16 \ -uniformx=false \ python main.py output cd .. # build and execute -sed -i "s/DNDIMS=2/DNDIMS=3/g" Makefile sed -i "s/false/true/g" src/param/implicit.c make all && make output timemax=5.0e+3 \ @@ -31,17 +33,26 @@ stat_rate=1.0e+0 \ stat_after=1.0e+3 \ coef_dt_adv=0.95 \ coef_dt_dif=0.95 \ -Ra=5.0e+3 \ +Ra=${Ra} \ Pr=${Pr} \ mpirun -n 2 --oversubscribe ./a.out initial_condition/output # post process mkdir artifacts + +ly=${ly} \ +lz=${lz} \ +Ra=${Ra} \ +Pr=${Pr} \ python \ - docs/source/examples/nu/data/process.py \ - output/log/nusselt.dat \ - artifacts/nusselt_${Pr}_3d.png -echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci_${Pr}_3d.txt -echo "Date :" $(date) >> artifacts/ci_${Pr}_3d.txt -echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci_${Pr}_3d.txt + docs/source/examples/nusselt/data/process.py \ + output/log/heat_transfer.dat \ + output/log/injected_squared_velocity.dat \ + output/log/dissipated_squared_velocity.dat \ + output/log/dissipated_squared_temperature.dat \ + artifacts/nusselt.png + +echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci.txt +echo "Date :" $(date) >> artifacts/ci.txt +echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci.txt diff --git a/docs/source/examples/nusselt/data/process.py b/docs/source/examples/nusselt/data/process.py new file mode 100644 index 00000000..23f0a97b --- /dev/null +++ b/docs/source/examples/nusselt/data/process.py @@ -0,0 +1,60 @@ +import os +import sys +import numpy as np +import matplotlib +matplotlib.use("Agg") +from matplotlib import pyplot + +def load(fname): + data = np.loadtxt(fname) + x = data[:, 0] + y = data[:, 1] + return x, y + +def get_ref_heat_transfer(): + ly = os.environ.get("ly") + lz = os.environ.get("lz") + Ra = os.environ.get("Ra") + Pr = os.environ.get("Pr") + ly = float(ly) + lz = float(lz) if lz else 1. + Ra = float(Ra) + Pr = float(Pr) + return ly * lz / Ra**0.5 / Pr**0.5 + +if __name__ == "__main__": + argv = sys.argv + assert 6 == len(argv) + ifnames = argv[1:-1] + ofname = argv[-1] + xs = list() + ys = list() + for ifname in ifnames: + x, y = load(ifname) + xs.append(x) + ys.append(y) + q_ref = get_ref_heat_transfer() + converters = [ + lambda x: x / q_ref, + lambda x: (x + q_ref) / q_ref, + lambda x: (x + q_ref) / q_ref, + lambda x: x / q_ref, + ] + for cnt, converter in enumerate(converters): + ys[cnt] = converter(ys[cnt]) + fig = pyplot.figure() + ax = fig.add_subplot() + ax.plot(xs[0], np.abs(ys[1] - ys[0]), color="#FF0000") + ax.plot(xs[0], np.abs(ys[2] - ys[0]), color="#0000FF") + ax.plot(xs[0], np.abs(ys[3] - ys[0]), color="#33AA00") + kwrds = { + "title": "", + "xlabel": "time", + "ylabel": "deviations", + "yticks": [1.e-16, 1.e-12, 1.e-8, 1.e-4, 1.e+0], + "yscale": "log", + } + ax.set(**kwrds) + pyplot.savefig(ofname) + pyplot.close() + diff --git a/docs/source/examples/nusselt/main.rst b/docs/source/examples/nusselt/main.rst new file mode 100644 index 00000000..71fea5a8 --- /dev/null +++ b/docs/source/examples/nusselt/main.rst @@ -0,0 +1,63 @@ + +.. _example_nu_agreement: + +######################### +Nusselt Number Agreements +######################### + +************* +Configuration +************* + +I consider three Prandtl numbers: :math:`Pr = 10^{-1}, 10^0, 10^1`. +The other parameters are listed below. + +****** +Result +****** + +:math:`Nu` calculated by four different formulae are considered: + +* reference: heat flux on the walls + +* red: energy injection + +* blue: kinetic energy dissipation + +* green: thermal energy dissipation + +Their deviations from the reference value :math:`Nu_{wall}` are plotted to highlight the difference. +The reason why the lines are partly discontinuous is that the discrepancies are smaller than :math:`10^{-15}`. + +2D, :math:`Pr = 10^{-1}`: + +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/nusselt-2d-1.e-1/nusselt.png + :width: 600 + +2D, :math:`Pr = 10^{ 0}`: + +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/nusselt-2d-1.e+0/nusselt.png + :width: 600 + +2D, :math:`Pr = 10^{ 1}`: + +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/nusselt-2d-1.e+1/nusselt.png + :width: 600 + +3D, :math:`Pr = 10^{-1}`: + +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/nusselt-3d-1.e-1/nusselt.png + :width: 600 + +3D, :math:`Pr = 10^{ 0}`: + +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/nusselt-3d-1.e+0/nusselt.png + :width: 600 + +3D, :math:`Pr = 10^{ 1}`: + +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/nusselt-3d-1.e+1/nusselt.png + :width: 600 + +The deviations should be small enough (around the rounding error). + diff --git a/docs/source/examples/typical/data/divergence.py b/docs/source/examples/typical/data/divergence.py index 679806bf..951d701f 100644 --- a/docs/source/examples/typical/data/divergence.py +++ b/docs/source/examples/typical/data/divergence.py @@ -14,7 +14,7 @@ def load(fname, xmin): if __name__ == "__main__": argv = sys.argv - assert(3 == len(argv)) + assert 3 == len(argv) ifname = argv[1] ofname = argv[2] xmin = 200. diff --git a/docs/source/examples/typical/data/exec_2d.sh b/docs/source/examples/typical/data/exec_2d.sh index d9305b31..a4e0ce62 100644 --- a/docs/source/examples/typical/data/exec_2d.sh +++ b/docs/source/examples/typical/data/exec_2d.sh @@ -3,14 +3,18 @@ set -x set -e +lx=1.0e+0 +ly=2.0e+0 +Ra=1.0e+8 +Pr=1.0e+1 + # set initial condition cd initial_condition make output -lx=1.0e+0 \ -ly=2.0e+0 \ +lx=${lx} \ +ly=${ly} \ glisize=128 \ gljsize=256 \ -uniformx=false \ python main.py output cd .. @@ -25,33 +29,53 @@ stat_rate=1.0e-1 \ stat_after=2.0e+2 \ coef_dt_adv=0.95 \ coef_dt_dif=0.95 \ -Ra=1.0e+8 \ -Pr=1.0e+1 \ +Ra=${Ra} \ +Pr=${Pr} \ mpirun -n 2 --oversubscribe ./a.out initial_condition/output # post process mkdir artifacts + +# visualise python \ docs/source/examples/typical/data/snapshot_2d.py \ $(find output/save -type d | sort | tail -n 1) \ - artifacts/snapshot_2d.png + artifacts/snapshot.png + +# divergence-t plot python \ docs/source/examples/typical/data/divergence.py \ - output/log/divergence.dat \ - artifacts/divergence_2d.png + output/log/max_divergence.dat \ + artifacts/divergence.png + +# Nu-t plot +ly=${ly} \ +Ra=${Ra} \ +Pr=${Pr} \ python \ docs/source/examples/typical/data/nusselt_time.py \ - output/log/nusselt.dat \ - artifacts/nusselt_time_2d.png + output/log/heat_transfer.dat \ + output/log/injected_squared_velocity.dat \ + output/log/dissipated_squared_velocity.dat \ + output/log/dissipated_squared_temperature.dat \ + artifacts/nusselt_time.png + +# Nu-x plot +ly=${ly} \ +Ra=${Ra} \ +Pr=${Pr} \ python \ docs/source/examples/typical/data/nusselt_x.py \ $(find output/stat -type d | sort | tail -n 1) \ - artifacts/nusselt_x_2d.png + artifacts/nusselt_x.png + +# standard deviations in x python \ docs/source/examples/typical/data/std.py \ $(find output/stat -type d | sort | tail -n 1) \ - artifacts/std_2d.png -echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci_2d.txt -echo "Date :" $(date) >> artifacts/ci_2d.txt -echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci_2d.txt + artifacts/std.png + +echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci.txt +echo "Date :" $(date) >> artifacts/ci.txt +echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci.txt diff --git a/docs/source/examples/typical/data/exec_3d.sh b/docs/source/examples/typical/data/exec_3d.sh index 644e88e1..4b3ed723 100644 --- a/docs/source/examples/typical/data/exec_3d.sh +++ b/docs/source/examples/typical/data/exec_3d.sh @@ -3,21 +3,25 @@ set -x set -e +lx=1.0e+0 +ly=1.0e+0 +lz=1.0e+0 +Ra=1.0e+6 +Pr=1.0e+1 + # set initial condition cd initial_condition make output -lx=1.0e+0 \ -ly=1.0e+0 \ -lz=1.0e+0 \ +lx=${lx} \ +ly=${ly} \ +lz=${lz} \ glisize=32 \ gljsize=32 \ glksize=32 \ -uniformx=false \ python main.py output cd .. # build and execute -sed -i "s/DNDIMS=2/DNDIMS=3/g" Makefile make all && make output timemax=3.0e+2 \ wtimemax=6.0e+2 \ @@ -28,33 +32,55 @@ stat_rate=1.0e-1 \ stat_after=2.0e+2 \ coef_dt_adv=0.95 \ coef_dt_dif=0.95 \ -Ra=1.0e+6 \ -Pr=1.0e+1 \ +Ra=${Ra} \ +Pr=${Pr} \ mpirun -n 2 --oversubscribe ./a.out initial_condition/output # post process mkdir artifacts + +# visualise python \ docs/source/examples/typical/data/snapshot_3d.py \ $(find output/save -type d | sort | tail -n 1) \ - artifacts/snapshot_3d.png + artifacts/snapshot.png + +# divergence-t plot python \ docs/source/examples/typical/data/divergence.py \ - output/log/divergence.dat \ - artifacts/divergence_3d.png + output/log/max_divergence.dat \ + artifacts/divergence.png + +# Nu-t plot +ly=${ly} \ +lz=${lz} \ +Ra=${Ra} \ +Pr=${Pr} \ python \ docs/source/examples/typical/data/nusselt_time.py \ - output/log/nusselt.dat \ - artifacts/nusselt_time_3d.png + output/log/heat_transfer.dat \ + output/log/injected_squared_velocity.dat \ + output/log/dissipated_squared_velocity.dat \ + output/log/dissipated_squared_temperature.dat \ + artifacts/nusselt_time.png + +# Nu-x plot +ly=${ly} \ +lz=${lz} \ +Ra=${Ra} \ +Pr=${Pr} \ python \ docs/source/examples/typical/data/nusselt_x.py \ $(find output/stat -type d | sort | tail -n 1) \ - artifacts/nusselt_x_3d.png + artifacts/nusselt_x.png + +# standard deviations in x python \ docs/source/examples/typical/data/std.py \ $(find output/stat -type d | sort | tail -n 1) \ - artifacts/std_3d.png -echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci_3d.txt -echo "Date :" $(date) >> artifacts/ci_3d.txt -echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci_3d.txt + artifacts/std.png + +echo "OS :" $(cat /etc/os-release | grep PRETTY_NAME | awk -F "=" '{print $2}') >> artifacts/ci.txt +echo "Date :" $(date) >> artifacts/ci.txt +echo "Hash :" $(git rev-parse HEAD) >> artifacts/ci.txt diff --git a/docs/source/examples/typical/data/nusselt_time.py b/docs/source/examples/typical/data/nusselt_time.py index 3376ac41..d0237129 100644 --- a/docs/source/examples/typical/data/nusselt_time.py +++ b/docs/source/examples/typical/data/nusselt_time.py @@ -1,3 +1,4 @@ +import os import sys import numpy as np import matplotlib @@ -6,36 +7,59 @@ def load(fname, xmin): data = np.loadtxt(fname) - x = data[:, 0] - y0 = data[:, 1] - y1 = data[:, 2] - y2 = data[:, 3] - y3 = data[:, 4] - y0 = y0[x > xmin] - y1 = y1[x > xmin] - y2 = y2[x > xmin] - y3 = y3[x > xmin] - x = x[x > xmin] - return x, y0, y1, y2, y3 + x = data[:, 0] + y = data[:, 1] + y = y[xmin < x] + x = x[xmin < x] + return x, y + +def get_ref_heat_transfer(): + ly = os.environ.get("ly") + lz = os.environ.get("lz") + Ra = os.environ.get("Ra") + Pr = os.environ.get("Pr") + ly = float(ly) + lz = float(lz) if lz else 1. + Ra = float(Ra) + Pr = float(Pr) + return ly * lz / Ra**0.5 / Pr**0.5 if __name__ == "__main__": argv = sys.argv - assert(3 == len(argv)) - ifname = argv[1] - ofname = argv[2] - is2d = True if "2" in ofname else False + assert 6 == len(argv) + ifnames = argv[1:-1] + ofname = argv[-1] + is2d = True if None == os.environ.get("lz") else False xmin = 200. - x, y0, y1, y2, y3 = load(ifname, xmin) + q_ref = get_ref_heat_transfer() fig = pyplot.figure() ax = fig.add_subplot(111) - ax.plot(x, y0, color="#FF0000", label="heat flux") - ax.plot(x, y1, color="#0000FF", label="injection") - ax.plot(x, y2, color="#33AA00", label="kinetic dissipation") - ax.plot(x, y3, color="#FF00FF", label="thermal dissipation") + colors = [ + "#FF0000", + "#0000FF", + "#33AA00", + "#FF00FF", + ] + labels = [ + "heat transfer", + "kinetic energy injection", + "kinetic energy dissipation", + "thermal energy dissipation", + ] + converters = [ + lambda x: x / q_ref, + lambda x: (x + q_ref) / q_ref, + lambda x: (x + q_ref) / q_ref, + lambda x: x / q_ref, + ] + for color, label, converter, ifname in zip(colors, labels, converters, ifnames): + x, y = load(ifname, xmin) + y = converter(y) + ax.plot(x, y, color=color, label=label) if is2d: # van der Poel et al., JFM, 2013 - y4 = np.full(x.shape, 27.25) - ax.plot(x, y4, color="#000000", linestyle="--") + y = np.full(x.shape, 27.25) + ax.plot(x, y, color="#000000", linestyle="dashed") kwrds = { "title": "", "xlim": [xmin, 300.], diff --git a/docs/source/examples/typical/data/nusselt_x.py b/docs/source/examples/typical/data/nusselt_x.py index b1f02920..f0d6e469 100644 --- a/docs/source/examples/typical/data/nusselt_x.py +++ b/docs/source/examples/typical/data/nusselt_x.py @@ -5,29 +5,36 @@ matplotlib.use("Agg") from matplotlib import pyplot +def get_ref_heat_transfer(): + ly = os.environ.get("ly") + lz = os.environ.get("lz") + Ra = os.environ.get("Ra") + Pr = os.environ.get("Pr") + ly = float(ly) + lz = float(lz) if lz else 1. + Ra = float(Ra) + Pr = float(Pr) + return ly * lz / Ra**0.5 / Pr**0.5 + def load_and_process(dname): - coef = np.load(f"{dname}/t_dif.npy") - xf = np.load(f"{dname}/xf.npy") - xc = np.load(f"{dname}/xc.npy") - num = np.load(f"{dname}/num.npy") - t1 = np.load(f"{dname}/t1.npy") - uxt = np.load(f"{dname}/uxt.npy") - glsizes = np.load(f"{dname}/glsizes.npy") - if 3 == len(glsizes): - ngrids = 1. * glsizes[1] * glsizes[2] - else: - ngrids = 1. * glsizes[1] - # divide by the number of grids in the homogeneous direction(s) - t1 /= ngrids - uxt /= ngrids + xf = np.load(f"{dname}/xf.npy") + num = np.load(f"{dname}/num.npy") + adv = np.load(f"{dname}/adv.npy") + dif = np.load(f"{dname}/dif.npy") + is3d = 3 == len(adv.shape) + # normalise + adv /= get_ref_heat_transfer() + dif /= get_ref_heat_transfer() # divide by the number of samples - t1 /= num - uxt /= num - # adv - adv = 1. / coef * uxt - # dif - dif = -1. * np.diff(t1) / np.diff(xc) - # average [0:1/2] and [1/2:1] + adv /= num + dif /= num + # average in the homogeneous direction(s) + adv = np.sum(adv, axis=0) + dif = np.sum(dif, axis=0) + if is3d: + adv = np.sum(adv, axis=0) + dif = np.sum(dif, axis=0) + # average bottom-half and top-half nx = len(xf) // 2 adv = 0.5 * adv + 0.5 * adv[::-1] dif = 0.5 * dif + 0.5 * dif[::-1] @@ -35,7 +42,7 @@ def load_and_process(dname): if __name__ == "__main__": argv = sys.argv - assert(3 == len(argv)) + assert 3 == len(argv) idname = argv[1] ofname = argv[2] x, adv, dif = load_and_process(idname) diff --git a/docs/source/examples/typical/data/std.py b/docs/source/examples/typical/data/std.py index 5333b7a4..ac67a8c6 100644 --- a/docs/source/examples/typical/data/std.py +++ b/docs/source/examples/typical/data/std.py @@ -7,10 +7,6 @@ def load(dname): glsizes = np.load(f"{dname}/glsizes.npy") is3d = True if 3 == len(glsizes) else False - if is3d: - ngrids = 1. * glsizes[1] * glsizes[2] - else: - ngrids = 1. * glsizes[1] num = np.load(f"{dname}/num.npy") xf = np.load(f"{dname}/xf.npy") xc = np.load(f"{dname}/xc.npy") @@ -26,7 +22,7 @@ def load(dname): uz2 = None t1 = np.load(f"{dname}/t1.npy") t2 = np.load(f"{dname}/t2.npy") - return is3d, num * ngrids, xf, xc, ux1, ux2, uy1, uy2, uz1, uz2, t1, t2 + return is3d, num, xf, xc, ux1, ux2, uy1, uy2, uz1, uz2, t1, t2 def compute_rms(data1, data2): rms2 = data2 - np.power(data1, 2.) @@ -57,12 +53,22 @@ def trunc(arr): ux2 = compute_rms(ux1, ux2) uy2 = compute_rms(uy1, uy2) if is3d: - uz2 = compute_rms(uz1, uz2) - t2 = 0.5*(t2[:]+t2[::-1]) - ux2 = 0.5*(ux2[:]+ux2[::-1]) - uy2 = 0.5*(uy2[:]+uy2[::-1]) + uz2 = compute_rms(uz1, uz2) + t2 = np.average(t2, axis=0) + ux2 = np.average(ux2, axis=0) + uy2 = np.average(uy2, axis=0) + if is3d: + uz2 = np.average(uz2, axis=0) + if is3d: + t2 = np.average(t2, axis=0) + ux2 = np.average(ux2, axis=0) + uy2 = np.average(uy2, axis=0) + uz2 = np.average(uz2, axis=0) + t2 = 0.5 * t2[:] + 0.5 * t2[::-1] + ux2 = 0.5 * ux2[:] + 0.5 * ux2[::-1] + uy2 = 0.5 * uy2[:] + 0.5 * uy2[::-1] if is3d: - uz2 = 0.5*(uz2[:]+uz2[::-1]) + uz2 = 0.5 * uz2[:] + 0.5 * uz2[::-1] # xf = trunc(xf) xc = trunc(xc) diff --git a/docs/source/examples/typical/main.rst b/docs/source/examples/typical/main.rst index 2e47288d..9cace4ac 100644 --- a/docs/source/examples/typical/main.rst +++ b/docs/source/examples/typical/main.rst @@ -3,121 +3,69 @@ .. include:: /references.txt -############################# -Typical 2D and 3D simulations -############################# - -.. mydetails:: Signatures - - .. literalinclude:: data/ci_2d.txt - :language: text - - .. literalinclude:: data/ci_3d.txt - :language: text - -************* -Configuration -************* - -.. mydetails:: Two-dimensional case: - - .. literalinclude:: data/exec_2d.sh - :language: sh - :linenos: - -.. mydetails:: Three-dimensional case: - - .. literalinclude:: data/exec_3d.sh - :language: sh - :linenos: +############# +Typical Cases +############# ************** Visualisations ************** -Temperature fields at the end of the simulations are visualised here. - -* 2D: - - .. image:: data/snapshot_2d.png - :width: 600 - - .. mydetails:: Script - - .. literalinclude:: data/snapshot_2d.py - :language: python - :linenos: +Temperature fields at the end of the simulations. -* 3D +2D: - .. image:: data/snapshot_3d.png - :width: 600 +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-2d/snapshot.png + :width: 600 - .. mydetails:: Script +3D: - .. literalinclude:: data/snapshot_3d.py - :language: python - :linenos: +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-3d/snapshot.png + :width: 600 **************************** Incompressibility constraint **************************** -The local divergence of the velocity fields should be sufficiently small, which is monitored during the simulation and checked here. +Maximum divergence of the velocity field, which should be sufficiently small. -* 2D +2D: - .. image:: data/divergence_2d.png - :width: 600 +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-2d/divergence.png + :width: 600 -* 3D +3D: - .. image:: data/divergence_3d.png - :width: 600 - -.. mydetails:: Script - - .. literalinclude:: data/divergence.py - :language: python - :linenos: - -.. seealso:: - - :ref:`src/logging/divergence.c ` +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-3d/divergence.png + :width: 600 *************** Nusselt numbers *************** -===================== -As a function of time -===================== +========= +Evolution +========= :math:`Nu` calculated using the different formulae, which are monitored during the run, are shown as a function of time: - * red: heat fluxes on the walls - - * blue: energy input +* red: heat fluxes on the walls - * green: kinetic energy dissipation +* blue: energy input - * magenta: thermal energy dissipation +* green: kinetic energy dissipation -* 2D +* magenta: thermal energy dissipation - .. image:: data/nusselt_time_2d.png - :width: 600 +2D: -* 3D +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-2d/nusselt_time.png + :width: 600 - .. image:: data/nusselt_time_3d.png - :width: 600 +3D: -.. mydetails:: Script - - .. literalinclude:: data/nusselt_time.py - :language: python - :linenos: +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-3d/nusselt_time.png + :width: 600 .. note:: @@ -125,41 +73,45 @@ As a function of time .. seealso:: - Check :ref:`Nusselt number relations ` to see the definition of each :math:`Nu`, and :ref:`src/logging/nusselt ` to see how they are computed. + :ref:`Nusselt number relations `. -======= -Average -======= +========================= +Temporary-Averaged Values +========================= -There are two contributions, advective contribution: +As derived :ref:`here `, there are two contributions which transfer heat: advective contribution: .. math:: - \ave{\ux T}{y,z,t}, + \sumzc + \sumyc + \frac{J}{\sfact{1}} + \vel{1} + \ave{T}{\gcs{1}}, and diffusive contribution: .. math:: - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{d}{dx} \ave{T}{y,z,t}, - -which are a function of the wall-normal position :math:`x` and shown separately: - -* 2D + - + \sumzc + \sumyc + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{J}{\sfact{1}} + \frac{1}{\sfact{1}} + \dif{T}{\gcs{1}}. - .. image:: data/nusselt_x_2d.png - :width: 600 +After averaged over time and homogeneous directions, they are displayed as a function of the wall-normal position :math:`x` here: -* 3D +2D: - .. image:: data/nusselt_x_3d.png - :width: 600 +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-2d/nusselt_x.png + :width: 600 -.. mydetails:: Script +3D: - .. literalinclude:: data/nusselt_x.py - :language: python - :linenos: +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-3d/nusselt_x.png + :width: 600 ******************* Standard deviations @@ -167,24 +119,17 @@ Standard deviations Variances of (red) :math:`\ux`, (blue) :math:`\uy`, (magenta) :math:`\uz`, and (green) :math:`T` are shown here. -* 2D - - .. image:: data/std_2d.png - :width: 600 - -* 3D +2D: - .. image:: data/std_3d.png - :width: 600 +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-2d/std.png + :width: 600 -.. mydetails:: Script +3D: - .. literalinclude:: data/std.py - :language: python - :linenos: +.. image:: https://raw.githubusercontent.com/NaokiHori/SimpleNSSolver/artifacts/artifacts/typical-3d/std.png + :width: 600 .. note:: - Although the :math:`y` and the :math:`z` directions are homogeneous, the blue and the magenta lines may show a deviation. - This is attributed to the low :math:`Ra` and the short simulation time to reduce the running cost. + Although the :math:`y` and the :math:`z` directions are homogeneous, the blue and magenta lines may deviate, which is attributed to the low :math:`Ra` and short time. diff --git a/docs/source/implementation/decide_dt.rst b/docs/source/implementation/decide_dt.rst deleted file mode 100644 index 28b33194..00000000 --- a/docs/source/implementation/decide_dt.rst +++ /dev/null @@ -1,99 +0,0 @@ - -.. include:: /references.txt - -.. _decide_dt: - -################## -`src/decide_dt.c`_ -################## - -.. _src/decide_dt.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/decide_dt.c - -This file contains functions which determine the next time step size ``dt`` (:math:`\Delta t`) used to integrate the governing equations. - -.. mydeclare:: /../../src/decide_dt.c - :language: c - :tag: decide_dt - -As discussed in :ref:`the temporal integration techniques `, there are two time scales in the current system which affect the stability to integrate the equations in time, which are considered separately here: - -.. myliteralinclude:: /../../src/decide_dt.c - :language: c - :tag: compute advective and diffusive constraints - -After all possible time step sizes (one scalar value from the advective terms, ``NDIMS`` values from the diffusive terms) are computed, I extract the smallest value - -.. myliteralinclude:: /../../src/decide_dt.c - :language: c - :tag: choose smallest value as dt - -and use it as the next time step. - -.. note:: - - When the diffusive terms are treated implicitly, I eliminate the constraint in the direction. - Namely, I only take into account the diffusive constraints if the direction is treated explicitly. - -********************* -Advective constraints -********************* - -.. mydeclare:: /../../src/decide_dt.c - :language: c - :tag: decide_dt_adv - -Advective restriction is computed here, in particular by computing the ratio of the local grid size and the magnitude of the local velocity: - -* :math:`x` direction - - .. myliteralinclude:: /../../src/decide_dt.c - :language: c - :tag: compute grid-size over velocity in x - -* :math:`y` direction - - .. myliteralinclude:: /../../src/decide_dt.c - :language: c - :tag: compute grid-size over velocity in y - -* :math:`z` direction - - .. myliteralinclude:: /../../src/decide_dt.c - :language: c - :tag: compute grid-size over velocity in z - -.. note:: - - A small number is added to avoid zero divisions. - -After unifying the result among all processes, a safety factor (given by the user) is multiplied: - -.. myliteralinclude:: /../../src/decide_dt.c - :language: c - :tag: unify result, multiply safety factor - -********************* -Diffusive constraints -********************* - -.. mydeclare:: /../../src/decide_dt.c - :language: c - :tag: decide_dt_dif - -Diffusive restrictions in all dimensions are computed here following - -.. math:: - - \frac{C}{2 \times \text{NDIMS}} \left[ \min \left( \Delta x_i \right) \right]^2, - -in each dimension, where :math:`C` is a pre-factor (the inverse of the diffusivity times the safety factor) and :math:`\min \left( \Delta x_i \right)` is the smallest grid size in the direction: - -.. myliteralinclude:: /../../src/decide_dt.c - :language: c - :tag: compute diffusive constraints - -.. note:: - - Roughly speaking the safety factors are smaller than 1. - Although the three-step Runge-Kutta scheme which is used throughout this project allows slightly larger value than 1 (c.f. |COSTA2018|), one should remember that, the larger :math:`\Delta t` is used, the more dissipative the scheme becomes (c.f. |MORINISHI1998|). - diff --git a/docs/source/implementation/domain.rst b/docs/source/implementation/domain.rst deleted file mode 100644 index 285c4c6f..00000000 --- a/docs/source/implementation/domain.rst +++ /dev/null @@ -1,88 +0,0 @@ - -.. _domain: - -############### -`src/domain.c`_ -############### - -.. _src/domain.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/domain.c - -This file contains functions for manipulating a structure ``domain_t``, which stores spatial information about the domain as well as information about `the domain decomposition `_. - -************** -Initialisation -************** - -.. mydeclare:: /../../src/domain.c - :language: c - :tag: domain_init - -A structure ``domain_t`` is defined in `include/domain.h `_, which contains bunch of parameters: - -.. myliteralinclude:: /../../include/domain.h - :language: c - :tag: definition of a structure domain_t - -This function ``domain_init`` allocates and initialises a structure ``domain_t`` and its members, whose pointer is returned. - -There are several important steps, which are summarised as follows: - -#. Loading spatial information - - The size of the **global** array ``glsizes`` (see :ref:`the domain setup `), which should be provided by the user as a set of initial conditions, is loaded. - - .. myliteralinclude:: /../../src/domain.c - :language: c - :tag: load spatial information - - At the same time, the physical sizes (lengths) of the domain (``lengths``) and the grid positions in the :math:`x` direction (``xf`` and ``xc``) are loaded. - - .. seealso:: - - ``domain_save`` described below. - -#. Compute grid sizes - - Based on the loaded information in the previous step, grid sizes in all directions are computed here: - - .. myliteralinclude:: /../../src/domain.c - :language: c - :tag: compute grid sizes - - Here ``dxf`` and ``dxc`` define face-to-face and center-to-center distances in the :math:`x` direction, respectively. - In the other directions, since grids are equidistantly positioned, grid sizes are simply given by the length of the domain divided by the number of grids. - -#. Decomposing the domain into ``pencils``. - - Since I know the global size of the system now, my next objective is to split the whole domain and to distribute each segment to each process, which is achieved by a function ``sdecomp.construct`` provided by `SimpleDecomp `_ library: - - .. myliteralinclude:: /../../src/domain.c - :language: c - :tag: initialise sdecomp to distribute the domain - - .. note:: - - In the three-dimensional case, the number of processes in the :math:`y` and the :math:`z` directions is adjusted to minimise the cost of pencil rotations, which are crucial for :ref:`solving the Poisson equations efficiently `. - -#. Compute local array sizes and offsets - - One the domain decomposition is complete, I can calculate the size of the **local** array ``mysizes`` and offsets in terms of the global positions ``offsets``: - - .. myliteralinclude:: /../../src/domain.c - :language: c - :tag: local array sizes and offsets - -************* -Load and save -************* - -.. mydeclare:: /../../src/domain.c - :language: c - :tag: domain_load - -.. mydeclare:: /../../src/domain.c - :language: c - :tag: domain_save - -These functions load or save some of the members in ``domain_t`` related to the domain sizes and the resolutions. - diff --git a/docs/source/implementation/fileio.rst b/docs/source/implementation/fileio.rst deleted file mode 100644 index 2d386d12..00000000 --- a/docs/source/implementation/fileio.rst +++ /dev/null @@ -1,38 +0,0 @@ - -.. _fileio: - -############### -`src/fileio.c`_ -############### - -.. _src/fileio.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/fileio.c - -``fileio.c`` contains functions that handle file input/output operations. -Mostly they are designed to read and write files in `NPY format `_. - -The reasons for using the ``NPY`` format in this project are as follows: - -#. Being binary - - I should avoid saving arrays in non-binary (text, e.g. ASCII) format, as the original floating-point data will be modulated and the file size increases even further. - -#. Being simple - - To be able to recover the original data later, I need to store the datatype and shape as metadata with the raw data. - In a ``NPY`` file, this information is stored in the header as ``descr`` and ``shape`` values respectively. - - .. note:: - - The `HDF5 format `_ offers the same or more general functionality. - However, it is too general to store simple scalars, vectors, and structured arrays used in this library (see `this page `_). - Moreover, I would like to reduce the number of external libraries to keep things simple. - -#. Being general - - `NumPy `_ is one of the most popular libraries for scientific computing (especially for post-processing and visualisation with `Matplotlib `_), and the ``NPY`` format is its native binary format. - Thanks to its simple structure, this format is easily accessible from other languages (e.g. `Rust `_, `MATLAB `_). - -.. seealso:: - - Reading and writing ``NPY`` headers rely on my library `SimpleNpyIO `_. - diff --git a/docs/source/implementation/fluid/boundary.rst b/docs/source/implementation/fluid/boundary.rst deleted file mode 100644 index 2beaeb32..00000000 --- a/docs/source/implementation/fluid/boundary.rst +++ /dev/null @@ -1,98 +0,0 @@ - -.. _fluid_boundary: - -##################### -`src/fluid/boundary`_ -##################### - -.. _src/fluid/boundary: https://github.com/NaokiHori/SimpleNSSolver/tree/main/src/fluid/boundary - -This directory contains functions to update the boundary values of the velocity, the pressure, the scalar potential, and the temperature. -In particular, there are two major roles which are processed here: - -* imposing given boundary conditions, - -* exchanging the halo cells using the inter-process communications. - -.. mydeclare:: /../../src/fluid/boundary/ux.c - :language: c - :tag: fluid_update_boundaries_ux - -.. mydeclare:: /../../src/fluid/boundary/uy.c - :language: c - :tag: fluid_update_boundaries_uy - -.. mydeclare:: /../../src/fluid/boundary/uz.c - :language: c - :tag: fluid_update_boundaries_uz - -.. mydeclare:: /../../src/fluid/boundary/p.c - :language: c - :tag: fluid_update_boundaries_p - -.. mydeclare:: /../../src/fluid/boundary/psi.c - :language: c - :tag: fluid_update_boundaries_psi - -.. mydeclare:: /../../src/fluid/boundary/t.c - :language: c - :tag: fluid_update_boundaries_t - -******************* -Boundary conditions -******************* - -Since the domain is wall-bounded in :math:`x`, - -* ``ux``: impermeable condition, - -* ``uy``: no-slip condition, - -* ``uz``: no-slip condition, - -* ``p``: Neumann condition, - -* ``psi``: Neumann condition, - -* ``t``: Dirichlet condition, - -are to be imposed on the walls: - -.. myliteralinclude:: /../../src/fluid/boundary/ux.c - :language: c - :tag: set boundary values - -.. myliteralinclude:: /../../src/fluid/boundary/uy.c - :language: c - :tag: set boundary values - -.. myliteralinclude:: /../../src/fluid/boundary/uz.c - :language: c - :tag: set boundary values - -.. myliteralinclude:: /../../src/fluid/boundary/p.c - :language: c - :tag: set boundary values - -.. myliteralinclude:: /../../src/fluid/boundary/psi.c - :language: c - :tag: set boundary values - -.. myliteralinclude:: /../../src/fluid/boundary/t.c - :language: c - :tag: set boundary values - -******************* -Halo communications -******************* - -Finite-difference methods evaluate the derivatives using the surrounding values. -Since I decompose the domain into several chunks, to evaluate the derivatives close to the edges, I need information which the process does not have by default. - -Basically, when updating the momentum or the temperature fields, each process is responsible for updating the scalar values between ``1`` and ``jsize`` in the :math:`y` direction (see :ref:`src/fluid/predict `). -Thus communications are needed to obtain values at ``j = 0`` and ``jsize+1``, which are necessary to evaluate derivatives at ``j = 1`` and ``jsize``, respectively. - -.. seealso:: - - :ref:`src/halo.c ` - diff --git a/docs/source/implementation/fluid/compute_potential/dct.rst b/docs/source/implementation/fluid/compute_potential/dct.rst deleted file mode 100644 index 760bd2c2..00000000 --- a/docs/source/implementation/fluid/compute_potential/dct.rst +++ /dev/null @@ -1,112 +0,0 @@ -#################################### -`src/fluid/compute_potential/dct.c`_ -#################################### - -.. _src/fluid/compute_potential/dct.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/fluid/compute_potential/dct.c - -Since I assume the spacing is uniform, I can adopt the ``FFT``-based eigen decomposition in the :math:`x` direction as well as in the other homogeneous directions. - -It is possible to adopt the discrete fast Fourier transforms in all directions whose cost is :math:`\mathcal{O} \left( N \log N \right)` in each direction. -Only for the last direction, however, I can solve the equation using the Thomas algorithm whose cost is :math:`\mathcal{O} \left( N \right)`. -Although the difference should be small, I use the Thomas algorithm since the linear solver is implemented already and used in the other parts of the project. - -#. Project :math:`x` direction to wave space - - .. math:: - - f \left( x, y \right) - \rightarrow - f \left( k_x, y \right), - - .. math:: - - f \left( x, y, z \right) - \rightarrow - f \left( k_x, y, z \right). - - .. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: project x to wave space - -#. Transpose ``x1pencils`` to ``y1pencils`` - - Memory order is changed from :math:`x`-contiguous to :math:`y`-contiguous to benefit the following procedures. - - .. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: transpose real x1pencil to y1pencil - -#. (3D only) Project :math:`y` direction to wave space - - .. math:: - - f \left( k_x, y, z \right) - \rightarrow - f \left( k_x, k_y, z \right). - - .. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: project y to wave space - -#. (3D only) Transpose ``y1pencils`` to ``z1pencils`` - - Memory order is changed from :math:`y`-contiguous to :math:`z`-contiguous to benefit the following procedures. - - .. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: transpose complex y1pencil to z1pencil - -#. Solve linear systems - - I am left with the linear systems (tri-diagonal matrices), which are solved here. - - .. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: solve linear systems - -#. (3D only) Transpose ``z1pencils`` to ``y1pencils`` - - Memory order is changed from :math:`z`-contiguous to :math:`y`-contiguous to benefit the following procedures. - - .. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: transpose complex z1pencil to y1pencil - -#. (3D only) Project :math:`y` direction to physical space - - .. math:: - - f \left( k_x, k_y, z \right) - \rightarrow - f \left( k_x, y, z \right) - - .. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: project y to physical space - -#. Transpose ``y1pencils`` to ``x1pencils`` - - Memory order is changed from :math:`y`-contiguous to :math:`x`-contiguous to benefit the following procedures. - - .. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: transpose real y1pencil to x1pencil - -#. Project :math:`x` direction to physical space - - .. math:: - - f \left( k_x, y \right) - \rightarrow - f \left( x, y \right), - - .. math:: - - f \left( k_x, y, z \right) - \rightarrow - f \left( x, y, z \right). - - .. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: project x to physical space - diff --git a/docs/source/implementation/fluid/compute_potential/dft.rst b/docs/source/implementation/fluid/compute_potential/dft.rst deleted file mode 100644 index 3479ac74..00000000 --- a/docs/source/implementation/fluid/compute_potential/dft.rst +++ /dev/null @@ -1,125 +0,0 @@ -#################################### -`src/fluid/compute_potential/dft.c`_ -#################################### - -.. _src/fluid/compute_potential/dft.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/fluid/compute_potential/dft.c - -Since I assume the spacing can be non-uniform, I cannot adopt the ``FFT``-based eigen decomposition in the :math:`x` direction. -Thus, I first treat the other homogeneous directions by means of ``DFT`` to diagonalise the systems, which is followed by solving the linear systems in the :math:`x` direction. - -#. Transpose ``x1pencils`` to ``y1pencils`` - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: transpose real x1pencil to y1pencil - -#. Project :math:`y` direction to wave space - - .. math:: - - f \left( x, y \right) - \rightarrow - f \left( x, k_y \right), - - .. math:: - - f \left( x, y, z \right) - \rightarrow - f \left( x, k_y, z \right). - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: project y to wave space - -#. (2D only) Transpose ``y1pencils`` to ``x1pencils`` - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: transpose complex y1pencil to x1pencil - -#. (3D only) Transpose ``y1pencils`` to ``z1pencils`` - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: transpose complex y1pencil to z1pencil - -#. (3D only) Project :math:`z` direction to wave space - - .. math:: - - f \left( x, k_y, z \right) - \rightarrow - f \left( x, k_y, k_z \right). - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: project z to wave space - -#. (3D only) Transpose ``z1pencils`` to ``x2pencils`` - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: transpose complex z1pencil to x2pencil - -#. Solve linear systems - - I am left with the linear systems (tri-diagonal matrices), which are solved here. - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: transpose complex y1pencil to z1pencil - -#. (2D only) Transpose ``x1pencils`` to ``y1pencils`` - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: transpose complex x1pencil to y1pencil - -#. (3D only) Transpose ``x2pencils`` to ``z1pencils`` - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: transpose complex x2pencil to z1pencil - -#. (3D only) Project :math:`z` direction to physical space - - .. math:: - - f \left( x, k_y, k_z \right) - \rightarrow - f \left( x, k_y, z \right). - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: project z to physical space - -#. (3D only) Transpose ``z1pencils`` to ``y1pencils`` - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: transpose complex z1pencil to y1pencil - -#. Project :math:`y` direction to physical space - - .. math:: - - f \left( x, k_y \right) - \rightarrow - f \left( x, y \right), - - .. math:: - - f \left( x, k_y, z \right) - \rightarrow - f \left( x, y, z \right). - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: project y to physical space - -#. Transpose ``y1pencils`` to ``x1pencils`` - - .. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: transpose real y1pencil to x1pencil - diff --git a/docs/source/implementation/fluid/compute_potential/main.rst b/docs/source/implementation/fluid/compute_potential/main.rst deleted file mode 100644 index 3bd2b3f3..00000000 --- a/docs/source/implementation/fluid/compute_potential/main.rst +++ /dev/null @@ -1,70 +0,0 @@ - -.. _fluid_compute_potential: - -############################## -`src/fluid/compute_potential`_ -############################## - -.. _src/fluid/compute_potential: https://github.com/NaokiHori/SimpleNSSolver/tree/main/src/fluid/compute_potential - -This directory contains functions to compute a scalar potential :math:`\psi` which projects a non-solenoidal velocity field to the solenoidal one by solving a Poisson equation - -.. math:: - - \frac{\delta^2 \psi}{\delta x_i \delta x_i} - = - \frac{1}{\gamma \Delta t} \frac{\delta u_i^*}{\delta x_i}. - -The reason why I need to solve this equation is discussed in :ref:`the SMAC method `. - -The mathematical background is briefly described in the following pages: - -.. toctree:: - :maxdepth: 1 - - math/governing_equation - math/orthogonal_decomposition - -.. mydeclare:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: fluid_compute_potential_dft - -.. mydeclare:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: fluid_compute_potential_dct - -.. warning:: - - Notice the difference between the global array sizes ``glisize``, ``gljsize`` (given as the input values) and the local array sizes ``myisize``, ``myjsize`` (``pencil`` sizes). - They are frequently switched in the description below because of the extensive use of the pencil rotations. - -This function solves the Poisson equation described above. -Although it is a simple Poisson equation, the implementation is slightly complicated. -To make things easier, I encapsulate many complicated stuffs into one structure, which needs to be initialised when this function is called for the first time: - -To solve the equation, I need the right-hand side of the equation, which is computed and stored in a buffer: - -.. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: compute right-hand side of Poisson equation - -.. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: compute right-hand side of Poisson equation - -.. note:: - - Two buffers are used, ``buf0`` and ``buf1``. - The right-hand-side values are assigned to ``buf0``. - -In this project, I use two algorithms to solve the Poisson equation. -One is a normal algorithm which depends on the discrete Fourier transforms (``DFT`` version), which is versatile and can be used anytime. - -Another one is more efficient thanks to the discrete cosine transforms (``DCT`` version) and is approximately two times faster since the number of ``all-to-all`` communications are roughly halved. -However, this version is only applicable and invoked when the grid points in the :math:`x` direction are equidistantly placed. - -.. toctree:: - - dft - dct - diff --git a/docs/source/implementation/fluid/compute_potential/math/governing_equation.rst b/docs/source/implementation/fluid/compute_potential/math/governing_equation.rst deleted file mode 100644 index 791d014f..00000000 --- a/docs/source/implementation/fluid/compute_potential/math/governing_equation.rst +++ /dev/null @@ -1,59 +0,0 @@ - -.. _poisson_governing_equation: - -################## -Governing equation -################## - -For simplicity, in this section, I consider a two-dimensional Poisson equation: - -.. math:: - - \der{}{x} \der{p}{x} - + - \der{}{y} \der{p}{y} - = - q, - -where - -.. math:: - - p = p \left( x, y \right), - -.. math:: - - q = q \left( x, y \right) - -and - -.. math:: - - x \in \left[ 0, l_x \right], - -.. math:: - - y \in \left[ 0, l_y \right]. - -The Neumann boundary condition is imposed in the :math:`x` direction: - -.. math:: - - \vat{\frac{\partial p}{\partial x}}{x = 0} - = - \vat{\frac{\partial p}{\partial x}}{x = l_x} - = - 0, - -while the periodicity is assumed in the :math:`y` direction: - -.. math:: - - \vat{\frac{\partial^n p}{\partial y^n}}{y = 0} - = - \vat{\frac{\partial^n p}{\partial y^n}}{y = l_y}, - -where :math:`n = 0, 1, 2, \cdots`. - -In :ref:`the next section `, the discretisation of this equation is described and discussed. - diff --git a/docs/source/implementation/fluid/compute_potential/math/orthogonal_decomposition.rst b/docs/source/implementation/fluid/compute_potential/math/orthogonal_decomposition.rst deleted file mode 100644 index 6202d0f7..00000000 --- a/docs/source/implementation/fluid/compute_potential/math/orthogonal_decomposition.rst +++ /dev/null @@ -1,323 +0,0 @@ - -.. _poisson_orthogonal_decomposition: - -######################## -Orthogonal decomposition -######################## - -By adopting the second-order-accurate central-difference approximation, the two-dimensional Poisson equation formulated in :ref:`the previous section ` is discretised as - -.. math:: - - \frac{ - \frac{ - \vat{p}{\pipp, \pjc} - - - \vat{p}{\pic, \pjc} - }{ - \vat{\Delta x}{\pip} - } - - - \frac{ - \vat{p}{\pic, \pjc} - - - \vat{p}{\pimm, \pjc} - }{ - \vat{\Delta x}{\pim} - } - }{ - \vat{\Delta x}{\pic} - } - + - \frac{ - \frac{ - \vat{p}{\pic, \pjpp} - - - \vat{p}{\pic, \pjc } - }{ - \Delta y - } - - - \frac{ - \vat{p}{\pic, \pjc } - - - \vat{p}{\pic, \pjmm} - }{ - \Delta y - } - }{ - \Delta y - } - = - \vat{q}{\pic, \pjc}, - -which is defined at each cell center. - -Note that I assume the grids in the :math:`y` direction are uniformly-distributed, while those in the :math:`x` direction can vary. - -Solving the above equation is equivalent to handle a linear system whose size is :math:`N_x \times N_y`, where :math:`N_x` and :math:`N_y` are the number of cell centers in each direction. -Solving it in a direct way such that the incompressibility is perfectly satisfied (in a computational sense) is impractical. - -To solve the system efficiently, I consider the orthogonal decomposition of the system in the uniform direction (:math:`y`), i.e. I aim to reduce the scattered nature of the original linear system by diagonalising it in the :math:`y` direction. - -For notational simplicity, I neglect the :math:`x` indices for a while. - -Since the grid points are equidistantly spaced and a periodic boundary condition is assumed in the :math:`y` direction, I expand the equation using the trigonometric functions (Fourier series): - -.. math:: - - \vat{p}{\pjc} - \equiv - \sum_{l = 0}^{N_y - 1} - \vat{P}{l} - \exp \left( \frac{2 \pi}{N_y} I \pjc l \right), - -where :math:`I` is the imaginary unit :math:`\sqrt{-1}`, while :math:`P_l` is the :math:`l`-th (discrete) wave number. -Note that :math:`P_l` satisfies the Hermitian symmetry since :math:`p_j \in \mathbb{R}`. - -The two neighbouring points which are necessary to evaluate the discrete Poisson equation are given by - -.. math:: - - \vat{p}{\pjmm} - & - = - \sum_{l = 0}^{N_y - 1} - \vat{P}{l} - \exp \left[ \frac{2 \pi}{N_y} I \left( \pjmm \right) l \right] - - & - = - \sum_{l = 0}^{N_y - 1} - \vat{P}{l} - \exp \left( \frac{2 \pi}{N_y} I \pjc l \right) - \exp \left( - \frac{2 \pi}{N_y} I l \right), - - \vat{p}{\pjpp} - & - = - \sum_{l = 0}^{N_y - 1} - \vat{P}{l} - \exp \left[ \frac{2 \pi}{N_y} I \left( \pjpp \right) l \right] - - & - = - \sum_{l = 0}^{N_y - 1} - \vat{P}{l} - \exp \left( \frac{2 \pi}{N_y} I \pjc l \right) - \exp \left( + \frac{2 \pi}{N_y} I l \right). - -Thus I find the left-hand side of the discrete Poisson equation consists of - -.. math:: - - \frac{ - \frac{ - \vat{p}{\pipp, \pjc} - - - \vat{p}{\pic, \pjc} - }{ - \vat{\Delta x}{\pip} - } - - - \frac{ - \vat{p}{\pic, \pjc} - - - \vat{p}{\pimm, \pjc} - }{ - \vat{\Delta x}{\pim} - } - }{ - \vat{\Delta x}{\pic} - } - & - = - \frac{1}{\Delta x_{\pic} \Delta x_{\pim}} - \sum_{l = 0}^{N_y - 1} - \vat{P}{\pimm, l} - \exp \left( \frac{2 \pi}{N_y} I \pjc l \right) - - & - - - \left( - \frac{1}{\Delta x_{\pic} \Delta x_{\pim}} - + - \frac{1}{\Delta x_{\pic} \Delta x_{\pip}} - \right) - \sum_{l = 0}^{N_y - 1} - \vat{P}{\pic, l} - \exp \left( \frac{2 \pi}{N_y} I \pjc l \right) - - & - + - \frac{1}{\Delta x_{\pip} \Delta x_{\pip}} - \sum_{l = 0}^{N_y - 1} - \vat{P}{\pipp, l} - \exp \left( \frac{2 \pi}{N_y} I \pjc l \right) - -and - -.. math:: - - \frac{1}{\Delta y^2} - \left( - p_{\pic, \pjpp} - - - 2 p_{\pic, \pjc} - + - p_{\pic, \pjmm} - \right) - & - = - \frac{1}{\Delta y^2} - \sum_{l = 0}^{N_y - 1} - \vat{P}{\pic, l} - \exp \left( \frac{2 \pi}{N_y} I \pjc l \right) - \left[ - \exp \left( + \frac{2 \pi}{N_y} I l \right) - - - 2 - + - \exp \left( - \frac{2 \pi}{N_y} I l \right) - \right] - - & - = - \frac{1}{\Delta y^2} - \sum_{l = 0}^{N_y - 1} - \vat{P}{\pic, l} - \exp \left( \frac{2 \pi}{N_y} I \pjc l \right) - \left[ - 2 - \cos \left( \frac{2 \pi}{N_y} l \right) - - - 2 - \right] - - & - = - - - \sum_{l = 0}^{N_y - 1} - \vat{P}{\pic, l} - \exp \left( \frac{2 \pi}{N_y} I \pjc l \right) - \frac{ - 4 \sin^2 \left( \frac{\pi}{N_y} l \right) - }{ - \Delta y^2 - }, - -while the right-hand side yields - -.. math:: - - q_{\pic, \pjc} - = - \sum_{l = 0}^{N_y - 1} - \vat{Q}{\pic, l} - \exp \left( \frac{2 \pi}{N_y} I \pjc l \right). - -Finally, by adopting the orthogonality of the trigonometric functions, I obtain - -.. math:: - - \frac{1}{\Delta x_{\pic} \Delta x_{\pim}} - \vat{P}{\pimm, l} - - - \left( - \frac{1}{\Delta x_{\pic} \Delta x_{\pim}} - + - \frac{1}{\Delta x_{\pic} \Delta x_{\pip}} - + - \frac{ - 4 \sin^2 \left( \frac{\pi}{N_y} l \right) - }{ - \Delta y^2 - } - \right) - \vat{P}{\pic, l} - + - \frac{1}{\Delta x_{\pip} \Delta x_{\pip}} - \vat{P}{\pipp, l} - = - \vat{Q}{\pic, l}, - -where - -.. math:: - - P_{\pic, l} - \equiv - \sum_{l = 0}^{N_y - 1} - \vat{p}{\pic, \pjc} - \exp \left( - \frac{2 \pi}{N_y} I \pjc l \right), - - P_{\pic, l} - \equiv - \sum_{l = 0}^{N_y - 1} - \vat{p}{\pic, \pjc} - \exp \left( - \frac{2 \pi}{N_y} I \pjc l \right), - -and the conversions between the physical space to the spectral space can be done using the Fast Fourier Transforms. - -Note that the :math:`j` variance which was present in the original equation is now gone, and instead I obtain a linear system in :math:`x` direction, which is tri-diagonal: - -.. math:: - - \vat{l}{\pic} \vat{P}{\pimm, l} - + - \vat{c}{\pic} \vat{P}{\pic, l} - + - \vat{u}{\pic} \vat{P}{\pipp, l} - = - \vat{Q}{\pic, l}. - -See above for the definitions of the coefficients. - -The lower- and upper-diagonal parts are assigned here: - -.. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: initialise tri-diagonal matrix in x direction - -.. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: initialise tri-diagonal matrix in y direction - -.. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: initialise tri-diagonal matrix in z direction - -which are initialised when the solver is called for the first time and are reused. - -The center-diagonal part is computed every time when the system is solved: - -.. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: set center diagonal components - -.. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: set center diagonal components - -The wave numbers are computed beforehand: - -.. myliteralinclude:: /../../src/fluid/compute_potential/dft.c - :language: c - :tag: initialise eigenvalues in homogeneous directions - -.. myliteralinclude:: /../../src/fluid/compute_potential/dct.c - :language: c - :tag: initialise eigenvalues in homogeneous directions - -Note that the resulting total computational cost is roughly - -.. math:: - - \mathcal{O} \left( N_x N_y \log N_y \right), - -which is a drastic reduction from the original value: - -.. math:: - - \mathcal{O} \left( N_x^2 N_y^2 \right). - diff --git a/docs/source/implementation/fluid/correct_velocity.rst b/docs/source/implementation/fluid/correct_velocity.rst deleted file mode 100644 index f35cd4cf..00000000 --- a/docs/source/implementation/fluid/correct_velocity.rst +++ /dev/null @@ -1,77 +0,0 @@ - -.. _fluid_correct_velocity: - -############################# -`src/fluid/correct_velocity`_ -############################# - -.. _src/fluid/correct_velocity: https://github.com/NaokiHori/SimpleNSSolver/tree/main/src/fluid/correct_velocity - -This directory contains functions to project (correct) the non-solenoidal velocity :math:`u_i^*` to the solenoidal (and new-step) field :math:`u_i^{n+1}` following the relation - -.. math:: - - u_i^{n+1} - - - u_i^* - = - - \frac{\delta \psi}{\delta x_i} \gamma \Delta t, - -where :math:`\psi` is a scalar potential which was computed by :ref:`fluid_compute_potential `. - -``fluid_correct_velocity`` is a main function, which calls sub functions which update each velocity field. - -.. seealso:: - - :ref:`SMAC method `. - -.. mydeclare:: /../../src/fluid/correct_velocity/ux.c - :language: c - :tag: fluid_correct_velocity_ux - -.. mydeclare:: /../../src/fluid/correct_velocity/uy.c - :language: c - :tag: fluid_correct_velocity_uy - -.. mydeclare:: /../../src/fluid/correct_velocity/uz.c - :language: c - :tag: fluid_correct_velocity_uz - -The pre-factor is computed here: - -.. myliteralinclude:: /../../src/fluid/correct_velocity/main.c - :language: c - :tag: compute prefactor gamma dt - -The above equation is directly discretised and implemented: - -.. myliteralinclude:: /../../src/fluid/correct_velocity/ux.c - :language: c - :tag: correct x velocity - -.. myliteralinclude:: /../../src/fluid/correct_velocity/uy.c - :language: c - :tag: correct y velocity - -.. myliteralinclude:: /../../src/fluid/correct_velocity/uz.c - :language: c - :tag: correct z velocity - -Also the boundary values are imposed and the halo cells are updated: - -.. myliteralinclude:: /../../src/fluid/correct_velocity/ux.c - :language: c - :tag: update boundary and halo cells - -.. myliteralinclude:: /../../src/fluid/correct_velocity/uy.c - :language: c - :tag: update boundary and halo cells - -.. myliteralinclude:: /../../src/fluid/correct_velocity/uz.c - :language: c - :tag: update boundary and halo cells - -.. seealso:: - - :ref:`Boudary treatments `. - diff --git a/docs/source/implementation/fluid/init.rst b/docs/source/implementation/fluid/init.rst deleted file mode 100644 index a3cdaf71..00000000 --- a/docs/source/implementation/fluid/init.rst +++ /dev/null @@ -1,43 +0,0 @@ - -.. _fluid_init: - -################### -`src/fluid/init.c`_ -################### - -.. _src/fluid/init.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/fluid/init.c - -This section discusses how the velocity and the temperature fields are initialised. - -.. mydeclare:: /../../src/fluid/init.c - :language: c - :tag: fluid_init - -First, arrays associated to ``fluid_t`` (arrays to store the flow field, buffers to keep the intermediate information, etc.) are allocated: - -.. myliteralinclude:: /../../src/fluid/init.c - :language: c - :tag: allocate arrays - -Then the fields are loaded from the corresponding files which are contained in a specified directory, which is given as the argument (see ``exec.sh``): - -.. myliteralinclude:: /../../src/fluid/init.c - :language: c - :tag: load flow fields - -The boundary conditions are imposed and the halo cells are communicated: - -.. myliteralinclude:: /../../src/fluid/init.c - :language: c - :tag: impose boundary conditions and communicate halo cells - -.. note:: - - The shape of the arrays (number of boundary cells and halo cells) is identical except for :math:`\ux`. - -Also the momentum and the thermal diffusivities are computed and stored: - -.. myliteralinclude:: /../../src/fluid/init.c - :language: c - :tag: compute diffusivities - diff --git a/docs/source/implementation/fluid/integrate/compute_rhs.rst b/docs/source/implementation/fluid/integrate/compute_rhs.rst deleted file mode 100644 index 9a19c877..00000000 --- a/docs/source/implementation/fluid/integrate/compute_rhs.rst +++ /dev/null @@ -1,167 +0,0 @@ - -.. _fluid_compute_rhs: - -#################################### -`src/fluid/integrate/compute_rhs.c`_ -#################################### - -.. _src/fluid/integrate/compute_rhs.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/fluid/integrate/compute_rhs.c - -Functions to compute the right-hand-side terms of the momentum and the internal energy equation are described here. - -See :ref:`the temporal discretisation ` and :ref:`the spatial discretisation ` for details. - -.. mydeclare:: /../../src/fluid/integrate/compute_rhs.c - :language: c - :tag: fluid_compute_rhs - -To update a scalar field from :math:`\left\{ \cdots \right\}^k` to :math:`\left\{ \cdots \right\}^{k+1}` following the method discussed in :ref:`the temporal discretisation `, I need the right-hand-side terms: the advective terms :math:`A^k`, and the diffusive terms :math:`D^k`. -I also need the pressure-gradient contribution :math:`P^k` to the momentum balance, and in addition the buoyancy forcing is coupled in the :math:`x` direction. - -The following three buffers are used: - -.. list-table:: Buffers - :widths: 25 25 50 - :header-rows: 1 - - * - Name - - Pre-factor - - Description - * - ``srca`` - - :math:`\alpha^k` - - Explicit terms, current contribution - * - ``srcb`` - - :math:`\beta^k` - - Explicit terms, previous contribution - * - ``srcg`` - - :math:`\gamma^k` - - Implicit terms - -First of all, - - * ``srca``, which contains the information of the previous Runge-Kutta step, is copied to ``srcb``, so that the new values can be assigned to ``srca``. - - * ``srca`` and ``srcg`` are zero-cleared. - -.. myliteralinclude:: /../../src/fluid/integrate/compute_rhs.c - :language: c - :tag: copy previous k-step source term and reset - -Note that the zero-clearing is not needed for :math:`\beta` buffers even at :math:`k = 0` since the Runge-Kutta coefficient :math:`\beta^0` is 0. - -Then all different advective and diffusive contributions are added to those buffers. - -.. note:: - - The mathematical forms and their derivations are extensively discussed in :ref:`the spatial discretisation `. - In short, the schemes are designed to keep the properties which the original governing equations have. - - Hereafter, superscripts :math:`k`, which denote the Runge-Kutta iteration and should be on all scalar fields, are dropped for notational simplicity. - -* Advective contributions - - Since they are always treated explicitly in time, they are added to ``srca``. - - .. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: advective contributions, always explicit - - * :ref:`Advection of x momentum in x ` - * :ref:`Advection of x momentum in y ` - * :ref:`Advection of x momentum in z ` - - .. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: advective contributions, always explicit - - * :ref:`Advection of y momentum in x ` - * :ref:`Advection of y momentum in y ` - * :ref:`Advection of y momentum in z ` - - .. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: advective contributions, always explicit - - * :ref:`Advection of z momentum in x ` - * :ref:`Advection of z momentum in y ` - * :ref:`Advection of z momentum in z ` - - .. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: advective contributions, always explicit - - * :ref:`Advection of T in x ` - * :ref:`Advection of T in y ` - * :ref:`Advection of T in z ` - -* Diffusive contributions - - The treatment is user-dependent and determined at runtime (see ``exec.sh``). - When the term is treated explicitly in time, it is added to ``srca``; otherwise to ``srcg``: - - .. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: diffusive contributions, can be explicit or implicit - - * :ref:`Diffusion of x momentum in x ` - * :ref:`Diffusion of x momentum in y ` - * :ref:`Diffusion of x momentum in z ` - - .. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: diffusive contributions, can be explicit or implicit - - * :ref:`Diffusion of y momentum in x ` - * :ref:`Diffusion of y momentum in y ` - * :ref:`Diffusion of y momentum in z ` - - .. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: diffusive contributions, can be explicit or implicit - - * :ref:`Diffusion of z momentum in x ` - * :ref:`Diffusion of z momentum in y ` - * :ref:`Diffusion of z momentum in z ` - - .. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: diffusive contributions, can be explicit or implicit - - * :ref:`Diffusion of T in x ` - * :ref:`Diffusion of T in y ` - * :ref:`Diffusion of T in z ` - - All diffusive terms contain the discrete Laplace operators, which are constant in time. - Thus I compute them when the simulation is launched (see :ref:`domain/ `) and reuse the results. - - Also the diffusivities for the momentum equation - - .. math:: - - \frac{\sqrt{Pr}}{\sqrt{Ra}} - - and for the internal energy - - .. math:: - - \frac{1}{\sqrt{Pr}\sqrt{Ra}} - - are constant in time (see :ref:`the governing equations `), which is pre-computed: - - .. myliteralinclude:: /../../src/fluid/init.c - :language: c - :tag: compute diffusivities - -* Buoyancy (:math:`x` momentum equation) - - The behaviour of this part is dominated by a flag ``param_add_buoyancy`` (see :ref:`src/param `), which determines whether the buoyancy force under the Boussinesq approximation is added to the right-hand side of the :math:`x` momentum equation (``true``) or not (``false``). - When ``param_add_buoyancy`` is ``false``, the temperature field behaves as a passive scalar field. - - When ``param_add_buoyancy`` is ``true``, as discussed in :ref:`the governing equations `, the buoyancy force results in :math:`T` (a simple body force term) in the :math:`x` momentum equation. - Since :math:`T` are defined at cell centers, I need to interpolate them to the :math:`x` cell faces (where :math:`\ux` is located), where arithmetic average should be used. - - .. note:: - - One might be tempted to use values which are linearly interpolated or weighted by cell volume, which breaks the energy balance. - See :ref:`the spatial discretisation ` for details, in particular :ref:`Nusselt number relations `. - diff --git a/docs/source/implementation/fluid/integrate/main.rst b/docs/source/implementation/fluid/integrate/main.rst deleted file mode 100644 index 62ed7a81..00000000 --- a/docs/source/implementation/fluid/integrate/main.rst +++ /dev/null @@ -1,51 +0,0 @@ - -.. _fluid_integrate: - -###################### -`src/fluid/integrate`_ -###################### - -.. _src/fluid/integrate: https://github.com/NaokiHori/SimpleNSSolver/tree/main/src/fluid/integrate - -This directory contains functions to predict the new scalar fields: the velocity and the temperature at the next Runge-Kutta level. -Although updating the temperature field is completed here, another step is needed for the velocity field since the updated field in general does not satisfy the divergence-free condition. - -There are mainly three steps: - -#. Compute right-hand-side terms of the Runge-Kutta scheme - - In general the update processes can be written as - - .. math:: - - q^{k+1} - = - q^{k} - + - \Delta q. - - Here :math:`\Delta q` is computed and stored. - - .. note:: - - At this point, only the advective, diffusive, and the pressure gradient effects are added to the buffer, where I have not taken into account the buoyancy force yet. - -#. Couple external effects - - External effects such as the buoyancy force is appended here. - The reason why I separate this process from the :ref:`fluid_compute_rhs ` is to make the other couplings (e.g. driving pressure, no-slip effects induced by the immersed boundary method) easier by exposing the API. - -#. Predict field - - :math:`q` is updated from :math:`q^k` to :math:`q^{k+1}` by solving an ordinary differential equation for each quantity. - - .. note:: - - While the temperature field is fully updated by this function, the momentum field is not divergence-free at this point, which will be considered in :ref:`the next step `. - -.. toctree:: - :maxdepth: 1 - - compute_rhs - predict_field - diff --git a/docs/source/implementation/fluid/integrate/predict_field.rst b/docs/source/implementation/fluid/integrate/predict_field.rst deleted file mode 100644 index 48cbe122..00000000 --- a/docs/source/implementation/fluid/integrate/predict_field.rst +++ /dev/null @@ -1,140 +0,0 @@ - -.. _fluid_predict_field: - -###################################### -`src/fluid/integrate/predict_field.c`_ -###################################### - -.. _src/fluid/integrate/compute_rhs.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/fluid/integrate/predict_field.c - -Functions to update the momentum field - -.. math:: - - u_i^n \rightarrow u_i^* - -and the temperature field - -.. math:: - - T^n \rightarrow T^{n+1} - -are discussed here. - -See :ref:`the temporal discretisation ` and :ref:`the spatial discretisation ` for details. - -.. note:: - - See :ref:`fluid/update_pressure.c ` for the pressure field, which is not discussed here since the pressure is assumed to be a scalar potential to enforce the incompressibility. - -.. mydeclare:: /../../src/fluid/integrate/predict_field.c - :language: c - :tag: fluid_predict_field - -The right-hand-side terms of the Runge-Kutta scheme are already computed by :ref:`fluid_compute_rhs `. -Here, those terms are adopted to integrate the scalar fields in time. - -.. note:: - - Although :math:`T` is used throughout this page for notational simplicity, the same applies to the other quantities (i.e. velocity in each direction). - -First of all, the increment of the scalar field :math:`\delta T \equiv T^{k+1} - T^{k}` is computed: - -.. math:: - - \delta T = \left( - \alpha^k \mathcal{A} - + \beta^k \mathcal{B} - + \gamma^k \mathcal{G} - \right) \Delta t, - -where :math:`\mathcal{A}`, :math:`\mathcal{B}`, and :math:`\mathcal{G}` are source terms which are already computed in ``compute_src`` and correspond to ``srca``, ``srcb``, and ``srcg``, respectively. - -As discussed in :ref:`the temporal discretisation `, the update process differs for different diffusive treatments. -When all diffusive terms are treated explicitly in time, I simply have - -.. math:: - - T^{k+1} = T^k + \delta T. - -When the diffusive terms are partially treated implicitly (e.g. only :math:`x` direction), I have - -.. math:: - - T^{k+1} = T^k + \left( 1 - \frac{\gamma^k \Delta t}{2 \sqrt{Pr} \sqrt{Ra}} \frac{\delta^2}{\delta x^2} \right)^{-1} \delta T, - -where linear systems in :math:`x` direction needs to be solved before being added to :math:`T^k`. - -When all diffusive terms are treated implicitly, I have - -.. math:: - - T^{k+1} - = - T^k - + - \left( 1 - \frac{\gamma^k \Delta t}{2 \sqrt{Pr} \sqrt{Ra}} \frac{\delta^2}{\delta y^2} \right)^{-1} - \left( 1 - \frac{\gamma^k \Delta t}{2 \sqrt{Pr} \sqrt{Ra}} \frac{\delta^2}{\delta x^2} \right)^{-1} - \delta T, - -or - -.. math:: - - T^{k+1} - = - T^k - + - \left( 1 - \frac{\gamma^k \Delta t}{2 \sqrt{Pr} \sqrt{Ra}} \frac{\delta^2}{\delta z^2} \right)^{-1} - \left( 1 - \frac{\gamma^k \Delta t}{2 \sqrt{Pr} \sqrt{Ra}} \frac{\delta^2}{\delta y^2} \right)^{-1} - \left( 1 - \frac{\gamma^k \Delta t}{2 \sqrt{Pr} \sqrt{Ra}} \frac{\delta^2}{\delta x^2} \right)^{-1} - \delta T, - -where linear systems in each direction should be solved before being added to :math:`T^k`. - -On the basis of these relationships, the procedure for updating the field is as follows. - -#. Compute :math:`\delta T` - - The temperature increment :math:`\delta T` is computed and assigned to a buffer: - - .. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: compute increments - - .. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: compute increments - - .. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: compute increments - - .. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: compute increments - -#. Solve linear systems in each direction (**only when** the diffusive term in the direction is treated implicitly) - - See :ref:`src/linear_system.c `. - -#. Update :math:`T^k` to :math:`T^{k+1}` - - Now the increment :math:`\delta T` is ready, which is added to the temperature ``temp``: - - .. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: the field is actually updated - - .. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: the field is actually updated - - .. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: the field is actually updated - - .. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: the field is actually updated - diff --git a/docs/source/implementation/fluid/main.rst b/docs/source/implementation/fluid/main.rst deleted file mode 100644 index 5c38cdb1..00000000 --- a/docs/source/implementation/fluid/main.rst +++ /dev/null @@ -1,81 +0,0 @@ - -.. _fluid: - -############ -`src/fluid`_ -############ - -.. _src/fluid: https://github.com/NaokiHori/SimpleNSSolver/tree/main/src/fluid - -This directory contains source files which are used to integrate the :ref:`governing equations `. - -.. toctree:: - :maxdepth: 1 - - boundary - compute_potential/main - correct_velocity - init - integrate/main - update_pressure - -******* -fluid_t -******* - -A structure ``fluid_t`` is defined in `include/fluid.h `_, which contains all arrays and buffers which are used to integrate the momentum field: - -.. myliteralinclude:: /../../include/fluid.h - :language: c - :tag: definition of a structure fluid_t - -Each member plays the following role: - -* ``u[xyz]`` - - Velocity in the :math:`x`, :math:`y` and :math:`z` directions (:math:`\ux`, :math:`\uy` and :math:`\uz`) - -* ``p`` - - Pressure (:math:`p`, roughly speaking) - -* ``psi`` - - Scalar potential which projects the non-solenoidal velocity field to the solenoidal one (:math:`\psi`) - -* ``t`` - - Temperature field (or passive scalar field when the buoyancy force is neglected) - -* ``src(u[xyz]|t)[abg]`` - - Right-hand-side terms of the momentum and the internal-energy equations (i.e. source terms of the Runge-Kutta scheme). - Suffices ``a``, ``b`` and ``g`` are used to distinguish source terms (implying :math:`\alpha`, :math:`\beta`, and :math:`\gamma`, respectively). - -* ``m_dif``, ``t_dif`` - - Since there are two fields, two parameters :math:`Ra` and :math:`Pr` are needed to identify the diffusive process. - To avoid computing the diffusivities ``m_dif``: - - .. math:: - - \frac{{\sqrt{Pr}}}{\sqrt{Ra}} - - and ``t_dif``: - - .. math:: - - \frac{1}{\sqrt{Pr}} \frac{1}{\sqrt{Ra}} - - every time, they are pre-computed and stored: - - .. myliteralinclude:: /../../src/fluid/init.c - :language: c - :tag: compute diffusivities - -.. seealso:: - - :ref:`SMAC method ` - - :ref:`Integrating the temperature field ` - diff --git a/docs/source/implementation/fluid/update_pressure.rst b/docs/source/implementation/fluid/update_pressure.rst deleted file mode 100644 index 82538fcd..00000000 --- a/docs/source/implementation/fluid/update_pressure.rst +++ /dev/null @@ -1,92 +0,0 @@ - -.. _fluid_update_pressure: - -.. include:: /references.txt - -############################## -`src/fluid/update_pressure.c`_ -############################## - -.. _src/fluid/update_pressure.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/fluid/update_pressure.c - -Update the pressure field :math:`p` using the scalar potential :math:`\psi`. - -.. mydeclare:: /../../src/fluid/update_pressure.c - :language: c - :tag: fluid_update_pressure - -To update the pressure field, a scalar potential :math:`\psi`, which is computed by :ref:`solving the Poisson equation `, is added: - -.. math:: - - p^{k+1} - = - p^k - + - \psi, - -which is the *explicit* contribution. - -.. myliteralinclude:: /../../src/fluid/update_pressure.c - :language: c - :tag: explicit contribution - -As discussed in :ref:`the temporal discretisation `, when the diffusive terms are treated implicitly, additional terms appear, e.g. - -.. math:: - - p^{k+1} - = - p^k - + - \psi - - - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{\delta^2 \psi}{\delta x^2}, - -when the :math:`x` direction is treated implicitly (the *implicit* contributions). -The same applies to the other directions. - -.. myliteralinclude:: /../../src/fluid/update_pressure.c - :language: c - :tag: x implicit contribution - -.. myliteralinclude:: /../../src/fluid/update_pressure.c - :language: c - :tag: y implicit contribution - -.. myliteralinclude:: /../../src/fluid/update_pressure.c - :language: c - :tag: z implicit contribution - -The pre-factor - -.. math:: - - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} - -is computed here: - -.. myliteralinclude:: /../../src/fluid/update_pressure.c - :language: c - :tag: gamma dt diffusivity / 2 - -Also ``fluid->diffusivity`` is computed here: - -.. myliteralinclude:: /../../src/fluid/init.c - :language: c - :tag: compute diffusivities - -Finally the boundary condition is imposed on the new pressure field and the halo values are communicated: - -.. myliteralinclude:: /../../src/fluid/update_pressure.c - :language: c - :tag: impose boundary conditions and communicate halo cells - -.. note:: - - In theory, the Helmholtz equations should be solved with respect to the new velocity field :math:`u_i^{k+1}`. - For practical convenience, on the other hand, I solve them w.r.t. the predicted velocity field :math:`u_i^{*}`, which is the origin of the implicit contributions here. - See :ref:`SMAC method ` for more details. - - The implicit contributions, however, are known to play minor roles (e.g. |KAJISHIMA1999|). - diff --git a/docs/source/implementation/halo/main.rst b/docs/source/implementation/halo/main.rst deleted file mode 100644 index c5622a30..00000000 --- a/docs/source/implementation/halo/main.rst +++ /dev/null @@ -1,42 +0,0 @@ - -.. _halo: - -############# -`src/halo.c`_ -############# - -.. _src/halo.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/halo.c - -This file contains functions which take care of the inter-process communication in order to exchange the edge (halo) information. - -******** -Overview -******** - -#. Identifying the neighbours - - I need to check from which process (to which process) I receive (send) the halo information. - This is achieved by using ``sdecomp.get_neighbours``. - -#. Packing and unpacking - - Data to be communicated is not usually contiguous in memory. - To encapsulate the packing-unpacking procedures, I define ``MPI_Datatype`` for each scalar field. - Because the staggered grid is used, different data types should be used for each variable. - -******* -Details -******* - -.. mydetails:: :math:`y` halo cells - - .. include:: y-halo.rst - -.. mydetails:: :math:`z` halo cells (3D only) - - .. include:: z-halo.rst - -.. note:: - - In spite of the redundancy and the increase in the size of the data, the boundary cells in the :math:`x` direction are also communicated for simplicity. - diff --git a/docs/source/implementation/halo/y-halo.rst b/docs/source/implementation/halo/y-halo.rst deleted file mode 100644 index 7d6ddae2..00000000 --- a/docs/source/implementation/halo/y-halo.rst +++ /dev/null @@ -1,35 +0,0 @@ -* Two-dimensional case - - I need - - * :math:`\ux`: ``i = [1: isize + 1]`` - - * others: ``i = [0: isize + 1]`` - - at ``j = 0`` and ``j = jsize+1``, which are contained by the process in the negative and positive :math:`y` directions, respectively. - Since these elements are contiguous in the memory, I define an ``MPI_Datatype`` using ``MPI_Type_contiguous``. - -* Three-dimensional case - - I need - - * :math:`\ux`: ``i = [1: isize + 1]`` and ``k = [0: ksize + 1]`` - - * :math:`\uy`, :math:`\uz`, :math:`p`, :math:`T`: ``i = [0: isize + 1]`` and ``k = [0: ksize + 1]`` - - at ``j = 0`` and ``j = jsize + 1``. - - Since - - * there are ``ksize + 2`` blocks, - - * each block has contiguous elements (``isize + 1`` for :math:`\ux`, ``isize + 2`` otherwise), - - * stride between each block is ``(isize + 1) * (jsize + 2)`` for :math:`\ux`, ``(isize + 2) * (jsize + 2)`` otherwise, - - I define ``MPI_Datatype`` using ``MPI_Type_vector``. - -.. myliteralinclude:: /../../src/halo.c - :language: c - :tag: define datatype in y - diff --git a/docs/source/implementation/halo/z-halo.rst b/docs/source/implementation/halo/z-halo.rst deleted file mode 100644 index d1fc1428..00000000 --- a/docs/source/implementation/halo/z-halo.rst +++ /dev/null @@ -1,20 +0,0 @@ -I need - - * :math:`\ux`: ``i = [1: isize + 1]`` and ``j = [0: jsize + 1]`` - - * others: ``i = [0: isize + 1]`` and ``j = [0: jsize + 1]`` - -at ``k = 0`` and ``k = ksize + 1``. - -Since they are two-dimensional (:math:`xy`) slices which are contiguous in memory, I define ``MPI_Datatype`` using ``MPI_Type_contiguous``: - -.. myliteralinclude:: /../../src/halo.c - :language: c - :tag: define datatype in z - -.. note:: - - ``UZ(i, 0, ksize + 1)`` are needed to describe the advection of :math:`\uy` in the :math:`z` direction. - Also ``UY(i, jsize + 1, 0)`` are needed to describe the advection of :math:`\uz` in the :math:`y` direction. - Although these information are not contained by the neighbouring processes originally, they are obtained after :math:`y` halo cells are updated. - diff --git a/docs/source/implementation/integrate.rst b/docs/source/implementation/integrate.rst deleted file mode 100644 index a13311ed..00000000 --- a/docs/source/implementation/integrate.rst +++ /dev/null @@ -1,78 +0,0 @@ - -.. _integrate: - -################## -`src/integrate.c`_ -################## - -.. _src/integrate.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/integrate.c - -``integrate.c`` contains a central function which integrates the :ref:`governing equations `. - -======================= -Deciding time step size -======================= - -Before going into the Runge-Kutta iteration, the size of the time step ``dt`` is decided: - -.. myliteralinclude:: /../../src/integrate.c - :language: c - :tag: decide time step size - -====================== -Runge-Kutta iterations -====================== - -The time integration is based on the three-step Runge-Kutta scheme. - -.. note:: - - At the beginning of each loop, I assume that the halo cells are communicated, and the proper boundary conditions are already enforced. - - To achieve the second-order accuracy in time, the following process is repeated for three times to update the scalar fields from step :math:`n` to step :math:`n+1`. - -#. Update velocity field - - The velocities and the temperature field are updated by integrating the momentum and the internal-energy equations: - - .. myliteralinclude:: /../../src/integrate.c - :language: c - :tag: predict flow field - - Since I weakly couple the temperature and the momentum fields, the previous temperature field is used to compute the buoyancy force, which is added as a body force in the momentum equation. - - In the first part :ref:`fluid_compute_rhs `, the right-hand-side terms of the equations are computed, which are used to actually update the velocity field in the second part :ref:`fluid_predict_field `. - - .. seealso:: - - :ref:`Numerical method ` for more details. - -#. Compute scalar potential and correct velocity - - Usually, the new velocity field computed in the previous step does not satisfy the mass conservation. - To enforce the incompressibility, a scalar potential :math:`\psi` is computed by solving a Poisson equation here: - - .. myliteralinclude:: /../../src/integrate.c - :language: c - :tag: compute scalar potential - - which is used to correct the velocity field to be solenoidal: - - .. myliteralinclude:: /../../src/integrate.c - :language: c - :tag: correct velocity field to satisfy mass conservation - -#. Update pressure - - Finally the pressure is updated: - - .. myliteralinclude:: /../../src/integrate.c - :language: c - :tag: update pressure - -.. seealso:: - - :ref:`SMAC method ` - - :ref:`Integrating the temperature field ` - diff --git a/docs/source/implementation/linear_system.rst b/docs/source/implementation/linear_system.rst deleted file mode 100644 index 306e9af0..00000000 --- a/docs/source/implementation/linear_system.rst +++ /dev/null @@ -1,442 +0,0 @@ - -.. _linear_system: - -###################### -`src/linear_system.c`_ -###################### - -.. _src/linear_system.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/linear_system.c - -This file contains functions to solve the systems of linear equations: - -.. math:: - - A_{ij} x_j = b_i, - -in particular it aims to solve this system when :math:`A_{ij}` is a tri-diagonal matrix. - -As discussed in the temporal discretisations of :ref:`the momentum equation ` and :ref:`the equation of internal energy `, when the diffusive terms are treated implicitly, I need to solve - -.. math:: - - u_i^* - = - u_i^k - + - \left( 1 - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{\delta^2}{\delta y^2} \right)^{-1} - \left( 1 - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{\delta^2}{\delta x^2} \right)^{-1} - \delta u_i - -for the momentum equation, while - -.. math:: - - T^{k+1} - = - T^k - + - \left( 1 - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{\delta^2}{\delta y^2} \right)^{-1} - \left( 1 - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{\delta^2}{\delta x^2} \right)^{-1} - \delta T - -for the temperature field, respectively. -Here, I only consider two-dimensional cases since the extension to three-dimensional cases is straightforward. - -For the sake of notational simplicity, I write the right-hand-side of these equations as - -.. math:: - - \left( 1 - k \frac{\delta^2}{\delta y^2} \right)^{-1} - \left( 1 - k \frac{\delta^2}{\delta x^2} \right)^{-1} - q, - -where :math:`k` is a constant value - -.. math:: - - \begin{cases} - \text{Momentum} & k \equiv \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}}, \\ - \text{Temperature} & k \equiv \frac{\gamma^k \Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}}, - \end{cases} - -and :math:`q` is a scalar field which can be one of :math:`\ux`, :math:`\uy`, :math:`\uz` and :math:`T`. - -.. mydetails:: Implementation (assigning to a variable ``prefactor``): - - .. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: gamma dt diffusivity / 2 - - .. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: gamma dt diffusivity / 2 - - .. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: gamma dt diffusivity / 2 - - .. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: gamma dt diffusivity / 2 - - .. note:: - - Although the grid sizes (:math:`\Delta x_{\xic}`, :math:`\Delta x_{\pic}`, :math:`\Delta y`, and :math:`\Delta z`) are constant in time, :math:`k` is time-dependent (:math:`\gamma^k \Delta t`) and thus I need to initialise the tri-diagonal coefficients every time step. - -I look at the Laplace operator in the :math:`x` direction, i.e. - -.. math:: - - q^{\prime} - = - \left( 1 - k \frac{\delta^2}{\delta x^2} \right)^{-1} - q, - -or equivalently - -.. math:: - - \left( 1 - k \frac{\delta^2}{\delta x^2} \right) - q^{\prime} - = - q. - -Since I adopt the second-order-accurate central-difference scheme, - -.. math:: - - \frac{\delta^2}{\delta x^2} - -is discretised as - -.. math:: - - \vat{\frac{\delta^2 q^{\prime}}{\delta x^2}}{\xic} - \approx - {\chi}_{\ximm} \vat{q^{\prime}}{\ximm} - + {\chi}_{\xic } \vat{q^{\prime}}{\xic } - + {\chi}_{\xipp} \vat{q^{\prime}}{\xipp}, - -where - -.. math:: - - \begin{aligned} - {\chi}_{\ximm} & = \frac{1}{\Delta x_{\xic} \Delta x_{\xim}}, \\ - {\chi}_{\xipp} & = \frac{1}{\Delta x_{\xic} \Delta x_{\xip}}, \\ - {\chi}_{\xic } & = -{\chi}_{\ximm}-{\chi}_{\xipp} - \end{aligned} - -for :math:`q \leftarrow \ux`, otherwise - -.. math:: - - \vat{\frac{\delta^2 q^{\prime}}{\delta x^2}}{\pic} - \approx - {\chi}_{\pimm} \vat{q^{\prime}}{\pimm} - + {\chi}_{\pic } \vat{q^{\prime}}{\pic } - + {\chi}_{\pipp} \vat{q^{\prime}}{\pipp}, - -where - -.. math:: - - {\chi}_{\yimm} & = \frac{1}{\Delta x_{\yic} \Delta x_{\yim}}, \\ - {\chi}_{\yipp} & = \frac{1}{\Delta x_{\yic} \Delta x_{\yip}}, \\ - {\chi}_{\yic } & = -{\chi}_{\yimm}-{\chi}_{\yipp}. - -Thus, I have - -.. math:: - - \left( 1 - k \frac{\delta^2}{\delta x^2} \right) - \vat{q^{\prime}}{\xic} - & \approx - - k \vat{\chi}{\xipp} \vat{q^{\prime}}{\xipp} - + \left( 1 - k \vat{\chi}{\xic } \vat{q^{\prime}}{\xic } \right) - - k \vat{\chi}{\ximm} \vat{q^{\prime}}{\ximm} \\ - & \equiv - \vat{u}{\xic} \vat{q^{\prime}}{\xipp} - + \vat{c}{\xic} \vat{q^{\prime}}{\xic } - + \vat{l}{\xic} \vat{q^{\prime}}{\ximm} \\ - & = - \vat{q}{\xic} - -for :math:`q \leftarrow \ux`, otherwise - -.. math:: - - \left( 1 - k \frac{\delta^2}{\delta x^2} \right) - \vat{q^{\prime}}{\pic} - & \approx - - k \vat{\chi}{\pipp} \vat{q^{\prime}}{\pipp} - + \left( 1 - k \vat{\chi}{\pic } \vat{q^{\prime}}{\pic } \right) - - k \vat{\chi}{\pimm} \vat{q^{\prime}}{\pimm} \\ - & \equiv - \vat{u}{\pic} \vat{q^{\prime}}{\pipp} - + \vat{c}{\pic} \vat{q^{\prime}}{\pic } - + \vat{l}{\pic} \vat{q^{\prime}}{\pimm} \\ - & = - \vat{q}{\pic}. - -I can write them using the following matrix forms (in particular the tri-diagonal linear systems): - -.. math:: - - \newcommand\ia{\frac{3}{2}} - \newcommand\ib{\frac{5}{2}} - \newcommand\id{i-\frac{1}{2}} - \newcommand\ie{i+\frac{1}{2}} - \newcommand\if{i+\frac{3}{2}} - \newcommand\ih{\text{isize}-\frac{3}{2}} - \newcommand\ii{\text{isize}-\frac{1}{2}} - \begin{bmatrix} - c_{\ia} & u_{\ia} & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 \\ - l_{\ib} & c_{\ib} & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 \\ - \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots \\ - 0 & 0 & \cdots & c_{\id} & u_{\id} & 0 & \cdots & 0 & 0 \\ - 0 & 0 & \cdots & l_{\ie} & c_{\ie} & u_{\ie} & \cdots & 0 & 0 \\ - 0 & 0 & \cdots & 0 & l_{\if} & c_{\if} & \cdots & 0 & 0 \\ - \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ - 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & c_{\ih} & u_{\ih} \\ - 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & l_{\ii} & c_{\ii} - \end{bmatrix} - \begin{bmatrix} - \vat{q^{\prime}}{\ia} \\ - \vat{q^{\prime}}{\ib} \\ - \vdots \\ - \vat{q^{\prime}}{\id} \\ - \vat{q^{\prime}}{\ie} \\ - \vat{q^{\prime}}{\if} \\ - \vdots \\ - \vat{q^{\prime}}{\ih} \\ - \vat{q^{\prime}}{\ii} - \end{bmatrix} - = - \begin{bmatrix} - \vat{q}{\ia} \\ - \vat{q}{\ib} \\ - \vdots \\ - \vat{q}{\id} \\ - \vat{q}{\ie} \\ - \vat{q}{\if} \\ - \vdots \\ - \vat{q}{\ih} \\ - \vat{q}{\ii} - \end{bmatrix} - -for :math:`q \leftarrow \ux`, otherwise - -.. math:: - - \newcommand\ia{1} - \newcommand\ib{2} - \newcommand\id{i-1} - \newcommand\ie{i } - \newcommand\if{i+1} - \newcommand\ih{\text{isize}-1} - \newcommand\ii{\text{isize} } - \begin{bmatrix} - c_{\ia} & u_{\ia} & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 \\ - l_{\ib} & c_{\ib} & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 \\ - \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots \\ - 0 & 0 & \cdots & c_{\id} & u_{\id} & 0 & \cdots & 0 & 0 \\ - 0 & 0 & \cdots & l_{\ie} & c_{\ie} & u_{\ie} & \cdots & 0 & 0 \\ - 0 & 0 & \cdots & 0 & l_{\if} & c_{\if} & \cdots & 0 & 0 \\ - \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ - 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & c_{\ih} & u_{\ih} \\ - 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & l_{\ii} & c_{\ii} - \end{bmatrix} - \begin{bmatrix} - \vat{q^{\prime}}{\ia} \\ - \vat{q^{\prime}}{\ib} \\ - \vdots \\ - \vat{q^{\prime}}{\id} \\ - \vat{q^{\prime}}{\ie} \\ - \vat{q^{\prime}}{\if} \\ - \vdots \\ - \vat{q^{\prime}}{\ih} \\ - \vat{q^{\prime}}{\ii} - \end{bmatrix} - = - \begin{bmatrix} - \vat{q}{\ia} \\ - \vat{q}{\ib} \\ - \vdots \\ - \vat{q}{\id} \\ - \vat{q}{\ie} \\ - \vat{q}{\if} \\ - \vdots \\ - \vat{q}{\ih} \\ - \vat{q}{\ii} - \end{bmatrix}. - -.. mydetails:: Boundary treatments - - I take :math:`\ux` as an example. - Originally, in the vicinity of the walls (one-grid apart from the walls, at :math:`\frac{3}{2}` or at :math:`\text{isize}-\frac{1}{2}`), I have - - .. math:: - - l_{\frac{3}{2}} q^{\prime}_{\text{left wall}} - + - c_{\frac{3}{2}} q^{\prime}_{\frac{3}{2}} - + - u_{\frac{3}{2}} q^{\prime}_{\frac{5}{2}} - = - q_{\frac{3}{2}}, - - .. math:: - - l_{\text{isize}-\frac{1}{2}} q^{\prime}_{\text{isize}-\frac{3}{2}} - + - c_{\text{isize}-\frac{1}{2}} q^{\prime}_{\text{isize}-\frac{1}{2}} - + - u_{\text{isize}-\frac{1}{2}} q^{\prime}_{\text{right wall}} - = - q_{\text{isize}-\frac{1}{2}}. - - Since :math:`q` is the delta form (:math:`q = \delta \ux \equiv \ux^* - \ux^k`), :math:`q` is equal to :math:`0` on the boundaries - - .. math:: - - & q^{\prime}_{\text{left wall}} \equiv 0, \\ - & q^{\prime}_{\text{right wall}} \equiv 0, - - because I assume that the walls are impermeable, and the Neumann boundary condition is imposed on the scalar potential :math:`\psi`, indicating that the correction step does not alter the values on the walls. - Thus, I conclude - - .. math:: - - c_{\frac{3}{2}} q^{\prime}_{\frac{3}{2}} - + - u_{\frac{3}{2}} q^{\prime}_{\frac{5}{2}} - = - q_{\frac{3}{2}}, - - .. math:: - - l_{\text{isize}-\frac{1}{2}} q^{\prime}_{\text{isize}-\frac{3}{2}} - + - c_{\text{isize}-\frac{1}{2}} q^{\prime}_{\text{isize}-\frac{1}{2}} - = - q_{\text{isize}-\frac{1}{2}}, - - which are the corrections taking into account the boundary conditions. - -Since :math:`y` and :math:`z` directions are homogeneous (periodic boundary conditions are imposed and grids are equidistantly placed), matrix in the left-hand side describing the Laplace operators in the :math:`y` and :math:`z` directions - -.. math:: - - \frac{\delta^2}{\delta y^2}, \frac{\delta^2}{\delta z^2} - -are - -.. math:: - - \begin{bmatrix} - c & u & \cdots & 0 & 0 & 0 & \cdots & 0 & l \\ - l & c & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 \\ - \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots \\ - 0 & 0 & \cdots & c & u & 0 & \cdots & 0 & 0 \\ - 0 & 0 & \cdots & l & c & u & \cdots & 0 & 0 \\ - 0 & 0 & \cdots & 0 & l & c & \cdots & 0 & 0 \\ - \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ - 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & c & u \\ - u & 0 & \cdots & 0 & 0 & 0 & \cdots & l & c - \end{bmatrix}, - -where the coefficients are - -.. math:: - - u = l &= - k \frac{1}{\Delta y^2}, \\ - c &= 1 + k \frac{2}{\Delta y^2}, - -or - -.. math:: - - u = l &= - k \frac{1}{\Delta z^2}, \\ - c &= 1 + k \frac{2}{\Delta z^2}, - -respectively. - -The tri-diagonal matrix is solved by :ref:`the Thomas algorithm `. - -.. note:: - - In this project, the solver requests that all systems are present on the memory, i.e. one process should know everything about the system to be solved. - For systems in the :math:`x` direction, it is by default fulfilled since the domain is not decomposed. - On the other hand, for systems in the :math:`y` and :math:`z` directions, this is not satisfied. - Thus I need `the pencil rotations `_ to solve linear systems in the :math:`y` and :math:`z` directions. - -I need three steps to solve a linear system using this module: - - * Initialisation - - * Execution - - * Finalisation - -In this file the initialisation and the finalisation are implemented. -The execution step is not handled since it is just to call :ref:`the Thomas algorithm `. - -************** -Initialisation -************** - -.. mydeclare:: /../../src/linear_system.c - :language: c - :tag: linear_system_init - -This function plays the following roles: - - #. allocating the internal buffers (if needed), - - #. preparing for the pencil rotations (if needed), - - #. initialising (the coefficients of) the tri-diagonal matrices (if needed). - -================ -Allocate buffers -================ - -``y1pencil`` and ``z2pencil`` are only needed when the corresponding direction is treated implicitly. - -.. myliteralinclude:: /../../src/linear_system.c - :language: c - :tag: allocate pencils if needed - -=========================== -Initialise pencil rotations -=========================== - -``xy`` rotations and ``xz`` rotations are only needed when the :math:`y` and the :math:`z` directions are treated implicitly, respectively. - -.. myliteralinclude:: /../../src/linear_system.c - :language: c - :tag: between x1 and y1 - -.. myliteralinclude:: /../../src/linear_system.c - :language: c - :tag: between x1 and z2 - -======================= -Initialise the matrices -======================= - -Solver in each direction is only needed when the direction is treated implicitly. - -.. myliteralinclude:: /../../src/linear_system.c - :language: c - :tag: Thomas algorithm in x direction - -.. myliteralinclude:: /../../src/linear_system.c - :language: c - :tag: Thomas algorithm in y direction - -.. myliteralinclude:: /../../src/linear_system.c - :language: c - :tag: Thomas algorithm in z direction - diff --git a/docs/source/implementation/logging/divergence.rst b/docs/source/implementation/logging/divergence.rst deleted file mode 100644 index 201693e3..00000000 --- a/docs/source/implementation/logging/divergence.rst +++ /dev/null @@ -1,53 +0,0 @@ - -.. _logging_check_divergence: - -########## -Divergence -########## - -.. mydeclare:: /../../src/logging/divergence.c - :language: c - :tag: logging_check_divergence - -This function computes the divergence of the velocity field and write the result to a file as well as the simulation time units at the moment. - -Local divergence of the velocity field is computed as - -.. math:: - - \dder{\ux}{x} - + - \dder{\uy}{y} - + - \dder{\uz}{z} - = - \frac{ - \vat{\ux}{\pip, \pjc, \pkc} - - - \vat{\ux}{\pim, \pjc, \pkc} - }{\Delta x_{\pic}} - + - \frac{ - \vat{\uy}{\pic, \pjp, \pkc} - - - \vat{\uy}{\pic, \pjm, \pkc} - }{\Delta y} - + - \frac{ - \vat{\uz}{\pic, \pjc, \pkp} - - - \vat{\uz}{\pic, \pjc, \pkm} - }{\Delta z}, - -which is defined at each cell center :math:`\left( \pic, \pjc, \pkc \right)`. - -.. myliteralinclude:: /../../src/logging/divergence.c - :language: c - :tag: compute local divergence - -Among others, the maximum value of the local divergence ``divmax``, which should be comparable to the machine epsilon (depending on the grid configuration), is checked: - -.. myliteralinclude:: /../../src/logging/divergence.c - :language: c - :tag: check maximum - diff --git a/docs/source/implementation/logging/energy.rst b/docs/source/implementation/logging/energy.rst deleted file mode 100644 index 2e837f41..00000000 --- a/docs/source/implementation/logging/energy.rst +++ /dev/null @@ -1,108 +0,0 @@ -#################### -Quadratic quantities -#################### - -.. mydeclare:: /../../src/logging/energy.c - :language: c - :tag: logging_check_energy - -This function computes the discrete kinetic energies (quadratic quantities in each direction): - -.. math:: - - k_x = \int \frac{1}{2} \ux^2 dx dy dz, \\ - k_y = \int \frac{1}{2} \uy^2 dx dy dz, \\ - k_z = \int \frac{1}{2} \uz^2 dx dy dz, \\ - -and the discrete thermal energy (quadratic quantity): - -.. math:: - - h = \int \frac{1}{2} T^2 dx dy dz - -and outputs the results to a file. - -************************************************* -Kinetic quadratic quantity in :math:`x` direction -************************************************* - - .. math:: - - \sum_{i} \sum_{j} \sum_{k} - \vat{ - \left( - \frac{1}{2} - \ux^2 - \Delta x - \Delta y - \Delta z - \right) - }{\xic, \xjc, \xkc} - - .. myliteralinclude:: /../../src/logging/energy.c - :language: c - :tag: compute quadratic quantity in x direction - -************************************************* -Kinetic quadratic quantity in :math:`y` direction -************************************************* - - .. math:: - - \sum_{i} \sum_{j} \sum_{k} - \vat{ - \left( - \frac{1}{2} - \uy^2 - \Delta x - \Delta y - \Delta z - \right) - }{\yic, \yjc, \ykc} - - .. myliteralinclude:: /../../src/logging/energy.c - :language: c - :tag: compute quadratic quantity in y direction - -************************************************* -Kinetic quadratic quantity in :math:`z` direction -************************************************* - - .. math:: - - \sum_{i} \sum_{j} \sum_{k} - \vat{ - \left( - \frac{1}{2} - \uz^2 - \Delta x - \Delta y - \Delta z - \right) - }{\zic, \zjc, \zkc} - - .. myliteralinclude:: /../../src/logging/energy.c - :language: c - :tag: compute quadratic quantity in z direction - -************************** -Thermal quadratic quantity -************************** - - .. math:: - - \sum_{i} \sum_{j} \sum_{k} - \vat{ - \left( - \frac{1}{2} - T^2 - \Delta x - \Delta y - \Delta z - \right) - }{\pic, \pjc, \pkc} - - .. myliteralinclude:: /../../src/logging/energy.c - :language: c - :tag: compute thermal energy - diff --git a/docs/source/implementation/logging/main.rst b/docs/source/implementation/logging/main.rst deleted file mode 100644 index ade5b704..00000000 --- a/docs/source/implementation/logging/main.rst +++ /dev/null @@ -1,47 +0,0 @@ - -.. _logging: - -############## -`src/logging`_ -############## - -.. _src/logging: https://github.com/NaokiHori/SimpleNSSolver/tree/main/src/logging - -This directory contains source files to compute and output quantities to be monitored during the simulation. - -.. mydeclare:: /../../src/logging/main.c - :language: c - :tag: check_and_output - -This function is a main function and an entry point of this directory, which calls other functions which compute and output log data discussed below. - -.. mydeclare:: /../../src/logging/main.c - :language: c - :tag: show_progress - -This function displays the current status such as - - * time step ``step`` - - * simulation time ``time`` - - * time step size ``dt`` - - * elapsed time ``wtime`` - -.. myliteralinclude:: /../../src/logging/main.c - :language: c - :tag: show progress to standard output and file - -Note that this information is displayed to ``stdout`` and is also written to a file (by default ``output/log/progress.dat``). - -Monitored quantities and their implementations are described below: - -.. toctree:: - :maxdepth: 1 - - divergence - momentum - energy - nusselt - diff --git a/docs/source/implementation/logging/momentum.rst b/docs/source/implementation/logging/momentum.rst deleted file mode 100644 index 57d4b840..00000000 --- a/docs/source/implementation/logging/momentum.rst +++ /dev/null @@ -1,38 +0,0 @@ -######## -Momentum -######## - -.. mydeclare:: /../../src/logging/momentum.c - :language: c - :tag: logging_check_momentum - -This function computes the net momentum in each direction (e.g. :math:`\int \ux dx dy dz`) and writes them to a file. - -The discrete form leads to - -.. math:: - - \sum_{i} \sum_{j} \sum_{k} - \vat{ - \left( - \ux - \Delta x - \Delta y - \Delta z - \right) - }{\xic, \xjc, \xkc}, - -which are implemented as - -.. myliteralinclude:: /../../src/logging/momentum.c - :language: c - :tag: compute total x-momentum - -.. myliteralinclude:: /../../src/logging/momentum.c - :language: c - :tag: compute total y-momentum - -.. myliteralinclude:: /../../src/logging/momentum.c - :language: c - :tag: compute total z-momentum - diff --git a/docs/source/implementation/logging/nusselt.rst b/docs/source/implementation/logging/nusselt.rst deleted file mode 100644 index e8d14473..00000000 --- a/docs/source/implementation/logging/nusselt.rst +++ /dev/null @@ -1,22 +0,0 @@ - -.. _logging_nusselt: - -############### -Nusselt numbers -############### - -.. mydeclare:: /../../src/logging/nusselt/main.c - :language: c - :tag: logging_check_nusselt - -This function computes the instantaneous Nusselt number (heat transfer enhancement by the convective effects) :math:`Nu \left( t \right)` based on the various definitions and write the results to a file. -I compute :math:`Nu` in several ways, which should return the same result in a statistical sense. - -* :ref:`Heat flux on the walls ` - -* :ref:`Kinetic energy injection ` - -* :ref:`Kinetic energy dissipation ` - -* :ref:`Thermal energy dissipation ` - diff --git a/docs/source/implementation/main.rst b/docs/source/implementation/main.rst deleted file mode 100644 index 1ec7b324..00000000 --- a/docs/source/implementation/main.rst +++ /dev/null @@ -1,183 +0,0 @@ - -.. _implementation: - -############## -Implementation -############## - -The purpose of this section is to explain the details of the implementation. - -This project adopts a directory structure which is widely used: - -* Under `include `_, structures and non-static functions are declared. - -* Under `src `_, all functions are implemented. - -Although I try to document in the code, it cat be too verbose if I explain all things there. -The following pages are mainly used to give additional information. -In particular I aim to describe :ref:`the discretisations of the equations ` and their implementations side-by-side. - -.. toctree:: - :maxdepth: 1 - - decide_dt - domain - fileio - fluid/main - halo/main - integrate - linear_system - logging/main - param - statistics - tdm/main - -.. seealso:: - - Please check the README files placed under ``src`` to explain the role of each directory and file. - -.. note:: - - The main solver always try to load a flow field from a specified directory, i.e. a set of initial conditions is to be given externally. - See `initial_condition/main.py `_. - -************* -`src/main.c`_ -************* - -.. _src/main.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/main.c - -This file contains a main function ``main`` which is an entry point of this solver and calls all other functions. - -.. mydeclare:: /../../src/main.c - :language: c - :tag: main - -In the following, the overview of the solver is briefly described. - -===== -Setup -===== - -Since this is a PDE solver, initial condition for each scalar field and the coordinates (i.e. grid points) are required, which should be stored as ``NPY`` files under a specific directory. -Normally you can create the initial condition using the separated program under directory ``initial_condition/``. -I expect the name of the directory where all the initial flow fields and the coordinate systems are stored is passed as the argument: for example, - -.. code-block:: console - - mpirun -n 16 ./a.out initial_condition/output - -when you start from the initial condition, while - -.. code-block:: console - - mpirun -n 16 ./a.out output/save/step0000012345 - -when you restart from the intermediate flow field at ``12345`` step. - -Run-time parameters (such as the maximum simulation time) are given as environment variables (i.e. ``export KEY=VALUE``) when the program is launched. -Although a straightforward way is to give them in front of ``mpirun`` (e.g. ``KEY1=VALUE1 KEY2=VALUE2 ... mpirun ... ./a.out``), it is cumbersome. -A more convenient way is to define them in a file: see `exec.sh `_ for example. -Other parameters, which are less frequently changed but still possibly changed, are configured under :ref:`param `. - -============== -Initialisation -============== - -First of all, ``MPI`` is launched by invoking ``MPI_Init``, and time when the simulator is started is recorded: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: launch MPI, start timer - -``wtimes`` are used to check the elapsed time to terminate the simulator (see below). - -I expect the name of the directory where the initial conditions are stored is passed as the argument: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: find name of directory where IC is stored - -From this directory, the time step ``step`` and the time units ``time`` are loaded: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: initialise time step and time units - -Also from this directory, the whole initial flow information is loaded and assigned to the corresponding structures: - -* Domain decomposition and coordinate systems: ``domain_t`` - -* Flow field, velocity, pressure, temperature: ``fluid_t`` - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: initialise structures - -Finally other auxiliary objects (logger, flow fields saver, statistics collector) are initialised and scheduled: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: initialise auxiliary objects - -========= -Main loop -========= - -In this part, :ref:`the main solver ` is iteratively invoked to integrate the equations in time. -Please refer to :ref:`the temporal discretisation ` for more details about the method. - -After the scalar fields have been updated, the time step ``step`` and the time units ``time`` should also be synchronised: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: update step and simulation / wall time - -Also I check the elapsed time by recording the current wall time. - -The simulator will abort if one of the abort conditions is met: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: terminate if one of the following conditions is met - -Values to be monitored during the simulation (e.g. divergence) are computed and written to the screen or to files regularly: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: compute and output log regularly - -Also the flow fields are regularly dumped: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: save flow fields regularly - -In addition, temporally-averaged statistics are regularly computed and collected: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: collect statistics regularly - -============ -Finalisation -============ - -The last flow fields (velocity, pressure, and temperature), which are necessary to restart the simulation later, are saved: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: save final flow fields - -Also the statistics are saved to files if collected: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: save collected statistics - -Finally ``MPI`` is finalised: - -.. myliteralinclude:: /../../src/main.c - :language: c - :tag: finalise MPI - diff --git a/docs/source/implementation/param.rst b/docs/source/implementation/param.rst deleted file mode 100644 index 8a26cc5f..00000000 --- a/docs/source/implementation/param.rst +++ /dev/null @@ -1,18 +0,0 @@ - -.. _param: - -############ -`src/param`_ -############ - -.. _src/param: https://github.com/NaokiHori/SimpleNSSolver/tree/main/src/param - -This directory contains source files to set fixed parameters which are constant throughout a simulation. - -All parameters are declared in a header file ``include/param.h``: - -.. mydetails:: ``param.h`` - - .. literalinclude:: /../../include/param.h - :language: c - diff --git a/docs/source/implementation/statistics.rst b/docs/source/implementation/statistics.rst deleted file mode 100644 index 5640aa1a..00000000 --- a/docs/source/implementation/statistics.rst +++ /dev/null @@ -1,79 +0,0 @@ - -.. _statistics: - -################### -`src/statistics.c`_ -################### - -.. _src/statistics.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/statistics.c - -This file contains some functions which are used to collect temporally-averaged statistics and to output the results. - -******** -Overview -******** - -:math:`\ave{q}{t}`, which is a temporally-averaged value of a scalar field :math:`q`: - -.. math:: - - \ave{q}{t} - = - \frac{\int_{t} q dt}{\int_{t} dt}, - -is numerically approximated as: - -.. math:: - - \frac{\sum_{n=0}^{N-1} q_n}{N}, - -namely :math:`q` at :math:`N` different time steps are averaged. - -By default, I collect the following quantities: - -.. list-table:: Quantities - :widths: 25 25 50 - :header-rows: 1 - - * - Title - - Position - - Description - * - :math:`\sum_{t} \ux` - - :math:`x` cell face - - N/A - * - :math:`\sum_{t} \ux^2` - - :math:`x` cell face - - N/A - * - :math:`\sum_{t} \uy` - - :math:`y` cell face - - N/A - * - :math:`\sum_{t} \uy^2` - - :math:`y` cell face - - N/A - * - :math:`\sum_{t} \uz` - - :math:`z` cell face - - 3D only - * - :math:`\sum_{t} \uz^2` - - :math:`z` cell face - - 3D only - * - :math:`\sum_{t} T` - - cell center - - N/A - * - :math:`\sum_{t} T^2` - - cell center - - N/A - * - :math:`\sum_{t} \ux T` - - :math:`x` cell face - - N/A - -************** -Implementation -************** - -A structure ``statistics_t``, which is defined in `include/statistics.h `_, contains methods which collect and save the statistics. - -.. note:: - - Although each statistical data is collected as a two-dimensional (or three-dimensional) array, it is saved after summed in the homogeneous directions to reduce the storage requirement. - This behaviour can be changed by a flag ``g_reduction`` defined in ``statistics.c``. - diff --git a/docs/source/implementation/tdm/backward.rst b/docs/source/implementation/tdm/backward.rst deleted file mode 100644 index 620dd4fb..00000000 --- a/docs/source/implementation/tdm/backward.rst +++ /dev/null @@ -1,74 +0,0 @@ - -.. _backward_substitution: - -##################### -Backward substitution -##################### - -After :ref:`the forward substitution `, I am left with the following system: - -.. math:: - - \begin{bmatrix} - 1 & v_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ - 0 & 1 & v_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ - 0 & 0 & 1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ - \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & 1 & v_{i-1} & 0 & \cdots & 0 & 0 & 0 \\ - 0 & 0 & 0 & \cdots & 0 & 1 & v_i & \cdots & 0 & 0 & 0 \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 1 & \cdots & 0 & 0 & 0 \\ - \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 1 & v_{n-3} & 0 \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 1 & v_{n-2} \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 1 - \end{bmatrix} - \begin{bmatrix} - x_0 \\ - x_1 \\ - x_2 \\ - \vdots \\ - x_{i-1} \\ - x_{i } \\ - x_{i+1} \\ - \vdots \\ - x_{n-3} \\ - x_{n-2} \\ - x_{n-1} - \end{bmatrix} - = - \begin{bmatrix} - r_0 \\ - r_1 \\ - r_2 \\ - \vdots \\ - r_{i-1} \\ - r_{i } \\ - r_{i+1} \\ - \vdots \\ - r_{n-3} \\ - r_{n-2} \\ - r_{n-1} - \end{bmatrix}. - -In the last row, I have - -.. math:: - - x_{n-1} = r_{n-1}, - -which has already been computed in :ref:`the forward substitution `. - -Also, since I have - -.. math:: - - x_i = r_i - v_i x_{i+1}, - -I can compute :math:`x_i` one after another (sequentially from :math:`i = n-2` to :math:`i = 0`): - -.. myliteralinclude:: /../../src/tdm.c - :language: c - :tag: backward substitution - -Note again that ``q`` is shared among :math:`x_i` (output) and :math:`q_i` (input) in the code. - diff --git a/docs/source/implementation/tdm/forward.rst b/docs/source/implementation/tdm/forward.rst deleted file mode 100644 index a231f052..00000000 --- a/docs/source/implementation/tdm/forward.rst +++ /dev/null @@ -1,169 +0,0 @@ - -.. _forward_substitution: - -#################### -Forward substitution -#################### - -First I try to eliminate the lower-diagonal components :math:`l_1, l_2, \cdots, l_{n-2}, l_{n-1}`, which is called the forward substitution. - -**************** -:math:`i = 0, 1` -**************** - -To get started, I look at the top two rows: - -.. math:: - - \begin{bmatrix} - c_0 & u_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_0 \\ - l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_1 - \end{bmatrix}. - -Dividing the first row by :math:`c_0` yields - -.. math:: - - & - \begin{bmatrix} - 1 & u_0 / c_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_0 / c_0 \\ - l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_1 - \end{bmatrix} \\ - & - = \\ - & - \begin{bmatrix} - 1 & v_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & r_0 \\ - l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_1 - \end{bmatrix}, - -namely - -.. math:: - - v_0 \leftarrow \frac{u_0}{c_0}, - -.. math:: - - r_0 \leftarrow \frac{q_0}{c_0}. - -In the code, I have - -.. myliteralinclude:: /../../src/tdm.c - :language: c - :tag: divide the first row by center-diagonal term - -.. note:: - - One may notice that ``u`` is modified in the above equation. - If I implement in this way, I need to re-initialise :math:`u_i` for each right-hand-side term, which is redundant. - To avoid this, I use a buffer ``v``, in which the modified ``u`` is stored instead of overwriting ``u``. - -Next, to eliminate :math:`l_1`, I subtract *the first row times* :math:`l_1` from *the second row*, yielding - -.. math:: - - \begin{bmatrix} - 1 & v_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & r_0 \\ - 0 & c_1 - l_1 v_0 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_1 - l_1 r_0 - \end{bmatrix}, - -or - -.. math:: - - & - \begin{bmatrix} - 1 & v_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & r_0 \\ - 0 & 1 & \frac{u_1}{c_1 - l_1 v_0} & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \frac{q_1 - l_1 r_0}{c_1 - l_1 v_0} - \end{bmatrix}, \\ - & - = \\ - & - \begin{bmatrix} - 1 & v_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & r_0 \\ - 0 & 1 & v_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & r_1 - \end{bmatrix}. - -*********************************** -General :math:`(i-1)` and :math:`i` -*********************************** - -I consider to extend the above process to a general :math:`i-1`-th and :math:`i`-th rows: - -.. math:: - - \begin{bmatrix} - 0 & 0 & 0 & \cdots & 1 & v_{i-1} & 0 & \cdots & 0 & 0 & 0 & r_{i-1} \\ - 0 & 0 & 0 & \cdots & l_i & c_i & u_i & \cdots & 0 & 0 & 0 & q_{i } - \end{bmatrix}, - -where the upper row (:math:`i-1`-th row) has already been updated, while the bottom row (:math:`i`-th row) is to be updated now. - -Now let us consider to eliminate :math:`l_i`: - -.. math:: - - \begin{bmatrix} - 0 & 0 & 0 & \cdots & 1 & v_{i-1} & 0 & \cdots & 0 & 0 & 0 & r_{i-1} \\ - 0 & 0 & 0 & \cdots & 0 & c_i - l_i v_{i-1} & u_i & \cdots & 0 & 0 & 0 & q_{i} - l_i r_{i-1} - \end{bmatrix}, - -or - -.. math:: - - & - \begin{bmatrix} - 0 & 0 & 0 & \cdots & 1 & v_{i-1} & 0 & \cdots & 0 & 0 & 0 & r_{i-1} \\ - 0 & 0 & 0 & \cdots & 0 & 1 & \frac{u_i}{c_i - l_i v_{i-1}} & \cdots & 0 & 0 & 0 & \frac{q_{i} - l_i r_{i-1}}{c_i - l_i v_{i-1}} - \end{bmatrix} \\ - & - = \\ - & - \begin{bmatrix} - 0 & 0 & 0 & \cdots & 1 & v_{i-1} & 0 & \cdots & 0 & 0 & 0 & r_{i-1} \\ - 0 & 0 & 0 & \cdots & 0 & 1 & v_{i} & \cdots & 0 & 0 & 0 & r_{i } - \end{bmatrix}, - -namely, - -.. math:: - - v_i \leftarrow \frac{u_i}{c_i - l_i v_{i-1}}, - -.. math:: - - r_i \leftarrow \frac{q_i - l_i r_{i-1}}{c_i - l_i v_{i-1}}. - -This is repeated from :math:`i = 1` to :math:`n - 2`: - -.. myliteralinclude:: /../../src/tdm.c - :language: c - :tag: forward substitution - -***************** -:math:`i = n - 1` -***************** - -Basically I can do the same thing. -For the last row :math:`i = n-1`, however, the denominator - -.. math:: - - c_{n-1} - l_{n-1} v_{n-2} - -can be :math:`0`, namely the rank of the matrix is :math:`n-1`. - -This is expected, since I often impose the Neumann or the periodic boundary conditions, which can only solve the differential equations up to a constant. - -In order to take into account the singularity and to avoid the resulting zero divisions, I need a special treatment: - -.. myliteralinclude:: /../../src/tdm.c - :language: c - :tag: last row, do the same thing but consider singularity - -.. seealso:: - - This forward substitution is followed by :ref:`the backward substitution `. - diff --git a/docs/source/implementation/tdm/main.rst b/docs/source/implementation/tdm/main.rst deleted file mode 100644 index 24260f56..00000000 --- a/docs/source/implementation/tdm/main.rst +++ /dev/null @@ -1,17 +0,0 @@ - -.. _tdm: - -############ -`src/tdm.c`_ -############ - -.. _src/tdm.c: https://github.com/NaokiHori/SimpleNSSolver/blob/main/src/tdm.c - -Implementations of the Thomas algorithm and the Sherman-Morrison formula are included. - -.. toctree:: - :maxdepth: 1 - - overview - method_and_implementation - diff --git a/docs/source/implementation/tdm/method_and_implementation.rst b/docs/source/implementation/tdm/method_and_implementation.rst deleted file mode 100644 index e5472bdd..00000000 --- a/docs/source/implementation/tdm/method_and_implementation.rst +++ /dev/null @@ -1,130 +0,0 @@ -######################### -Method and implementation -######################### - -**************** -Thomas algorithm -**************** - -======== -Overview -======== - -Let us consider a typical tri-diagonal system whose size is :math:`n`: - -.. math:: - - \begin{bmatrix} - c_0 & u_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ - l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ - 0 & l_2 & c_2 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ - \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & c_{i-1} & u_{i-1} & 0 & \cdots & 0 & 0 & 0 \\ - 0 & 0 & 0 & \cdots & l_{i } & c_{i } & u_{i } & \cdots & 0 & 0 & 0 \\ - 0 & 0 & 0 & \cdots & 0 & l_{i+1} & c_{i+1} & \cdots & 0 & 0 & 0 \\ - \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & c_{n-3} & u_{n-3} & 0 \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & l_{n-2} & c_{n-2} & u_{n-2} \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & l_{n-1} & c_{n-1} - \end{bmatrix} - \begin{bmatrix} - x_0 \\ - x_1 \\ - x_2 \\ - \vdots \\ - x_{i-1} \\ - x_{i } \\ - x_{i+1} \\ - \vdots \\ - x_{n-3} \\ - x_{n-2} \\ - x_{n-1} - \end{bmatrix} - = - \begin{bmatrix} - q_0 \\ - q_1 \\ - q_2 \\ - \vdots \\ - q_{i-1} \\ - q_{i } \\ - q_{i+1} \\ - \vdots \\ - q_{n-3} \\ - q_{n-2} \\ - q_{n-1} - \end{bmatrix}, - -where :math:`x` is the answer of the system and to be computed. -In the code, a buffer ``q`` is used to store the solution :math:`x` as well as to store the input (right-hand-side terms). -Thus the input array is overwritten by the solver. - -.. note:: - - Although ``l[0]`` and ``u[n-1]`` are not used in this case, ``l``, ``c``, and ``u`` all have the length :math:`n` for simplicity. - In particular these values are used for periodic systems. - -I first look at how this matrix is solved. -For notational simplicity, I concatenate the tri-diagonal matrix and the right-hand-side term: - -.. math:: - - \begin{bmatrix} - c_0 & u_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_0 \\ - l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_1 \\ - 0 & l_2 & c_2 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_2 \\ - \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & c_{i-1} & u_{i-1} & 0 & \cdots & 0 & 0 & 0 & q_{i-1} \\ - 0 & 0 & 0 & \cdots & l_{i } & c_{i } & u_{i } & \cdots & 0 & 0 & 0 & q_{i } \\ - 0 & 0 & 0 & \cdots & 0 & l_{i+1} & c_{i+1} & \cdots & 0 & 0 & 0 & q_{i+1} \\ - \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & c_{n-3} & u_{n-3} & 0 & q_{n-3} \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & l_{n-2} & c_{n-2} & u_{n-2} & q_{n-2} \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & l_{n-1} & c_{n-1} & q_{n-1} - \end{bmatrix}, - -and consider `the Gaussian elimination `_. - -Our objective is to convert the tri-diagonal matrix to an identity matrix (i.e. matrix inversion): - -.. math:: - - \begin{bmatrix} - 1 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & x_0 \\ - 0 & 1 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & x_1 \\ - 0 & 0 & 1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & x_2 \\ - \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & 1 & 0 & 0 & \cdots & 0 & 0 & 0 & x_{i-1} \\ - 0 & 0 & 0 & \cdots & 0 & 1 & 0 & \cdots & 0 & 0 & 0 & x_{i } \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 1 & \cdots & 0 & 0 & 0 & x_{i+1} \\ - \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 1 & 0 & 0 & x_{n-3} \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 1 & 0 & x_{n-2} \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 1 & x_{n-1} - \end{bmatrix}. - -To solve this system, there are mainly step steps: forward substitution, and the backward substitution, which are explained below. - -The above system assumes Dirichlet or Neumann boundary conditions. -Extension to the periodic boundaries is also discussed below. - -.. toctree:: - :maxdepth: 1 - - forward - backward - sherman_morrison - -.. note:: - - Although the function implemented in this project behaves similarly as the function implemented in `LAPACK `_, there are mainly two differences: - - * No pivoting - - Although functions in ``LAPACK`` include pivoting operations to stabilise the system, functions implemented in this source do not since all systems to be solved in this project are (semi-) diagonally dominant. - - * Treatment of the singularity - - When a singularity is detected, functions in ``LAPACK`` terminate. - This is not preferable in this project, since singular systems appear because of the Neumann or the periodic boundary conditions. - diff --git a/docs/source/implementation/tdm/overview.rst b/docs/source/implementation/tdm/overview.rst deleted file mode 100644 index 18e2a141..00000000 --- a/docs/source/implementation/tdm/overview.rst +++ /dev/null @@ -1,114 +0,0 @@ -######## -Overview -######## - -This source file contains several functions to solve tri-diagonal linear systems (I name the title based on the abbreviation of the ``tri-diagonal matrix``). -As usual, one needs to follow the three steps: - -#. Initialisation - - Internal buffers are allocated. - -#. Execution - - The systems are solved for specific right-hand-side terms. - -#. Finalisation - - Buffers are deallocated. - -************** -Initialisation -************** - -.. mydeclare:: /../../src/tdm.c - :language: c - :tag: construct - -This step is to initialise the following structure which stores all information about the linear systems to be solved: - -.. myliteralinclude:: /../../src/tdm.c - :language: c - :tag: definition of tdm_info_t_ - -Once this constructor is called, one can assign the information about the tri-diagonal matrix: - -.. mydeclare:: /../../src/tdm.c - :language: c - :tag: get_l - -.. mydeclare:: /../../src/tdm.c - :language: c - :tag: get_c - -.. mydeclare:: /../../src/tdm.c - :language: c - :tag: get_u - -Example: - -.. code-block:: c - - // call constructor - tdm_info_t *info = null; - int retval = tdm.construct(size, nrhs, is_periodic, is_complex, &info); - if(0 != retval){ - printf("plan creation failed\n"); - exit(1); - } - // initialise tri-diagonal matrix - double *l = null; - double *c = null; - double *u = null; - tdm.get_l(info, l); - tdm.get_c(info, c); - tdm.get_u(info, u); - for(int i = 0; i < size; i++){ - l[i] = set_lower_diagonals(i); - c[i] = set_centr_diagonals(i); - u[i] = set_upper_diagonals(i); - } - -.. note:: - - ``l``, ``c`` and ``u`` are not changed in the following procedures and thus one can reuse them. - -************ -Solve system -************ - -.. mydeclare:: /../../src/tdm.c - :language: c - :tag: solve - -By calling this function (``tdm.solve(...)``), the linear systems are solved. - -Example: - -.. code-block:: c - - // initialise right-hand-side term(s) - for(int j = 0; j < nrhs; j++){ - for(int i = 0; i < size; i++){ - data[j * size + i] = set_right_hand_side(i, j); - } - } - // call API - tdm.solve(info, data); - -Note that, as long as the tri-diagonal matrix is identical (``n``, ``l``, ``c`` and ``u`` are the same), one can solve multiple (``nrhs``) inputs simultaneously. - -************ -Finalisation -************ - -.. mydeclare:: /../../src/tdm.c - :language: c - :tag: destruct - -If ``info`` is no longer used, call this destructor to free all internal memory: - -.. code-block:: c - - tdm.destruct(info); - diff --git a/docs/source/implementation/tdm/sherman_morrison.rst b/docs/source/implementation/tdm/sherman_morrison.rst deleted file mode 100644 index 3f90ce7c..00000000 --- a/docs/source/implementation/tdm/sherman_morrison.rst +++ /dev/null @@ -1,294 +0,0 @@ - -.. _sherman_morrison: - -######################## -Sherman-Morrison formula -######################## - -The original Thomas algorithm only considers the tri-diagonal matrix. -With periodic boundary conditions, right-top and left-bottom corners have non-zero values (see below). - -Fortunately, by using `the Sherman-Morrison formula `_, I can handle this minor correction in the framework of the Thomas algorithm. - -Now, I consider the following system: - -.. math:: - - \begin{bmatrix} - c_0 & u_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & l_0 \\ - l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ - 0 & l_2 & c_2 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ - \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & c_{i-1} & u_{i-1} & 0 & \cdots & 0 & 0 & 0 \\ - 0 & 0 & 0 & \cdots & l_{i } & c_{i } & u_{i } & \cdots & 0 & 0 & 0 \\ - 0 & 0 & 0 & \cdots & 0 & l_{i+1} & c_{i+1} & \cdots & 0 & 0 & 0 \\ - \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & c_{n-3} & u_{n-3} & 0 \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & l_{n-2} & c_{n-2} & u_{n-2} \\ - u_{n-1} & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & l_{n-1} & c_{n-1} - \end{bmatrix} - \begin{bmatrix} - x_0 \\ - x_1 \\ - x_2 \\ - \vdots \\ - x_{i-1} \\ - x_{i } \\ - x_{i+1} \\ - \vdots \\ - x_{n-3} \\ - x_{n-2} \\ - x_{n-1} - \end{bmatrix} - = - \begin{bmatrix} - q_0 \\ - q_1 \\ - q_2 \\ - \vdots \\ - q_{i-1} \\ - q_{i } \\ - q_{i+1} \\ - \vdots \\ - q_{n-3} \\ - q_{n-2} \\ - q_{n-1} - \end{bmatrix}, - -where one may notice that the top-right and the bottom-left corners have non-zero values. - -Since I have - -.. math:: - - \begin{alignat}{5} - & c_0 x_0 & & + u_0 x_1 & & + l_0 x_{n-1} & & = q_0 & \,\,\, & 0 \text{-th row} \\ - & l_{n-2} x_{n-3} & & + c_{n-2} x_{n-2} & & + u_{n-2} x_{n-1} & & = q_{n-2} & \,\,\, & \left( n-2 \right) \text{-th row} - \end{alignat} - -or - -.. math:: - - \begin{alignat}{3} - & c_0 x_0 & & + u_0 x_1 & & = q_0 - l_0 x_{n-1} \\ - & l_{n-2} x_{n-3} & & + c_{n-2} x_{n-2} & & = q_{n-2} - u_{n-2} x_{n-1}, - \end{alignat} - -I can *shrink* the system: - -.. math:: - - \begin{bmatrix} - c_0 & u_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ - l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ - 0 & l_2 & c_2 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ - \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & c_{i-1} & u_{i-1} & 0 & \cdots & 0 & 0 & 0 \\ - 0 & 0 & 0 & \cdots & l_{i } & c_{i } & u_{i } & \cdots & 0 & 0 & 0 \\ - 0 & 0 & 0 & \cdots & 0 & l_{i+1} & c_{i+1} & \cdots & 0 & 0 & 0 \\ - \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & c_{n-4} & u_{n-4} & 0 \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & l_{n-3} & c_{n-3} & u_{n-3} \\ - 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & l_{n-2} & c_{n-2} - \end{bmatrix} - \begin{bmatrix} - x_0 \\ - x_1 \\ - x_2 \\ - \vdots \\ - x_{i-1} \\ - x_{i } \\ - x_{i+1} \\ - \vdots \\ - x_{n-4} \\ - x_{n-3} \\ - x_{n-2} - \end{bmatrix} - = - \begin{bmatrix} - q_0 - l_0 x_{n-1} \\ - q_1 \\ - q_2 \\ - \vdots \\ - q_{i-1} \\ - q_{i } \\ - q_{i+1} \\ - \vdots \\ - q_{n-4} \\ - q_{n-3} \\ - q_{n-2} - u_{n-2} x_{n-1} - \end{bmatrix}, - -i.e. I moved :math:`x_{n-1}` from the left-hand side to the right-hand side, and the size of the system is now :math:`n - 1`. - -For notational simplicity, hereafter I write this as - -.. math:: - - \underline{\underline{A}} \, \underline{x} = \underline{q}. - -One may notice that this treatment has removed the additional components in the original matrix coming from the periodicity, and as a result I go back to the tri-diagonal system. - -The new system, however, includes an unknown :math:`x_{n-1}`. -As soon as I try to start the forward substitution, I would be in trouble since the first row in the right-hand side includes unknown value. -To resolve this situation, I consider to split the system into two problems: - -.. math:: - - {\underline{\underline{A}}} \, {\underline{x}}^0 & = {\underline{q}}^0, \\ - {\underline{\underline{A}}} \, {\underline{x}}^1 & = {\underline{q}}^1, - -where I define - -.. math:: - - \underline{q}^0 - = - \begin{bmatrix} - q_0 \\ - q_1 \\ - q_2 \\ - \vdots \\ - q_{i-1} \\ - q_{i } \\ - q_{i+1} \\ - \vdots \\ - q_{n-4} \\ - q_{n-3} \\ - q_{n-2} - \end{bmatrix}, - \underline{q}^1 - = - \begin{bmatrix} - - l_0 \\ - 0 \\ - 0 \\ - \vdots \\ - 0 \\ - 0 \\ - 0 \\ - \vdots \\ - 0 \\ - 0 \\ - - u_{n-2} - \end{bmatrix}, - -which satisfies - -.. math:: - - {\underline{q}} - = - {\underline{q}}^0 - + - x_{n-1} - {\underline{q}}^1. - -Note that the superscripts are used to distinguish the two problems (not the exponents). - -Since these two systems: - -.. math:: - - {\underline{\underline{A}}} \, {\underline{x}}^0 & = {\underline{q}}^0, \\ - {\underline{\underline{A}}} \, {\underline{x}}^1 & = {\underline{q}}^1, - -do not contain any unknown, I can solve them as two independent tri-diagonal systems: - -.. math:: - - {\underline{x}} - = - {\underline{x}}^0 - + - x_{n-1} - \times - {\underline{x}}^1, - -indicating that, the solution of the original system is the superposition of the solutions of the two tri-diagonal systems. - -The last piece is how to find :math:`x_{n-1}`, which is obtained by looking at the relation: - -.. math:: - - u_{n-1} x_0 + l_{n-1} x_{n-2} + c_{n-1} x_{n-1} = q_{n-1}, - -which appears in the last row of the original (:math:`n \times n`) system. - -Since I have - -.. math:: - - x_0 & = x_0^0 + x_{n-1} \times x_0^1, \\ - x_{n-2} & = x_{n-2}^0 + x_{n-1} \times x_{n-2}^1, \\ - -I notice - -.. math:: - - \left( u_{n-1} x_0^0 + l_{n-1} x_{n-2}^0 \right) - + \left( u_{n-1} x_0^1 + l_{n-1} x_{n-2}^1 + c_{n-1} \right) x_{n-1} - = q_{n-1}, - -and thus - -.. math:: - - x_{n-1} = - \frac{q_{n-1} - u_{n-1} x_0^0 - l_{n-1} x_{n-2}^0} - {c_{n-1} + u_{n-1} x_0^1 + l_{n-1} x_{n-2}^1}. - -This relation indicates that :math:`x_{n-1}` can be computed after solving the two shrunk linear systems - -.. math:: - - {\underline{\underline{A}}} \, {\underline{x}}^0 = {\underline{q}}^0`, - -.. math:: - - {\underline{\underline{A}}} \, {\underline{x}}^1 = {\underline{q}}^1`. - -Here is the summary and the corresponding implementation: - -#. Solve :math:`{\underline{\underline{A}}} \, {\underline{x}}^1 = {\underline{q}}^1`: - - .. myliteralinclude:: /../../src/tdm.c - :language: c - :tag: solve additional system coming from periodicity - -#. Solve :math:`{\underline{\underline{A}}} \, {\underline{x}}^0 = {\underline{q}}^0`: - - .. myliteralinclude:: /../../src/tdm.c - :language: text - :tag: solve normal system - - .. note:: - - The input argument ``q`` includes multiple (``nrhs``, corresponding to the loop whose index is ``j``) right-hand-side terms. - I assign the pointer of each right-hand side to ``q0`` here. - -#. Find :math:`x_{n-1}` - - :math:`x_{n-1}` is updated following - - .. math:: - - x_{n-1} = \frac{q_{n-1} - u_{n-1} x_0^0 - l_{n-1} x_{n-2}^0}{c_{n-1} + u_{n-1} x_0^1 + l_{n-1} x_{n-2}^1}: - - .. myliteralinclude:: /../../src/tdm.c - :language: c - :tag: find x_{n-1} - -#. Compute the solution of the original system :math:`{\underline{\underline{A}}} \, {\underline{x}} = {\underline{q}}` - - I use - - .. math:: - - {\underline{x}} = {\underline{x}}^0 + x_{n-1} \times {\underline{x}}^1: - - .. myliteralinclude:: /../../src/tdm.c - :language: c - :tag: solve original system - diff --git a/docs/source/index.rst b/docs/source/index.rst index 72e2ff26..3e8c4332 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -14,8 +14,6 @@ Documentation introduction equations/main numerical_method/main - implementation/main examples/main - misc references diff --git a/docs/source/misc.rst b/docs/source/misc.rst deleted file mode 100644 index b952eef0..00000000 --- a/docs/source/misc.rst +++ /dev/null @@ -1,18 +0,0 @@ -#### -Misc -#### - -* Optimisation - - Although the implementation likely has many optimisation opportunities, I respect and simply follow `this suggestion `_. - Also I allow users to configure compiler optimisation options, and it may be possible to improve performance in certain cases by specifying link time optimisation or proper vectorisation. - -* Absence of the final memory deallocations - - I intentionally omit the memory deallocation of the variables which survive throughout the simulation to simplify the whole things. - On the other hand, I make sure that variables which are temporally used inside functions are properly freed to avoid memory leak. - -* HTML theme of the documentation - - `Alabaster `_ is used after the colours are flipped. - diff --git a/docs/source/numerical_method/linear_system.rst b/docs/source/numerical_method/linear_system.rst new file mode 100644 index 00000000..dba89ab6 --- /dev/null +++ b/docs/source/numerical_method/linear_system.rst @@ -0,0 +1,165 @@ + +.. _linear_system: + +############# +Linear System +############# + +To :ref:`treat the diffusive terms implicitly `, we need to solve a linear system: + +.. math:: + + \left\{ + 1 + - + C + \frac{1}{J} + \dif{}{\gcs{i}} + \left( + \frac{J}{\sfact{i}} + \frac{1}{\sfact{i}} + \dif{}{\gcs{i}} + \right) + \right\} + p + = + q, + +where :math:`C` is a coefficient (e.g., coming from the diffusivity and time-step sizes). + +***************** +Laplace Operators +***************** + +Note that linear systems involve discrete Laplace operators: + +.. math:: + + \frac{1}{J} + \dif{}{\gcs{i}} + \left( + \frac{J}{\sfact{i}} + \frac{1}{\sfact{i}} + \dif{}{\gcs{i}} + \right), + +which are pre-computed as follows. + +Wall-normal velocity: + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: vector laplacian in x + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: vector laplacian in y + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: vector laplacian in z + +Stream-wise velocity: + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: vector laplacian in x + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: vector laplacian in y + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: vector laplacian in z + +Span-wise velocity: + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: vector laplacian in x + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: vector laplacian in y + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: vector laplacian in z + +Temperature: + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: scalar laplacian in x + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: scalar laplacian in y + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: scalar laplacian in z + +********************** +Solving Linear Systems +********************** + +Wall-normal velocity: + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: solve linear systems in x + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: solve linear systems in y + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: solve linear systems in z + +Stream-wise velocity: + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: solve linear systems in x + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: solve linear systems in y + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: solve linear systems in z + +Span-wise velocity: + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: solve linear systems in x + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: solve linear systems in y + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: solve linear systems in z + +Temperature: + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: solve linear systems in x + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: solve linear systems in y + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: solve linear systems in z + +We need to solve :ref:`tri-diagonal matrix `. + diff --git a/docs/source/numerical_method/main.rst b/docs/source/numerical_method/main.rst index 2af9b48e..6a71b8f0 100644 --- a/docs/source/numerical_method/main.rst +++ b/docs/source/numerical_method/main.rst @@ -2,27 +2,28 @@ .. _numerics: ################ -Numerical method +Numerical Method ################ Numerical treatments of :ref:`the governing equations ` are discussed here. -* :ref:`Temporal discretisation ` +In the first part, the discretization of the variables in the computational (discrete) domain is discussed. +Although there are :ref:`five equations `, the relations with respect to :math:`k` and :math:`h` are not solved numerically because they depend on the other three. +These dependencies, which are obvious in theory, are so fragile that they are not satisfied if we do not treat the main three relations properly. +The proper spatial numerical treatment is our main interest. - Conventional techniques to integrate the ordinary differential equations in time (e.g. SMAC method, implicit treatment of the diffusive terms) are briefly discussed. +In the second part, conventional techniques to integrate the given ordinary differential equations in time are briefly discussed. +Specifically, we focus on the SMAC method for dealing with the momentum balance and the incompressibility constraint, and the implicit treatment of the diffusive terms. -* :ref:`Spatial discretisation ` - - How the variables are discretised in the computational (discrete) domain is discussed. - Although there are :ref:`five equations `, :math:`k` and :math:`h` are not solved numerically because they depend on the other three. - - These *implicit* dependencies are easily broken if I do not treat the three main equations properly. - For instance, the kinetic energy (squared velocity) is artificially added or removed if the momentum equation is not properly discretised in space. - This correct numerical treatment is of my main interest in this section. +To update the flow field, we need to solve linear systems (when diffusive terms are treated implicitly) and a Poisson equation, which are separately discussed. +Both rely on solving tri-diagonal matrices, which is also elaborated. .. toctree:: - :hidden: + :maxdepth: 1 - temporal_discretisation/main spatial_discretisation/main + temporal_discretisation/main + linear_system + poisson + tdm diff --git a/docs/source/numerical_method/poisson.rst b/docs/source/numerical_method/poisson.rst new file mode 100644 index 00000000..1b59c03e --- /dev/null +++ b/docs/source/numerical_method/poisson.rst @@ -0,0 +1,427 @@ + +.. _poisson_equation: + +################ +Poisson Equation +################ + +To :ref:`integrate a momentum field in time ` (specifically to enforce :ref:`the incompressibility `), we need to solve a Poisson equation: + +.. math:: + + \frac{1}{J} + \dif{}{\gcs{1}} + \left( + \frac{J}{\sfact{1}} + \frac{1}{\sfact{1}} + \dif{p}{\gcs{1}} + \right) + + + \frac{1}{J} + \dif{}{\gcs{2}} + \left( + \frac{J}{\sfact{2}} + \frac{1}{\sfact{2}} + \dif{p}{\gcs{2}} + \right) + + + \frac{1}{J} + \dif{}{\gcs{3}} + \left( + \frac{J}{\sfact{3}} + \frac{1}{\sfact{3}} + \dif{p}{\gcs{3}} + \right) + = + q, + +where :math:`p` and :math:`q` are both defined at cell centers :math:`\left( \ccidx{i}, \ccidx{j}, \ccidx{k} \right)`. + +Since directions except :math:`x` are homogeneous (i.e., periodic boundary conditions are imposed and the grid sizes are equal), we consider the spectral representation (discrete backward Fourier transform) of :math:`p`: + +.. math:: + + \newcommand{ \wavy}{\exp \left( I \frac{2 \pi}{\ngp{2}} j m \right)} + \newcommand{ \wavz}{\exp \left( I \frac{2 \pi}{\ngp{3}} k n \right)} + \newcommand{\iwavy}{\exp \left( - I \frac{2 \pi}{\ngp{2}} j m^\prime \right)} + \newcommand{\iwavz}{\exp \left( - I \frac{2 \pi}{\ngp{3}} k n^\prime \right)} + \vat{p}{\ccidx{i}, \ccidx{j}, \ccidx{k}} + = + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + \wavy + \wavz, + +where :math:`I` is the imaginary unit :math:`\sqrt{-1}`. +Note that the discrete forward Fourier transform yields + +.. math:: + + \sum_{k = 0}^{\ngp{3} - 1} + \sum_{j = 0}^{\ngp{2} - 1} + \vat{p}{\ccidx{i}, \ccidx{j}, \ccidx{k}} + \iwavy + \iwavz + & + = + \sum_{k = 0}^{\ngp{3} - 1} + \sum_{j = 0}^{\ngp{2} - 1} + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + \wavy + \wavz + \iwavy + \iwavz + + & + = + \sum_{k = 0}^{\ngp{3} - 1} + \sum_{j = 0}^{\ngp{2} - 1} + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + \delta_{m m^\prime} + \delta_{n n^\prime} + + & + = + \ngp{2} + \ngp{3} + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + \delta_{m m^\prime} + \delta_{n n^\prime} + + & + = + \ngp{2} + \ngp{3} + \vat{P}{\ccidx{i}, \ccidx{m^\prime}, \ccidx{n^\prime}}. + +Since we have + +.. math:: + + \vat{p}{\ccidx{i} \pm 1, \ccidx{j}, \ccidx{k}} + & + = + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \vat{P}{\ccidx{i} \pm 1, \ccidx{m}, \ccidx{n}} + \wavy + \wavz, + + \vat{p}{\ccidx{i}, \ccidx{j} \pm 1, \ccidx{k}} + & + = + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + \exp \left( \pm I \frac{2 \pi}{\ngp{2}} m \right) + \wavy + \wavz, + + \vat{p}{\ccidx{i}, \ccidx{j}, \ccidx{k} \pm 1} + & + = + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + \exp \left( \pm I \frac{2 \pi}{\ngp{3}} n \right) + \wavy + \wavz, + +the second-order derivatives in the Poisson equation can be reformulated as + +.. math:: + + \newcommand{\coefl}{C_l} + \newcommand{\coefu}{C_u} + \frac{1}{J} + \dif{}{\gcs{1}} + \left( + \frac{J}{\sfact{1}} + \frac{1}{\sfact{1}} + \dif{p}{\gcs{1}} + \right) + & + = + \coefl + \vat{p}{\ccidx{i} - 1, \ccidx{j}, \ccidx{k}} + - + \left( + \coefl + + + \coefu + \right) + \vat{p}{\ccidx{i}, \ccidx{j}, \ccidx{k}} + + + \coefu + \vat{p}{\ccidx{i} + 1, \ccidx{j}, \ccidx{k}} + + & + = + \coefl + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \vat{P}{\ccidx{i} - 1, \ccidx{m}, \ccidx{n}} + \wavy + \wavz + - + \left( + \coefl + + + \coefu + \right) + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + \wavy + \wavz + + + \coefu + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \vat{P}{\ccidx{i} + 1, \ccidx{m}, \ccidx{n}} + \wavy + \wavz, + + \frac{1}{J} + \dif{}{\gcs{2}} + \left( + \frac{J}{\sfact{2}} + \frac{1}{\sfact{2}} + \dif{p}{\gcs{2}} + \right) + & + = + \frac{1}{\sfact{2}} + \frac{1}{\sfact{2}} + \left( + \vat{p}{\ccidx{i}, \ccidx{j} - 1, \ccidx{k}} + - + 2 + \vat{p}{\ccidx{i}, \ccidx{j} , \ccidx{k}} + + + \vat{p}{\ccidx{i}, \ccidx{j} + 1, \ccidx{k}} + \right) + + & + = + \frac{1}{\sfact{2}} + \frac{1}{\sfact{2}} + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + \wavy + \wavz + \left\{ + \exp \left( I \frac{2 \pi}{\ngp{2}} m \right) + - + 2 + + + \exp \left( - I \frac{2 \pi}{\ngp{2}} m \right) + \right\} + + & + = + - + \frac{1}{\sfact{2}} + \frac{1}{\sfact{2}} + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + 4 + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + \wavy + \wavz + \sin^2 \left( \frac{\pi}{\ngp{2}} m \right), + + \frac{1}{J} + \dif{}{\gcs{3}} + \left( + \frac{J}{\sfact{3}} + \frac{1}{\sfact{3}} + \dif{p}{\gcs{3}} + \right) + & + = + - + \frac{1}{\sfact{3}} + \frac{1}{\sfact{3}} + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + 4 + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + \wavy + \wavz + \sin^2 \left( \frac{\pi}{\ngp{3}} n \right), + +where + +.. math:: + + \coefl + & + \equiv + \frac{ + 1 + }{ + \vat{J}{\ccidx{i}} + } + \frac{ + \vat{J}{\cmidx{i}} + }{ + \vat{\sfact{1}}{\cmidx{i}} + } + \frac{ + 1 + }{ + \vat{\sfact{1}}{\cmidx{i}} + }, + + \coefu + & + \equiv + \frac{ + 1 + }{ + \vat{J}{\ccidx{i}} + } + \frac{ + \vat{J}{\cpidx{i}} + }{ + \vat{\sfact{1}}{\cpidx{i}} + } + \frac{ + 1 + }{ + \vat{\sfact{1}}{\cpidx{i}} + }, + +which are computed here: + +.. myliteralinclude:: /../../src/fluid/compute_potential.c + :language: c + :tag: initialise tri-diagonal matrix in x direction + +Consequently, we obtain + +.. math:: + + & + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \coefl + \vat{P}{\ccidx{i} - 1, \ccidx{m}, \ccidx{n}} + \wavy + \wavz + + + + & + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \left\{ + - + \left( + \coefl + + + \coefu + \right) + - + 4 + \frac{1}{\sfact{2}} + \frac{1}{\sfact{2}} + \sin^2 \left( \frac{\pi}{\ngp{2}} m \right) + - + 4 + \frac{1}{\sfact{3}} + \frac{1}{\sfact{3}} + \sin^2 \left( \frac{\pi}{\ngp{3}} n \right) + \right\} + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + \wavy + \wavz + + + + & + \sum_{n = 0}^{\ngp{3} - 1} + \sum_{m = 0}^{\ngp{2} - 1} + \coefu + \vat{P}{\ccidx{i} + 1, \ccidx{m}, \ccidx{n}} + \wavy + \wavz + + = + & + \vat{q}{\ccidx{i}, \ccidx{j}, \ccidx{k}}. + +By applying the discrete forward Fourier transform, we obtain + +.. math:: + + & + \coefl + \vat{P}{\ccidx{i} - 1, \ccidx{m}, \ccidx{n}} + + + + & + \left\{ + - + \left( + \coefl + + + \coefu + \right) + - + 4 + \frac{1}{\sfact{2}} + \frac{1}{\sfact{2}} + \sin^2 \left( \frac{\pi}{\ngp{2}} m \right) + - + 4 + \frac{1}{\sfact{3}} + \frac{1}{\sfact{3}} + \sin^2 \left( \frac{\pi}{\ngp{3}} n \right) + \right\} + \vat{P}{\ccidx{i}, \ccidx{m}, \ccidx{n}} + + + + & + \coefu + \vat{P}{\ccidx{i} + 1, \ccidx{m}, \ccidx{n}} + + = + & + \frac{1}{\ngp{2}} + \frac{1}{\ngp{3}} + \sum_{k = 0}^{\ngp{3} - 1} + \sum_{j = 0}^{\ngp{2} - 1} + \vat{q}{\ccidx{i}, \ccidx{j}, \ccidx{k}} + \exp \left( - I \frac{2 \pi}{\ngp{2}} j m \right) + \exp \left( - I \frac{2 \pi}{\ngp{3}} k n \right). + +The resulting :ref:`tri-diagonal matrix ` for each :math:`m` and :math:`n` is solved here: + +.. myliteralinclude:: /../../src/fluid/compute_potential.c + :language: c + :tag: solve tri-diagonal matrices + +The sinusoidal functions (wave numbers) are computed here: + +.. myliteralinclude:: /../../src/fluid/compute_potential.c + :language: c + :tag: y eigenvalues + +.. myliteralinclude:: /../../src/fluid/compute_potential.c + :language: c + :tag: z eigenvalues + +The overall procedure can be found here: + +.. myliteralinclude:: /../../src/fluid/compute_potential.c + :language: c + :tag: solve Poisson equation + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/example1.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/example1.rst deleted file mode 100644 index c5b67af2..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/example1.rst +++ /dev/null @@ -1,120 +0,0 @@ - -.. include:: /references.txt - -##################################### -Linearly-interpolated advective terms -##################################### - -******* -Summary -******* - -Advective terms do not only transfer the transported quantity but also increase or decrease it, which breaks physics and tends to destabilise the solver. -This artificial error stays even a very small time step :math:`\Delta t` is adopted. - -******* -Details -******* - -When I interpolate a quantity from the neighbouring two points, linear interpolations generally give the most accurate results. - -Here, instead of the energy-conserving scheme derived before, I use the following discrete governing equations: - -.. math:: - - \der{\ux}{t} - + - \dder{ - \dintrpa{\ux}{x} - \dintrpa{\ux}{x} - }{x} - + - \dder{ - \color{red}{\dintrpv{\uy}{x}} - \dintrpa{\ux}{y} - }{y} - = - -\dder{p}{x} - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{\ux}{x} \right) - + - \dder{}{y} \left( \dder{\ux}{y} \right) - \right\} - + - \color{red}{\dintrpa{T}{x}}, - -.. math:: - - \der{\uy}{t} - + - \dder{ - \dintrpa{\ux}{y} - \color{red}{\dintrpa{\uy}{x}} - }{x} - + - \dder{ - \dintrpa{\uy}{y} - \dintrpa{\uy}{y} - }{y} - = - -\dder{p}{y} - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{\uy}{x} \right) - + - \dder{}{y} \left( \dder{\uy}{y} \right) - \right\}, - -.. math:: - - \der{T}{t} - + - \dder{ - \ux - \color{red}{\dintrpa{T}{x}} - }{x} - + - \dder{ - \uy - \dintrpa{T}{y} - }{y} - = - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{T}{x} \right) - + - \dder{}{y} \left( \dder{T}{y} \right) - \right\}, - -where the reddish terms, which are originally arithmetically- or volumetrically-interpolated, are replaced by the linear interpolations. - -The velocity and the temperature fields are randomly disturbed initially, and the time step size is fixed to :math:`\Delta t = 10^{-2}`. - -I monitor the changes in the volume-integrated quadratic quantities :math:`K` and :math:`H` for two types of schemes: - -.. image:: images/adv_energy.png - :width: 800 - -One might be tempted to investigate the effects of the time step size :math:`\Delta t`. -To check the dependence, I vary the time step size :math:`\Delta t` to see the convergence: - -.. math:: - - \epsilon_{k} \equiv \left| \frac{K_{t = 10}- K_0}{K_0} \right|, \\ - \epsilon_{h} \equiv \left| \frac{H_{t = 10}- H_0}{H_0} \right|. - -The results are shown here: - -.. image:: images/adv_convergence.png - :width: 800 - -I notice that the energy-conserving scheme shows third-order convergence, which is consistent with the previous reports (see e.g. |MORINISHI1998|, |HAM2002| and |COPPOLA2019|). -In short, the decrease is because of the temporal integration since the explicit Runge-Kutta schemes tend to dissipate them. -Note that the total energies are always reduced when the energy-conserving scheme is adopted, which is a favourable feature from a stability point of view. - -With the linear interpolations, on the other hand, the errors never shrink if the time step size is very small, indicating that make :math:`\Delta t` smaller does not help to stabilise the solver. -In addition, the total energies are generally increased and may lead to the divergence of the solver. - -In the normal cases, the results are not affected so much because of the viscous dissipation, which generally works to stabilise the fields. -For highly-turbulent wall-bounded flows with largely stretched grid, however, using an energy-consistent scheme could be useful to obtain more reliable and stable solutions. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/example2.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/example2.rst deleted file mode 100644 index d12ca2b2..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/example2.rst +++ /dev/null @@ -1,81 +0,0 @@ - -.. include:: /references.txt - -################# -Dissipation rates -################# - -******* -Summary -******* - -Intuitive ways to compute the dissipation rates are compared with the consistent ways. -Inconsistent schemes tend to underestimate the dissipations, and as a result the perfect Nusselt number agreements are broken. - -******* -Details -******* - -I consider - -.. math:: - - \der{u_i}{x_j} \der{u_i}{x_j} - = \left( \der{\ux}{x} \right)^2 - + \left( \der{\ux}{y} \right)^2 - + \left( \der{\uy}{x} \right)^2 - + \left( \der{\uy}{y} \right)^2, - -which is necessary to compute the kinetic dissipation rate :math:`\epsilon_{k}`. - -A more intuitive way to compute :math:`\der{\ux}{y}` at the cell center might be - -.. math:: - - \vat{\frac{\delta u_x}{\delta y}}{i,j} - = - \frac{1}{2} \left( - \frac{\vat{\ux}{\pim,\pjpp} - \vat{\ux}{\pim,\pjmm}}{2 \Delta y} - + - \frac{\vat{\ux}{\pip,\pjpp} - \vat{\ux}{\pip,\pjmm}}{2 \Delta y} - \right). - -Similarly, the discretisation of :math:`\der{T}{y}`, which is essential to compute the thermal dissipation rate - -.. math:: - - \der{T}{x_i} \der{T}{x_i} - = \left( \der{T}{x} \right)^2 - + \left( \der{T}{y} \right)^2, - -could be given as - -.. math:: - \vat{\frac{\delta T}{\delta y}}{i,j} - = - \frac{ - \vat{T}{\pic,\pjpp} - - \vat{T}{\pic,\pjmm} - }{2 \Delta y}. - -.. seealso:: - - A consistent scheme is given :ref:`here ` and :ref:`here `. - -Here, I compare this inconsistent discretisations with the consistent ones. - -My interest is :math:`Nu_{\epsilon_{k}}` and :math:`Nu_{\epsilon_{h}}`, which are the Nusselt numbers computed based on the kinetic and the thermal dissipation rates, respectively. -The resolution is varied to see the effects: - -.. image:: images/dif_convergence.png - :width: 800 - -Note that :math:`Nu` are shown after normalised by the reference value :math:`Nu_{wall}`, and thus they should be unity. - -I find that the ratios indeed give unity when I evaluate the dissipation rates in the consistent ways, which is irrespective to the resolution. -Note that, although :math:`Nu` is a function of the spatial resolution (of course), the agreement of the different :math:`Nu` definitions is always satisfied. - -When the inconsistent schemes are adopted, on the other hand, :math:`Nu_{\epsilon_{k}}` and :math:`Nu_{\epsilon_{h}}` do not agree to the reference value. -Generally they give lower values and underestimate the Nusselt number. -Obviously the exact energy balance derived in :ref:`the Nusselt number relations ` are violated. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/images/adv_convergence.png b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/images/adv_convergence.png deleted file mode 100644 index eb1e34414a17149d5e5c661b889d7e5ab254ba4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48739 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 diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/images/adv_energy.png b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/images/adv_energy.png deleted file mode 100644 index 7c357fea7503b3f2ef51fe0ff57e5ae4ab7dfcc8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40587 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 diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/main.rst deleted file mode 100644 index b6a2e928..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/inconsistent_results/main.rst +++ /dev/null @@ -1,22 +0,0 @@ - -.. _inconsistent_results: - -#################### -Inconsistent results -#################### - -In :ref:`the previous part `, I extensively discuss a scheme to satisfy the following properties: - - * The advective terms conserve the discrete momenta and the quadratic quantities. - - * The diffusive terms dissipate the transported quantities properly. - -One might be tempted to know whether these rigorous formulations are really necessary. -In this part, I try to answer this question by comparing with another intuitive discretisation. - -.. toctree:: - :maxdepth: 1 - - example1 - example2 - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/continuous_domain.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/continuous_domain.rst deleted file mode 100644 index fa94004b..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/continuous_domain.rst +++ /dev/null @@ -1,157 +0,0 @@ -################# -Continuous domain -################# - -By taking the inner product of the momentum balance and :math:`u_i`, I have - -.. math:: - - u_i \der{u_i}{t} & - + u_i \frac{1}{J} \der{}{\xi^j} \left\{ \left( J \der{\xi^j}{x_j} u_j \right) u_i \right\} - + u_i \frac{1}{J} \der{}{\xi^i} \left( J \der{\xi^i}{x_i} p \right) \\ & - - u_i \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \der{}{\xi^j} \left( \mst{j}{j} \der{u_i}{\xi^j} \right) - - u_i f_i - = 0. - -Each term yields the following relation. - -******************* -Temporal derivative -******************* - -.. math:: - - u_i \der{u_i}{t} = \der{}{t} \left( u_i u_i \right) - \der{u_i}{t} u_i - -and thus - -.. math:: - - u_i \der{u_i}{t} = \der{k}{t}, - -where :math:`k \equiv \frac{1}{2} u_i u_i`. - -********* -Advection -********* - -.. math:: - - \frac{1}{J} \der{}{\xi^i} \left\{ \left( J \der{\xi^i}{x_i} u_i \right) k \right\}. - -***************** -Pressure gradient -***************** - -.. math:: - - \frac{1}{J} \der{}{\xi^i} \left( J \der{\xi^i}{x_i} u_i p \right) - - - p \frac{1}{J} \der{}{\xi^i} \left( J \der{\xi^i}{x_i} u_i \right), - -where the second term vanishes because of the incompressibility constraint. - -********* -Diffusion -********* - -.. math:: - - - \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \der{}{\xi^j} \left( \mst{j}{j} u_i \der{u_i}{\xi^j} \right) - + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \mst{j}{j} \der{u_i}{\xi^j} \der{u_i}{\xi^j}. - -************** -Buoyancy force -************** - -This term remains as it is. - -******* -Summary -******* - -In summary, I have the equation of the kinetic energy balance as - -.. math:: - - \der{k}{t} & - + \frac{1}{J} \der{}{\xi^i} \left\{ \left( J \der{\xi^i}{x_i} u_i \right) k \right\} - + \frac{1}{J} \der{}{\xi^i} \left( J \der{\xi^i}{x_i} u_i p \right) \\ & - - \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \der{}{\xi^j} \left( \mst{j}{j} u_i \der{u_i}{\xi^j} \right) - + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \mst{j}{j} \der{u_i}{\xi^j} \der{u_i}{\xi^j} - - u_i f_i - = 0. - -Similarly, by multiplying the equation of the internal energy by the temperature :math:`T`, I obtain - -.. math:: - - \der{h}{t} & - + \frac{1}{J} \der{}{\xi^i} \left\{ \left( J \der{\xi^i}{x_i} u_i \right) h \right\} - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J} \der{}{\xi^j} \left( \mst{j}{j} T \der{T}{\xi^j} \right) \\ & - + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J} \mst{j}{j} \der{T}{\xi^j} \der{T}{\xi^j} - = 0, - -where :math:`h \equiv \frac{1}{2} T^2`. - -Now I discuss the evolution of the total energies. -The total kinetic energy inside the domain :math:`K` is - -.. math:: - - \int k dx dy - -in the original coordinate system, while - -.. math:: - - \int k J d\gx d\gy - -in the transformed coordinate system. - -I obtain the governing equation with respect to :math:`K` by integrating the equation of :math:`k` inside the domain: - -.. math:: - - \der{K}{t} & - + \int \der{}{\xi^i} \left\{ \left( J \der{\xi^i}{x_i} u_i \right) k \right\} d\gx d\gy - + \int \der{}{\xi^i} \left( J \der{\xi^i}{x_i} u_i p \right) d\gx d\gy \\ & - - \int \frac{\sqrt{Pr}}{\sqrt{Ra}} \der{}{\xi^j} \left( \mst{j}{j} u_i \der{u_i}{\xi^j} \right) d\gx d\gy - + \int \frac{\sqrt{Pr}}{\sqrt{Ra}} \mst{j}{j} \der{u_i}{\xi^j} \der{u_i}{\xi^j} d\gx d\gy \\ & - - \int J u_i f_i d\gx d\gy - = 0. - -The second, the third and the fourth terms are conservative and thus the integrated values are determined solely by the boundary fluxes because of the divergence theorem. -For the Rayleigh-Bénard convections considered in this project, periodic boundary conditions or impermeable and no-slip walls (:math:`u_i = 0`) are assumed, and thus these terms vanish. - -Thus I have - -.. math:: - - \der{K}{t} - + \int \frac{\sqrt{Pr}}{\sqrt{Ra}} \mst{j}{j} \der{u_i}{\xi^j} \der{u_i}{\xi^j} d\gx d\gy - - \int J u_i f_i d\gx d\gy - = 0. - -When the system is in the statistically-steady state - -.. math:: - - \ave{\partial K / \partial t}{t} = 0, - -I notice that the energy injection by the body force is exactly balanced by the viscous dissipation. - -There is an analogue relation for :math:`H = \int h dx dy = \int h J d\gx d\gy`: - -.. math:: - - \der{H}{t} & - + \int \der{}{\xi^i} \left\{ \left( J \der{\xi^i}{x_i} u_i \right) h \right\} d\gx d\gy \\ & - - \int \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{}{\xi^j} \left( \mst{j}{j} T \der{T}{\xi^j} \right) d\gx d\gy - + \int \frac{1}{\sqrt{Pr} \sqrt{Ra}} \mst{j}{j} \der{T}{\xi^j} \der{T}{\xi^j} d\gx d\gy - = 0. - -The second term is zero because of the divergence theorem again. -Regarding the third term, it remains here since the thermal energy is continuously injected by the conduction (:math:`\partial T / \partial x`) on the walls. -This injection is exactly dissipated by the last term in a statistical sense. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/governing_equations.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/governing_equations.rst deleted file mode 100644 index 773472b8..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/governing_equations.rst +++ /dev/null @@ -1,100 +0,0 @@ -############################ -Discrete governing equations -############################ - -.. note:: - - Obviously the resulting schemes are identical to what I derive :ref:`here `. - -**************************** -Incompressibility constraint -**************************** - -.. math:: - - \frac{\vat{\ux}{\pip,\pjc}-\vat{\ux}{\pim,\pjc}}{\Delta x_{\pic}} - + - \frac{\vat{\uy}{\pic,\pjp}-\vat{\uy}{\pic,\pjm}}{\Delta y_{\pjc}} - = - 0. - -******** -Momentum -******** - -.. math:: - - \der{\vat{\ux}{\xic,\xjc}}{t} - = - & - \frac{ - \vat{\dintrpa{\Delta y \ux}{\gx}}{\xip,\xjc} \vat{\dintrpa{\ux}{\gx}}{\xip,\xjc} - - \vat{\dintrpa{\Delta y \ux}{\gx}}{\xim,\xjc} \vat{\dintrpa{\ux}{\gx}}{\xim,\xjc} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & - \frac{ - \vat{\dintrpa{\Delta x \uy}{\gx}}{\xic,\xjp} \vat{\dintrpa{\ux}{\gy}}{\xic,\xjp} - - \vat{\dintrpa{\Delta x \uy}{\gx}}{\xic,\xjm} \vat{\dintrpa{\ux}{\gy}}{\xic,\xjm} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & -\frac{ - \vat{\Delta y p}{\xip,\xjc} - - \vat{\Delta y p}{\xim,\xjc} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta y}{\Delta x} \diffe{\ux}{\gx}}{\xip,\xjc} - - \vat{\frac{\Delta y}{\Delta x} \diffe{\ux}{\gx}}{\xim,\xjc} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta x}{\Delta y} \diffe{\ux}{\gy}}{\xic,\xjp} - - \vat{\frac{\Delta x}{\Delta y} \diffe{\ux}{\gy}}{\xic,\xjm} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & + \vat{\dintrpa{T}{\gx}}{\xic,\xjc}. - -.. math:: - - \der{\vat{\uy}{\yic,\yjc}}{t} - = - & - \frac{ - \vat{\dintrpa{\Delta y \ux}{\gy}}{\yip,\yjc} \vat{\dintrpa{\uy}{\gx}}{\yip,\yjc} - - \vat{\dintrpa{\Delta y \ux}{\gy}}{\yim,\yjc} \vat{\dintrpa{\uy}{\gx}}{\yim,\yjc} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & - \frac{ - \vat{\dintrpa{\Delta x \uy}{\gy}}{\yic,\yjp} \vat{\dintrpa{\uy}{\gy}}{\yic,\yjp} - - \vat{\dintrpa{\Delta x \uy}{\gy}}{\yic,\yjm} \vat{\dintrpa{\uy}{\gy}}{\yic,\yjm} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & -\frac{ - \vat{\Delta x p}{\yic,\yjp} - - \vat{\Delta x p}{\yic,\yjm} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta y}{\Delta x} \diffe{\uy}{\gx}}{\yip,\yjc} - - \vat{\frac{\Delta y}{\Delta x} \diffe{\uy}{\gx}}{\yim,\yjc} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta x}{\Delta y} \diffe{\uy}{\gy}}{\yic,\yjp} - - \vat{\frac{\Delta x}{\Delta y} \diffe{\uy}{\gy}}{\yic,\yjm} - }{\Delta x_{\yic} \Delta y_{\yjc}}. - -*************** -Internal energy -*************** - -.. math:: - - \der{\vat{T}{\pic,\pjc}}{t} - = - & - \frac{ - \vat{\Delta y \ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - - \vat{\Delta y \ux}{\pim,\pjc} \vat{\dintrpa{T}{\gx}}{\pim,\pjc} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & - \frac{ - \vat{\Delta x \uy}{\pic,\pjp} \vat{\dintrpa{T}{\gy}}{\pic,\pjp} - - \vat{\Delta x \uy}{\pic,\pjm} \vat{\dintrpa{T}{\gy}}{\pic,\pjm} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{ - \vat{\frac{\Delta y}{\Delta x} \diffe{T}{\gx}}{\pip,\pjc} - - \vat{\frac{\Delta y}{\Delta x} \diffe{T}{\gx}}{\pim,\pjc} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{ - \vat{\frac{\Delta x}{\Delta y} \diffe{T}{\gy}}{\pic,\pjp} - - \vat{\frac{\Delta x}{\Delta y} \diffe{T}{\gy}}{\pic,\pjm} - }{\Delta x_{\pic} \Delta y_{\pjc}}. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/kinetic_energy.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/kinetic_energy.rst deleted file mode 100644 index fd93a1b9..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/kinetic_energy.rst +++ /dev/null @@ -1,430 +0,0 @@ -####################### -Discrete kinetic energy -####################### - -I consider to multiply the momentum balance in the :math:`x` direction by :math:`\vat{\ux}{\xic,\xjc}`, yielding - -.. math:: - - \vat{\ux}{\xic,\xjc} \der{\vat{\ux}{\xic,\xjc}}{t} - = - & - \vat{\ux}{\xic,\xjc} \frac{ - \vat{\dintrpa{\Delta y \ux}{\gx}}{\xip,\xjc} \vat{\dintrpa{\ux}{\gx}}{\xip,\xjc} - - \vat{\dintrpa{\Delta y \ux}{\gx}}{\xim,\xjc} \vat{\dintrpa{\ux}{\gx}}{\xim,\xjc} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & - \vat{\ux}{\xic,\xjc} \frac{ - \vat{\dintrpa{\Delta x \uy}{\gx}}{\xic,\xjp} \vat{\dintrpa{\ux}{\gy}}{\xic,\xjp} - - \vat{\dintrpa{\Delta x \uy}{\gx}}{\xic,\xjm} \vat{\dintrpa{\ux}{\gy}}{\xic,\xjm} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & - \vat{\ux}{\xic,\xjc} \frac{ - \vat{\Delta y p}{\xip,\xjc} - - \vat{\Delta y p}{\xim,\xjc} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & + \vat{\ux}{\xic,\xjc} \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta y}{\Delta x} \diffe{\ux}{\gx}}{\xip,\xjc} - - \vat{\frac{\Delta y}{\Delta x} \diffe{\ux}{\gx}}{\xim,\xjc} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & + \vat{u}{\xic,\xjc} \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta x}{\Delta y} \diffe{\ux}{\gy}}{\xic,\xjp} - - \vat{\frac{\Delta x}{\Delta y} \diffe{\ux}{\gy}}{\xic,\xjm} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & + \vat{\ux}{\xic,\xjc} \vat{\dintrpa{T}{\gx}}{\xic,\xjc}. - -Similarly, the momentum balance in the :math:`y` direction is multiplied by :math:`\vat{\uy}{\yic,\yjc}`: - -.. math:: - - \vat{\uy}{\yic,\yjc} \der{\vat{\uy}{\yic,\yjc}}{t} - = - & - \vat{\uy}{\yic,\yjc} \frac{ - \vat{\dintrpa{\Delta y \ux}{\gy}}{\yip,\yjc} \vat{\dintrpa{\uy}{\gx}}{\yip,\yjc} - - \vat{\dintrpa{\Delta y \ux}{\gy}}{\yim,\yjc} \vat{\dintrpa{\uy}{\gx}}{\yim,\yjc} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & - \vat{\uy}{\yic,\yjc} \frac{ - \vat{\dintrpa{\Delta x \uy}{\gy}}{\yic,\yjp} \vat{\dintrpa{\uy}{\gy}}{\yic,\yjp} - - \vat{\dintrpa{\Delta x \uy}{\gy}}{\yic,\yjm} \vat{\dintrpa{\uy}{\gy}}{\yic,\yjm} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & - \vat{\uy}{\yic,\yjc} \frac{ - \vat{\Delta x p}{\yic,\yjp} - - \vat{\Delta x p}{\yic,\yjm} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & + \vat{\uy}{\yic,\yjc} \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta y}{\Delta x} \diffe{\uy}{\gx}}{\yip,\yjc} - - \vat{\frac{\Delta y}{\Delta x} \diffe{\uy}{\gx}}{\yim,\yjc} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & + \vat{\uy}{\yic,\yjc} \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta x}{\Delta y} \diffe{\uy}{\gy}}{\yic,\yjp} - - \vat{\frac{\Delta x}{\Delta y} \diffe{\uy}{\gy}}{\yic,\yjm} - }{\Delta x_{\yic} \Delta y_{\yjc}}. - -The left-hand-side terms give the change of :math:`1/2 \ux^2` and :math:`1/2 \uy^2` in time, which are clearly relevant to the discrete kinetic energy. - -*************** -Advective terms -*************** - -============== -Local property -============== - -Since the velocity components are located at different positions, the discrete kinetic energy cannot be defined uniquely. -Here, I consider the squared velocities (:math:`\ux^2` and :math:`\uy^2`) separately at positions where they are defined. - -In the :math:`x` direction, the advection terms at :math:`\left( \xic, \xjc \right)` lead to - -.. math:: - - & - \frac{ - \vat{\dintrpa{\Delta y \ux}{\gx}}{\xip,\xjc} \frac{\vat{\ux}{\xipp,\xjc } \vat{\ux}{\xic ,\xjc }}{2} - - \vat{\dintrpa{\Delta y \ux}{\gx}}{\xim,\xjc} \frac{\vat{\ux}{\xic ,\xjc } \vat{\ux}{\ximm,\xjc }}{2} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & - \frac{ - \vat{\dintrpa{\Delta x \uy}{\gx}}{\xic,\xjp} \frac{\vat{\ux}{\xic ,\xjpp} \vat{\ux}{\xic ,\xjc }}{2} - - \vat{\dintrpa{\Delta x \uy}{\gx}}{\xic,\xjm} \frac{\vat{\ux}{\xic ,\xjc } \vat{\ux}{\xic ,\xjmm}}{2} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & - \frac{\vat{\ux^2}{\xic,\xjc}}{2} \frac{ - \vat{\dintrpa{\Delta y \ux}{\gx}}{\xip,\xjc} - - \vat{\dintrpa{\Delta y \ux}{\gx}}{\xim,\xjc} - + \vat{\dintrpa{\Delta x \uy}{\gx}}{\xic,\xjp} - - \vat{\dintrpa{\Delta x \uy}{\gx}}{\xic,\xjm} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & = \\ - & -\frac{1}{J_{\xic,\xjc}} \left\{ \diffe{}{\gx} \left( - \dintrpa{\Delta y \ux}{\gx} \frac{{\widehat{\ux^2}}^{\gx}}{2} - \right) \right\}_{\xic,\xjc} - - \frac{1}{J_{\xic,\xjc}} \left\{ \diffe{}{\gy} \left( - \dintrpa{\Delta x \uy}{\gx} \frac{{\widehat{\ux^2}}^{\gy}}{2} - \right) \right\}_{\xic,\xjc} \\ - & - \frac{\vat{u^2}{\xic,\xjc}}{2} \frac{1}{J_{\xic,\xjc}} \left\{ - \frac{J_{\xim,\xjc}}{2} \left( - \frac{ - \vat{\ux}{\xic, \xjc} - - \vat{\ux}{\ximm,\xjc} - }{\Delta x_{\xim}} - + \frac{ - \vat{\uy}{\xim,\xjp} - - \vat{\uy}{\xim,\xjm} - }{\Delta y_{\xjc}} - \right) - \right\} \\ - & - \frac{\vat{u^2}{\xic,\xjc}}{2} \frac{1}{J_{\xic,\xjc}} \left\{ - \frac{J_{\xip,\xjc}}{2} \left( - \frac{ - \vat{\ux}{\xipp,\xjc} - - \vat{\ux}{\xic, \xjc} - }{\Delta x_{\xip}} - + \frac{ - \vat{\uy}{\xip,\xjp} - - \vat{\uy}{\xip,\xjm} - }{\Delta y_{\xjc}} - \right) - \right\}, - -while in the :math:`y` direction at :math:`\left( \yic, \yjc \right)`, I have - -.. math:: - - & - \frac{ - \vat{\dintrpa{\Delta y \ux}{\gy}}{\yip,\yjc} \frac{\vat{\uy}{\yipp,\yjc } \vat{\uy}{\yic, \yjc }}{2} - - \vat{\dintrpa{\Delta y \ux}{\gy}}{\yim,\yjc} \frac{\vat{\uy}{\yic ,\yjc } \vat{\uy}{\yimm,\yjc }}{2} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & - \frac{ - \vat{\dintrpa{\Delta x \uy}{\gy}}{\yic,\yjp} \frac{\vat{\uy}{\yic ,\yjpp} \vat{\uy}{\yic ,\yjc }}{2} - - \vat{\dintrpa{\Delta x \uy}{\gy}}{\yic,\yjm} \frac{\vat{\uy}{\yic ,\yjc } \vat{\uy}{\yic ,\yjmm}}{2} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & - \frac{\vat{v^2}{\yic,\yjc}}{2} \frac{ - \vat{\dintrpa{\Delta y \ux}{\gy}}{\yip,\yjc} - - \vat{\dintrpa{\Delta y \ux}{\gy}}{\yim,\yjc} - + \vat{\dintrpa{\Delta x \uy}{\gy}}{\yic,\yjp} - - \vat{\dintrpa{\Delta x \uy}{\gy}}{\yic,\yjm} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & = \\ - & -\frac{1}{J_{\yic,\yjc}} \left\{ \diffe{}{\gx} \left( - \dintrpa{\Delta y \ux}{\gx} \frac{{\widehat{\uy^2}}^{\gx}}{2} - \right) \right\}_{\yic,\yjc} - - \frac{1}{J_{\yic,\yjc}} \left\{ \diffe{}{\gy} \left( - \dintrpa{\Delta x \uy}{\gx} \frac{{\widehat{\uy^2}}^{\gy}}{2} - \right) \right\}_{\yic,\yjc} \\ - & - \frac{\vat{\uy^2}{\yic,\yjc}}{2} \frac{1}{J_{\yic,\yjc}} \left\{ - \frac{J_{\yic,\yjm}}{2} \left( - \frac{ - \vat{\ux}{\yim,\yjm} - - \vat{\ux}{\yip,\yjm} - }{\Delta x_{\yic}} - + \frac{ - \vat{\uy}{\yic,\yjmm} - - \vat{\uy}{\yic,\yjc } - }{\Delta y_{\yjm}} - \right) - \right\} \\ - & - \frac{\vat{\uy^2}{\yic,\yjc}}{2} \frac{1}{J_{\yic,\yjc}} \left\{ - \frac{J_{\yic,\yjp}}{2} \left( - \frac{ - \vat{\ux}{\yim,\yjp} - - \vat{\ux}{\yip,\yjp} - }{\Delta x_{\yic}} - + \frac{ - \vat{\uy}{\yic,\yjpp} - - \vat{\uy}{\yic,\yjc } - }{\Delta y_{\yjp}} - \right) - \right\}. - -Note that the last terms are the volume average of the incompressibility constraints and thus zero. - -Here new symbols are introduced (quadratic quantities, which are the pseudo kinetic energies): - -.. math:: - - & \vat{\widehat{\ux^2}^{\gx}}{\xip,\xjc} \equiv \vat{\ux}{\xic,\xjc} \vat{\ux}{\xipp,\xjc }, \\ - & \vat{\widehat{\ux^2}^{\gy}}{\xic,\xjp} \equiv \vat{\ux}{\xic,\xjc} \vat{\ux}{\xic ,\xjpp}, - -in the :math:`x` direction, while - -.. math:: - - & \vat{\widehat{\uy^2}^{\gx}}{\yip,\yjc} \equiv \vat{\uy}{\yic,\yjc} \vat{v}{\yipp,\yjc }, \\ - & \vat{\widehat{\uy^2}^{\gy}}{\yic,\yjp} \equiv \vat{\uy}{\yic,\yjc} \vat{v}{\yic ,\yjpp}, - -in the :math:`y` direction. - -=============== -Global property -=============== - -Now I consider to integrate the above two advective terms in the whole domain. - -.. note:: - - The integral of a quantity :math:`q` - - .. math:: - - \int q dx dy = \int q J d\gx d\gy - - is discretised as - - .. math:: - - \sum q_{i,j} \Delta x_i \Delta y_j = \sum q_{i,j} J_{i,j} \Delta \gx_i \Delta \gy_j = \sum q_{i,j} J_{i,j} \,\,\, \left( \because \Delta \gx_i \equiv \Delta \gy_j \equiv 1 \right) - - in the general coordinate system. - -:math:`\der{K}{t}`, which is the evolution of the total (global) discrete kinetic energy :math:`K`, is given by the sum of - -.. math:: - - \sum_{\forall \ux \text{positions}, \left( \xic, \xjc \right)} \der{}{t} \left( \frac{1}{2} J \ux^2 \right)_{\xic,\xjc} - \equiv - \sum_{\forall \ux \text{positions}, \left( \xic, \xjc \right)} - \left[ - \begin{aligned} - & - \left\{ \diffe{}{\gx} \left( - \dintrpa{\Delta y \ux}{\gx} \frac{{\widehat{\ux^2}}^{\gx}}{2} - \right) \right\}_{\xic,\xjc} \\ - & - \left\{ \diffe{}{\gy} \left( - \dintrpa{\Delta x \uy}{\gx} \frac{{\widehat{\ux^2}}^{\gy}}{2} - \right) \right\}_{\xic,\xjc} \\ - & - \vat{\ux}{\xic,\xjc} \Delta y_{\xjc} \left( - \vat{p}{\xip,\xjc} - - \vat{p}{\xim,\xjc} - \right) \\ - \end{aligned} - \right] - -and - -.. math:: - - \sum_{\forall \uy \text{positions}, \left( \yic, \yjc \right)} \der{}{t} \left( \frac{1}{2} J \uy^2 \right)_{\yic,\yjc} - \equiv - \sum_{\forall \uy \text{positions}, \left( \yic, \yjc \right)} - \left[ - \begin{aligned} - & - \left\{ \diffe{}{\gx} \left( - \dintrpa{\Delta y \ux}{\gx} \frac{{\widehat{\uy^2}}^{\gx}}{2} - \right) \right\}_{\yic,\yjc} \\ - & - \left\{ \diffe{}{\gy} \left( - \dintrpa{\Delta x \uy}{\gx} \frac{{\widehat{\uy^2}}^{\gy}}{2} - \right) \right\}_{\yic,\yjc} \\ - & - \vat{\uy}{\yic,\yjc} \Delta x_{\yic} \left( - \vat{p}{\yic,\yjp} - - \vat{p}{\yic,\yjm} - \right) \\ - \end{aligned} - \right]. - -In each summation, the first two terms vanish since they are conservative. -Regarding the pressure gradient terms, they can be re-ordered as - -.. math:: - - \sum_{\forall p \text{positions}, \left( \pic, \pjc \right)} \vat{p}{\pic,\pjc} \Delta y_j \left( \vat{\ux}{\pip,\pjc} - \vat{\ux}{\pim,\pjc} \right) - + - \sum_{\forall p \text{positions}, \left( \pic, \pjc \right)} \vat{p}{\pic,\pjc} \Delta x_i \left( \vat{\uy}{\pic,\pjp} - \vat{\uy}{\pic,\pjm} \right), - -which is - -.. math:: - - \sum_{\forall p \text{positions}, \left( \pic, \pjc \right)} \vat{p J}{\pic,\pjc} \left( - \frac{\vat{\ux}{\pip,\pjc} - \vat{\ux}{\pim,\pjc}}{\Delta x_i} - + \frac{\vat{\uy}{\pic,\pjp} - \vat{\uy}{\pic,\pjm}}{\Delta y_j} - \right), - -and thus null because of the incompressibility constraint again. - -In summary, I notice that the current advective and pressure gradient terms do not contribute to the changes in the total quadratic quantity :math:`K`, i.e. the above scheme is indeed energy-conserving. - -*************** -Diffusive terms -*************** - -The diffusive terms play two roles: - - * transporting the kinetic energy, - - * dissipating the kinetic energy. - -The latter is important when computing the kinetic energy dissipation (and the Nusselt number). -To obtain a consistent discretisation, I try to mimic the relation in the continuous domain: the viscous dissipation arising from the momentum equation in the :math:`x` direction is given as - -.. math:: - - \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \mst{j}{j} \der{\ux}{\xi^j} \der{\ux}{\xi^j} - = \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \der{}{\xi^j} \left( \mst{j}{j} \ux \der{\ux}{\xi^j} \right) - - \ux \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \der{}{\xi^j} \left( \mst{j}{j} \der{\ux}{\xi^j} \right), - -which is directly discretised: - -.. math:: - - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left\{ - \vat{\left( \mst{x}{x} \dintrpa{\ux}{\gx} \right) \diffe{\ux}{\gx}}{\xip,\xjc} - - \vat{\left( \mst{x}{x} \dintrpa{\ux}{\gx} \right) \diffe{\ux}{\gx}}{\xim,\xjc} - \right\} \\ - + - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left\{ - \vat{\left( \mst{y}{y} \dintrpa{\ux}{\gy} \right) \diffe{\ux}{\gy}}{\xic,\xjp} - - \vat{\left( \mst{y}{y} \dintrpa{\ux}{\gy} \right) \diffe{\ux}{\gy}}{\xic,\xjm} - \right\} \\ - - - & \vat{\ux}{\xic,\xjc} \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left\{ - \vat{\left( \mst{x}{x} \diffe{\ux}{\gx} \right)}{\xip,\xjc} - - \vat{\left( \mst{x}{x} \diffe{\ux}{\gx} \right)}{\xim,\xjc} - \right\} \\ - - - & \vat{\ux}{\xic,\xjc} \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left\{ - \vat{\left( \mst{y}{y} \diffe{\ux}{\gy} \right)}{\xic,\xjp} - - \vat{\left( \mst{y}{y} \diffe{\ux}{\gy} \right)}{\xic,\xjm} - \right\}, - -which is - -.. math:: - - & + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left( \vat{\dintrpa{\ux}{\gx}}{\xip,\xjc} - \vat{\ux}{\xic,\xjc} \right) \vat{\left( \mst{x}{x} \diffe{\ux}{\gx} \right)}{\xip,\xjc} \\ - & - \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left( \vat{\dintrpa{\ux}{\gx}}{\xim,\xjc} - \vat{\ux}{\xic,\xjc} \right) \vat{\left( \mst{x}{x} \diffe{\ux}{\gx} \right)}{\xim,\xjc} \\ - & + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left( \vat{\dintrpa{\ux}{\gy}}{\xic,\xjp} - \vat{\ux}{\xic,\xjc} \right) \vat{\left( \mst{y}{y} \diffe{\ux}{\gy} \right)}{\xic,\xjp} \\ - & - \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left( \vat{\dintrpa{\ux}{\gy}}{\xic,\xjm} - \vat{\ux}{\xic,\xjc} \right) \vat{\left( \mst{y}{y} \diffe{\ux}{\gy} \right)}{\xic,\xjm}. - -Since I have - -.. math:: - \begin{aligned} - & \vat{\dintrpa{\ux}{\gx}}{\xip,\xjc} - \vat{\ux}{\xic,\xjc} - = \frac{\vat{\ux}{\xipp,\xjc} + \vat{\ux}{\xic ,\xjc}}{2} - \vat{\ux}{\xic,\xjc} - = + \frac{\vat{\ux}{\xipp,\xjc} - \vat{\ux}{\xic ,\xjc}}{2} - = + \frac{1}{2} \vat{\diffe{\ux}{\gx}}{\xip,\xjc} \\ - & \vat{\dintrpa{\ux}{\gx}}{\xim,\xjc} - \vat{\ux}{\xic,\xjc} - = \frac{\vat{\ux}{\xic ,\xjc} + \vat{\ux}{\ximm,\xjc}}{2} - \vat{\ux}{\xic,\xjc} - = - \frac{\vat{\ux}{\xic ,\xjc} - \vat{\ux}{\ximm,\xjc}}{2} - = - \frac{1}{2} \vat{\diffe{\ux}{\gx}}{\xim,\xjc} \\ - & \vat{\dintrpa{\ux}{\gy}}{\xic,\xjp} - \vat{\ux}{\xic,\xjc} - = \frac{\vat{\ux}{\xic,\xjpp} + \vat{\ux}{\xic,\xjc }}{2} - \vat{\ux}{\xic,\xjc} - = + \frac{\vat{\ux}{\xic,\xjpp} - \vat{\ux}{\xic,\xjc }}{2} - = + \frac{1}{2} \vat{\diffe{\ux}{\gy}}{\xic,\xjp} \\ - & \vat{\dintrpa{\ux}{\gy}}{\xic,\xjm} - \vat{\ux}{\xic,\xjc} - = \frac{\vat{\ux}{\xic,\xjc } + \vat{\ux}{\xic,\xjmm}}{2} - \vat{\ux}{\xic,\xjc} - = - \frac{\vat{\ux}{\xic,\xjc } - \vat{\ux}{\xic,\xjmm}}{2} - = - \frac{1}{2} \vat{\diffe{\ux}{\gy}}{\xic,\xjm} - \end{aligned} - -in the bulk, I finally notice that the viscous dissipation arising from the momentum equation in the :math:`x` direction leads to - -.. math:: - - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left[ - \frac{ - \vat{\left\{ \mst{x}{x} \left( \diffe{\ux}{\gx} \right)^2 \right\}}{\xip,\xjc} - + \vat{\left\{ \mst{x}{x} \left( \diffe{\ux}{\gx} \right)^2 \right\}}{\xim,\xjc} - }{2} - + \frac{ - \vat{\left\{ \mst{y}{y} \left( \diffe{\ux}{\gy} \right)^2 \right\}}{\xic,\xjp} - + \vat{\left\{ \mst{y}{y} \left( \diffe{\ux}{\gy} \right)^2 \right\}}{\xic,\xjm} - }{2} - \right] \\ - = - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left\{ - \frac{1}{2} J_{\xip,\xjc} \left( \vat{\frac{\diffe{\ux}{\gx}}{\Delta x}}{\xip,\xjc} \right)^2 - \right\} \\ - + - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left\{ - \frac{1}{2} J_{\xim,\xjc} \left( \vat{\frac{\diffe{\ux}{\gx}}{\Delta x}}{\xim,\xjc} \right)^2 - \right\} \\ - + - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left\{ - \frac{1}{2} J_{\xic,\xjp} \left( \vat{\frac{\diffe{\ux}{\gy}}{\Delta y}}{\xic,\xjp} \right)^2 - \right\} \\ - + - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\xic,\xjc}} \left\{ - \frac{1}{2} J_{\xic,\xjm} \left( \vat{\frac{\diffe{\ux}{\gy}}{\Delta y}}{\xic,\xjm} \right)^2 - \right\} - -at :math:`\left( \xic, \xjc \right).` - -Following a similar manner, by using the relation - -.. math:: - - \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \mst{j}{j} \der{\uy}{\xi^j} \der{\uy}{\xi^j} - = \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \der{}{\xi^j} \left( \mst{j}{j} \uy \der{\uy}{\xi^j} \right) - - \uy \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \der{}{\xi^j} \left( \mst{j}{j} \der{\uy}{\xi^j} \right), - -I can derive the viscous dissipation coming from the momentum equation in the :math:`y` direction as - -.. math:: - - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\yic,\yjc}} \left[ - \frac{ - \vat{\left\{ \mst{x}{x} \left( \diffe{\uy}{\gx} \right)^2 \right\}}{\yip,\yjc} - + \vat{\left\{ \mst{x}{x} \left( \diffe{\uy}{\gx} \right)^2 \right\}}{\yim,\yjc} - }{2} - + \frac{ - \vat{\left\{ \mst{y}{y} \left( \diffe{\uy}{\gy} \right)^2 \right\}}{\yic,\yjp} - + \vat{\left\{ \mst{y}{y} \left( \diffe{\uy}{\gy} \right)^2 \right\}}{\yic,\yjm} - }{2} - \right] \\ - = - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\yic,\yjc}} \left\{ - \frac{1}{2} J_{\yip,\yjc} \left( \vat{\frac{\diffe{\uy}{\gx}}{\Delta x}}{\yip,\yjc} \right)^2 - \right\} \\ - + - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\yic,\yjc}} \left\{ - \frac{1}{2} J_{\yim,\yjc} \left( \vat{\frac{\diffe{\uy}{\gx}}{\Delta x}}{\yim,\yjc} \right)^2 - \right\} \\ - + - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\yic,\yjc}} \left\{ - \frac{1}{2} J_{\yic,\yjp} \left( \vat{\frac{\diffe{\uy}{\gy}}{\Delta y}}{\yic,\yjp} \right)^2 - \right\} \\ - + - & \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J_{\yic,\yjc}} \left\{ - \frac{1}{2} J_{\yic,\yjm} \left( \vat{\frac{\diffe{\uy}{\gy}}{\Delta y}}{\yic,\yjm} \right)^2 - \right\} - -at :math:`\left( \yic, \yjc \right).` - -.. note:: - - For the :math:`y` velocities, in the vicinity of the walls, the coefficient :math:`1/2` should be corrected to :math:`1` since :math:`\uy` is defined exactly on the walls. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/main.rst deleted file mode 100644 index c332b1c7..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/main.rst +++ /dev/null @@ -1,19 +0,0 @@ -############### -Discrete domain -############### - -.. note:: - - Throughout the project, I assume that the :math:`x` direction is the wall-normal direction and the grid sizes can vary, while :math:`y` direction is the homogeneous direction and the grid space is uniform. - For generality, however, I take into account the grid size variations also in :math:`y` direction in the following discussion. - -The discrete governing equations in the strong conservation forms are discussed here. -In addition, the quadratic quantities and the energy balance relations are discussed. - -.. toctree:: - :maxdepth: 1 - - governing_equations - kinetic_energy - thermal_quadratic_quantity - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/thermal_quadratic_quantity.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/thermal_quadratic_quantity.rst deleted file mode 100644 index fac7451d..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/discrete_domain/thermal_quadratic_quantity.rst +++ /dev/null @@ -1,173 +0,0 @@ -################################### -Discrete thermal quadratic quantity -################################### - -The equation of the squared temperature leads to - -.. math:: - - \vat{T}{\pic,\pjc} \der{\vat{T}{\pic,\pjc}}{t} - = - & - \vat{T}{\pic,\pjc} \frac{ - \vat{\Delta y \ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - - \vat{\Delta y \ux}{\pim,\pjc} \vat{\dintrpa{T}{\gx}}{\pim,\pjc} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & - \vat{T}{\pic,\pjc} \frac{ - \vat{\Delta x \uy}{\pic,\pjp} \vat{\dintrpa{T}{\gy}}{\pic,\pjp} - - \vat{\Delta x \uy}{\pic,\pjm} \vat{\dintrpa{T}{\gy}}{\pic,\pjm} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & + \vat{T}{\pic,\pjc} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{ - \vat{\frac{\Delta y}{\Delta x} \diffe{T}{\gx}}{\pip,\pjc} - - \vat{\frac{\Delta y}{\Delta x} \diffe{T}{\gx}}{\pim,\pjc} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & + \vat{T}{\pic,\pjc} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{ - \vat{\frac{\Delta x}{\Delta y} \diffe{T}{\gy}}{\pic,\pjp} - - \vat{\frac{\Delta x}{\Delta y} \diffe{T}{\gy}}{\pic,\pjm} - }{\Delta x_{\pic} \Delta y_{\pjc}}. - -*************** -Advective terms -*************** - -The advection terms of the squared temperature leads to - -.. math:: - - & - \frac{ - \vat{\left( \Delta y \ux \right)}{\pip,\pjc} \frac{\vat{T}{\pic ,\pjc } \vat{T}{\pipp,\pjc }}{2} - - \vat{\left( \Delta y \ux \right)}{\pim,\pjc} \frac{\vat{T}{\pimm,\pjc } \vat{T}{\pic ,\pjc }}{2} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & - \frac{ - \vat{\left( \Delta x \uy \right)}{\pic,\pjp} \frac{\vat{T}{\pic ,\pjc } \vat{T}{\pic ,\pjpp}}{2} - - \vat{\left( \Delta x \uy \right)}{\pic,\pjm} \frac{\vat{T}{\pic ,\pjmm} \vat{T}{\pic ,\pjc }}{2} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & - \frac{\vat{T^2}{\pic,\pjc}}{2} \left( - \frac{ - \vat{\Delta y \ux}{\pip,\pjc} - - \vat{\Delta y \ux}{\pim,\pjc} - }{\Delta x_{\pic} \Delta y_{\pjc}} - + \frac{ - \vat{\Delta x \uy}{\pic,\pjp} - - \vat{\Delta x \uy}{\pic,\pjm} - }{\Delta x_{\pic} \Delta y_{\pjc}} - \right) \\ - & = \\ - & - \frac{1}{J_{\pic,\pjc}} \left\{ \diffe{}{\gx} \left( \Delta y \ux \frac{\widehat{T^2}^{\gx}}{2} \right) \right\}_{\pic,\pjc} - - \frac{1}{J_{\pic,\pjc}} \left\{ \diffe{}{\gy} \left( \Delta x \uy \frac{\widehat{T^2}^{\gy}}{2} \right) \right\}_{\pic,\pjc} \\ - & - \frac{\vat{T^2}{\pic,\pjc}}{2} \left( - \frac{ - \vat{\ux}{\pip,\pjc} - - \vat{\ux}{\pim,\pjc} - }{\Delta x_{\pic}} - + \frac{ - \vat{\uy}{\pic,\pjp} - - \vat{\uy}{\pic,\pjm} - }{\Delta y_{\pjc}} - \right), - -where the last term is null because of the incompressibility constraint. -The first two terms are in conservative forms and thus do not contribute to the change in the total :math:`T^2`. -Note that quadratic quantities are defined as follows: - -.. math:: - - \vat{\widehat{T^2}^{\gx}}{\pip,\pjc} = \vat{T}{\pic,\pjc} \vat{T}{\pipp,\pjc}, \\ - \vat{\widehat{T^2}^{\gy}}{\pic,\pjp} = \vat{T}{\pic,\pjc} \vat{T}{\pic,\pjpp}. - -In summary, I notice that the current advective terms do not contribute to the changes in the total quadratic quantity :math:`H`, i.e. the above scheme is indeed energy-conserving. - -*************** -Diffusive terms -*************** - -The local thermal energy dissipation :math:`\dfrac{1}{J} \mst{i}{i} \der{T}{\gx^i} \der{T}{\gx^i}` in the continuous level is given as - -.. math:: - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J} \der{}{\gx^i} \left( \mst{i}{i} T \der{T}{\gx^i} \right) - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J} T \der{}{\xi^i} \left( \mst{i}{i} \der{T}{\gx^i} \right). - -The first conservative term is discretised as - -.. math:: - - & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J_{\pic,\pjc}} \left\{ - \vat{\left( \mst{x}{x} T \diffe{T}{\gx} \right)}{\pip,\pjc} - - \vat{\left( \mst{x}{x} T \diffe{T}{\gx} \right)}{\pim,\pjc} - \right\} \\ - + & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J_{\pic,\pjc}} \left\{ - \vat{\left( \mst{y}{y} T \diffe{T}{\gy} \right)}{\pic,\pjp} - - \vat{\left( \mst{y}{y} T \diffe{T}{\gy} \right)}{\pic,\pjm} - \right\}, - -while the other term leads - -.. math:: - - & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J_{\pic,\pjc}} \vat{T}{\pic,\pjc} \left\{ - \vat{\left( \mst{x}{x} \diffe{T}{\gx} \right)}{\pip,\pjc} - - \vat{\left( \mst{x}{x} \diffe{T}{\gx} \right)}{\pim,\pjc} - \right\} \\ - + & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J_{\pic,\pjc}} \vat{T}{\pic,\pjc} \left\{ - \vat{\left( \mst{y}{y} \diffe{T}{\gy} \right)}{\pic,\pjp} - - \vat{\left( \mst{y}{y} \diffe{T}{\gy} \right)}{\pic,\pjm} - \right\}. - -Since - -.. math:: - \begin{aligned} - & + \vat{\left( \dintrpa{T}{\gx} \diffe{T}{\gx} \right)}{\pip,\pjc} - \vat{T}{\pic,\pjc} \vat{\diffe{T}{\gx}}{\pip,\pjc} = \left( + \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - \vat{T}{\pic,\pjc} \right) \vat{\diffe{T}{\gx}}{\pip,\pjc} = \frac{1}{2} \vat{\left( \diffe{T}{\gx} \right)^2}{\pip,\pjc}, \\ - & - \vat{\left( \dintrpa{T}{\gx} \diffe{T}{\gx} \right)}{\pim,\pjc} + \vat{T}{\pic,\pjc} \vat{\diffe{T}{\gx}}{\pim,\pjc} = \left( - \vat{\dintrpa{T}{\gx}}{\pim,\pjc} + \vat{T}{\pic,\pjc} \right) \vat{\diffe{T}{\gx}}{\pim,\pjc} = \frac{1}{2} \vat{\left( \diffe{T}{\gx} \right)^2}{\pim,\pjc}, \\ - & + \vat{\left( \dintrpa{T}{\gy} \diffe{T}{\gy} \right)}{\pic,\pjp} - \vat{T}{\pic,\pjc} \vat{\diffe{T}{\gy}}{\pic,\pjp} = \left( + \vat{\dintrpa{T}{\gy}}{\pic,\pjp} - \vat{T}{\pic,\pjc} \right) \vat{\diffe{T}{\gy}}{\pic,\pjp} = \frac{1}{2} \vat{\left( \diffe{T}{\gy} \right)^2}{\pic,\pjp}, \\ - & - \vat{\left( \dintrpa{T}{\gy} \diffe{T}{\gy} \right)}{\pic,\pjm} + \vat{T}{\pic,\pjc} \vat{\diffe{T}{\gy}}{\pic,\pjm} = \left( - \vat{\dintrpa{T}{\gy}}{\pic,\pjm} + \vat{T}{\pic,\pjc} \right) \vat{\diffe{T}{\gy}}{\pic,\pjm} = \frac{1}{2} \vat{\left( \diffe{T}{\gy} \right)^2}{\pic,\pjm}, - \end{aligned} - -the difference of these two terms is - -.. math:: - - & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J_{\pic,\pjc}} \left[ - \frac{ - \vat{\left\{ \mst{x}{x} \left( \diffe{T}{\gx} \right)^2 \right\}}{\pip,\pjc} - + \vat{\left\{ \mst{x}{x} \left( \diffe{T}{\gx} \right)^2 \right\}}{\pim,\pjc} - }{2} - \right] \\ - + - & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J_{\pic,\pjc}} \left[ - \frac{ - \vat{\left\{ \mst{y}{y} \left( \diffe{T}{\gy} \right)^2 \right\}}{\pic,\pjp} - + \vat{\left\{ \mst{y}{y} \left( \diffe{T}{\gy} \right)^2 \right\}}{\pic,\pjm} - }{2} - \right] \\ - = - & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J_{\pic,\pjc}} \left[ - \frac{1}{2} J_{\pip,\pjc} \left( \vat{\frac{\diffe{T}{\gx}}{\Delta x}}{\pip,\pjc} \right)^2 - \right] \\ - + & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J_{\pic,\pjc}} \left[ - \frac{1}{2} J_{\pim,\pjc} \left( \vat{\frac{\diffe{T}{\gx}}{\Delta x}}{\pim,\pjc} \right)^2 - \right] \\ - + & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J_{\pic,\pjc}} \left[ - \frac{1}{2} J_{\pic,\pjp} \left( \vat{\frac{\diffe{T}{\gy}}{\Delta y}}{\pic,\pjp} \right)^2 - \right] \\ - + & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J_{\pic,\pjc}} \left[ - \frac{1}{2} J_{\pic,\pjm} \left( \vat{\frac{\diffe{T}{\gy}}{\Delta y}}{\pic,\pjm} \right)^2 - \right], - -which is the local thermal energy dissipation rate. - -.. note:: - - In the vicinity of the walls, the coefficient :math:`1/2` should be corrected to unity since :math:`T` is defined exactly on the walls. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/main.rst deleted file mode 100644 index f29959fa..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/main.rst +++ /dev/null @@ -1,13 +0,0 @@ -##################### -Conservation property -##################### - -Quadratic quantities and their conservations are discussed in the general coordinate system. - -.. toctree:: - :maxdepth: 1 - - continuous_domain - discrete_domain/main - nusselt/main - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/heat_flux.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/heat_flux.rst deleted file mode 100644 index 8c081663..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/heat_flux.rst +++ /dev/null @@ -1,148 +0,0 @@ -I consider to average the equation of the internal energy balance: - -.. math:: - - \der{\vat{T}{\pic,\pjc}}{t} - & + \frac{ - \vat{\Delta y \ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - - \vat{\Delta y \ux}{\pim,\pjc} \vat{\dintrpa{T}{\gx}}{\pim,\pjc} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & + \frac{ - \vat{\Delta x \uy}{\pic,\pjp} \vat{\dintrpa{T}{\gy}}{\pic,\pjp} - - \vat{\Delta x \uy}{\pic,\pjm} \vat{\dintrpa{T}{\gy}}{\pic,\pjm} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{ - \vat{\frac{\Delta y}{\Delta x} \diffe{T}{\gx}}{\pip,\pjc} - - \vat{\frac{\Delta y}{\Delta x} \diffe{T}{\gx}}{\pim,\pjc} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{ - \vat{\frac{\Delta x}{\Delta y} \diffe{T}{\gy}}{\pic,\pjp} - - \vat{\frac{\Delta x}{\Delta y} \diffe{T}{\gy}}{\pic,\pjm} - }{\Delta x_{\pic} \Delta y_{\pjc}} = 0 - -in the :math:`y` direction and in time. - -In the original coordinate system :math:`\left( x, y \right)`, integrating a quantity :math:`q` in :math:`y` direction is written as - -.. math:: - - \int q dy, - -which is - -.. math:: - - \int q \der{y}{\gy} d\gy - -in the general coordinate system :math:`\left( \gx, \gy \right)`. -When discretised, it is - -.. math:: - - \sum_j q_j \Delta y_j. - -Based on this relation, I consider to average the equation of the internal energy. - -The first term vanishes since I am interested in the statistically-steady state. - -The third and fifth terms lead to - -.. math:: - - \sum_j \left( - \vat{\uy}{\pic,\pjp} \vat{\dintrpa{T}{\gy}}{\pic,\pjp} - - \vat{\uy}{\pic,\pjm} \vat{\dintrpa{T}{\gy}}{\pic,\pjm} - \right) - -and - -.. math:: - - \sum_j - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left( - \vat{\frac{1}{\Delta y} \diffe{T}{\gy}}{\pic,\pjp} - - \vat{\frac{1}{\Delta y} \diffe{T}{\gy}}{\pic,\pjm} - \right), - -which are zero because of the periodicity in the :math:`y` direction. - -Thus I have - -.. math:: - - & \frac{1}{l_y} \sum_{\pjc} \frac{\Delta y_{\pjc}}{\Delta x_{\pic}} \left\{ - \left( - \vat{\ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - - \vat{\ux}{\pim,\pjc} \vat{\dintrpa{T}{\gx}}{\pim,\pjc} - \right) - \right\} \\ - & = \frac{1}{l_y} \sum_{\pjc} \frac{\Delta y_{\pjc}}{\Delta x_{\pic}} \left\{ - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \left( - \vat{\frac{1}{\Delta x} \diffe{T}{\gx}}{\pip,\pjc} - - \vat{\frac{1}{\Delta x} \diffe{T}{\gx}}{\pim,\pjc} - \right) - \right\}. - -Now I integrate this relation in the :math:`x` direction (:math:`\int q dx \approx \sum_i q_i \Delta x_i`) to a specific position :math:`i`, yielding - -.. math:: - - & \frac{1}{l_y} \sum_{\pic = 1}^{I} \sum_{\pjc} \Delta y_{\pjc} \left\{ - \sqrt{Pr} \sqrt{Ra} \left( - \vat{\ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - - \vat{\ux}{\pim,\pjc} \vat{\dintrpa{T}{\gx}}{\pim,\pjc} - \right) - - \left( - \vat{\frac{1}{\Delta x} \diffe{T}{\gx}}{\pip,\pjc} - - \vat{\frac{1}{\Delta x} \diffe{T}{\gx}}{\pim,\pjc} - \right) - \right\} \\ - & = - \frac{1}{l_y} \sum_{\pjc} \Delta y_{\pjc} \left\{ - \sqrt{Pr} \sqrt{Ra} \left( - \vat{\ux}{\pip ,\pjc} \vat{\dintrpa{T}{\gx}}{\pip ,\pjc} - - \vat{\ux}{\frac{1}{2},\pjc} \vat{\dintrpa{T}{\gx}}{\frac{1}{2},\pjc} - \right) - - \left( - \vat{\frac{1}{\Delta x} \diffe{T}{\gx}}{\pip ,\pjc} - - \vat{\frac{1}{\Delta x} \diffe{T}{\gx}}{\frac{1}{2},\pjc} - \right) - \right\} = 0, - -i.e. - -.. math:: - - \frac{1}{l_y} \sum_{\pjc} \Delta y_{\pjc} \left( - \sqrt{Pr} \sqrt{Ra} \, \vat{u}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - - \vat{\frac{1}{\Delta x} \diffe{T}{\gx}}{\pip,\pjc} - \right) - = - \frac{1}{l_y} \sum_{\pjc} \Delta y_{\pjc} \left( - \sqrt{Pr} \sqrt{Ra} \, \vat{u}{\frac{1}{2},\pjc} \vat{\dintrpa{T}{\gx}}{\frac{1}{2},\pjc} - - \vat{\frac{1}{\Delta x} \diffe{T}{\gx}}{\frac{1}{2},\pjc} - \right). - -Note that this relation holds for all :math:`i`. - -The left-hand side denotes the value at a specific wall-normal location (:math:`x` cell faces where :math:`u_x` are defined), while the right-hand side describes the value on the left wall. -Since I assume the walls are impermeable, the first term (advection of the temperature) vanishes and thus I finally notice - -.. math:: - - Nu - & = - \frac{1}{l_y} \sum_{\pjc} \Delta y_{\pjc} \left( - - \vat{\frac{1}{\Delta x} \diffe{T}{\gx}}{\text{wall},\pjc} - \right) \\ - & = - \frac{1}{l_y} \sum_{\pjc} \Delta y_{\pjc} \left( - \sqrt{Pr} \sqrt{Ra} \, \vat{\ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - - \vat{\frac{1}{\Delta x} \diffe{T}{\gx}}{\pip,\pjc} - \right). - -.. note:: - - In the continuous level, this Nusselt constancy in the wall-normal direction holds for all :math:`x`. - After discretised, this relation is satisfied only where :math:`u_x` are defined. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/injection.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/injection.rst deleted file mode 100644 index 03be2b2d..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/injection.rst +++ /dev/null @@ -1,31 +0,0 @@ -I consider to average the above equation in the :math:`x` direction, from the left to the right walls, yielding - -.. math:: - - Nu &= \frac{1}{l_x l_y} \sum_{\pic} \sum_{\pjc} \left( - \Delta x_{\pip} \Delta y_{\pjc} \sqrt{Pr} \sqrt{Ra} \, \vat{\ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - \right) \\ - & - \frac{1}{l_x l_y} \sum_{\pjc} \left( - \Delta y_{\pjc} \vat{T}{\text{right wall},\pjc} - - \Delta y_{\pjc} \vat{T}{\text{left wall},\pjc} - \right). - -Because of the normalisations, :math:`l_x` and :math:`\vat{T}{\text{left wall},\pjc} - \vat{T}{\text{right wall},\pjc}` are unity. -Thus I have - -.. math:: - - Nu = \frac{1}{l_x l_y} \sum_{\pic} \sum_{\pjc} \left( - \Delta x_{\pip} \Delta y_{\pjc} \sqrt{Pr} \sqrt{Ra} \, \vat{\ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - \right) - + 1, - -or equivalently - -.. math:: - - Nu = \frac{1}{l_x l_y} \sum_{\forall \ux \text{positions}, \left( \pip, \pjc \right)} \left( - \sqrt{Pr} \sqrt{Ra} \, J_{\pip,\pjc} \vat{\ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - \right) - + 1. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/kinetic.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/kinetic.rst deleted file mode 100644 index d7ac667a..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/kinetic.rst +++ /dev/null @@ -1,85 +0,0 @@ -Next, I move on to the kinetic energy equation to find the other relation. -As discussed above, I have the equation of the total kinetic energy :math:`\der{K}{t}`, which is increased by the energy injection by the buoyancy force: - -.. math:: - - \sum_{\forall \ux \text{positions}, \left( \pip, \pjc \right)} J_{\pip,\pjc} \vat{\ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc}, - -while reduced by the viscous dissipation coming from the momentum equation in :math:`x` direction: - -.. math:: - - \sum_{\forall \ux \text{positions}, \left( \xic, \xjc \right)} \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \frac{1}{2} J_{\xip,\xjc} \left( \vat{\frac{\diffe{\ux}{\gx}}{\Delta x}}{\xip,\xjc} \right)^2 - + \frac{1}{2} J_{\xim,\xjc} \left( \vat{\frac{\diffe{\ux}{\gx}}{\Delta x}}{\xim,\xjc} \right)^2 - + \frac{1}{2} J_{\xic,\xjp} \left( \vat{\frac{\diffe{\ux}{\gy}}{\Delta y}}{\xic,\xjp} \right)^2 - + \frac{1}{2} J_{\xic,\xjm} \left( \vat{\frac{\diffe{\ux}{\gy}}{\Delta y}}{\xic,\xjm} \right)^2 - \right\} - -and the one in :math:`y` direction: - -.. math:: - - \sum_{\forall \uy \text{positions}, \left( \yic, \yjc \right)} \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \frac{1}{2} J_{\yip,\yjc} \left( \vat{\frac{\diffe{\uy}{\gx}}{\Delta x}}{\yip,\yjc} \right)^2 - + \frac{1}{2} J_{\yim,\yjc} \left( \vat{\frac{\diffe{\uy}{\gx}}{\Delta x}}{\yim,\yjc} \right)^2 - + \frac{1}{2} J_{\yic,\yjp} \left( \vat{\frac{\diffe{\uy}{\gy}}{\Delta y}}{\yic,\yjp} \right)^2 - + \frac{1}{2} J_{\yic,\yjm} \left( \vat{\frac{\diffe{\uy}{\gy}}{\Delta y}}{\yic,\yjm} \right)^2 - \right\}. - -Again, I consider to average this equation in the whole domain and in time. - -The evolution of :math:`K` vanishes as usual since I am interested in the statistically-steady state. -The buoyancy forcing leads - -.. math:: - - \frac{1}{l_x l_y} \sum_{\forall \ux \text{positions}, \left( \pip, \pjc \right)} J_{\pip,\pjc} \vat{\ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc}. - -The total viscous dissipation is given as the sum of - -.. math:: - - \frac{1}{l_x l_y} \sum_{\forall \ux \text{positions}, \left( \xic, \xjc \right)} \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \frac{1}{2} J_{\xip,\xjc} \left( \vat{\frac{\diffe{\ux}{\gx}}{\Delta x}}{\xip,\xjc} \right)^2 - + \frac{1}{2} J_{\xim,\xjc} \left( \vat{\frac{\diffe{\ux}{\gx}}{\Delta x}}{\xim,\xjc} \right)^2 - + \frac{1}{2} J_{\xic,\xjp} \left( \vat{\frac{\diffe{\ux}{\gy}}{\Delta y}}{\xic,\xjp} \right)^2 - + \frac{1}{2} J_{\xic,\xjm} \left( \vat{\frac{\diffe{\ux}{\gy}}{\Delta y}}{\xic,\xjm} \right)^2 - \right\} - -and - -.. math:: - - \frac{1}{l_x l_y} \sum_{\forall \uy \text{positions}, \left( \yic, \yjc \right)} \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \frac{1}{2} J_{\yip,\yjc} \left( \vat{\frac{\diffe{\uy}{\gx}}{\Delta x}}{\yip,\yjc} \right)^2 - + \frac{1}{2} J_{\yim,\yjc} \left( \vat{\frac{\diffe{\uy}{\gx}}{\Delta x}}{\yim,\yjc} \right)^2 - + \frac{1}{2} J_{\yic,\yjp} \left( \vat{\frac{\diffe{\uy}{\gy}}{\Delta y}}{\yic,\yjp} \right)^2 - + \frac{1}{2} J_{\yic,\yjm} \left( \vat{\frac{\diffe{\uy}{\gy}}{\Delta y}}{\yic,\yjm} \right)^2 - \right\}. - -Note that this summation is nothing else but the (discretised) kinetic dissipation rate :math:`\ave{\epsilon_k}{x,y,t}`, i.e. :math:`\left\langle s_{ij} s_{ij} \right\rangle_{S,t}` in the continuous level. - -Finally I find - -.. math:: - - \frac{1}{l_x l_y} \sum_{\forall \ux \text{positions}, \left( \pip, \pjc \right)} J_{\pip,\pjc} \vat{\ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - = - \ave{\epsilon_k}{x,y,t}, - -and, by using the relation I obtained in the previous Nusselt definition: - -.. math:: - - Nu = \frac{1}{l_x l_y} \sum_{\forall \ux \text{positions}, \left( \pip, \pjc \right)} \left( - \sqrt{Pr} \sqrt{Ra} \, J_{\pip,\pjc} \vat{\ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - \right) - + 1, - -I obtain - -.. math:: - - Nu = \sqrt{Pr} \sqrt{Ra} \ave{\epsilon_k}{x,y,t} + 1. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/main.rst deleted file mode 100644 index 7c57a19b..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/main.rst +++ /dev/null @@ -1,52 +0,0 @@ -####################### -Nusselt number relation -####################### - -The Nusselt number in the general coordinate system is defined as - -.. math:: - - Nu - \equiv - - \ave{\vat{\der{T}{x}}{\text{wall}}}{y,t} - = - - \ave{\frac{\int \vat{\der{T}{x}}{\text{wall}} dy}{\int dy}}{t} - = - - \ave{\frac{\int \der{\gx}{x} \vat{\der{T}{\gx}}{\text{wall}} \der{y}{\gy} d\gy}{\int \der{y}{\gy} d\gy}}{t}. - -After discretised, I have - -.. math:: - - - \ave{\frac{\sum_j \frac{1}{\Delta x_{\text{wall}}} \vat{\diffe{T}{\gx}}{\text{wall}} \Delta y_j }{\sum_j \Delta y_j}}{t} - = - - \frac{1}{l_y} \ave{\sum_j \frac{1}{\Delta x_{\text{wall}}} \vat{\diffe{T}{\gx}}{\text{wall}} \Delta y_j}{t}, - -where :math:`l_y` is the domain size in the :math:`y` direction. - -In this part, I look into the details of the additional relations which are satisfied in the Rayleigh-Bénard convections in this project. - -********* -Heat flux -********* - -.. include:: heat_flux.rst - -**************** -Energy injection -**************** - -.. include:: injection.rst - -************************** -Kinetic energy dissipation -************************** - -.. include:: kinetic.rst - -************************** -Thermal energy dissipation -************************** - -.. include:: thermal.rst - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/thermal.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/thermal.rst deleted file mode 100644 index b5ea8994..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/conservation_property/nusselt/thermal.rst +++ /dev/null @@ -1,70 +0,0 @@ -Again I focus on the thermal energy equation; in particular I consider the equation of the **total** thermal energy :math:`\der{H}{t}`. -As discussed above, this quantity is increased by the thermal conduction on the walls: - -.. math:: - - & \int \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{}{\xi^j} \left( \mst{j}{j} T \der{T}{\xi^j} \right) d\gx d\gy \\ - \approx - & \sum_i \sum_j \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left\{ - \vat{\left( \mst{x}{x} T \diffe{T}{\gx} \right)}{\pip,\pjc} - - \vat{\left( \mst{x}{x} T \diffe{T}{\gx} \right)}{\pim,\pjc} - + \vat{\left( \mst{y}{y} T \diffe{T}{\gy} \right)}{\pic,\pjp} - - \vat{\left( \mst{y}{y} T \diffe{T}{\gy} \right)}{\pic,\pjm} - \right\} \\ - = - & \sum_i \sum_j \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left\{ - \vat{\left( \mst{x}{x} T \diffe{T}{\gx} \right)}{\pip,\pjc} - - \vat{\left( \mst{x}{x} T \diffe{T}{\gx} \right)}{\pim,\pjc} - \right\} \\ - = - & \sum_j \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left\{ - \vat{\left( \mst{x}{x} T \diffe{T}{\gx} \right)}{\text{right wall},\pjc} - - \vat{\left( \mst{x}{x} T \diffe{T}{\gx} \right)}{\text{left wall},\pjc} - \right\}, \\ - = - & - \sum_j \frac{1}{\sqrt{Pr} \sqrt{Ra}} \vat{\left( \frac{\Delta y_j}{\Delta x_{\text{left wall}}} \diffe{T}{\gx} \right)}{\text{left wall},\pjc}, - -where :math:`T_{\text{left wall}} \equiv 1` and :math:`T_{\text{right wall}} \equiv 0` are used. - -On the other hand, :math:`H` is reduced by the dissipation: - -.. math:: - - & \int \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{}{\xi^j} \left( \mst{j}{j} T \der{T}{\xi^j} \right) d\gx d\gy \\ - \approx - & \sum_i \sum_j \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J_{\pic,\pjc}} \left\{ - \frac{1}{2} J_{\pip,\pjc} \left( \vat{\frac{\diffe{T}{\gx}}{\Delta x}}{\pip,\pjc} \right)^2 - + \frac{1}{2} J_{\pim,\pjc} \left( \vat{\frac{\diffe{T}{\gx}}{\Delta x}}{\pim,\pjc} \right)^2 - + \frac{1}{2} J_{\pic,\pjp} \left( \vat{\frac{\diffe{T}{\gy}}{\Delta y}}{\pic,\pjp} \right)^2 - + \frac{1}{2} J_{\pic,\pjm} \left( \vat{\frac{\diffe{T}{\gy}}{\Delta y}}{\pic,\pjm} \right)^2 - \right\}. - -Again, I average this equation in the whole domain and in time. - -The evolution of :math:`H` vanishes as usual since I am interested in the statistically-steady state. - -The thermal conduction leads - -.. math:: - - - \frac{1}{l_x l_y} \sum_j \frac{1}{\sqrt{Pr} \sqrt{Ra}} \vat{\left( \frac{\Delta y_j}{\Delta x_{\text{left wall}}} \diffe{T}{\gx} \right)}{\text{left wall},\pjc} = Nu \,\, \left( \because l_x \equiv 1 \right), - -while the dissipation leads - -.. math:: - - \frac{1}{l_x l_y} \sum_i \sum_j \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left\{ - \frac{1}{2} J_{\pip,\pjc} \left( \vat{\frac{\diffe{T}{\gx}}{\Delta x}}{\pip,\pjc} \right)^2 - + \frac{1}{2} J_{\pim,\pjc} \left( \vat{\frac{\diffe{T}{\gx}}{\Delta x}}{\pim,\pjc} \right)^2 - + \frac{1}{2} J_{\pic,\pjp} \left( \vat{\frac{\diffe{T}{\gy}}{\Delta y}}{\pic,\pjp} \right)^2 - + \frac{1}{2} J_{\pic,\pjm} \left( \vat{\frac{\diffe{T}{\gy}}{\Delta y}}{\pic,\pjm} \right)^2 - \right\}, - -which is the thermal dissipation rate :math:`\ave{\epsilon_h}{x,y,t} = \ave{\der{T}{x_i} \der{T}{x_i}}{S,t}` in the continuous level. - -Thus I notice - -.. math:: - - Nu = \sqrt{Pr} \sqrt{Ra} \ave{\epsilon_h}{x,y,t}. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/continuous_governing_equations.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/continuous_governing_equations.rst deleted file mode 100644 index 80595f20..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/continuous_governing_equations.rst +++ /dev/null @@ -1,66 +0,0 @@ -############################## -Continuous governing equations -############################## - -The non-dimensional governing equations derived :ref:`here ` are, in the strong conservation form, as follows. - -.. note:: - - :math:`\mst{i}{j} = J \der{\xi^i}{x_k} \der{\xi^j}{x_k}` is the mesh skewness tensor. - -**************************** -Incompressibility constraint -**************************** - -.. math:: - - \frac{1}{J} \der{}{\xi^i} \left( J \der{\xi^i}{x_i} u_i \right) = 0 - -******** -Momentum -******** - -.. math:: - - \der{u_i}{t} - + \frac{1}{J} \der{}{\xi^j} \left\{ \left( J \der{\xi^j}{x_j} u_j \right) u_i \right\} - = - - \frac{1}{J} \der{}{\xi^i} \left( J \der{\xi^i}{x_i} p \right) - + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \der{}{\xi^j} \left( \mst{j}{j} \der{u_i}{\xi^j} \right) - + T \delta_{ix} - -************** -Kinetic energy -************** - -.. math:: - - \der{k}{t} - + \frac{1}{J} \der{}{\xi^i} \left\{ \left( J \der{\xi^i}{x_i} u_i \right) \left( k + p \right) \right\} - = - \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{1}{J} \der{}{\xi^j} \left( \mst{j}{j} u_i \der{u_i}{\xi^j} \right) - - \frac{\sqrt{Pr}}{\sqrt{Ra}} \mst{i}{i} \der{u_i}{\xi^j} \der{u_i}{\xi^j} - + u_i T \delta_{ix} - -*************** -Internal energy -*************** - -.. math:: - - \der{T}{t} - + \frac{1}{J} \der{}{\xi^i} \left\{ \left( J \der{\xi^i}{x_i} u_i \right) T \right\} - = \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J} \der{}{\xi^i} \left( \mst{i}{i} \der{T}{\xi^i} \right) - -************************** -Thermal quadratic quantity -************************** - -.. math:: - - \der{h}{t} - + \frac{1}{J} \der{}{\xi^i} \left\{ \left( J \der{\xi^i}{x_i} u_i \right) h \right\} - = - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{1}{J} \der{}{\xi^i} \left( \mst{i}{i} T \der{T}{\xi^i} \right) - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \mst{i}{i} \der{T}{\xi^j} \der{T}{\xi^j} - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/discrete_governing_equations.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/discrete_governing_equations.rst deleted file mode 100644 index f2c3e68d..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/discrete_governing_equations.rst +++ /dev/null @@ -1,124 +0,0 @@ -############################ -Discrete governing equations -############################ - -The governing equations in the strong conservation form derived in the previous section are directly discretised based on the following notes. -For simplicity equations which are not treated numerically (kinetic energy, thermal quadratic quantity) are omitted. - -.. note:: - - * Since the new cooridnate system is designed to satisfy :math:`\Delta \xi \equiv \Delta \eta \equiv 1`, interpolations and differentiations are given as the arithmetic averages and the subtractions of two neighbouring values, respectively. - - * The Jacobian and the scaling factors should be interpolated as well as the variables. - - * The mesh skewness tensors lead to - - .. math:: - - \mst{x}{x} = \frac{\Delta y}{\Delta x}, - \mst{y}{y} = \frac{\Delta x}{\Delta y}. - -**************************** -Incompressibility constraint -**************************** - -.. math:: - - \frac{ - \vat{\ux}{\pip,\pjc} - - \vat{\ux}{\pim,\pjc} - }{\Delta x_{\pic}} - + \frac{ - \vat{\uy}{\pic,\pjp} - - \vat{\uy}{\pic,\pjm} - }{\Delta y_{\pjc}} - = 0, - -which is evaluated at :math:`\left( \pic, \pjc \right)`. - -**************** -Momentum balance -**************** - -.. math:: - - \der{\vat{\ux}{\xic,\xjc}}{t} - = - & - \frac{ - \vat{\dintrpa{\Delta y \ux}{\gx}}{\xip,\xjc} \vat{\dintrpa{\ux}{\gx}}{\xip,\xjc} - - \vat{\dintrpa{\Delta y \ux}{\gx}}{\xim,\xjc} \vat{\dintrpa{\ux}{\gx}}{\xim,\xjc} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & - \frac{ - \vat{\dintrpa{\Delta x \uy}{\gx}}{\xic,\xjp} \vat{\dintrpa{\ux}{\gy}}{\xic,\xjp} - - \vat{\dintrpa{\Delta x \uy}{\gx}}{\xic,\xjm} \vat{\dintrpa{\ux}{\gy}}{\xic,\xjm} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & -\frac{ - \vat{\Delta y p}{\xip,\xjc} - - \vat{\Delta y p}{\xim,\xjc} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta y}{\Delta x} \diffe{\ux}{\gx}}{\xip,\xjc} - - \vat{\frac{\Delta y}{\Delta x} \diffe{\ux}{\gx}}{\xim,\xjc} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta x}{\Delta y} \diffe{\ux}{\gy}}{\xic,\xjp} - - \vat{\frac{\Delta x}{\Delta y} \diffe{\ux}{\gy}}{\xic,\xjm} - }{\Delta x_{\xic} \Delta y_{\xjc}} \\ - & + \vat{\dintrpa{T}{\gx}}{\xic,\xjc}, - -which is evaluated at :math:`\left( \xic, \xjc \right)`. - -.. math:: - - \der{\vat{\uy}{\yic,\yjc}}{t} - = - & - \frac{ - \vat{\dintrpa{\Delta y \ux}{\gy}}{\yip,\yjc} \vat{\dintrpa{\uy}{\gx}}{\yip,\yjc} - - \vat{\dintrpa{\Delta y \ux}{\gy}}{\yim,\yjc} \vat{\dintrpa{\uy}{\gx}}{\yim,\yjc} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & - \frac{ - \vat{\dintrpa{\Delta x \uy}{\gy}}{\yic,\yjp} \vat{\dintrpa{\uy}{\gy}}{\yic,\yjp} - - \vat{\dintrpa{\Delta x \uy}{\gy}}{\yic,\yjm} \vat{\dintrpa{\uy}{\gy}}{\yic,\yjm} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & -\frac{ - \vat{\Delta x p}{\yic,\yjp} - - \vat{\Delta x p}{\yic,\yjm} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta y}{\Delta x} \diffe{\uy}{\gx}}{\yip,\yjc} - - \vat{\frac{\Delta y}{\Delta x} \diffe{\uy}{\gx}}{\yim,\yjc} - }{\Delta x_{\yic} \Delta y_{\yjc}} \\ - & + \frac{\sqrt{Pr}}{\sqrt{Ra}} \frac{ - \vat{\frac{\Delta x}{\Delta y} \diffe{\uy}{\gy}}{\yic,\yjp} - - \vat{\frac{\Delta x}{\Delta y} \diffe{\uy}{\gy}}{\yic,\yjm} - }{\Delta x_{\yic} \Delta y_{\yjc}}, - -which is evaluated at :math:`\left( \yic, \yjc \right)`. - -*************** -Internal energy -*************** - -.. math:: - - \der{\vat{T}{\pic,\pjc}}{t} - = - & - \frac{ - \vat{\Delta y \ux}{\pip,\pjc} \vat{\dintrpa{T}{\gx}}{\pip,\pjc} - - \vat{\Delta y \ux}{\pim,\pjc} \vat{\dintrpa{T}{\gx}}{\pim,\pjc} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & - \frac{ - \vat{\Delta x \uy}{\pic,\pjp} \vat{\dintrpa{T}{\gy}}{\pic,\pjp} - - \vat{\Delta x \uy}{\pic,\pjm} \vat{\dintrpa{T}{\gy}}{\pic,\pjm} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{ - \vat{\frac{\Delta y}{\Delta x} \diffe{T}{\gx}}{\pip,\pjc} - - \vat{\frac{\Delta y}{\Delta x} \diffe{T}{\gx}}{\pim,\pjc} - }{\Delta x_{\pic} \Delta y_{\pjc}} \\ - & + \frac{1}{\sqrt{Pr} \sqrt{Ra}} \frac{ - \vat{\frac{\Delta x}{\Delta y} \diffe{T}{\gy}}{\pic,\pjp} - - \vat{\frac{\Delta x}{\Delta y} \diffe{T}{\gy}}{\pic,\pjm} - }{\Delta x_{\pic} \Delta y_{\pjc}}. - -which is evaluated at :math:`\left( \pic, \pjc \right)`. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/main.rst deleted file mode 100644 index 1fac5e10..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/main.rst +++ /dev/null @@ -1,19 +0,0 @@ - -.. _appendix_strong_conservation: - -.. include:: /references.txt - -##################################################### -Strong conservation form and energy-conserving scheme -##################################################### - -The energy-conserving scheme, which is derived :ref:`here `, is derived using the other method. - -.. toctree:: - :maxdepth: 1 - - strong_conservation_form - continuous_governing_equations - discrete_governing_equations - conservation_property/main - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/strong_conservation_form.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/strong_conservation_form.rst deleted file mode 100644 index f91a62f7..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/appendix/strong_conservation_form/strong_conservation_form.rst +++ /dev/null @@ -1,334 +0,0 @@ -######################## -Strong conservation form -######################## - -I consider to project the governing equations to a new coordinate system. - -To begin with, I take the incompressibility constraint: - -.. math:: - - \der{u_i}{x_i} - = - 0 - -as an example. -I try to replace the derivatives :math:`\partial / \partial x_i` by :math:`\partial / \partial \xi^i` as - -.. math:: - - \der{\xi^j}{x_i} \der{u_i}{\xi^j} - = \der{ \xi}{x} \der{\ux}{ \xi} - + \der{\eta}{x} \der{\ux}{\eta} - + \der{ \xi}{y} \der{\uy}{ \xi} - + \der{\eta}{y} \der{\uy}{\eta}. - -I would like to make the right-hand-side terms conservative, i.e. :math:`\partial / \partial \xi^j \left( \cdots \right)`. -To do so, I consider the relation between the old and the new coordinate systems: - -.. math:: - - \begin{pmatrix} - \der{}{x} \\ - \der{}{y} - \end{pmatrix} - = - \begin{pmatrix} - \der{\xi}{x} & \der{\eta}{x} \\ - \der{\xi}{y} & \der{\eta}{y} - \end{pmatrix} - \begin{pmatrix} - \der{}{ \xi} \\ - \der{}{\eta} - \end{pmatrix} - -or - -.. math:: - - \begin{pmatrix} - \der{}{ \xi} \\ - \der{}{\eta} - \end{pmatrix} - = - \begin{pmatrix} - \der{x}{ \xi} & \der{y}{ \xi} \\ - \der{x}{\eta} & \der{y}{\eta} - \end{pmatrix} - \begin{pmatrix} - \der{}{x} \\ - \der{}{y} - \end{pmatrix}. - -Since the second equation yields - -.. math:: - - \begin{pmatrix} - \der{}{x} \\ - \der{}{y} - \end{pmatrix} - = - \frac{1}{\der{x}{ \xi} \der{y}{\eta} - \der{y}{ \xi} \der{x}{\eta}} - \begin{pmatrix} - \der{y}{\eta} & -\der{x}{\eta} \\ - - - \der{y}{ \xi} & \der{x}{ \xi} - \end{pmatrix} - \begin{pmatrix} - \der{}{ \xi} \\ - \der{}{\eta} - \end{pmatrix}, - -I find - -.. math:: - - \frac{1}{ - \der{x}{ \xi} \der{y}{\eta} - - - \der{x}{\eta} \der{y}{ \xi} - } - \begin{pmatrix} - \der{y}{\eta} & -\der{x}{\eta} \\ - -\der{y}{ \xi} & \der{x}{ \xi} - \end{pmatrix} - = - \begin{pmatrix} - \der{ \xi}{x} & \der{ \xi}{y} \\ - \der{\eta}{x} & \der{\eta}{y} - \end{pmatrix}, - -where the denominator is the determinant of the Jacobian matrix. - -As a result, the incompressibility constraint leads to - -.. math:: - - \der{\xi^j}{x_i} \der{u_i}{\xi^j} - &= \der{ x}{ \xi} \der{\ux}{ \xi} - + \der{ x}{\eta} \der{\ux}{\eta} - + \der{ y}{ \xi} \der{\uy}{ \xi} - + \der{ y}{\eta} \der{\uy}{\eta} \\ - &= \frac{1}{J} \der{ y}{\eta} \der{\ux}{ \xi} - - \frac{1}{J} \der{ y}{ \xi} \der{\ux}{\eta} - - \frac{1}{J} \der{ x}{\eta} \der{\uy}{ \xi} - + \frac{1}{J} \der{ x}{ \xi} \der{\uy}{\eta}. - -Next, I consider to update the velocity, i.e. trying to write down the equation using the contra-variant component :math:`U^i`. -Assigning - -.. math:: - - \begin{pmatrix} - \ux \\ - \uy - \end{pmatrix} - \equiv - \begin{pmatrix} - \frac{\partial x}{\partial t} \\ - \frac{\partial y}{\partial t} - \end{pmatrix} - = - \begin{pmatrix} - \der{ x}{ \xi} & \der{ x}{\eta} \\ - \der{ y}{ \xi} & \der{ y}{\eta} - \end{pmatrix} - \begin{pmatrix} - \frac{\partial \xi}{\partial t} \\ - \frac{\partial \eta}{\partial t} - \end{pmatrix} - = - \begin{pmatrix} - \der{ x}{ \xi} & \der{ x}{\eta} \\ - \der{ y}{ \xi} & \der{ y}{\eta} - \end{pmatrix} - \begin{pmatrix} - U^x \\ - U^y - \end{pmatrix} - -yields - -.. math:: - - &+ \frac{1}{J} \der{ y}{\eta} \frac{\partial}{\partial \xi} \left( \der{ x}{ \xi} U^x + \der{ x}{\eta} U^y \right) \\ - &- \frac{1}{J} \der{ y}{ \xi} \frac{\partial}{\partial \eta} \left( \der{ x}{ \xi} U^x + \der{ x}{\eta} U^y \right) \\ - &- \frac{1}{J} \der{ x}{\eta} \frac{\partial}{\partial \xi} \left( \der{ y}{ \xi} U^x + \der{ y}{\eta} U^y \right) \\ - &+ \frac{1}{J} \der{ x}{ \xi} \frac{\partial}{\partial \eta} \left( \der{ y}{ \xi} U^x + \der{ y}{\eta} U^y \right). - -Terms including :math:`U` are - -.. math:: - - \begin{aligned} - &+ \frac{1}{J} \der{ y}{\eta} \frac{\partial}{\partial \xi} \left( \der{ x}{ \xi} U^x \right) \\ - &- \frac{1}{J} \der{ y}{ \xi} \frac{\partial}{\partial \eta} \left( \der{ x}{ \xi} U^x \right) \\ - &- \frac{1}{J} \der{ x}{\eta} \frac{\partial}{\partial \xi} \left( \der{ y}{ \xi} U^x \right) \\ - &+ \frac{1}{J} \der{ x}{ \xi} \frac{\partial}{\partial \eta} \left( \der{ y}{ \xi} U^x \right) - \end{aligned} - = - \begin{aligned} - &+ \frac{U^x}{J} \der{ y}{\eta} \frac{\partial}{\partial \xi} \left( \der{ x}{ \xi} \right) + \frac{1}{J} \der{ y}{\eta} \frac{\partial U^x}{\partial \xi} \der{ x}{ \xi} \\ - &- \frac{U^x}{J} \der{ y}{ \xi} \frac{\partial}{\partial \eta} \left( \der{ x}{ \xi} \right) - \frac{1}{J} \der{ y}{ \xi} \frac{\partial U^x}{\partial \eta} \der{ x}{ \xi} \\ - &- \frac{U^x}{J} \der{ x}{\eta} \frac{\partial}{\partial \xi} \left( \der{ y}{ \xi} \right) - \frac{1}{J} \der{ x}{\eta} \frac{\partial U^x}{\partial \xi} \der{ y}{ \xi} \\ - &+ \frac{U^x}{J} \der{ x}{ \xi} \frac{\partial}{\partial \eta} \left( \der{ y}{ \xi} \right) + \frac{1}{J} \der{ x}{ \xi} \frac{\partial U^x}{\partial \eta} \der{ y}{ \xi} - \end{aligned} - \equiv - \begin{aligned} - &\left( 1-1 \right) + \left( 1-2 \right) \\ - &\left( 2-1 \right) + \left( 2-2 \right) \\ - &\left( 3-1 \right) + \left( 3-2 \right) \\ - &\left( 4-1 \right) + \left( 4-2 \right) - \end{aligned}, - -.. math:: - - \left( 1-2 \right) + \left( 3-2 \right) = - + \frac{1}{J} \der{ y}{\eta} \frac{\partial U^x}{\partial \xi} \der{ x}{ \xi} - - \frac{1}{J} \der{ x}{\eta} \frac{\partial U^x}{\partial \xi} \der{ y}{ \xi} - = \frac{1}{J} \left( \der{ x}{ \xi} \der{ y}{\eta} - \der{ x}{\eta} \der{ y}{ \xi} \right) \frac{\partial U^x}{\partial \xi} - = \frac{1}{J} J \frac{\partial U^x}{\partial \xi}, - -.. math:: - - \left( 2-2 \right) + \left( 4-2 \right) = - - \frac{1}{J} \der{ y}{ \xi} \frac{\partial U^x}{\partial \eta} \der{ x}{ \xi} - + \frac{1}{J} \der{ x}{ \xi} \frac{\partial U^x}{\partial \eta} \der{ y}{ \xi} - = 0, - -.. math:: - - \begin{aligned} - & \left( 1-1 \right) + \left( 2-1 \right) + \left( 3-1 \right) + \left( 4-1 \right) \\ - & = - + \frac{U^x}{J} \der{ y}{\eta} \frac{\partial}{\partial \xi} \left( \der{ x}{ \xi} \right) - - \frac{U^x}{J} \der{ y}{ \xi} \frac{\partial}{\partial \eta} \left( \der{ x}{ \xi} \right) - - \frac{U^x}{J} \der{ x}{\eta} \frac{\partial}{\partial \xi} \left( \der{ y}{ \xi} \right) - + \frac{U^x}{J} \der{ x}{ \xi} \frac{\partial}{\partial \eta} \left( \der{ y}{ \xi} \right) \\ - & = - + \frac{U^x}{J} \der{ y}{\eta} \frac{\partial}{\partial \xi} \left( \der{ x}{ \xi} \right) - - \frac{U^x}{J} \der{ y}{ \xi} \frac{\partial}{\partial \xi} \left( \der{ x}{\eta} \right) - - \frac{U^x}{J} \der{ x}{\eta} \frac{\partial}{\partial \xi} \left( \der{ y}{ \xi} \right) - + \frac{U^x}{J} \der{ x}{ \xi} \frac{\partial}{\partial \xi} \left( \der{ y}{\eta} \right) \\ - & = - \frac{U^x}{J} \left[ - + \der{ x}{ \xi} \frac{\partial}{\partial \xi} \left( \der{ y}{\eta} \right) - + \der{ y}{\eta} \frac{\partial}{\partial \xi} \left( \der{ x}{ \xi} \right) - - \der{ x}{\eta} \frac{\partial}{\partial \xi} \left( \der{ y}{ \xi} \right) - - \der{ y}{ \xi} \frac{\partial}{\partial \xi} \left( \der{ x}{\eta} \right) - \right] \\ - & = - \frac{U^x}{J} \left[ - + \frac{\partial}{\partial \xi} \left( - \der{ x}{ \xi} \der{ y}{\eta} - \right) - - \frac{\partial}{\partial \xi} \left( - \der{ x}{\eta} \der{ y}{ \xi} - \right) - \right] \\ - & = - \frac{U^x}{J} \frac{\partial}{\partial \xi} \left( \der{ x}{ \xi} \der{ y}{\eta} - \der{ x}{\eta} \der{ y}{ \xi} \right) - = \frac{U^x}{J} \frac{\partial J}{\partial \xi}, - \end{aligned} - -and as a result - -.. math:: - - \frac{1}{J} \frac{\partial}{\partial \xi} \left( J U^x \right). - -Similarly I notice the terms involving :math:`U^y` yield - -.. math:: - - \frac{1}{J} \frac{\partial}{\partial \eta} \left( J U^y \right). - -Thus I obtain - -.. math:: - - \frac{\partial u_i}{\partial x_i} - = - \frac{\partial \xi^j}{\partial x_i} \frac{\partial u_i}{\partial \xi^j} - = - \frac{1}{J} \frac{\partial}{\partial \xi^i} \left( J U^i \right) - = - 0. - -Since - -.. math:: - - \begin{pmatrix} - U^x \\ - U^y - \end{pmatrix} - = - \frac{1}{J} - \begin{pmatrix} - \der{ y}{\eta} & -\der{ x}{\eta} \\ - -\der{ y}{ \xi} & \der{ x}{ \xi} - \end{pmatrix} - \begin{pmatrix} - \ux \\ - \uy - \end{pmatrix} - = - \begin{pmatrix} - \der{ x}{ \xi} & \der{ y}{ \xi} \\ - \der{ x}{\eta} & \der{ y}{\eta} - \end{pmatrix} - \begin{pmatrix} - \ux \\ - \uy - \end{pmatrix} - -or - -.. math:: - - U^j = \frac{\partial \xi^j}{\partial x_i} u_i, - -I finally obtain - -.. math:: - - \frac{\partial u_i}{\partial x_i} - = - \frac{\partial \xi^j}{\partial x_i} \frac{\partial u_i}{\partial \xi^j} - = - \frac{1}{J} \frac{\partial}{\partial \xi^j} \left( J \frac{\partial \xi^j}{\partial x_i} u_i \right) - = - 0, - -which is the conservative form of the incompressibility constraint defined in the new coordinate system (strong conservation form). - -By comparing the second term with the third one, I notice - -.. math:: - - \frac{\partial \xi^j}{\partial x_i} \frac{\partial u_i}{\partial \xi^j} - = - \frac{1}{J} \left\{ \frac{\partial}{\partial \xi^j} \left( J \frac{\partial \xi^j}{\partial x_i} \right) \right\} u_i - + \frac{1}{J} J \frac{\partial \xi^j}{\partial x_i} \frac{\partial u_i}{\partial \xi^j} - = - \frac{1}{J} \left\{ \frac{\partial}{\partial \xi^j} \left( J \frac{\partial \xi^j}{\partial x_i} \right) \right\} u_i - + \frac{\partial \xi^j}{\partial x_i} \frac{\partial u_i}{\partial \xi^j} - = - 0, - -and thus I find the following important identity: - -.. math:: - - \frac{1}{J} \left\{ \frac{\partial}{\partial \xi^j} \left( J \frac{\partial \xi^j}{\partial x_i} \right) \right\} u_i = 0. - -By following the same procedure, I notice that this relation even holds for general tensors (not limited to the first-order tensors). -This relation is used to derive the strong conservation forms of the momentum and internal energy balances. - -.. note:: - - Since the current projection keeps the orthogonality, the Jacobian matrix is a diagonal matrix. - Thus, :math:`\der{\xi^j}{x_i}` can be reduced to :math:`\der{\xi^i}{x_i}`. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/incompressibility.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/incompressibility.rst deleted file mode 100644 index 7da30c4a..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/incompressibility.rst +++ /dev/null @@ -1,31 +0,0 @@ - -.. _eq_mass_discrete: - -############################# -:ref:`Mass balance ` -############################# - -Defined at :math:`\left( \pic, \pjc, \pkc \right)`: - -.. math:: - - \dder{\ux}{x} - + - \dder{\uy}{y} - + - \dder{\uz}{z} - = - 0, - -and thus - -.. myliteralinclude:: /../../src/logging/divergence.c - :language: c - :tag: compute local divergence - -.. seealso:: - - * :ref:`fluid/correct_velocity ` - - * :ref:`fluid/compute_potential ` - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_x.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_x.rst deleted file mode 100644 index 9b8f71f4..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_x.rst +++ /dev/null @@ -1,85 +0,0 @@ - -.. _impl_adv_t_x: - -############################# -Advection of temperature in x -############################# - -.. math:: - - - - \vat{ - \dintrpv{ - \ux - \dder{T}{x} - }{x} - }{\pic, \pjc, \pkc} - = - & - - - \frac{1}{2} - \frac{\Delta x_{\pim}}{\Delta x_{\pic}} - \vat{\ux}{\pim, \pjc, \pkc} - \vat{\dder{T}{x}}{\pim, \pjc, \pkc} \\ - & - - - \frac{1}{2} - \frac{\Delta x_{\pip}}{\Delta x_{\pic}} - \vat{\ux}{\pip, \pjc, \pkc} - \vat{\dder{T}{x}}{\pip, \pjc, \pkc}, - -and thus - -.. math:: - - l_{\pic, \pjc, \pkc} T_{\pimm, \pjc, \pkc} - + - c_{\pic, \pjc, \pkc} T_{\pic , \pjc, \pkc} - + - u_{\pic, \pjc, \pkc} T_{\pipp, \pjc, \pkc}, - -where - -.. math:: - - l_{\pic, \pjc, \pkc} - \equiv - + - \frac{1}{2} - \frac{\Delta x_{\pim}}{\Delta x_{\pic}} - \vat{\ux}{\pim, \pjc, \pkc} - \frac{1}{\Delta x_{\pim}} - = - + - \frac{1}{2} - \frac{1}{\Delta x_{\pic}} - \vat{\ux}{\pim, \pjc, \pkc}, - -.. math:: - - u_{\pic, \pjc, \pkc} - \equiv - - - \frac{1}{2} - \frac{\Delta x_{\pip}}{\Delta x_{\pic}} - \vat{\ux}{\pip, \pjc, \pkc} - \frac{1}{\Delta x_{\pip}} - = - - - \frac{1}{2} - \frac{1}{\Delta x_{\pic}} - \vat{\ux}{\pip, \pjc, \pkc}, - -.. math:: - - c_{\pic, \pjc, \pkc} - \equiv - - - l_{\pic, \pjc, \pkc} - - - u_{\pic, \pjc, \pkc}. - -.. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: T is transported by ux - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_y.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_y.rst deleted file mode 100644 index 25839e0e..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_y.rst +++ /dev/null @@ -1,71 +0,0 @@ - -.. _impl_adv_t_y: - -############################# -Advection of temperature in y -############################# - -.. math:: - - - - \vat{ - \dintrpa{ - \uy - \dder{T}{y} - }{y} - }{\pic, \pjc, \pkc} - = - & - - - \frac{1}{2} - \vat{\uy}{\pic, \pjm, \pkc} - \vat{\dder{T}{y}}{\pic, \pjm, \pkc} \\ - & - - - \frac{1}{2} - \vat{\uy}{\pic, \pjp, \pkc} - \vat{\dder{T}{y}}{\pic, \pjp, \pkc}, - -and thus - -.. math:: - - l_{\pic, \pjc, \pkc} T_{\pic, \pjmm, \pkc} - + - c_{\pic, \pjc, \pkc} T_{\pic, \pjc , \pkc} - + - u_{\pic, \pjc, \pkc} T_{\pic, \pjpp, \pkc}, - -where - -.. math:: - - l_{\pic, \pjc, \pkc} - \equiv - + - \frac{1}{2} - \frac{1}{\Delta y} - \vat{\uy}{\pic, \pjm, \pkc}, - -.. math:: - - u_{\pic, \pjc, \pkc} - \equiv - - - \frac{1}{2} - \frac{1}{\Delta y} - \vat{\uy}{\pic, \pjp, \pkc}, - -.. math:: - - c_{\pic, \pjc, \pkc} - \equiv - - - l_{\pic, \pjc, \pkc} - - - u_{\pic, \pjc, \pkc}. - -.. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: T is transported by uy - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_z.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_z.rst deleted file mode 100644 index 3ce50a39..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/adv_z.rst +++ /dev/null @@ -1,71 +0,0 @@ - -.. _impl_adv_t_z: - -############################# -Advection of temperature in z -############################# - -.. math:: - - - - \vat{ - \dintrpa{ - \uz - \dder{T}{z} - }{z} - }{\pic, \pjc, \pkc} - = - & - - - \frac{1}{2} - \vat{\uz}{\pic, \pjc, \pkm} - \vat{\dder{T}{z}}{\pic, \pjc, \pkm} \\ - & - - - \frac{1}{2} - \vat{\uz}{\pic, \pjc, \pkp} - \vat{\dder{T}{z}}{\pic, \pjc, \pkp}, - -and thus - -.. math:: - - l_{\pic, \pjc, \pkc} T_{\pic, \pjc, \pkmm} - + - c_{\pic, \pjc, \pkc} T_{\pic, \pjc, \pkc } - + - u_{\pic, \pjc, \pkc} T_{\pic, \pjc, \pkpp}, - -where - -.. math:: - - l_{\pic, \pjc, \pkc} - \equiv - + - \frac{1}{2} - \frac{1}{\Delta z} - \vat{\uz}{\pic, \pjc, \pkm}, - -.. math:: - - u_{\pic, \pjc, \pkc} - \equiv - - - \frac{1}{2} - \frac{1}{\Delta z} - \vat{\uz}{\pic, \pjc, \pkp}, - -.. math:: - - c_{\pic, \pjc, \pkc} - \equiv - - - l_{\pic, \pjc, \pkc} - - - u_{\pic, \pjc, \pkc}. - -.. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: T is transported by uz - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_x.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_x.rst deleted file mode 100644 index 602dcc6d..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_x.rst +++ /dev/null @@ -1,54 +0,0 @@ - -.. _impl_dif_t_x: - -############################# -Diffusion of temperature in x -############################# - -.. math:: - - \vat{ - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x} \dder{T}{x} - }{\pic, \pjc, \pkc}, - -which is the product of the diffusivity and - -.. math:: - - l_{\pic} T_{\pimm, \pjc, \pkc} - + - c_{\pic} T_{\pic , \pjc, \pkc} - + - u_{\pic} T_{\pipp, \pjc, \pkc}, - -where - -.. math:: - - l_{\pic} - \equiv - \frac{1}{\Delta x_{\pic}} \frac{1}{\Delta x_{\pim}}, - -.. math:: - - u_{\pic} - \equiv - \frac{1}{\Delta x_{\pic}} \frac{1}{\Delta x_{\pip}}, - -.. math:: - - c_{\pic} - \equiv - - - l_{\pic} - - - u_{\pic}. - -.. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: T is diffused in x - -.. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: Laplacian w.r.t. temp in x - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_y.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_y.rst deleted file mode 100644 index 33b2433c..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_y.rst +++ /dev/null @@ -1,54 +0,0 @@ - -.. _impl_dif_t_y: - -############################# -Diffusion of temperature in y -############################# - -.. math:: - - \vat{ - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{y} \dder{T}{y} - }{\pic, \pjc, \pkc}, - -which is the product of the diffusivity and - -.. math:: - - l T_{\pic, \pjmm, \pkc} - + - c T_{\pic, \pjc , \pkc} - + - u T_{\pic, \pjpp, \pkc}, - -where - -.. math:: - - l - \equiv - \frac{1}{\Delta y} \frac{1}{\Delta y}, - -.. math:: - - u - \equiv - \frac{1}{\Delta y} \frac{1}{\Delta y}, - -.. math:: - - c - \equiv - - - l - - - u. - -.. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: T is diffused in y - -.. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: Laplacian in y - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_z.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_z.rst deleted file mode 100644 index c8505987..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/dif_z.rst +++ /dev/null @@ -1,54 +0,0 @@ - -.. _impl_dif_t_z: - -############################# -Diffusion of temperature in z -############################# - -.. math:: - - \vat{ - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{z} \dder{T}{z} - }{\pic, \pjc, \pkc}, - -which is the product of the diffusivity and - -.. math:: - - l T_{\pic, \pjc, \pkmm} - + - c T_{\pic, \pjc, \pkc } - + - u T_{\pic, \pjc, \pkpp}, - -where - -.. math:: - - l - \equiv - \frac{1}{\Delta z} \frac{1}{\Delta z}, - -.. math:: - - u - \equiv - \frac{1}{\Delta z} \frac{1}{\Delta z}, - -.. math:: - - c - \equiv - - - l - - - u. - -.. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: T is diffused in z - -.. myliteralinclude:: /../../src/fluid/integrate/t.c - :language: c - :tag: Laplacian in z - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/main.rst deleted file mode 100644 index 149e040b..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/internal_energy/main.rst +++ /dev/null @@ -1,53 +0,0 @@ -.. _eq_temperature_discrete: - -############################################### -:ref:`Internal energy balance ` -############################################### - -Defined at :math:`\left( \pic, \pjc, \pkc \right)`: - -.. math:: - - \der{T}{t} - = - & - - - \dintrpv{ - \ux - \dder{T}{x} - }{x} - - - \dintrpa{ - \uy - \dder{T}{y} - }{y} - - - \dintrpa{ - \uz - \dder{T}{z} - }{z} \\ - & - + - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{T}{x} \right) - + - \dder{}{y} \left( \dder{T}{y} \right) - + - \dder{}{z} \left( \dder{T}{z} \right) - \right\}. - -.. toctree:: - :maxdepth: 1 - :caption: Implmentation - - adv_x - adv_y - adv_z - dif_x - dif_y - dif_z - -.. seealso:: - - * :ref:`fluid_compute_rhs ` - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/squared_temperature.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/squared_temperature.rst deleted file mode 100644 index 9deba19f..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/squared_temperature.rst +++ /dev/null @@ -1,58 +0,0 @@ - -.. _eq_squared_temperature_discrete: - -################################################################### -:ref:`Equation of the squared temperature ` -################################################################### - -Although the following relation is not implemented, it plays an important role in the conservation properties of the discretised equations. - -Defined as the volume-integrated quantity: - -.. math:: - - & - \der{}{t} \left( - \sum_{\pkc} \sum_{\pjc} \sum_{\pic} \frac{1}{2} T^2 \Delta x \Delta y \Delta z - \right) \\ - = - & - - - \sum_{\pkc} \sum_{\pjc} \sum_{\pic} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \left[ - \dintrpv{ - C - \left( \dder{T}{x} \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{T}{y} \right)^2 - }{y} - + - \dintrpa{ - \left( \dder{T}{z} \right)^2 - }{z} - \right] - \Delta x \Delta y \Delta z \\ - & - + - \sum_{\pkc} \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \left( - \vat{ - T \dder{T}{x} - }{x = 1} - - - \vat{ - T \dder{T}{x} - }{x = 0} - \right) - \Delta y \Delta z, - -where :math:`C` is a constant to take into account the no-slip boundary effect. - -.. seealso:: - - * :ref:`logging/nusselt/thermal_energy_dissipation.c ` - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/squared_velocity.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/squared_velocity.rst deleted file mode 100644 index eced28f4..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/squared_velocity.rst +++ /dev/null @@ -1,90 +0,0 @@ - -.. _eq_squared_velocity_discrete: - -############################################################# -:ref:`Equation of the squared velocity ` -############################################################# - -Although the following relation is not implemented, it plays an important role in the conservation properties of the discretised equations. - -Defined as the volume-integrated quantity: - -.. math:: - - & - \der{}{t} \left( - \sum_{\xkc} \sum_{\xjc} \sum_{\xic} \frac{1}{2} \ux^2 \Delta x \Delta y \Delta z - + - \sum_{\ykc} \sum_{\yjc} \sum_{\yic} \frac{1}{2} \uy^2 \Delta x \Delta y \Delta z - + - \sum_{\zkc} \sum_{\zjc} \sum_{\zic} \frac{1}{2} \uy^2 \Delta x \Delta y \Delta z - \right) \\ - = - & - - - \sum_{\xkc} \sum_{\xjc} \sum_{\xic} - \frac{\sqrt{Pr}}{\sqrt{Ra}} - \left[ - \dintrpv{ - \left( \dder{\ux}{x} \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{\ux}{y} \right)^2 - }{y} - + - \dintrpa{ - \left( \dder{\ux}{z} \right)^2 - }{z} - \right] - \Delta x \Delta y \Delta z \\ - & - - - \sum_{\ykc} \sum_{\yjc} \sum_{\yic} - \frac{\sqrt{Pr}}{\sqrt{Ra}} - \left[ - \dintrpv{ - C - \left( \dder{\uy}{x} \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{\uy}{y} \right)^2 - }{y} - + - \dintrpa{ - \left( \dder{\uy}{z} \right)^2 - }{z} - \right] - \Delta x \Delta y \Delta z \\ - & - - - \sum_{\zkc} \sum_{\zjc} \sum_{\zic} - \frac{\sqrt{Pr}}{\sqrt{Ra}} - \left[ - \dintrpv{ - C - \left( \dder{\uz}{x} \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{\uz}{y} \right)^2 - }{y} - + - \dintrpa{ - \left( \dder{\uz}{z} \right)^2 - }{z} - \right] - \Delta x \Delta y \Delta z \\ - & - + - \sum_{\xkc} \sum_{\xjc} \sum_{\xic} - \ux \dintrpa{T}{x} - \Delta x \Delta y \Delta z, - -where :math:`C` is a constant to take into account the no-slip boundary effect. - -.. seealso:: - - * :ref:`logging/nusselt/kinetic_energy_dissipation.c ` - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_x.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_x.rst deleted file mode 100644 index 64287cc8..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_x.rst +++ /dev/null @@ -1,71 +0,0 @@ - -.. _impl_adv_x_x: - -############################ -Advection of x momentum in x -############################ - -.. math:: - - - - \vat{ - \dintrpv{ - \dintrpa{\ux}{x} - \dder{\ux}{x} - }{x} - }{\xic, \xjc, \xkc} - = - & - - - \frac{1}{2} - \frac{\Delta x_{\xim}}{\Delta x_{\xic}} - \vat{\dintrpa{\ux}{x}}{\xim, \xjc, \xkc} - \vat{\dder{\ux}{x}}{\xim, \xjc, \xkc} \\ - & - - - \frac{1}{2} - \frac{\Delta x_{\xip}}{\Delta x_{\xic}} - \vat{\dintrpa{\ux}{x}}{\xip, \xjc, \xkc} - \vat{\dder{\ux}{x}}{\xip, \xjc, \xkc}, - -and thus - -.. math:: - - l_{\xic, \xjc, \xkc} {\ux}_{\ximm, \xjc, \xkc} - + - c_{\xic, \xjc, \xkc} {\ux}_{\xic , \xjc, \xkc} - + - u_{\xic, \xjc, \xkc} {\ux}_{\xipp, \xjc, \xkc}, - -where - -.. math:: - - l_{\xic, \xjc, \xkc} - \equiv - + \frac{1}{2} \frac{\Delta x_{\xim}}{\Delta x_{\xic}} \vat{\dintrpa{\ux}{x}}{\xim, \xjc, \xkc} \frac{1}{\Delta x_{\xim}} - = - + \frac{1}{2} \frac{1}{\Delta x_{\xic}} \vat{\dintrpa{\ux}{x}}{\xim, \xjc, \xkc}, - -.. math:: - - u_{\xic, \xjc, \xkc} - \equiv - - \frac{1}{2} \frac{\Delta x_{\xip}}{\Delta x_{\xic}} \vat{\dintrpa{\ux}{x}}{\xip, \xjc, \xkc} \frac{1}{\Delta x_{\xip}} - = - - \frac{1}{2} \frac{1}{\Delta x_{\xic}} \vat{\dintrpa{\ux}{x}}{\xip, \xjc, \xkc}, - -.. math:: - - c_{\xic, \xjc, \xkc} - \equiv - - - l_{\xic, \xjc, \xkc} - - - u_{\xic, \xjc, \xkc}. - -.. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: ux is transported by ux - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_y.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_y.rst deleted file mode 100644 index 34a07224..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_y.rst +++ /dev/null @@ -1,61 +0,0 @@ - -.. _impl_adv_x_y: - -############################ -Advection of x momentum in y -############################ - -.. math:: - - - - \vat{ - \dintrpa{ - \dintrpv{\uy}{x} - \dder{\ux}{y} - }{y} - }{\xic, \xjc, \xkc} - = - & - - - \frac{1}{2} \vat{\dintrpv{\uy}{x}}{\xic, \xjm, \xkc} \vat{\dder{\ux}{y}}{\xic, \xjm, \xkc} \\ - & - - - \frac{1}{2} \vat{\dintrpv{\uy}{x}}{\xic, \xjp, \xkc} \vat{\dder{\ux}{y}}{\xic, \xjp, \xkc}, - -and thus - -.. math:: - - l_{\xic, \xjc, \xkc} {\ux}_{\xic, \xjmm, \xkc} - + - c_{\xic, \xjc, \xkc} {\ux}_{\xic, \xjc , \xkc} - + - u_{\xic, \xjc, \xkc} {\ux}_{\xic, \xjpp, \xkc}, - -where - -.. math:: - - l_{\xic, \xjc, \xkc} - \equiv - + \frac{1}{2} \frac{1}{\Delta y} \vat{\dintrpv{\uy}{x}}{\xic, \xjm, \xkc}, - -.. math:: - - u_{\xic, \xjc, \xkc} - \equiv - - \frac{1}{2} \frac{1}{\Delta y} \vat{\dintrpv{\uy}{x}}{\xic, \xjp, \xkc}, - -.. math:: - - c_{\xic, \xjc, \xkc} - \equiv - - - l_{\xic, \xjc, \xkc} - - - u_{\xic, \xjc, \xkc}. - -.. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: ux is transported by uy - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_z.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_z.rst deleted file mode 100644 index 5830d0ab..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/adv_z.rst +++ /dev/null @@ -1,61 +0,0 @@ - -.. _impl_adv_x_z: - -############################ -Advection of x momentum in z -############################ - -.. math:: - - - - \vat{ - \dintrpa{ - \dintrpv{\uz}{x} - \dder{\ux}{z} - }{z} - }{\xic, \xjc, \xkc} - = - & - - - \frac{1}{2} \vat{\dintrpv{\uz}{x}}{\xic, \xjc, \xkm} \vat{\dder{\ux}{z}}{\xic, \xjc, \xkm} \\ - & - - - \frac{1}{2} \vat{\dintrpv{\uz}{x}}{\xic, \xjc, \xkp} \vat{\dder{\ux}{z}}{\xic, \xjc, \xkp}, - -and thus - -.. math:: - - l_{\xic, \xjc, \xkc} {\ux}_{\xic, \xjc, \xkmm} - + - c_{\xic, \xjc, \xkc} {\ux}_{\xic, \xjc, \xkc } - + - u_{\xic, \xjc, \xkc} {\ux}_{\xic, \xjc, \xkpp}, - -where - -.. math:: - - l_{\xic, \xjc, \xkc} - \equiv - + \frac{1}{2} \frac{1}{\Delta z} \vat{\dintrpv{\uz}{x}}{\xic, \xjc, \xkm}, - -.. math:: - - u_{\xic, \xjc, \xkc} - \equiv - - \frac{1}{2} \frac{1}{\Delta z} \vat{\dintrpv{\uz}{x}}{\xic, \xjc, \xkp}, - -.. math:: - - c_{\xic, \xjc, \xkc} - \equiv - - - l_{\xic, \xjc, \xkc} - - - u_{\xic, \xjc, \xkc}. - -.. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: ux is transported by uz - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_x.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_x.rst deleted file mode 100644 index 0229979f..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_x.rst +++ /dev/null @@ -1,54 +0,0 @@ - -.. _impl_dif_x_x: - -############################ -Diffusion of x momentum in x -############################ - -.. math:: - - \vat{ - \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x} \dder{\ux}{x} - }{\xic, \xjc, \xkc}, - -which is the product of the diffusivity and - -.. math:: - - l_{\xic} {\ux}_{\ximm, \xjc, \xkc} - + - c_{\xic} {\ux}_{\xic , \xjc, \xkc} - + - u_{\xic} {\ux}_{\xipp, \xjc, \xkc}, - -where - -.. math:: - - l_{\xic} - \equiv - \frac{1}{\Delta x_{\xic}} \frac{1}{\Delta x_{\xim}}, - -.. math:: - - u_{\xic} - \equiv - \frac{1}{\Delta x_{\xic}} \frac{1}{\Delta x_{\xip}}, - -.. math:: - - c_{\xic} - \equiv - - - l_{\xic} - - - u_{\xic}. - -.. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: ux is diffused in x - -.. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: Laplacian w.r.t. ux in x - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_y.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_y.rst deleted file mode 100644 index c53cdc7d..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_y.rst +++ /dev/null @@ -1,54 +0,0 @@ - -.. _impl_dif_x_y: - -############################ -Diffusion of x momentum in y -############################ - -.. math:: - - \vat{ - \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{y} \dder{\ux}{y} - }{\xic, \xjc, \xkc}, - -which is the product of the diffusivity and - -.. math:: - - l {\ux}_{\xic, \xjmm, \xkc} - + - c {\ux}_{\xic, \xjc , \xkc} - + - u {\ux}_{\xic, \xjpp, \xkc}, - -where - -.. math:: - - l - \equiv - \frac{1}{\Delta y} \frac{1}{\Delta y}, - -.. math:: - - u - \equiv - \frac{1}{\Delta y} \frac{1}{\Delta y}, - -.. math:: - - c - \equiv - - - l - - - u. - -.. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: ux is diffused in y - -.. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: Laplacian in y - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_z.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_z.rst deleted file mode 100644 index 18b8e05d..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/dif_z.rst +++ /dev/null @@ -1,54 +0,0 @@ - -.. _impl_dif_x_z: - -############################ -Diffusion of x momentum in z -############################ - -.. math:: - - \vat{ - \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{z} \dder{\ux}{z} - }{\xic, \xjc, \xkc}, - -which is the product of the diffusivity and - -.. math:: - - l {\ux}_{\xic, \xjc, \xkmm} - + - c {\ux}_{\xic, \xjc, \xkc } - + - u {\ux}_{\xic, \xjc, \xkpp}, - -where - -.. math:: - - l - \equiv - \frac{1}{\Delta z} \frac{1}{\Delta z}, - -.. math:: - - u - \equiv - \frac{1}{\Delta z} \frac{1}{\Delta z}, - -.. math:: - - c - \equiv - - - l - - - u. - -.. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: ux is diffused in z - -.. myliteralinclude:: /../../src/fluid/integrate/ux.c - :language: c - :tag: Laplacian in z - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/main.rst deleted file mode 100644 index ff3c9076..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/x_momentum/main.rst +++ /dev/null @@ -1,57 +0,0 @@ -########################################## -:ref:`Momentum balance in x ` -########################################## - -Defined at :math:`\left( \xic, \xjc, \xkc \right)`: - -.. math:: - - \der{\ux}{t} - = - & - - - \dintrpv{ - \dintrpa{\ux}{x} - \dder{\ux}{x} - }{x} - - - \dintrpa{ - \dintrpv{\uy}{x} - \dder{\ux}{y} - }{y} - - - \dintrpa{ - \dintrpv{\uz}{x} - \dder{\ux}{z} - }{z} \\ - & - - - \dder{p}{x} \\ - & - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{\ux}{x} \right) - + - \dder{}{y} \left( \dder{\ux}{y} \right) - + - \dder{}{z} \left( \dder{\ux}{z} \right) - \right\} \\ - & - + - \dintrpa{T}{x}. - -.. toctree:: - :maxdepth: 1 - :caption: Implmentation - - adv_x - adv_y - adv_z - dif_x - dif_y - dif_z - -.. seealso:: - - * :ref:`fluid_compute_rhs ` - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_x.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_x.rst deleted file mode 100644 index 937563fd..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_x.rst +++ /dev/null @@ -1,92 +0,0 @@ - -.. _impl_adv_y_x: - -############################ -Advection of y momentum in x -############################ - -.. math:: - - - - \vat{ - \dintrpv{ - \dintrpa{\ux}{y} - \dder{\uy}{x} - }{x} - }{\yic, \yjc, \ykc} - = - & - - - \frac{1}{2} - \frac{\Delta x_{\yim}}{\Delta x_{\yic}} - \vat{ - \dintrpa{\ux}{y} - }{\yim, \yjc, \ykc} - \vat{ - \dder{\uy}{x} - }{\yim, \yjc, \ykc} \\ - & - - - \frac{1}{2} \frac{\Delta x_{\yip}}{\Delta x_{\yic}} - \vat{ - \dintrpa{\ux}{y} - }{\yip, \yjc, \ykc} - \vat{ - \dder{\uy}{x} - }{\yip, \yjc, \ykc}, - -and thus - -.. math:: - - l_{\yic, \yjc, \ykc} {\uy}_{\yimm, \yjc, \ykc} - + - c_{\yic, \yjc, \ykc} {\uy}_{\yic , \yjc, \ykc} - + - u_{\yic, \yjc, \ykc} {\uy}_{\yipp, \yjc, \ykc}, - -where - -.. math:: - - l_{\yic, \yjc, \ykc} - \equiv - + - \frac{1}{2} - \frac{\Delta x_{\yim}}{\Delta x_{\yic}} - \vat{\dintrpa{\ux}{y}}{\yim, \yjc, \ykc} - \frac{1}{\Delta x_{\yim}} - = - + - \frac{1}{2} - \frac{1}{\Delta x_{\yic}} - \vat{\dintrpa{\ux}{y}}{\yim, \yjc, \ykc}, - -.. math:: - - u_{\yic, \yjc, \ykc} - \equiv - - - \frac{1}{2} - \frac{\Delta x_{\yip}}{\Delta x_{\yic}} - \vat{\dintrpa{\ux}{y}}{\yip, \yjc, \ykc} - \frac{1}{\Delta x_{\yip}} - = - - - \frac{1}{2} - \frac{1}{\Delta x_{\yic}} - \vat{\dintrpa{\ux}{y}}{\yip, \yjc, \ykc}, - -.. math:: - - c_{\yic, \yjc, \ykc} - \equiv - - - l_{\yic, \yjc, \ykc} - - - u_{\yic, \yjc, \ykc}. - -.. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: uy is transported by ux - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_y.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_y.rst deleted file mode 100644 index 85459aca..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_y.rst +++ /dev/null @@ -1,83 +0,0 @@ - -.. _impl_adv_y_y: - -############################ -Advection of y momentum in y -############################ - -.. math:: - - - - \vat{ - \dintrpa{ - \dintrpa{\uy}{y} - \dder{\uy}{y} - }{y} - }{\yic, \yjc, \ykc} - = - & - - - \frac{1}{2} - \vat{ - \dintrpa{\uy}{y} - }{\yic, \yjm, \ykc} - \vat{ - \dder{\uy}{y} - }{\yic, \yjm, \ykc} \\ - & - - - \frac{1}{2} - \vat{ - \dintrpa{\uy}{y} - }{\yic, \yjp, \ykc} - \vat{ - \dder{\uy}{y} - }{\yic, \yjp, \ykc}, - -and thus - -.. math:: - - l_{\yic, \yjc, \ykc} {\uy}_{\yic, \yjmm, \ykc} - + - c_{\yic, \yjc, \ykc} {\uy}_{\yic, \yjc , \ykc} - + - u_{\yic, \yjc, \ykc} {\uy}_{\yic, \yjpp, \ykc}, - -where - -.. math:: - - l_{\yic, \yjc, \ykc} - \equiv - + - \frac{1}{2} - \frac{1}{\Delta y} - \vat{ - \dintrpa{\uy}{y} - }{\yic, \yjm, \ykc}, - -.. math:: - - u_{\yic, \yjc, \ykc} - \equiv - - - \frac{1}{2} - \frac{1}{\Delta y} - \vat{ - \dintrpa{\uy}{y} - }{\yic, \yjp, \ykc}, - -.. math:: - - c_{\yic, \yjc, \ykc} - \equiv - - - l_{\yic, \yjc, \ykc} - - - u_{\yic, \yjc, \ykc}. - -.. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: uy is transported by uy - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_z.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_z.rst deleted file mode 100644 index ad422a98..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/adv_z.rst +++ /dev/null @@ -1,83 +0,0 @@ - -.. _impl_adv_y_z: - -############################ -Advection of y momentum in z -############################ - -.. math:: - - - - \vat{ - \dintrpa{ - \dintrpa{\uz}{y} - \dder{\uy}{z} - }{z} - }{\yic, \yjc, \ykc} - = - & - - - \frac{1}{2} - \vat{ - \dintrpa{\uz}{y} - }{\yic, \yjc, \ykm} - \vat{ - \dder{\uy}{z} - }{\yic, \yjc, \ykm} \\ - & - - - \frac{1}{2} - \vat{ - \dintrpa{\uz}{y} - }{\yic, \yjc, \ykp} - \vat{ - \dder{\uy}{z} - }{\yic, \yjc, \ykp}, - -and thus - -.. math:: - - l_{\yic, \yjc, \ykc} {\uy}_{\yic, \yjc, \ykmm} - + - c_{\yic, \yjc, \ykc} {\uy}_{\yic, \yjc, \ykc } - + - u_{\yic, \yjc, \ykc} {\uy}_{\yic, \yjc, \ykpp}, - -where - -.. math:: - - l_{\yic, \yjc, \ykc} - \equiv - + - \frac{1}{2} - \frac{1}{\Delta y} - \vat{ - \dintrpa{\uz}{y} - }{\yic, \yjc, \ykm}, - -.. math:: - - u_{\yic, \yjc, \ykc} - \equiv - - - \frac{1}{2} - \frac{1}{\Delta z} - \vat{ - \dintrpa{\uz}{y} - }{\yic, \yjc, \ykp}, - -.. math:: - - c_{\yic, \yjc, \ykc} - \equiv - - - l_{\yic, \yjc, \ykc} - - - u_{\yic, \yjc, \ykc}. - -.. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: uy is transported by uz - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_x.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_x.rst deleted file mode 100644 index c48cc65b..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_x.rst +++ /dev/null @@ -1,54 +0,0 @@ - -.. _impl_dif_y_x: - -############################ -Diffusion of y momentum in x -############################ - -.. math:: - - \vat{ - \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x} \dder{\uy}{x} - }{\yic, \yjc, \ykc}, - -which is the product of the diffusivity and - -.. math:: - - l_{\yic} \vat{\uy}{\yimm, \yjc, \ykc} - + - c_{\yic} \vat{\uy}{\yic , \yjc, \ykc} - + - u_{\yic} \vat{\uy}{\yipp, \yjc, \ykc}, - -where - -.. math:: - - \vat{l}{\yic} - \equiv - \frac{1}{\vat{\Delta x}{\yic}} \frac{1}{\vat{\Delta x}{\yim}}, - -.. math:: - - \vat{u}{\yic} - \equiv - \frac{1}{\vat{\Delta x}{\yic}} \frac{1}{\vat{\Delta x}{\yip}}, - -.. math:: - - \vat{c}{\yic} - \equiv - - - \vat{l}{\yic} - - - \vat{u}{\yic}. - -.. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: uy is diffused in x - -.. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: Laplacian w.r.t. uy in x - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_y.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_y.rst deleted file mode 100644 index 3a3912ed..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_y.rst +++ /dev/null @@ -1,58 +0,0 @@ - -.. _impl_dif_y_y: - -############################ -Diffusion of y momentum in y -############################ - -.. math:: - - \vat{ - \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{y} \dder{\uy}{y} - }{\yic, \yjc, \ykc}, - -which is the product of the diffusivity and - -.. math:: - - l \vat{\uy}{\yic, \yjmm, \ykc} - + - c \vat{\uy}{\yic, \yjc , \ykc} - + - u \vat{\uy}{\yic, \yjpp, \ykc}, - -where - -.. math:: - - l - \equiv - \frac{1}{\Delta y} - \frac{1}{\Delta y} - -.. math:: - - u - \equiv - \frac{1}{\Delta y} - \frac{1}{\Delta y} - -and - -.. math:: - - c - \equiv - - - l - - - u. - -.. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: uy is diffused in y - -.. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: Laplacian in y - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_z.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_z.rst deleted file mode 100644 index 6d6a127a..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/dif_z.rst +++ /dev/null @@ -1,58 +0,0 @@ - -.. _impl_dif_y_z: - -############################ -Diffusion of y momentum in z -############################ - -.. math:: - - \vat{ - \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{z} \dder{\uy}{z} - }{\xic, \xjc, \xkc}, - -which is the product of the diffusivity and - -.. math:: - - l \vat{\uy}{\xic, \xjc, \xkmm} - + - c \vat{\uy}{\xic, \xjc, \xkc } - + - u \vat{\uy}{\xic, \xjc, \xkpp}, - -where - -.. math:: - - l - \equiv - \frac{1}{\Delta z} - \frac{1}{\Delta z} - -.. math:: - - u - \equiv - \frac{1}{\Delta z} - \frac{1}{\Delta z} - -and - -.. math:: - - c - \equiv - - - l - - - u. - -.. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: uy is diffused in z - -.. myliteralinclude:: /../../src/fluid/integrate/uy.c - :language: c - :tag: Laplacian in z - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/main.rst deleted file mode 100644 index a9cfe941..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/y_momentum/main.rst +++ /dev/null @@ -1,54 +0,0 @@ -########################################## -:ref:`Momentum balance in y ` -########################################## - -Defined at :math:`\left( \yic, \yjc, \ykc \right)`: - -.. math:: - - \der{\uy}{t} - = - & - - - \dintrpv{ - \dintrpa{\ux}{y} - \dder{\uy}{x} - }{x} - - - \dintrpa{ - \dintrpa{\uy}{y} - \dder{\uy}{y} - }{y} - - - \dintrpa{ - \dintrpa{\uz}{y} - \dder{\uy}{z} - }{z} \\ - & - - - \dder{p}{y} \\ - & - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{\uy}{x} \right) - + - \dder{}{y} \left( \dder{\uy}{y} \right) - + - \dder{}{z} \left( \dder{\uy}{z} \right) - \right\}. - -.. toctree:: - :maxdepth: 1 - :caption: Implmentation - - adv_x - adv_y - adv_z - dif_x - dif_y - dif_z - -.. seealso:: - - * :ref:`fluid_compute_rhs ` - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_x.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_x.rst deleted file mode 100644 index 88e51ce6..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_x.rst +++ /dev/null @@ -1,93 +0,0 @@ - -.. _impl_adv_z_x: - -############################ -Advection of z momentum in x -############################ - -.. math:: - - - - \vat{ - \dintrpv{ - \dintrpa{\ux}{z} - \dder{\uz}{x} - }{x} - }{\zic, \zjc, \zkc} - = - & - - - \frac{1}{2} - \frac{\Delta x_{\zim}}{\Delta x_{\zic}} - \vat{ - \dintrpa{\ux}{z} - }{\zim, \zjc, \zkc} - \vat{ - \dder{\uz}{x} - }{\zim, \zjc, \zkc} \\ - & - - - \frac{1}{2} - \frac{\Delta x_{\zip}}{\Delta x_{\zic}} - \vat{ - \dintrpa{\ux}{z} - }{\zip, \zjc, \zkc} - \vat{ - \dder{\uz}{x} - }{\zip, \zjc, \zkc}, - -and thus - -.. math:: - - l_{\zic, \zjc, \zkc} {\uz}_{\zimm, \zjc, \zkc} - + - c_{\zic, \zjc, \zkc} {\uz}_{\zic , \zjc, \zkc} - + - u_{\zic, \zjc, \zkc} {\uz}_{\zipp, \zjc, \zkc}, - -where - -.. math:: - - l_{\zic, \zjc, \zkc} - \equiv - + - \frac{1}{2} - \frac{\Delta x_{\zim}}{\Delta x_{\zic}} - \vat{\dintrpa{\ux}{z}}{\zim, \zjc, \zkc} - \frac{1}{\Delta x_{\zim}} - = - + - \frac{1}{2} - \frac{1}{\Delta x_{\zic}} - \vat{\dintrpa{\ux}{z}}{\zim, \zjc, \zkc}, - -.. math:: - - u_{\zic, \zjc, \zkc} - \equiv - - - \frac{1}{2} - \frac{\Delta x_{\zip}}{\Delta x_{\zic}} - \vat{\dintrpa{\ux}{z}}{\zip, \zjc, \zkc} - \frac{1}{\Delta x_{\zip}} - = - - - \frac{1}{2} - \frac{1}{\Delta x_{\zic}} - \vat{\dintrpa{\ux}{z}}{\zip, \zjc, \zkc}, - -.. math:: - - c_{\zic, \zjc, \zkc} - \equiv - - - l_{\zic, \zjc, \zkc} - - - u_{\zic, \zjc, \zkc}. - -.. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: uz is transported by ux - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_y.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_y.rst deleted file mode 100644 index 7f8b8c6c..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_y.rst +++ /dev/null @@ -1,77 +0,0 @@ - -.. _impl_adv_z_y: - -############################ -Advection of z momentum in y -############################ - -.. math:: - - - - \vat{ - \dintrpa{ - \dintrpa{\uy}{z} - \dder{\uz}{y} - }{y} - }{\zic, \zjc, \zkc} - = - & - - - \vat{ - \dintrpa{\uy}{z} - }{\zic, \zjm, \zkc} - \vat{ - \dder{\uz}{y} - }{\zic, \zjm, \zkc} \\ - & - - - \vat{ - \dintrpa{\uy}{z} - }{\zic, \zjp, \zkc} - \vat{ - \dder{\uz}{y} - }{\zic, \zjp, \zkc}, - -and thus - -.. math:: - - l_{\zic, \zjc, \zkc} {\uz}_{\zic, \zjmm, \zkc} - + - c_{\zic, \zjc, \zkc} {\uz}_{\zic, \zjc , \zkc} - + - u_{\zic, \zjc, \zkc} {\uz}_{\zic, \zjpp, \zkc}, - -where - -.. math:: - - l_{\zic, \zjc, \zkc} - \equiv - + - \frac{1}{2} - \frac{1}{\Delta y} - \vat{\dintrpa{\uy}{z}}{\zic, \zjm, \zkc}, - -.. math:: - - u_{\zic, \zjc, \zkc} - \equiv - - - \frac{1}{2} - \frac{1}{\Delta y} - \vat{\dintrpa{\uy}{z}}{\zic, \zjp, \zkc}, - -.. math:: - - c_{\zic, \zjc, \zkc} - \equiv - - - l_{\zic, \zjc, \zkc} - - - u_{\zic, \zjc, \zkc}. - -.. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: uz is transported by uy - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_z.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_z.rst deleted file mode 100644 index 57ff8c73..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/adv_z.rst +++ /dev/null @@ -1,77 +0,0 @@ - -.. _impl_adv_z_z: - -############################ -Advection of z momentum in z -############################ - -.. math:: - - - - \vat{ - \dintrpa{ - \dintrpa{\uz}{z} - \dder{\uz}{z} - }{z} - }{\zic, \zjc, \zkc} - = - & - - - \vat{ - \dintrpa{\uz}{z} - }{\zic, \zjc, \zkm} - \vat{ - \dder{\uz}{z} - }{\zic, \zjc, \zkm} \\ - & - - - \vat{ - \dintrpa{\uz}{z} - }{\zic, \zjc, \zkp} - \vat{ - \dder{\uz}{z} - }{\zic, \zjc, \zkp}, - -and thus - -.. math:: - - l_{\zic, \zjc, \zkc} {\uz}_{\zic, \zjc, \zkmm} - + - c_{\zic, \zjc, \zkc} {\uz}_{\zic, \zjc, \zkc } - + - u_{\zic, \zjc, \zkc} {\uz}_{\zic, \zjc, \zkpp}, - -where - -.. math:: - - l_{\zic, \zjc, \zkc} - \equiv - + - \frac{1}{2} - \frac{1}{\Delta z} - \vat{\dintrpa{\uz}{z}}{\zic, \zjc, \zkm}, - -.. math:: - - u_{\zic, \zjc, \zkc} - \equiv - - - \frac{1}{2} - \frac{1}{\Delta z} - \vat{\dintrpa{\uz}{z}}{\zic, \zjc, \zkp}, - -.. math:: - - c_{\zic, \zjc, \zkc} - \equiv - - - l_{\zic, \zjc, \zkc} - - - u_{\zic, \zjc, \zkc}. - -.. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: uz is transported by uz - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_x.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_x.rst deleted file mode 100644 index 22e78208..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_x.rst +++ /dev/null @@ -1,56 +0,0 @@ - -.. _impl_dif_z_x: - -############################ -Diffusion of z momentum in x -############################ - -.. math:: - - \vat{ - \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x} \dder{\uz}{x} - }{\zic, \zjc, \zkc}, - -which is the product of the diffusivity and - -.. math:: - - l_{\zic} \vat{\uz}{\zimm, \zjc, \zkc} - + - c_{\zic} \vat{\uz}{\zic , \zjc, \zkc} - + - u_{\zic} \vat{\uz}{\zipp, \zjc, \zkc}, - -where - -.. math:: - - \vat{l}{\zic} - \equiv - \frac{1}{\vat{\Delta x}{\zic}} - \frac{1}{\vat{\Delta x}{\zim}}, - -.. math:: - - \vat{u}{\zic} - \equiv - \frac{1}{\vat{\Delta x}{\zic}} - \frac{1}{\vat{\Delta x}{\zip}}, - -.. math:: - - \vat{c}{\zic} - \equiv - - - \vat{l}{\zic} - - - \vat{u}{\zic}. - -.. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: uz is diffused in x - -.. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: Laplacian w.r.t. uz in x - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_y.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_y.rst deleted file mode 100644 index 986b3c8a..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_y.rst +++ /dev/null @@ -1,54 +0,0 @@ - -.. _impl_dif_z_y: - -############################ -Diffusion of z momentum in y -############################ - -.. math:: - - \vat{ - \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{y} \dder{\uy}{y} - }{\zic, \zjc, \zkc}, - -which is the product of the diffusivity and - -.. math:: - - l \vat{\uy}{\zic, \zjmm, \zkc} - + - c \vat{\uy}{\zic, \zjc , \zkc} - + - u \vat{\uy}{\zic, \zjpp, \zkc}, - -where - -.. math:: - - l - \equiv - \frac{1}{\Delta y} - \frac{1}{\Delta y}, - -.. math:: - - u - \equiv - \frac{1}{\Delta y} - \frac{1}{\Delta y}, - -.. math:: - - c - \equiv - - l - - u. - -.. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: uz is diffused in y - -.. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: Laplacian in y - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_z.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_z.rst deleted file mode 100644 index ace0f3a3..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/dif_z.rst +++ /dev/null @@ -1,54 +0,0 @@ - -.. _impl_dif_z_z: - -############################ -Diffusion of z momentum in z -############################ - -.. math:: - - \vat{ - \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{z} \dder{\uz}{z} - }{\xic, \xjc, \xkc}, - -which is the product of the diffusivity and - -.. math:: - - l \vat{\uz}{\xic, \xjc, \xkmm} - + - c \vat{\uz}{\xic, \xjc, \xkc } - + - u \vat{\uz}{\xic, \xjc, \xkpp}, - -where - -.. math:: - - l - \equiv - \frac{1}{\Delta z} - \frac{1}{\Delta z}, - -.. math:: - - u - \equiv - \frac{1}{\Delta z} - \frac{1}{\Delta z}, - -.. math:: - - c - \equiv - - l - - u. - -.. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: uz is diffused in z - -.. myliteralinclude:: /../../src/fluid/integrate/uz.c - :language: c - :tag: Laplacian in z - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/main.rst deleted file mode 100644 index 696e0d0a..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/conclusion/z_momentum/main.rst +++ /dev/null @@ -1,54 +0,0 @@ -########################################## -:ref:`Momentum balance in z ` -########################################## - -Defined at :math:`\left( \zic, \zjc, \zkc \right)`: - -.. math:: - - \der{\uz}{t} - = - & - - - \dintrpv{ - \dintrpa{\ux}{z} - \dder{\uz}{x} - }{x} - - - \dintrpa{ - \dintrpa{\uy}{z} - \dder{\uz}{y} - }{y} - - - \dintrpa{ - \dintrpa{\uz}{z} - \dder{\uz}{z} - }{z} \\ - & - - - \dder{p}{z} \\ - & - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{\uz}{x} \right) - + - \dder{}{y} \left( \dder{\uz}{y} \right) - + - \dder{}{z} \left( \dder{\uz}{z} \right) - \right\}. - -.. toctree:: - :maxdepth: 1 - :caption: Implmentation - - adv_x - adv_y - adv_z - dif_x - dif_y - dif_z - -.. seealso:: - - * :ref:`fluid_compute_rhs ` - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/conservative_form.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/conservative_form.rst deleted file mode 100644 index 06966b00..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/conservative_form.rst +++ /dev/null @@ -1,44 +0,0 @@ - -.. _basic_operators_conservative: - -################# -Conservative form -################# - -To begin with, I provide a definition of the terminology *conservative form* in both the continuous and discrete domains. -For simplicity, I assume that the domain is one-dimensional - -.. math:: - - x \in \left[ 0, l_x \right], - -whose edges (:math:`x = 0`, :math:`x = l_x`) are bounded or periodic. - -In this simple set-up, i consider - -.. math:: - - \der{q}{x}. - -Integrating this term in the whole domain - -.. math:: - - \int_{x = 0}^{x = l_x} \der{q}{x} dx - -of course yields - -.. math:: - - \vat{q}{l_x} - \vat{q}{0}. - -This equation states that the change in :math:`q` is determined by the fluxes at the boundaries, and if the fluxes are zero (or the domain is periodic), the total amount of :math:`q` is *conserved*. - -Thus, in the continuous domain, I call the term is *conservative* (or in the *conservative form*) when it is written as - -.. math:: - - \der{q}{x_i}, - -where :math:`q` can be an arbitrary tensor of any order. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/differentiation_and_integral.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/differentiation_and_integral.rst deleted file mode 100644 index 9c409af1..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/differentiation_and_integral.rst +++ /dev/null @@ -1,167 +0,0 @@ -############################ -Differentiation and integral -############################ - -.. note:: - - Hereafter :math:`\der{q}{x}` (\\partial) and :math:`\dder{q}{x}` (\\delta) are used to distinguish the derivatives in the continuous and discrete domains, respectively. - -To mimic the property of the conservative form in the discrete domain, I should discretise the first-order derivatives as follows. - -****************************** -Quantity defined at cell faces -****************************** - -When the quantity is defined at cell faces, its first-order derivative should be defined at cell centers: - -.. math:: - - \vat{\dder{q}{x}}{\pic} - = - \frac{\vat{q}{\pip} - \vat{q}{\pim}}{\vat{x}{\pip} - \vat{x}{\pim}}. - -At the same time, the integral of a quantity which is defined at cell centers should be discretised as - -.. math:: - - \sum_{center} - \vat{q}{\pic} - \vat{\Delta x}{\pic}, - -or hereafter simply - -.. math:: - - \sum_{\pic} - q - \Delta x, - -where cell-centered grid sizes are used to approximate :math:`dx`. - -.. mydetails:: Derivation - - .. note:: - - Although the derivation below is so verbase, I show it here for completeness. - - I consider a quantity :math:`q`, whose derivative is defined at each cell center, i.e. - - .. math:: - - \vat{\dder{q}{x}}{\pic}. - - Integrating the term in the whole domain discretely yields - - .. math:: - - \int \der{q}{x} dx - \approx - \sum_{i} \vat{ - \left( - \dder{q}{x} \Delta x - \right) - }{\pic}. - - Since :math:`\vat{\Delta x}{\pic}` denotes the cell size defined at each cell center, it is clearly - - .. math:: - - \vat{\Delta x}{\pic} - = - \vat{x}{\pip} - - - \vat{x}{\pim}. - - Now I consider to *mimic* the relation which is satisfied in the continuous domain: namely, the volume integral of this term should be given by the boundary values. - To do so, obviously the denominator of the gradient :math:`\delta x` should be equal to the size of the cell :math:`\vat{\Delta x}{\pic}`, giving - - .. math:: - - \vat{\delta x}{\pic} - = - \vat{x}{\pip} - - - \vat{x}{\pim}. - - Also, it is natural to use the same scheme for the numerator :math:`\delta q` since they have the same form: - - .. math:: - - \vat{\delta q}{\pic} - = - \vat{q}{\pip} - - - \vat{q}{\pim}. - - Thus, I conclude that - - .. math:: - - \vat{\dder{q}{x}}{\pic} - - should be described as - - .. math:: - - \frac{\vat{q}{\pip} - \vat{q}{\pim}}{\vat{x}{\pip} - \vat{x}{\pim}}. - -******************************** -Quantity defined at cell centers -******************************** - -When the quantity is defined at cell centers (in general, see below), its first-order derivative should be defined at cell faces: - -.. math:: - - \vat{\dder{q}{x}}{\pip} - = - \frac{\vat{q}{\pipp} - \vat{q}{\pic}}{\vat{x}{\pipp} - \vat{x}{\pic}}. - -At the same time, the integral of a quantity which is defined at cell faces should be discretised as - -.. math:: - - \sum_{face} - \vat{q}{\pip} - \vat{\Delta x}{\pip}, - -or hereafter simply - -.. math:: - - \sum_{\pip} - q - \Delta x, - -where cell-faced grid sizes are used to approximate :math:`dx`. - -The derivation is omitted since it is similar to the one for the cell-centered quantity discussed above. - -.. note:: - - In the vicinity of the boundaries, the cell-centered quantities are defined on the boundaries, i.e. at the cell-face positions. - Please refer to :ref:`the domain set-up `. - -*********************************************** -Inconsistent (non-conservative) differentiation -*********************************************** - -Sometimes the first-order derivative at cell center is given by - -.. math:: - - \frac{\vat{q}{\pipp} - \vat{q}{\pimm}}{\vat{x}{\pipp} - \vat{x}{\pimm}}, - -i.e. using the neighbouring cell-center values to evaluate the differentiation at a cell center. - -To keep the relation between the differentiation and the integral, the volume integral should be approximated correspondingly: - -.. math:: - - \int q dx - \approx - \sum q \left( \vat{x}{\pipp} - \vat{x}{\pimm} \right). - -This does not guarantee the conservative nature, since the neighbouring :math:`q` (e.g. :math:`q_{\pic}` and :math:`q_{\pipp}`) are not related and thus the bulk values do not cancel out to each other. -I should avoid using this kind of inconsistent schemes. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/interpolation.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/interpolation.rst deleted file mode 100644 index ac09f52c..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/interpolation.rst +++ /dev/null @@ -1,83 +0,0 @@ - -.. _basic_operators_interpolation: - -############# -Interpolation -############# - -Because of :ref:`the staggered grid arrangement `, variables are sometimes not defined at the locations I want. -In such cases, I need to interpolate the variables, which is discussed here. - -Three different symbols are used in this project, whose superscripts indicate the direction of the interpolation. - -***************************************** -Arithmetic average :math:`\dintrpa{q}{x}` -***************************************** - -This is the most intuitive averaging. - -============================= -From cell-center to cell-face -============================= - -.. math:: - - 2 \vat{\dintrpa{q}{x}}{\xic} - \equiv - \vat{q}{\xim} - + - \vat{q}{\xip}. - -============================= -From cell-face to cell-center -============================= - -.. math:: - - 2 \vat{\dintrpa{q}{x}}{\pic} - \equiv - \vat{q}{\pim} - + - \vat{q}{\pip}. - -************************************* -Volume average :math:`\dintrpv{q}{x}` -************************************* - -This is the average using the cell sizes as weights. - -============================= -From cell-center to cell-face -============================= - -.. math:: - - 2 \Delta x_{\xic} \vat{\dintrpv{q}{x}}{\xic} - \equiv - \Delta x_{\xim} \vat{q}{\xim} - + - \Delta x_{\xip} \vat{q}{\xip}. - -============================= -From cell-face to cell-center -============================= - -.. math:: - - 2 \Delta x_{\pic} \vat{\dintrpv{q}{x}}{\pic} - \equiv - \Delta x_{\pim} \vat{q}{\pim} - + - \Delta x_{\pip} \vat{q}{\pip}. - -.. note:: - - :math:`\dintrpv{q}{}` is identical to :math:`\dintrpa{q}{}` if it is used in the homegeneous (:math:`y` or :math:`z`) directions, since I assume that the grid points are equidistantly spaced. - -********************************** -Placeholder :math:`\dintrpu{q}{x}` -********************************** - -This symbol indicates that it does not have an explicit formulation yet, i.e. I do not know anything except it is interpolated in the given direction (prefix ``u`` implies unknown). -This is used as a placeholder, which must be replaced by one of the above two averages. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/main.rst deleted file mode 100644 index 93de20d5..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/basic_operators/main.rst +++ /dev/null @@ -1,16 +0,0 @@ - -.. _basic_operators: - -############### -Basic operators -############### - -Several basic discrete operators are derived and defined in this part. - -.. toctree:: - :maxdepth: 1 - - conservative_form - differentiation_and_integral - interpolation - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/incompressibility_and_poisson_equation.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/incompressibility_and_poisson_equation.rst deleted file mode 100644 index 5497fe4e..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/incompressibility_and_poisson_equation.rst +++ /dev/null @@ -1,168 +0,0 @@ - -.. _incompressibility_and_poisson_equation: - -###################################### -Incompressibility and Poisson equation -###################################### - -******** -Overview -******** - -In this part, I consider to discretise the incompressibility constraint - -.. math:: - - \der{u_i}{x_i} = 0 - -at the cell center :math:`\left( \pic, \pjc \right)`. - -Also the discretisation of the Poisson equation - -.. math:: - - \der{}{x_i} \der{\psi}{x_i} - = - \frac{1}{\Delta t} \der{u_i^*}{x_i} - -is derived. - -.. seealso:: - - Please refer to :ref:`the SMAC method ` to see why the Poisson equation appears. - -************************************* -Discrete incompressibility constraint -************************************* - -The incompressibility constraint at cell center :math:`\left( \pic, \pjc \right)` is discretised as - -.. math:: - - \dder{\ux}{x} - + - \dder{\uy}{y} - = - 0. - -.. mydetails:: Derivations - - As discussed in :ref:`the grid arrangement `, in this project, cell centers (where the pressure and the temperature are defined) are positioned in the middle of the surrounding cell faces (where the velocities are defined), i.e. - - .. math:: - - \vat{x}{\pic} - & = - \frac{1}{2} \vat{x}{\pip} - + - \frac{1}{2} \vat{x}{\pim}, \\ - \vat{y}{\pjc} - & = - \frac{1}{2} \vat{y}{\pjp} - + - \frac{1}{2} \vat{y}{\pjm}. - - Since the velocities are defined at the cell faces, it is natural to describe the incompressibility constraint at cell centers: - - .. math:: - - \der{u_i}{x_i} \left( x, y \right) - \approx - \vat{\dder{u_i}{x_i}}{\pic,\pjc} - = - \frac{\vat{\ux}{\pip} - \vat{\ux}{\pim}}{\vat{x}{\pip} - \vat{x}{\pim}} - + - \frac{\vat{\uy}{\pjp} - \vat{\uy}{\pjm}}{\vat{y}{\pjp} - \vat{y}{\pjm}} - = - 0, - - giving the discrete incompressibility constraint defined at the cell centers. - - .. note:: - - The continuity is *only* valid at each cell center. - This is clearly different from the continuous domain, where all infinitesimal control volumes obey it. - -************************* -Discrete Poisson equation -************************* - -The discrete Poisson equation at :math:`\left( \pic, \pjc \right)` is given by - -.. math:: - - \dder{}{x} \left( - \dder{\psi}{x} - \right) - + - \dder{}{y} \left( - \dder{\psi}{y} - \right) - = - \cdots. - -.. mydetails:: Derivations - - As discussed in :ref:`the temporal discretisation `, the continuity is enforced by the correction: - - .. math:: - - u_i^{n+1} - = - u_i^{*} - - - \der{\psi}{x_i} \Delta t. - - This relation holds on the cell faces: - - .. math:: - - \vat{\ux^{n+1}}{\pip} &= \vat{\ux^*}{\pip} - \vat{\der{\psi}{x}}{\pip} \Delta t, \\ - \vat{\ux^{n+1}}{\pim} &= \vat{\ux^*}{\pim} - \vat{\der{\psi}{x}}{\pim} \Delta t, \\ - \vat{\uy^{n+1}}{\pjp} &= \vat{\uy^*}{\pjp} - \vat{\der{\psi}{y}}{\pjp} \Delta t, \\ - \vat{\uy^{n+1}}{\pjm} &= \vat{\uy^*}{\pjm} - \vat{\der{\psi}{y}}{\pjm} \Delta t, - - where :math:`\psi` is a scalar potential which projects the non-solenoidal velocity field to the solenoidal one. - Assigning this four equations to the discrete incompressibility constraint, I have - - .. math:: - - \frac{ - \vat{\der{\psi}{x}}{\pip} - - \vat{\der{\psi}{x}}{\pim} - }{ - \vat{x}{\pip} - - \vat{x}{\pim} - } - + - \frac{ - \vat{\der{\psi}{y}}{\pjp} - - \vat{\der{\psi}{y}}{\pjm} - }{ - \vat{y}{\pjp} - - \vat{y}{\pjm} - } - = - \frac{1}{\Delta t} - \frac{\vat{\ux^*}{\pip} - \vat{\ux^*}{\pim}}{\vat{x}{\pip} - \vat{x}{\pim}} - + - \frac{\vat{\uy^*}{\pjp} - \vat{\uy^*}{\pjm}}{\vat{y}{\pjp} - \vat{y}{\pjm}}, - - which is the discrete pressure equation - - .. math:: - - \dder{}{x_i} \dder{\psi}{x_i} - = - \frac{1}{\Delta t} \dder{u_i^*}{x_i}. - - .. note:: - - When the grid sizes are not uniform, the above discretisation is different from the second-order-accurate approximation of the Taylor series expansion of - - .. math:: - - \der{}{x_i} \der{\psi}{x_i}. - - If one uses this, the incompressibility constraint is not satisfied. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/divergence_form.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/divergence_form.rst deleted file mode 100644 index 84cff46c..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/divergence_form.rst +++ /dev/null @@ -1,69 +0,0 @@ -############### -Divergence form -############### - -******* -Summary -******* - -I find the discrete form of the advection of :math:`T` is given by - -.. math:: - - \dder{\ux \dintrpu{T}{x}}{x} - + - \dder{\uy \dintrpa{T}{y}}{y}, - -where :math:`\dintrpu{T}{x}` has not been defined yet. - -********** -Derivation -********** - -As derived in :ref:`the governing equations `, the advection of the temperature is described as - -.. math:: - - u_i \der{T}{x_i}, - -which is the gradient form of the advective terms. -Thanks to the incompressibility constraint, I can make this term conservative: - -.. math:: - - u_i \der{T}{x_i} - + - T \der{u_i}{x_i} - = - \der{u_i T}{x_i}, - -whose right-hand-side term is known as the divergence form. - -Since the divergence form is inherently conservative, I obtain a discrete form of the advective term as - -.. math:: - - \dder{u_i T}{x_i} - = - \dder{\ux \dintrpu{T}{x}}{x} - + - \dder{\uy \dintrpu{T}{y}}{y}, - -which is defined at the cell center :math:`\left( \pic, \pjc \right)`. - -.. note:: - - Interpolations are not necessary for the velocities :math:`\ux` and :math:`\uy` since they are already there. - -Because of the uniform grid spacings in the :math:`y` direction, I can replace the interpolation in the :math:`y` direction with the arithmetic average: - -.. math:: - - \dder{u_i T}{x_i} - = - \dder{\ux \dintrpu{T}{x}}{x} - + - \dder{\uy \dintrpa{T}{y}}{y}. - -To complete the interpolation in the :math:`x` direction, I need to investigate the advection of the quadratic quantity. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/gradient_form.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/gradient_form.rst deleted file mode 100644 index 4f07a04c..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/gradient_form.rst +++ /dev/null @@ -1,212 +0,0 @@ -############# -Gradient form -############# - -******* -Summary -******* - -I find that the gradient form of the advective terms is - -.. math:: - - \dintrpv{ - \ux - \dder{T}{x} - }{x} - + - \dintrpa{ - \uy - \dder{T}{y} - }{y}. - -********** -Derivation -********** - -Analogous to the advective terms in the momentum transfer, the divergence and the gradient forms should be identical to each other via the incompressibility constraint, i.e. - -.. math:: - - u_i \dder{T}{x_i} - = - \dder{u_i T}{x_i} - - - T \dder{u_i}{x_i}, - -which is defined at the cell center :math:`\left( \pic, \pjc \right)`. - -Different from the momentumm advection, I do not have to interpolate the incompressibility constraint since it is located at the same position as the temperature. - -Thus the gradient form leads - -.. math:: - - \ux \dder{T}{x} - & = - \dder{\ux T}{x} - - - T \dder{\ux}{x} \\ - & = - \dder{ - \ux - \dintrpa{T}{x} - }{x} - - T \dder{ - \ux - }{x} \\ - & = - \frac{ - \vat{\ux}{\pip, \pjc} - \frac{ - \vat{T}{\pipp, \pjc} - + - \vat{T}{\pic, \pjc} - }{2} - - - \vat{\ux}{\pim, \pjc} - \frac{ - \vat{T}{\pic, \pjc} - + - \vat{T}{\pimm, \pjc} - }{2} - }{\Delta x_{\pic}} - - - \vat{T}{\pic, \pjc} \frac{ - \vat{\ux}{\pip, \pjc} - - - \vat{\ux}{\pim, \pjc} - }{\Delta x_{\pic}} \\ - & = - \vat{\ux}{\pip, \pjc} - \frac{1}{\Delta x_{\pic}} - \frac{ - \vat{ - \diffe{T}{x} - }{\pip, \pjc} - }{2} - + - \vat{\ux}{\pim, \pjc} - \frac{1}{\Delta x_{\pic}} - \frac{ - \vat{ - \diffe{T}{x} - }{\pim, \pjc} - }{2} \\ - & = - \frac{\Delta x_{\pip}}{2 \Delta x_{\pic}} \vat{ - \left( - \ux - \dder{T}{x} - \right) - }{\pip, \pjc} - + - \frac{\Delta x_{\pim}}{2 \Delta x_{\pic}} \vat{ - \left( - \ux - \dder{T}{x} - \right) - }{\pim, \pjc} \\ - & = - \vat{C}{\pip} \vat{ - \left( - \ux - \dder{T}{x} - \right) - }{\pip, \pjc} - + - \vat{C}{\pim} \vat{ - \left( - \ux - \dder{T}{x} - \right) - }{\pim, \pjc} \\ - & = - \color{red}{ - \vat{ - \dintrpv{ - \ux - \dder{T}{x} - }{x} - }{\pic, \pjc} - } - -and - -.. math:: - - \uy \dder{T}{y} - & = - \dder{\uy T}{y} - - - T \dder{\uy}{y} \\ - & = - \dder{ - \uy - \dintrpa{T}{y} - }{y} - - T \dder{ - \uy - }{y} \\ - & = - \frac{ - \vat{\uy}{\pic, \pjp} - \frac{ - \vat{T}{\pic, \pjpp} - + - \vat{T}{\pic, \pjc} - }{2} - - - \vat{\uy}{\pic, \pjm} - \frac{ - \vat{T}{\pic, \pjc} - + - \vat{T}{\pic, \pjmm} - }{2} - }{\Delta y} - - - \vat{T}{\pic, \pjc} \frac{ - \vat{\uy}{\pic, \pjp} - - - \vat{\uy}{\pic, \pjm} - }{\Delta y} \\ - & = - \vat{\uy}{\pic, \pjp} - \frac{1}{\Delta y} - \frac{ - \vat{ - \diffe{T}{y} - }{\pic, \pjp} - }{2} - + - \vat{\uy}{\pic, \pjm} - \frac{1}{\Delta y} - \frac{ - \vat{ - \diffe{T}{y} - }{\pic, \pjm} - }{2} \\ - & = - \frac{1}{2} \vat{ - \left( - \uy - \dder{T}{y} - \right) - }{\pic, \pjp} - + - \frac{1}{2} \vat{ - \left( - \uy - \dder{T}{y} - \right) - }{\pic, \pjm} \\ - & = - \color{red}{ - \vat{ - \dintrpa{ - \uy - \dder{T}{y} - }{y} - }{\pic, \pjc} - }. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/main.rst deleted file mode 100644 index 4feb6bda..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/main.rst +++ /dev/null @@ -1,14 +0,0 @@ -############### -Advective terms -############### - -In the continuous domain, the advective terms transport the quantity but do not increase nor decrease the amount of it. -My aim here is to discretise the advective terms which have this character with respect to the internal energy and the quadratic quantities. - -.. toctree:: - :maxdepth: 1 - - divergence_form - quadratic_quantities - gradient_form - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/quadratic_quantities.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/quadratic_quantities.rst deleted file mode 100644 index f371f583..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/advective_terms/quadratic_quantities.rst +++ /dev/null @@ -1,173 +0,0 @@ -#################### -Quadratic quantities -#################### - -******* -Summary -******* - -I find the discrete form of the advection of :math:`T` is given by - -.. math:: - - \dder{\ux \dintrpa{T}{x}}{x} - + - \dder{\uy \dintrpa{T}{y}}{y}, - -where the interpolation in the :math:`x` direction which was undefined is explicitly given. - -********** -Derivation -********** - -I consider the advection of the quadratic quantity :math:`h`. -As shown in :ref:`the governing equations `, this is obtained by multiplying the advective terms of the internal energy advection by :math:`T`. -The discrete counterpart is twofold, - -.. math:: - - T \dder{\ux \dintrpu{T}{x}}{x} - = - \color{blue}{ - \vat{ - T - }{\pic, \pjc} - } - \frac{ - \color{blue}{ - \vat{\ux}{\pip, \pjc} - } - \left( - \color{blue}{ - \vat{c^+}{\pipp} \vat{T}{\pipp, \pjc} - } - + - \vat{c^+}{\pic } \vat{T}{\pic, \pjc} - \right) - - - \color{blue}{ - \vat{\ux}{\pim, \pjc} - } - \left( - \vat{c^-}{\pic } \vat{T}{\pic, \pjc} - + - \color{blue}{ - \vat{c^-}{\pimm} \vat{T}{\pimm, \pjc} - } - \right) - }{\Delta x_{\pic}} - -and - -.. math:: - - T \dder{\uy \dintrpu{T}{y}}{y} - = - \vat{ - T - }{\pic, \pjc} - \frac{ - \vat{\uy}{\pic, \pjp} - \frac{ - \vat{T}{\pic, \pjpp} - + - \vat{T}{\pic, \pjc } - }{2} - - - \vat{\uy}{\pic, \pjm} - \frac{ - \vat{T}{\pic, \pjc } - + - \vat{T}{\pic, \pjmm} - }{2} - }{\Delta y}, - -where small letters :math:`c` are coefficients to be determined. - -To keep the net amount of :math:`h`, I enforce them to be conservative. -The second part yields - -.. math:: - - \dder{\uy q_y}{y} - + - \frac{ - \vat{T^2}{\pic, \pjc} - }{2} - \dder{\uy}{y}, - -where :math:`q_y` is the quadratic quantity, defined as the product of the surrounding :math:`T` at cell faces where :math:`\uy` are located, e.g. - -.. math:: - - \vat{q_y}{\pip, \pjc} - \equiv - \frac{1}{2} - \vat{T}{\pic, \pjc} - \vat{T}{\pipp, \pjc} - -at :math:`\left( \pip, \pjc \right)`. - -Since the first term is in a conservative form, the volume integral vanishes, while the second part is the residual, which is generally non-zero. - -Similarly, I have the following two terms in the :math:`x` component. - -================== -Quadratic quantity -================== - -To be consistent with the :math:`y` component, the bluish terms should include the other quadratic quantity :math:`q_x` and I expect them to be conservative. -To achieve this, I request the coefficients :math:`\vat{c^+}{\pipp}` and :math:`\vat{c^-}{\pimm}` to be :math:`1/2`. - -======== -Residual -======== - -The second part leads to - -.. math:: - - \vat{T^2}{\pic, \pjc} - \frac{ - \vat{c^+}{\pic } - \vat{\ux}{\pim, \pjc} - - - \vat{c^-}{\pic } - \vat{\ux}{\pim, \pjc} - }{\Delta x_{\pic}}. - -This should be canceled out with the :math:`y` residual: - -.. math:: - - \frac{ - \vat{T^2}{\pic, \pjc} - }{2} - \dder{\uy}{y}, - -concluding that the two coefficients :math:`\vat{c^+}{\pic }` and :math:`\vat{c^-}{\pic }` to be :math:`1/2`. - -In summary, all undefined coefficients are :math:`1/2` and thus I find :math:`\dintrpu{T}{x}` is the arithmetic average :math:`\dintrpa{T}{x}`. - -Finally I obtain the advective terms in divergence form: - -.. math:: - - \dder{\ux T}{x} - + - \dder{\uy T}{y} - = - \color{red}{ - \dder{ - \ux - \dintrpa{T}{x} - }{x} - + - \dder{ - \uy - \dintrpa{T}{y} - }{y} - } - -defined at the cell center :math:`\left( \pic, \pjc \right)`. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/diffusive_terms.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/diffusive_terms.rst deleted file mode 100644 index 77065557..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/diffusive_terms.rst +++ /dev/null @@ -1,359 +0,0 @@ - -.. _derivation_internal_diffusive_terms: - -############### -Diffusive terms -############### - -Diffusive terms in the internal energy equation diffuse the internal energy in all directions. -They also play a crucial role in diffusing and dissipating the quadratic quantity. - -******************************** -Diffusion of the internal energy -******************************** - -Since the diffusive terms are linear (and thus I can treat them implicitly in time, see :ref:`the temporal discretisation `), I can simply discretise them as - -.. math:: - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{T}{x} \right) - + - \dder{}{y} \left( \dder{T}{y} \right) - \right\} - -at :math:`\left( \pic, \pjc \right)` where :math:`T` is defined. -All necessary first-order differentiations are defined, so no interpolation is required. - -*************************************************** -Diffusion and dissipation of the quadratic quantity -*************************************************** - -In :ref:`the governing equations `, I considered the product of :math:`T` and the diffusive terms of the internal energy equation to derive the relation of the diffusion and the dissipation of :math:`h`: - -.. math:: - - T \der{}{x_i} \left( \der{T}{x_i} \right) - = - \der{}{x_i} \left( T \der{T}{x_i} \right) - - - \der{T}{x_i} \der{T}{x_i}, - -where the pre-factor (the diffusivity) :math:`1 / \sqrt{Pr} \sqrt{Ra}` are dropped for convenience. - -Here I consider the discrete counterpart. - -=================== -Left-hand-side term -=================== - -.. math:: - - T - \left\{ - \dder{}{x} \left( \dder{T}{x} \right) - + - \dder{}{y} \left( \dder{T}{y} \right) - \right\} - & = - \frac{ - \vat{T}{\pic, \pjc} - \vat{ - \dder{T}{x} - }{\pip, \pjc} - - - \vat{T}{\pic, \pjc} - \vat{ - \dder{T}{x} - }{\pim, \pjc} - }{\Delta x_{\pic}} \\ - & + - \frac{ - \vat{T}{\pic, \pjc} - \vat{ - \dder{T}{y} - }{\pic, \pjp} - - - \vat{T}{\pic, \pjc} - \vat{ - \dder{T}{y} - }{\pic, \pjm} - }{\Delta y}. - -===================================== -The first term on the right-hand side -===================================== - -.. math:: - - \dder{}{x} \left( T \dder{T}{x} \right) - + - \dder{}{y} \left( T \dder{T}{y} \right) - & = - \frac{ - \vat{ - \dintrpa{T}{x} - }{\pip, \pjc} - \vat{ - \dder{T}{x} - }{\pip, \pjc} - - - \vat{ - \dintrpa{T}{x} - }{\pim, \pjc} - \vat{ - \dder{T}{x} - }{\pim, \pjc} - }{\Delta x_{\pic}} \\ - & + - \frac{ - \vat{ - \dintrpa{T}{y} - }{\pic, \pjp} - \vat{ - \dder{T}{y} - }{\pic, \pjp} - - - \vat{ - \dintrpa{T}{y} - }{\pic, \pjm} - \vat{ - \dder{T}{y} - }{\pic, \pjm} - }{\Delta y}. - -.. note:: - - For the ease of the later discussion, I keep the above form. - However, I can further manipulate it to confirm that it is equal to - - .. math:: - - \dder{}{x} \dder{}{x} \left( \frac{1}{2} T^2 \right) - + - \dder{}{y} \dder{}{y} \left( \frac{1}{2} T^2 \right), - - which is the discrete diffusive terms of the squared temperature :math:`T^2 / 2`. - It is readily apparent that it is discretely conservative and thus does not contribute to the net change of the squared temperature, which is consistent with the continuous counterpart. - -================ -Dissipative term -================ - -Subtracting the first equation from the second one yields - -.. math:: - - & - \frac{ - \left( - \vat{ - \dintrpa{T}{x} - }{\pip, \pjc} - - - \vat{T}{\pic, \pjc} - \right) - \vat{ - \dder{T}{x} - }{\pip, \pjc} - - - \left( - \vat{ - \dintrpa{T}{x} - }{\pim, \pjc} - - - \vat{T}{\pic, \pjc} - \right) - \vat{ - \dder{T}{x} - }{\pim, \pjc} - }{\Delta x_{\pic}} \\ - + & - \frac{ - \left( - \vat{ - \dintrpa{T}{y} - }{\pic, \pjp} - - - \vat{T}{\pic, \pjc} - \right) - \vat{ - \dder{T}{y} - }{\pic, \pjp} - - - \left( - \vat{ - \dintrpa{T}{y} - }{\pic, \pjm} - - - \vat{T}{\pic, \pjc} - \right) - \vat{ - \dder{T}{y} - }{\pic, \pjm} - }{\Delta y} \\ - = & - \frac{1}{2} - \frac{1}{\Delta x_{\pic}} - \vat{C}{\pip} - \vat{ - \diffe{T}{x} - }{\pip, \pjc} - \vat{ - \dder{T}{x} - }{\pip, \pjc} - + - \frac{1}{2} - \frac{1}{\Delta x_{\pic}} - \vat{C}{\pim} - \vat{ - \diffe{T}{x} - }{\pim, \pjc} - \vat{ - \dder{T}{x} - }{\pim, \pjc} - \\ - + & - \frac{1}{2} - \frac{1}{\Delta y} - \vat{ - \diffe{T}{y} - }{\pic, \pjp} - \vat{ - \dder{T}{y} - }{\pic, \pjp} - + - \frac{1}{2} - \frac{1}{\Delta y} - \vat{ - \diffe{T}{y} - }{\pic, \pjm} - \vat{ - \dder{T}{y} - }{\pic, \pjm} \\ - = & - \frac{1}{2} - \frac{\Delta x_{\pip}}{\Delta x_{\pic}} - \vat{C}{\pip} - \left( - \vat{ - \dder{T}{x} - }{\pip, \pjc} - \right)^2 - + - \frac{1}{2} - \frac{\Delta x_{\pim}}{\Delta x_{\pic}} - \vat{C}{\pim} - \left( - \vat{ - \dder{T}{x} - }{\pim, \pjc} - \right)^2 - \\ - + & - \frac{1}{2} - \frac{1}{\Delta y} - \vat{ - \diffe{T}{y} - }{\pic, \pjp} - \vat{ - \dder{T}{y} - }{\pic, \pjp} - + - \frac{1}{2} - \frac{1}{\Delta y} - \vat{ - \diffe{T}{y} - }{\pic, \pjm} - \vat{ - \dder{T}{y} - }{\pic, \pjm} \\ - = & - \color{red}{ - \dintrpv{ - C \left( \dder{T}{x} \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{T}{y} \right)^2 - }{y} - }. - -Here :math:`C` is a coefficient to correct the boundary values: - -.. math:: - - C - = - \begin{cases} - \text{wall} & 2 \\ - \text{otherwise} & 1 - \end{cases}. - -This is necessary because :math:`T` is defined on the walls and the value is directly used rather than being interpolated. - -Now I consider to discretely integrate this equation in the volume. -The dissipative term simply yields - -.. math:: - - \sum_{\pjc} \sum_{\pic} - \left[ - \dintrpv{ - C \left( \dder{T}{x} \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{T}{y} \right)^2 - }{y} - \right] - \Delta x \Delta y, - -while the diffusive term yields - -.. math:: - - \sum_{\pjc} \sum_{\pic} - \left[ - \dder{}{x} \left( T \dder{T}{x} \right) - + - \dder{}{y} \left( T \dder{T}{y} \right) - \right] - \Delta x \Delta y - = - \sum_{\pjc} - \left( - \vat{ - T \dder{T}{x} - }{x = 1} - - - \vat{ - T \dder{T}{x} - }{x = 0} - \right) - \Delta y, - -which represents the exchange of the squared temperature through the diffusive process. - -.. seealso:: - - The reddish terms are computed to check :ref:`the instantaneous Nusselt number `. - -.. note:: - - A more intuitive way to discretise :math:`\der{T}{y}` at :math:`\left( \pic, \pjc \right)` might be - - .. math:: - - \frac{ - \vat{T}{\pic, \pjpp} - - - \vat{T}{\pic, \pjmm} - }{2 \Delta y}, - - and a similar way in the :math:`x` direction. - - This is :ref:`not conservative `, and is clearly different from what is derived in this section. - As a consequence the energy consistency is broken. - In particular, the dissipation tends to be underestimated, since the high frequencies are smoothened by the wider stencil. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/introduction.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/introduction.rst deleted file mode 100644 index 303a03f3..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/introduction.rst +++ /dev/null @@ -1,30 +0,0 @@ -In this part, I consider to discretise - -.. math:: - - \der{T}{t} - + - u_i \der{T}{x_i} - = - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{}{x_i} \der{T}{x_i} - -at the cell center :math:`\left( \pic, \pjc, \pkc \right)`. - -Also the balance of the quadratic quantity :math:`h \equiv T^2 / 2`: - -.. math:: - - \der{h}{t} - + - u_i \der{h}{x_i} - = - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{}{x_i} \left( T \der{T}{x_i} \right) - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{T}{x_i} \der{T}{x_i} - -is considered. - -.. note:: - - In the continuous domain, I obtain this equation by multiplying the temperature :math:`T` and the equation of the thermal energy balance, which is derived in :ref:`the governing equations `. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/main.rst deleted file mode 100644 index 17df8c4a..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/main.rst +++ /dev/null @@ -1,31 +0,0 @@ - -.. _internal_energy_balance: - -####################### -Internal energy balance -####################### - -************ -Introduction -************ - -.. include:: introduction.rst - -******* -Summary -******* - -.. include:: summary.rst - -********** -Derivation -********** - -In this part, I focus on how each term should be discretised to make things consistent, namely to mimic the global (integrated in the volume) properties of the equations. - -.. toctree:: - :maxdepth: 1 - - advective_terms/main - diffusive_terms - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/summary.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/summary.rst deleted file mode 100644 index 5fe9d3b6..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/internal_energy_balance/summary.rst +++ /dev/null @@ -1,28 +0,0 @@ -The following discrete equation is directly implemented in the code. - -.. math:: - - \der{T}{t} - + - \dintrpv{ - \ux - \dder{T}{x} - }{x} - + - \dintrpa{ - \uy - \dder{T}{y} - }{y} - = - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{T}{x} \right) - + - \dder{}{y} \left( \dder{T}{y} \right) - \right\}, - -which is defined at :math:`\left( \pic, \pjc \right)`. - -.. seealso:: - - :ref:`src/fluid/integrate/compute_rhs.c `. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/divergence_form.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/divergence_form.rst deleted file mode 100644 index 2404de4b..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/divergence_form.rst +++ /dev/null @@ -1,136 +0,0 @@ - -.. _momentum_advective_terms_divergence_form: - -############### -Divergence form -############### - -******* -Summary -******* - -The discrete advective terms are given by - -.. math:: - - \dder{ - \dintrpa{\ux}{x} - \dintrpa{\ux}{x} - }{x} - + - \dder{ - \dintrpu{\uy}{x} - \dintrpa{\ux}{y} - }{y} - -at :math:`\left( \xic, \xjc \right)`, and - -.. math:: - - \dder{ - \dintrpa{\ux}{y} - \dintrpu{\uy}{x} - }{x} - + - \dder{ - \dintrpa{\uy}{y} - \dintrpa{\uy}{y} - }{y} - -at :math:`\left( \yic, \yjc \right)`, respectively. -Note that the interpolations :math:`\dintrpu{\uy}{x}` and :math:`\dintrpu{\ux}{y}` are undefined here. - -********** -Derivation -********** - -To begin with, I focus on the advective terms in the momentum balance - -.. math:: - - u_j \der{u_i}{x_j}, - -which is known as the gradient form and is not conservative itself. -To make this term conservative, I use the relation: - -.. math:: - - u_j \der{u_i}{x_j} - + - u_i \der{u_j}{x_j} - = - \der{u_j u_i}{x_j}. - -Note that the second term on the left-hand side is the incompressibility constraint weighted by :math:`u_i`. -Thus, when the incompressibility constraint is satisfied, I can write the advective term in a conservative form, which is known as the divergence form. - -Since they are inherently momentum-conservative, I first discretise this term: - -.. math:: - - \dder{ - \dintrpu{\ux}{x} - \dintrpu{\ux}{x} - }{x} - + - \dder{ - \dintrpu{\uy}{x} - \dintrpu{\ux}{y} - }{y} - -at :math:`\left( \xic, \xjc \right)` and - -.. math:: - - \dder{ - \dintrpu{\ux}{y} - \dintrpu{\uy}{x} - }{x} - + - \dder{ - \dintrpu{\uy}{y} - \dintrpu{\uy}{y} - }{y} - -at :math:`\left( \yic, \yjc \right)`, which are clearly conservative. - -.. note:: - - There are two possibilities to write :math:`\ux \ux`. - - * :math:`\dintrpu{\left( \ux \right)^2}{x}`: interpolated after squared. - - * :math:`\left( \dintrpu{\ux}{x} \right)^2 = \dintrpu{\ux}{x} \dintrpu{\ux}{x}`: squared after interpolated. - - I take the second option since it is consistent with the other direction :math:`\dintrpu{\uy}{x} \dintrpu{\ux}{y}`. - -Placeholders :math:`\dintrpu{q}{x}` and :math:`\dintrpu{q}{y}` can be partially replaced by the arithmetic averages :math:`\dintrpa{q}{x}` and :math:`\dintrpa{q}{y}`, because cell centers are positioned in the middle of the surrounding cell faces in this project (see :ref:`the domain setup `), giving - -.. math:: - - \dder{ - \dintrpa{\ux}{x} - \dintrpa{\ux}{x} - }{x} - + - \dder{ - \dintrpu{\uy}{x} - \dintrpa{\ux}{y} - }{y} - -at :math:`\left( \xic, \xjc \right)` and - -.. math:: - - \dder{ - \dintrpa{\ux}{y} - \dintrpu{\uy}{x} - }{x} - + - \dder{ - \dintrpa{\uy}{y} - \dintrpa{\uy}{y} - }{y} - -at :math:`\left( \yic, \yjc \right)`. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/gradient_form.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/gradient_form.rst deleted file mode 100644 index 7fa52ae2..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/gradient_form.rst +++ /dev/null @@ -1,795 +0,0 @@ -############# -Gradient form -############# - -******* -Summary -******* - -Finally I find that the gradient form of the advective terms can be written as - -.. math:: - - \dintrpv{ - \dintrpa{\ux}{x} - \dder{\ux}{x} - }{x} - + - \dintrpa{ - \dintrpv{\uy}{x} - \dder{\ux}{y} - }{y} - -at :math:`\left( \xic, \xjc \right)` and - -.. math:: - - \dintrpv{ - \dintrpa{\ux}{y} - \dder{\uy}{x} - }{x} - + - \dintrpa{ - \dintrpa{\uy}{y} - \dder{\uy}{y} - }{y} - -at :math:`\left( \yic, \yjc \right)`. - -********** -Derivation -********** - -The gradient form of the advective terms in the :math:`x` momentum equation - -.. math:: - - \ux \dder{\ux}{x} - + - \uy \dder{\ux}{y}, - -and the terms in the :math:`y` momentum equation - -.. math:: - - \ux \dder{\uy}{x} - + - \uy \dder{\uy}{y} - -are written as - -.. math:: - - \dder{\ux \ux}{x} - + - \dder{\uy \ux}{y} - - - \ux \left( - \dder{\ux}{x} - + - \dder{\uy}{y} - \right) - -and - -.. math:: - - \dder{\ux \uy}{x} - + - \dder{\uy \uy}{y} - - - \uy \left( - \dder{\ux}{x} - + - \dder{\uy}{y} - \right), - -i.e. - -.. math:: - - \left( \text{divergence form} \right) - \ux \left( \text{incompressibility constraint} \right), - -and - -.. math:: - - \left( \text{divergence form} \right) - \uy \left( \text{incompressibility constraint} \right). - -Since the incompressibility constraint is defined at each cell center, I need to interpolate it to the cell face in-between: - -.. math:: - - \vat{ - \dder{u_i}{x_i} - }{\xic, \xjc} - = - \vat{ - \dintrpu{ - \dder{u_i}{x_i} - }{x} - }{\xic, \xjc}, - -and - -.. math:: - - \vat{ - \dder{u_i}{x_i} - }{\yic, \yjc} - = - \vat{ - \dintrpa{ - \dder{u_i}{x_i} - }{y} - }{\yic, \yjc}. - -In the :math:`y` direction, the interpolation is already known. -In the :math:`x` direction, I need to choose a proper interpolation. - -.. note:: - - :math:`\vat{\ux}{\xic,\xjc}` in front of the incompressibility constraint is controversial since there are mainly two possibilities: - - #. Use local value at :math:`\left( \xic, \xjc \right)` - - .. math:: - - \vat{ - \left( - \ux - \dintrpu{ - \dder{u_i}{x_i} - }{x} - \right) - }{\xic, \xjc} - - #. Interpolate as well as the incompressibility constraint - - .. math:: - - \vat{ - \dintrpu{ - \left( - \dintrpa{\ux}{x} - \dder{u_i}{x_i} - \right) - }{x} - }{\xic, \xjc} - - To answer this question, I focus on the :math:`y` component :math:`\uy \der{\ux}{y}`. - As shown in the divergence form, three :math:`\ux` are involved: :math:`\vat{\ux}{\xic,\xjp}`, :math:`\vat{\ux}{\xic,\xjc}`, and :math:`\vat{\ux}{\xic,\xjm}`, which are defined on the same :math:`x` cell face. - - If I adopt the second option, two additional :math:`\ux` components located at different :math:`x` cell faces are involved, which is not consistent because the divergence and the gradient forms have different :math:`\ux` information. - - Thus I notice that I should take the first option. - -To go further, I let - -.. math:: - - \vat{\dintrpu{q}{x}}{\xic} - = - \vat{C}{\xip} \vat{q}{\xip} - + - \vat{C}{\xim} \vat{q}{\xim}. - -Since this is a linear operation, the interpolations and the differentiations should be interchangeable: - -.. math:: - - \dintrpu{ - \dder{\ux}{x} - }{x} - = - \dder{ - \dintrpu{\ux}{x} - }{x}, - \dintrpu{ - \dder{\uy}{y} - }{x} - = - \dder{ - \dintrpu{\uy}{x} - }{y}. - -The second relation is obvious since the operations in the :math:`x` and the :math:`y` directions are independent. - -To satisfy the first relation, I should properly determine :math:`\vat{C}{\xip}` and :math:`\vat{C}{\xim}`. -The interpolation of the differentiation leads to - -.. math:: - - \vat{ - \dintrpu{ - \dder{\ux}{x} - }{x} - }{\xic, \xjc} - & = - \vat{C}{\xip} \vat{\dder{\ux}{x}}{\xip, \xjc} - + - \vat{C}{\xim} \vat{\dder{\ux}{x}}{\xim, \xjc} \\ - & = - \vat{C}{\xip} \frac{ - \vat{\ux}{\xipp, \xjc} - - - \vat{\ux}{\xic, \xjc} - }{\Delta x_{\xip}} - + - \vat{C}{\xim} \frac{ - \vat{\ux}{\xic, \xjc} - - - \vat{\ux}{\ximm, \xjc} - }{\Delta x_{\xim}}, - -while the differentiation of the interpolation leads to - -.. math:: - - \vat{ - \dder{ - \dintrpu{\ux}{x} - }{x} - }{\xic, \xjc} - & = - \frac{ - \vat{\dintrpu{\ux}{x}}{\xip, \xjc} - - - \vat{\dintrpu{\ux}{x}}{\xim, \xjc} - }{\Delta x_{\xic}} \\ - & = - \frac{ - \vat{\dintrpa{\ux}{x}}{\xip, \xjc} - - - \vat{\dintrpa{\ux}{x}}{\xim, \xjc} - }{\Delta x_{\xic}} \\ - & = - \frac{ - \frac{ - \vat{\ux}{\xipp, \xjc} - + - \vat{\ux}{\xic, \xjc} - }{2} - - - \frac{ - \vat{\ux}{\xic, \xjc} - + - \vat{\ux}{\ximm, \xjc} - }{2} - }{\Delta x_{\xic}}. - -By comparing these two equations, I notice - -.. math:: - - \vat{C}{\xip} - & = - \frac{\Delta x_{\xip}}{2 \Delta x_{\xic}}, \\ - \vat{C}{\xim} - & = - \frac{\Delta x_{\xim}}{2 \Delta x_{\xic}}, - -which is the volume average: :math:`\dintrpv{q}{x}`. - -Thus the gradient form of the advective terms is - -.. math:: - - \ux \dder{\ux}{x} - & = - \dder{\ux \ux}{x} - - - \ux \dder{\ux}{x} \\ - & = - \dder{ - \dintrpa{\ux}{x} - \dintrpa{\ux}{x} - }{x} - - - \ux \dintrpv{ - \dder{\ux}{x} - }{x} \\ - & = - \dder{ - \dintrpa{\ux}{x} - \dintrpa{\ux}{x} - }{x} - - - \ux \dder{ - \dintrpa{\ux}{x} - }{x} \\ - & = - \frac{ - \vat{\left( - \dintrpa{\ux}{x} - \dintrpa{\ux}{x} - \right)}{\xip, \xjc} - - - \vat{\left( - \dintrpa{\ux}{x} - \dintrpa{\ux}{x} - \right)}{\xim, \xjc} - }{\Delta x_{\xic}} - - - \vat{\ux}{\xic, \xjc} - \frac{ - \vat{\dintrpa{\ux}{x}}{\xip, \xjc} - - - \vat{\dintrpa{\ux}{x}}{\xim, \xjc} - }{\Delta x_{\xic}} \\ - & = - \vat{\dintrpa{\ux}{x}}{\xip, \xjc} - \frac{ - \vat{\dintrpa{\ux}{x}}{\xip, \xjc} - - - \vat{\ux}{\xic, \xjc} - }{\Delta x_{\xic}} - - - \vat{\dintrpa{\ux}{x}}{\xim, \xjc} - \frac{ - \vat{\dintrpa{\ux}{x}}{\xim, \xjc} - - - \vat{\ux}{\xic, \xjc} - }{\Delta x_{\xic}} \\ - & = - \vat{\dintrpa{\ux}{x}}{\xip, \xjc} - \frac{1}{\Delta x_{\xic}} \frac{ - \vat{\diffe{\ux}{x}}{\xip, \xjc} - }{2} - + - \vat{\dintrpa{\ux}{x}}{\xim, \xjc} - \frac{1}{\Delta x_{\xic}} \frac{ - \vat{\diffe{\ux}{x}}{\xim, \xjc} - }{2} \\ - & = - \frac{\Delta x_{\xip}}{2 \Delta x_{\xic}} - \vat{\left( \dintrpa{\ux}{x} \dder{\ux}{x} \right)}{\xip, \xjc} - + - \frac{\Delta x_{\xim}}{2 \Delta x_{\xic}} - \vat{\left( \dintrpa{\ux}{x} \dder{\ux}{x} \right)}{\xim, \xjc} \\ - & = - \vat{C}{\xip} - \vat{\left( \dintrpa{\ux}{x} \dder{\ux}{x} \right)}{\xip, \xjc} - + - \vat{C}{\xim} - \vat{\left( \dintrpa{\ux}{x} \dder{\ux}{x} \right)}{\xim, \xjc} \\ - & = - \color{red}{ - \vat{ - \dintrpv{ - \dintrpa{\ux}{x} \dder{\ux}{x} - }{x} - }{\xic, \xjc} - }, - -.. math:: - - \uy \dder{\ux}{x} - & = - \dder{\uy \ux}{x} - - - \uy \dder{\ux}{x} \\ - & = - \dder{ - \dintrpv{\uy}{x} - \dintrpa{\ux}{y} - }{y} - - - \ux \dintrpv{ - \dder{\uy}{y} - }{x} \\ - & = - \dder{ - \dintrpv{\uy}{x} - \dintrpa{\ux}{y} - }{y} - - - \ux \dder{ - \dintrpv{\uy}{x} - }{y} \\ - & = - \frac{ - \vat{ - \left( - \dintrpv{\uy}{x} - \dintrpa{\ux}{y} - \right) - }{\xic, \xjp} - - - \vat{ - \left( - \dintrpv{\uy}{x} - \dintrpa{\ux}{y} - \right) - }{\xic, \xjm} - }{\Delta y} - - - \vat{ - \ux - }{\xic, \xjc} - \frac{ - \vat{ - \dintrpv{\uy}{x} - }{\xic, \xjp} - - - \vat{ - \dintrpv{\uy}{x} - }{\xic, \xjm} - }{\Delta y} \\ - & = - \vat{\dintrpv{\uy}{x}}{\xic, \xjp} - \frac{ - \vat{\dintrpa{\ux}{y}}{\xic, \xjp} - - - \vat{\ux}{\xic, \xjc} - }{\Delta y} - - - \vat{\dintrpv{\uy}{x}}{\xic, \xjm} - \frac{ - \vat{\dintrpa{\ux}{y}}{\xic, \xjm} - - - \vat{\ux}{\xic, \xjc} - }{\Delta y} \\ - & = - \frac{1}{2} - \vat{ - \left( - \dintrpv{\uy}{x} - \dder{\ux}{y} - \right) - }{\xic, \xjp} - + - \frac{1}{2} \vat{ - \left( - \dintrpv{\uy}{x} - \dder{\ux}{y} - \right) - }{\xic, \xjm} \\ - & = - \color{red}{ - \vat{ - \dintrpa{ - \dintrpv{\uy}{x} - \dder{\ux}{y} - }{y} - }{\xic, \xjc} - }, - -.. math:: - - \ux \dder{\uy}{x} - & = - \dder{\ux \uy}{x} - - - \uy \dder{\ux}{x} \\ - & = - \dder{ - \dintrpa{\ux}{y} - \dintrpa{\uy}{x} - }{x} - - - \uy \dintrpa{ - \dder{\ux}{x} - }{y} \\ - & = - \dder{ - \dintrpa{\ux}{y} - \dintrpa{\uy}{x} - }{x} - - - \uy \dder{ - \dintrpa{\ux}{y} - }{x} \\ - & = - \frac{ - \vat{\left( - \dintrpa{\ux}{y} - \dintrpa{\uy}{x} - \right)}{\yip, \yjc} - - - \vat{\left( - \dintrpa{\ux}{y} - \dintrpa{\uy}{x} - \right)}{\yim, \yjc} - }{\Delta x_{\yic}} - - - \vat{\uy}{\yic, \yjc} - \frac{ - \vat{\dintrpa{\ux}{y}}{\yip, \yjc} - - - \vat{\dintrpa{\ux}{y}}{\yim, \yjc} - }{\Delta x_{\yic}} \\ - & = - \vat{\dintrpa{\ux}{y}}{\yip, \yjc} - \frac{ - \vat{\dintrpa{\uy}{x}}{\yip, \yjc} - - - \vat{\uy}{\yic, \yjc} - }{\Delta x_{\yic}} - - - \vat{\dintrpa{\ux}{y}}{\yim, \yjc} - \frac{ - \vat{\dintrpa{\uy}{x}}{\yim, \yjc} - - - \vat{\uy}{\yic, \yjc} - }{\Delta x_{\yic}} \\ - & = - \vat{\dintrpa{\ux}{y}}{\yip, \yjc} - \frac{1}{\Delta x_{\yic}} \frac{ - \vat{\diffe{\uy}{x}}{\yip, \yjc} - }{2} - + - \vat{\dintrpa{\ux}{y}}{\yim, \yjc} - \frac{1}{\Delta x_{\yic}} \frac{ - \vat{\diffe{\uy}{x}}{\yim, \yjc} - }{2} \\ - & = - \frac{\Delta x_{\yip}}{2 \Delta x_{\yic}} - \vat{\left( \dintrpa{\ux}{y} \dder{\uy}{x} \right)}{\yip, \yjc} - + - \frac{\Delta x_{\yim}}{2 \Delta x_{\yic}} - \vat{\left( \dintrpa{\ux}{y} \dder{\uy}{x} \right)}{\yim, \yjc} \\ - & = - \vat{C}{\yip} - \vat{\left( \dintrpa{\ux}{y} \dder{\uy}{x} \right)}{\yip, \yjc} - + - \vat{C}{\yim} - \vat{\left( \dintrpa{\ux}{y} \dder{\uy}{x} \right)}{\yim, \yjc} \\ - & = - \color{red}{ - \vat{ - \dintrpv{ - \dintrpa{\ux}{y} \dder{\uy}{x} - }{x} - }{\yic, \yjc} - }, - -and - -.. math:: - \uy \dder{\uy}{y} - & = - \dder{\uy \uy}{y} - - - \uy \dder{\uy}{y} \\ - & = - \dder{ - \dintrpa{\uy}{y} - \dintrpa{\uy}{y} - }{y} - - - \uy \dintrpa{ - \dder{\uy}{y} - }{y} \\ - & = - \dder{ - \dintrpa{\uy}{y} - \dintrpa{\uy}{y} - }{y} - - - \uy \dder{ - \dintrpa{\uy}{y} - }{y} \\ - & = - \frac{ - \vat{ - \left( - \dintrpa{\uy}{y} - \dintrpa{\uy}{y} - \right) - }{\yic, \yjp} - - - \vat{ - \left( - \dintrpa{\uy}{y} - \dintrpa{\uy}{y} - \right) - }{\yic, \yjm} - }{\Delta y} - - - \vat{ - \uy - }{\yic, \yjc} - \frac{ - \vat{ - \dintrpa{\uy}{y} - }{\yic, \yjp} - - - \vat{ - \dintrpa{\uy}{y} - }{\yic, \yjm} - }{\Delta y} \\ - & = - \vat{\dintrpa{\uy}{y}}{\yic, \yjp} - \frac{ - \vat{\dintrpa{\uy}{y}}{\yic, \yjp} - - - \vat{\uy}{\yic, \yjc} - }{\Delta y} - - - \vat{\dintrpa{\uy}{y}}{\yic, \yjm} - \frac{ - \vat{\dintrpa{\uy}{y}}{\yic, \yjm} - - - \vat{\uy}{\yic, \yjc} - }{\Delta y} \\ - & = - \frac{1}{2} \vat{ - \left( - \dintrpa{\uy}{y} - \dder{\uy}{y} - \right) - }{\yic, \yjp} - + - \frac{1}{2} \vat{ - \left( - \dintrpa{\uy}{y} - \dder{\uy}{y} - \right) - }{\yic, \yjm} \\ - & = - \color{red}{ - \vat{ - \dintrpa{ - \dintrpa{\uy}{y} - \dder{\uy}{y} - }{y} - }{\yic, \yjc} - }. - -******************* -Energy conservation -******************* - -Since the gradient form is equivalent to the divergence form as long as the incompressibility constaint is satisfied, the gradient form should conserve the discrete kinetic energy. -Here, I confirm this fact just for completeness. -By multiplying the :math:`x` component - -.. math:: - - \vat{ - \dintrpv{ - \dintrpa{\ux}{x} \dder{\ux}{x} - }{x} - }{\xic, \xjc} - + - \vat{ - \dintrpa{ - \dintrpv{\uy}{x} - \dder{\ux}{y} - }{y} - }{\xic, \xjc} - -with :math:`\vat{\ux}{\xic, \xjc}`, I obtain - -.. math:: - - & \vat{\ux}{\xic, \xjc} - \left( - \vat{ - \dintrpv{ - \dintrpa{\ux}{x} \dder{\ux}{x} - }{x} - }{\xic, \xjc} - + - \vat{ - \dintrpa{ - \dintrpv{\uy}{x} - \dder{\ux}{y} - }{y} - }{\xic, \xjc} - \right) \\ - & = - \vat{\ux}{\xic, \xjc} - \frac{\Delta x_{\xip}}{2 \Delta x_{\xic}} - \vat{\left( \dintrpa{\ux}{x} \dder{\ux}{x} \right)}{\xip, \xjc} - + - \vat{\ux}{\xic, \xjc} - \frac{\Delta x_{\xim}}{2 \Delta x_{\xic}} - \vat{\left( \dintrpa{\ux}{x} \dder{\ux}{x} \right)}{\xim, \xjc} \\ - & + - \vat{\ux}{\xic, \xjc} - \frac{1}{2} - \vat{ - \left( - \dintrpv{\uy}{x} - \dder{\ux}{y} - \right) - }{\xic, \xjp} - + - \vat{\ux}{\xic, \xjc} - \frac{1}{2} \vat{ - \left( - \dintrpv{\uy}{x} - \dder{\ux}{y} - \right) - }{\xic, \xjm}. - -By rearranging the differentiations (while keeping the averages), I have - -.. math:: - - & \dder{ - \dintrpa{\ux}{x} - q_{xx} - }{x} - - - \frac{ - \vat{\ux^2}{\xic, \xjc} - }{2} - \frac{ - \vat{ - \dintrpa{\ux}{x} - }{\xip, \xjc} - - - \vat{ - \dintrpa{\ux}{x} - }{\xim, \xjc} - }{\Delta x_{\xic}} \\ - + & - \dder{ - \dintrpu{\uy}{x} - q_{xy} - }{y} - - - \frac{ - \vat{\ux^2}{\xic, \xjc} - }{2} - \frac{ - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjp} - - - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjm} - }{\Delta y}, - -which are consisted of the conservative terms and the volume-weighted continuities at :math:`\left( \xim, \xjc \right)` and :math:`\left( \xip, \xjc \right)` and thus vanish when integrated in the whole domain. - -Similarly, the advective terms in the :math:`y` direction in the gradient form yields - -.. math:: - - & \vat{\uy}{\yic, \yjc} - \left( - \vat{ - \dintrpv{ - \dintrpa{\ux}{y} \dder{\uy}{x} - }{x} - }{\yic, \yjc} - + - \vat{ - \dintrpa{ - \dintrpa{\uy}{y} - \dder{\uy}{y} - }{y} - }{\yic, \yjc} - \right) \\ - & = - \vat{\uy}{\yic, \yjc} - \frac{\Delta x_{\yip}}{2 \Delta x_{\yic}} - \vat{\left( \dintrpa{\ux}{y} \dder{\uy}{x} \right)}{\yip, \yjc} - + - \vat{\uy}{\yic, \yjc} - \frac{\Delta x_{\yim}}{2 \Delta x_{\yic}} - \vat{\left( \dintrpa{\ux}{y} \dder{\uy}{x} \right)}{\yim, \yjc} \\ - & + - \vat{\uy}{\yic, \yjc} - \frac{1}{2} \vat{ - \left( - \dintrpa{\uy}{y} - \dder{\uy}{y} - \right) - }{\yic, \yjp} - + - \vat{\uy}{\yic, \yjc} - \frac{1}{2} \vat{ - \left( - \dintrpa{\uy}{y} - \dder{\uy}{y} - \right) - }{\yic, \yjm}. - -Again, I have the conservative terms and the volume-averaged continuities at :math:`\left( \yic, \yjm \right)` and :math:`\left( \yic, \yjp \right)`, which vanish when integrated in the whole domain. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/main.rst deleted file mode 100644 index 6dae4332..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/main.rst +++ /dev/null @@ -1,14 +0,0 @@ -############### -Advective terms -############### - -In the continuous domain, the advective terms transport the quantity but do not increase nor decrease the amount of it. -My aim here is to discretise the advective terms which have this character with respect to the momentum and the energy. - -.. toctree:: - :maxdepth: 1 - - divergence_form - quadratic_quantities - gradient_form - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/quadratic_quantities.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/quadratic_quantities.rst deleted file mode 100644 index 8d405a46..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/advective_terms/quadratic_quantities.rst +++ /dev/null @@ -1,723 +0,0 @@ -################## -Quadratic quantity -################## - -******* -Summary -******* - -In this part, I show the discrete advective terms of the momentum equations are give by - -.. math:: - - \dder{ - \dintrpa{\ux}{x} - \dintrpa{\ux}{x} - }{x} - + - \dder{ - \dintrpv{\uy}{x} - \dintrpa{\ux}{y} - }{y} - -at :math:`\left( \xic, \xjc \right)` and - -.. math:: - - \dder{ - \dintrpa{\ux}{y} - \dintrpa{\uy}{x} - }{x} - + - \dder{ - \dintrpa{\uy}{y} - \dintrpa{\uy}{y} - }{y} - -at :math:`\left( \yic, \yjc \right)`, respectively. - -Note that the interpolations :math:`\dintrpv{\uy}{x}` and :math:`\dintrpa{\ux}{y}`, which were undefined in :ref:`the previous part `, are now concluded. - -Also, the discrete analogue of the advection of the kinetic energy leads to - -.. math:: - - \dder{ - \dintrpa{\ux}{x} - q_{xx} - }{x} - + - \dder{ - \dintrpv{\uy}{x} - q_{xy} - }{y} - -and - -.. math:: - - \dder{ - \dintrpa{\ux}{y} - q_{yx} - }{x} - + - \dder{ - \dintrpa{\uy}{y} - q_{yy} - }{y}, - -where :math:`q_{ij}` are the discrete analogues of the squared velocity defined as - -.. math:: - - \vat{q_{xx}}{\xip, \xjc} - \equiv - \frac{1}{2} - \vat{\ux}{\xipp, \xjc} - \vat{\ux}{\xic, \xjc}, - -.. math:: - - \vat{q_{xy}}{\xic, \xjp} - \equiv - \frac{1}{2} - \vat{\ux}{\xic, \xjpp} - \vat{\ux}{\xic, \xjc }, - -and - -.. math:: - - \vat{q_{yx}}{\yip, \yjc} - \equiv - \frac{1}{2} - \vat{\uy}{\yipp, \yjc} - \vat{\uy}{\yic, \yjc}, - -.. math:: - - \vat{q_{yy}}{\yic, \yjp} - \equiv - \frac{1}{2} - \vat{\uy}{\yic, \yjpp} - \vat{\uy}{\yic, \yjc }. - -An important takeaway here is that these terms are conservative and vanish when integrated in the whole volume: - -.. math:: - - \sum_{\xjc} \sum_{\xic} - \left[ - \dder{ - \dintrpa{\ux}{x} - q_{xx} - }{x} - + - \dder{ - \dintrpv{\uy}{x} - q_{xy} - }{y} - \right]_{\xic, \xjc} - \Delta x_{\xic} \Delta y_{\xjc} - = - 0, - -.. math:: - - \sum_{\yjc} \sum_{\yic} - \left[ - \dder{ - \dintrpa{\ux}{y} - q_{yx} - }{x} - + - \dder{ - \dintrpa{\uy}{y} - q_{yy} - }{y} - \right]_{\yic, \yjc} - \Delta x_{\yic} \Delta y_{\yjc} - = - 0, - -i.e. they do not contribute to the increase nor decrease in the squared velocities. - -********** -Derivation -********** - -In the continuous domain, the advection of the kinetic energy is obtained by taking the inner product of the velocity and the momentum advections. -Here I consider the corresponding relation in the discrete domain. - -.. note:: - - In the continuous space, the local kinetic energy - - .. math:: - - k = k \left( t, x, y \right) - - is clearly the sum of - - .. math:: - - \frac{1}{2} \left[ \ux \left( t, x, y \right) \right]^2 - - and - - .. math:: - - \frac{1}{2} \left[ \uy \left( t, x, y \right) \right]^2. - - After discretised, however, the definition of the kinetic energy becomes ambiguous since :math:`\ux` and :math:`\uy` are defined at different positions. - - In this project, I consider these two contributions separately: - - #. :math:`\ux` contribution - - .. math:: - - \der{}{t} \vat{ - \left( \frac{1}{2} \ux^2 \right) - }{\xic, \xjc} + \cdots, - - which is obtained by multiplying the discrete :math:`x` momentum balance by :math:`\ux`. - - #. :math:`\uy` contribution - - .. math:: - - \der{}{t} \vat{ - \left( \frac{1}{2} \uy^2 \right) - }{\yic, \yjc} + \cdots, - - which is obtained by multiplying the discrete :math:`y` momentum balance by :math:`\uy`. - - I define the total and the discrete kinetic energy :math:`K = K \left( t \right)` as the sum of the discrete volume integrals of these two equations: - - .. math:: - - \der{K}{t} - = - \sum_{\xic} \sum_{\xjc} \der{}{t} \vat{ - \left( - \frac{1}{2} \ux^2 - \Delta x \Delta y - \right) - }{\xic, \xjc} - + - \sum_{\yic} \sum_{\yjc} \der{}{t} \vat{ - \left( - \frac{1}{2} \uy^2 - \Delta x \Delta y - \right) - }{\yic, \yjc}, - - which is investigated in this section. - -At :math:`\left( \xic, \xjc \right)` where :math:`\ux` is defined, by multiplying the :math:`x` momentum by :math:`\ux`, I have - -.. math:: - - \ux \left( - \dder{ - \dintrpa{\ux}{x} - \dintrpa{\ux}{x} - }{x} - + - \dder{ - \dintrpu{\uy}{x} - \dintrpa{\ux}{y} - }{y} - \right). - -The first term leads to - -.. math:: - - & \vat{\ux}{\xic, \xjc} - \frac{ - \vat{ - \dintrpa{\ux}{x} - }{\xip, \xjc} - \frac{ - \vat{\ux}{\xipp, \xjc} - + - \vat{\ux}{\xic , \xjc} - }{2} - - - \vat{ - \dintrpa{\ux}{x} - }{\xim, \xjc} - \frac{ - \vat{\ux}{\xic , \xjc} - + - \vat{\ux}{\ximm, \xjc} - }{2} - }{\Delta x_{\xic}} \\ - & = - \frac{ - \vat{ - \dintrpa{\ux}{x} - }{\xip, \xjc} - \frac{ - \vat{\ux}{\xipp, \xjc} - \vat{\ux}{\xic, \xjc} - }{2} - - - \vat{ - \dintrpa{\ux}{x} - }{\xim, \xjc} - \frac{ - \vat{\ux}{\xic, \xjc} - \vat{\ux}{\ximm, \xjc} - }{2} - }{\Delta x_{\xic}} - + - \frac{ - \vat{\ux^2}{\xic, \xjc} - }{2} - \frac{ - \vat{ - \dintrpa{\ux}{x} - }{\xip, \xjc} - - - \vat{ - \dintrpa{\ux}{x} - }{\xim, \xjc} - }{\Delta x_{\xic}} \\ - & = \dder{ - \dintrpa{\ux}{x} - q_{xx} - }{x} - + - \frac{ - \vat{\ux^2}{\xic, \xjc} - }{2} - \frac{ - \vat{ - \dintrpa{\ux}{x} - }{\xip, \xjc} - - - \vat{ - \dintrpa{\ux}{x} - }{\xim, \xjc} - }{\Delta x_{\xic}}, - -while the second term leads to - -.. math:: - - & \vat{\ux}{\xic, \xjc} - \frac{ - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjp} - \frac{ - \vat{\ux}{\xic, \xjpp} - + - \vat{\ux}{\xic, \xjc } - }{2} - - - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjm} - \frac{ - \vat{\ux}{\xic, \xjc } - + - \vat{\ux}{\xic, \xjmm} - }{2} - }{\Delta y} \\ - & = - \frac{ - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjp} - \frac{ - \vat{\uy}{\xic, \xjpp} - \vat{\uy}{\xic, \xjc } - }{2} - - - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjm} - \frac{ - \vat{\uy}{\xic, \xjc } - \vat{\uy}{\xic, \xjmm} - }{2} - }{\Delta y} - + - \frac{ - \vat{\ux^2}{\xic, \xjc} - }{2} - \frac{ - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjp} - - - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjm} - }{\Delta y} \\ - & = - \dder{ - \dintrpu{\uy}{x} - q_{xy} - }{y} - + - \frac{ - \vat{\ux^2}{\xic, \xjc} - }{2} - \frac{ - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjp} - - - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjm} - }{\Delta y}. - -Here I introduce the quadratic quantities :math:`q_{xx}` and :math:`q_{xy}`, which are defined as - -.. math:: - - \vat{q_{xx}}{\xip, \xjc} - \equiv - \frac{1}{2} - \vat{\ux}{\xipp, \xjc} - \vat{\ux}{\xic, \xjc}, - -and - -.. math:: - - \vat{q_{xy}}{\xic, \xjp} - \equiv - \frac{1}{2} - \vat{\ux}{\xic, \xjpp} - \vat{\ux}{\xic, \xjc }, - -respectively, which are the products of the two neighbouring velocities instead of the squared velocities. - -In analogy to the kinetic energy transport in the continuous domain, I request the sum of - -.. math:: - - \sum_{i} \sum_{j} - \left( - \dder{ - \dintrpa{\ux}{x} - q_{xx} - }{x} - + - \frac{ - \vat{\ux^2}{\xic, \xjc} - }{2} - \frac{ - \vat{ - \dintrpa{\ux}{x} - }{\xip, \xjc} - - - \vat{ - \dintrpa{\ux}{x} - }{\xim, \xjc} - }{\Delta x_{\xic}} - \right) - \vat{\Delta x}{\xic} - \Delta y - -and - -.. math:: - - \sum_{i} \sum_{j} - \left( - \dder{ - \dintrpu{\uy}{x} - q_{xy} - }{y} - + - \frac{ - \vat{\ux^2}{\xic, \xjc} - }{2} - \frac{ - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjp} - - - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjm} - }{\Delta y} - \right) - \vat{\Delta x}{\xic} - \Delta y - -are conserved. - -The first terms are conservative and thus they inherently satisfy the requirement. -The sum of the second terms yield - -.. math:: - - \frac{ - \vat{\ux^2}{\xic, \xjc} - }{2} - \left( - \frac{ - \vat{ - \dintrpa{\ux}{x} - }{\xip, \xjc} - - - \vat{ - \dintrpa{\ux}{x} - }{\xim, \xjc} - }{\Delta x_{\xic}} - + - \frac{ - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjp} - - - \vat{ - \dintrpu{\uy}{x} - }{\xic, \xjm} - }{\Delta y} - \right), - -which I request to vanish. - -The first term in the parenthesis can be written as - -.. math:: - - \frac{\Delta x_{\xip}}{2 \Delta x_{\xic}} \vat{\dder{\ux}{x}}{\xip, \xjc} - + - \frac{\Delta x_{\xim}}{2 \Delta x_{\xic}} \vat{\dder{\ux}{x}}{\xim, \xjc}. - -Although the explicit forms of :math:`\vat{\dintrpu{\uy}{x}}{\xic, \xjp}` and :math:`\vat{\dintrpu{\uy}{x}}{\xic, \xjm}` are unknown, they should be written as the linear combinations of the neighbouring velocities: - -.. math:: - - \vat{\dintrpu{\uy}{x}}{\xic, \cdots} - = - \vat{C}{\xip} \vat{\uy}{\xip, \cdots} - + - \vat{C}{\xim} \vat{\uy}{\xim, \cdots}, - -giving - -.. math:: - - \vat{C}{\xip} \vat{\dder{\uy}{y}}{\xip, \xjc} - + - \vat{C}{\xim} \vat{\dder{\uy}{y}}{\xim, \xjc}. - -Thus, I notice that, they vanish if - -.. math:: - - \vat{C}{\xip} - & = - \frac{\Delta x_{\xip}}{2 \Delta x_{\xic}}, \\ - \vat{C}{\xim} - & = - \frac{\Delta x_{\xim}}{2 \Delta x_{\xic}} - -hold because of the incompressibility constraint, and find that the placeholder should be the volume average: - -.. math:: - - \vat{\dintrpv{\uy}{x}}{\xic, \cdots} - = - \vat{C}{\xip} \vat{\uy}{\xip, \cdots} - + - \vat{C}{\xim} \vat{\uy}{\xim, \cdots}. - -Similarly, in the :math:`y` direction at :math:`\left( \yic, \yjc \right)` where :math:`\uy` is defined, I have - -.. math:: - - \uy \left( - \dder{ - \dintrpa{\ux}{y} - \dintrpu{\uy}{x} - }{x} - + - \dder{ - \dintrpa{\uy}{y} - \dintrpa{\uy}{y} - }{y} - \right). - -The second term is simply - -.. math:: - - \dder{\dintrpa{\uy}{y} q_{yy}}{y} - + - \frac{ - \vat{\uy^2}{\yic, \yjc} - }{2} - \frac{1}{2} \left( - \vat{\dder{\uy}{y}}{\yic, \yjp} - - - \vat{\dder{\uy}{y}}{\yic, \yjm} - \right), - -where :math:`q_{yy}` is the quadratic quantity defined as - -.. math:: - - \vat{q_{yy}}{\yic, \yjp} - \equiv - \frac{1}{2} - \vat{\uy}{\yic, \yjpp} - \vat{\uy}{\yic, \yjc }. - -Regarding the first term, I let the coefficients of the unknown interpolations as - -.. math:: - - \vat{ - \dintrpu{\uy}{x} - }{\yim, \yjc} - & \equiv - \vat{c^-}{\yimm} \vat{\uy}{\yimm} - + - \vat{c^-}{\yic } \vat{\uy}{\yic }, \\ - \vat{ - \dintrpu{\uy}{x} - }{\yip, \yjc} - & \equiv - \vat{c^+}{\yic } \vat{\uy}{\yic } - + - \vat{c^+}{\yipp} \vat{\uy}{\yipp}, - -giving - -.. math:: - - \color{blue}{\vat{\uy}{\yic, \yjc}} - \frac{ - \vat{ - \dintrpa{\ux}{y} - }{\yip, \yjc} - \left( - \color{blue}{ - \vat{c^+}{\yipp} \vat{\uy}{\yipp, \yjc} - } - + - \vat{c^+}{\yic } \vat{\uy}{\yic , \yjc} - \right) - - - \vat{ - \dintrpa{\ux}{y} - }{\yim, \yjc} - \left( - \vat{c^-}{\yic } \vat{\uy}{\yic , \yjc} - + - \color{blue}{ - \vat{c^-}{\yimm} \vat{\uy}{\yimm, \yjc} - } - \right) - }{\Delta x_{\yic}}. - -I notice two constraints to identify the coefficients. - -================== -Quadratic quantity -================== - -I focus on the terms coloured in blue to define the quadratic quantity, which request the coefficients :math:`\vat{c^+}{\yipp}` and :math:`\vat{c^-}{\yimm}` to be :math:`1/2`, so that - -.. math:: - - \vat{q_{yx}}{\yip, \yjc} - \equiv - \frac{1}{2} - \vat{\uy}{\yipp, \yjc} - \vat{\uy}{\yic, \yjc} - -can be defined and I am able to make the bluish terms conservative - -.. math:: - - \dder{ - \dintrpa{\ux}{y} - q_{yx} - }{x}. - -======== -Residual -======== - -The other term yields - -.. math:: - - \vat{\uy^2}{\yic, \yjc} - \frac{ - \vat{c^+}{\yic} - \vat{ - \dintrpa{\ux}{y} - }{\yip, \yjc} - - - \vat{c^-}{\yic} - \vat{ - \dintrpa{\ux}{y} - }{\yim, \yjc} - }{\Delta x_{\yic}}. - -To make it canceled out with the other residual, I notice the coefficients must be :math:`1/2` again. - -Thus, I notice that the arithmetic average :math:`\dintrpa{q}{x}` should be used for the unknown interpolations in the :math:`y` momentum advection. - -Finally, I conclude that the advective terms in the divergence form are - -.. math:: - - \der{ - \ux - \ux - }{x} - + - \der{ - \uy - \ux - }{y} - & = - \color{red}{ - \dder{ - \dintrpa{\ux}{x} - \dintrpa{\ux}{x} - }{x} - + - \dder{ - \dintrpv{\uy}{x} - \dintrpa{\ux}{y} - }{y} - }, \\ - \der{ - \ux - \uy - }{x} - + - \der{ - \uy - \uy - }{y} - & = - \color{red}{ - \dder{ - \dintrpa{\ux}{y} - \dintrpa{\uy}{x} - }{x} - + - \dder{ - \dintrpa{\uy}{y} - \dintrpa{\uy}{y} - }{y} - }. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/diffusive_terms.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/diffusive_terms.rst deleted file mode 100644 index 25bdebdc..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/diffusive_terms.rst +++ /dev/null @@ -1,534 +0,0 @@ - -.. _derivation_momentum_diffusive_terms: - -############### -Diffusive terms -############### - -Diffusive terms in the momentum equations diffuse the momentum in all directions. -They also play a crucial role in diffusing and dissipating the kinetic energy. -In this project, the injected energy should be eventually dissipated by these terms since there is no other energy sink. - -****************** -Momentum diffusion -****************** - -Since the diffusive terms are linear (and thus I can treat them implicitly in time, see :ref:`the temporal discretisation `), I can simply discretise them as - -.. math:: - - \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{\ux}{x} \right) - + - \dder{}{y} \left( \dder{\ux}{y} \right) - \right\} - -in the :math:`x` direction at :math:`\left( \xic, \xjc \right)`, while - -.. math:: - - \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{\uy}{x} \right) - + - \dder{}{y} \left( \dder{\uy}{y} \right) - \right\} - -in the :math:`y` direction at :math:`\left( \yic, \yjc \right)`. -All necessary first-order differentiations are defined, so no interpolation is required. - -.. note:: - - The results are **not** the same as the corresponding Taylor series expansions of the second-order derivatives when the grid sizes are not equal. - -****************** -Energy dissipation -****************** - -In :ref:`the governing equations `, I took the inner product of :math:`u_i` and the diffusive terms of the momentum equations to derive the relation of the diffusion and the dissipation of :math:`k`: - -.. math:: - - u_i \der{}{x_j} \left( \der{u_i}{x_j} \right) - = - \der{}{x_j} \left( u_i \der{u_i}{x_j} \right) - - - \der{u_i}{x_j} \der{u_i}{x_j}, - -where the pre-factor (the diffusivity) :math:`\sqrt{Pr} / \sqrt{Ra}` are dropped for convenience. -Here I consider the discrete counterpart. - -=================== -Left-hand-side term -=================== - -The left-hand-side term yields - -.. math:: - - \ux - \left\{ - \dder{}{x} \left( \dder{\ux}{x} \right) - + - \dder{}{y} \left( \dder{\ux}{y} \right) - \right\} - & = - \frac{ - \vat{\ux}{\xic, \xjc} - \vat{ - \dder{\ux}{x} - }{\xip, \xjc} - - - \vat{\ux}{\xic, \xjc} - \vat{ - \dder{\ux}{x} - }{\xim, \xjc} - }{\Delta x_{\xic}} \\ - & + - \frac{ - \vat{\ux}{\xic, \xjc} - \vat{ - \dder{\ux}{y} - }{\xic, \xjp} - - - \vat{\ux}{\xic, \xjc} - \vat{ - \dder{\ux}{y} - }{\xic, \xjm} - }{\Delta y} - -and - -.. math:: - - \uy - \left\{ - \dder{}{x} \left( \dder{\uy}{x} \right) - + - \dder{}{y} \left( \dder{\uy}{y} \right) - \right\} - & = - \frac{ - \vat{\uy}{\yic, \yjc} - \vat{ - \dder{\uy}{x} - }{\yip, \yjc} - - - \vat{\uy}{\yic, \yjc} - \vat{ - \dder{\uy}{x} - }{\yim, \yjc} - }{\Delta x_{\yic}} \\ - & + - \frac{ - \vat{\uy}{\yic, \yjc} - \vat{ - \dder{\uy}{y} - }{\yic, \yjp} - - - \vat{\uy}{\yic, \yjc} - \vat{ - \dder{\uy}{y} - }{\yic, \yjm} - }{\Delta y} - -at :math:`\left( \xic, \xjc \right)` and :math:`\left( \yic, \yjc \right)`, respectively. - -===================================== -The first term in the right-hand side -===================================== - -The first term on the right-hand side leads to - -.. math:: - - \dder{}{x} \left( \ux \dder{\ux}{x} \right) - + - \dder{}{y} \left( \ux \dder{\ux}{y} \right) - & = - \frac{ - \vat{ - \dintrpa{\ux}{x} - }{\xip, \xjc} - \vat{ - \dder{\ux}{x} - }{\xip, \xjc} - - - \vat{ - \dintrpa{\ux}{x} - }{\xim, \xjc} - \vat{ - \dder{\ux}{x} - }{\xim, \xjc} - }{\Delta x_{\xic}} \\ - & + - \frac{ - \vat{ - \dintrpa{\ux}{y} - }{\xic, \xjp} - \vat{ - \dder{\ux}{y} - }{\xic, \xjp} - - - \vat{ - \dintrpa{\ux}{y} - }{\xic, \xjm} - \vat{ - \dder{\ux}{y} - }{\xic, \xjm} - }{\Delta y} - -and - -.. math:: - - \dder{}{x} \left( \uy \dder{\uy}{x} \right) - + - \dder{}{y} \left( \uy \dder{\uy}{y} \right) - & = - \frac{ - \vat{ - \dintrpa{\uy}{x} - }{\yip, \yjc} - \vat{ - \dder{\uy}{x} - }{\yip, \yjc} - - - \vat{ - \dintrpa{\uy}{x} - }{\yim, \yjc} - \vat{ - \dder{\uy}{x} - }{\yim, \yjc} - }{\Delta x_{\yic}} \\ - & + - \frac{ - \vat{ - \dintrpa{\uy}{y} - }{\yic, \yjp} - \vat{ - \dder{\uy}{y} - }{\yic, \yjp} - - - \vat{ - \dintrpa{\uy}{y} - }{\yic, \yjm} - \vat{ - \dder{\uy}{y} - }{\yic, \yjm} - }{\Delta y}. - -.. note:: - - For the ease of the later discussion, I keep the above form. - However, I can further manipulate them to confirm that they are equal to - - .. math:: - - \dder{}{x} \dder{}{x} \left( \frac{1}{2} \ux^2 \right) - + - \dder{}{y} \dder{}{y} \left( \frac{1}{2} \ux^2 \right) - - and - - .. math:: - - \dder{}{x} \dder{}{x} \left( \frac{1}{2} \uy^2 \right) - + - \dder{}{y} \dder{}{y} \left( \frac{1}{2} \uy^2 \right), - - which are the discrete diffusive terms of the squared velocity :math:`\ux^2 / 2` and :math:`\uy^2 / 2`, respectively. - It is readily apparent that they are discretely conservative and thus do not contribute to the net change of the squared velocities, which is consistent with the continuous counterpart. - -================ -Dissipative term -================ - -Subtracting the first equation from the second one yields - -.. math:: - - & - \frac{ - \left( - \vat{ - \dintrpa{\ux}{x} - }{\xip, \xjc} - - - \vat{\ux}{\xic, \xjc} - \right) - \vat{ - \dder{\ux}{x} - }{\xip, \xjc} - - - \left( - \vat{ - \dintrpa{\ux}{x} - }{\xim, \xjc} - - - \vat{\ux}{\xic, \xjc} - \right) - \vat{ - \dder{\ux}{x} - }{\xim, \xjc} - }{\Delta x_{\xic}} \\ - + & - \frac{ - \left( - \vat{ - \dintrpa{\ux}{y} - }{\xic, \xjp} - - - \vat{\ux}{\xic, \xjc} - \right) - \vat{ - \dder{\ux}{y} - }{\xic, \xjp} - - - \left( - \vat{ - \dintrpa{\ux}{y} - }{\xic, \xjm} - - - \vat{\ux}{\xic, \xjc} - \right) - \vat{ - \dder{\ux}{y} - }{\xic, \xjm} - }{\Delta y} - -and - -.. math:: - - & - \frac{ - \left( - \vat{ - \dintrpa{\uy}{x} - }{\yip, \yjc} - - - \vat{\uy}{\yic, \yjc} - \right) - \vat{ - \dder{\uy}{x} - }{\yip, \yjc} - - - \left( - \vat{ - \dintrpa{\uy}{x} - }{\yim, \yjc} - - - \vat{\uy}{\yic, \yjc} - \right) - \vat{ - \dder{\uy}{x} - }{\yim, \yjc} - }{\Delta x_{\yic}} \\ - + & - \frac{ - \left( - \vat{ - \dintrpa{\uy}{y} - }{\yic, \yjp} - - - \vat{\uy}{\yic, \yjc} - \right) - \vat{ - \dder{\uy}{y} - }{\yic, \yjp} - - - \left( - \vat{ - \dintrpa{\uy}{y} - }{\yic, \yjm} - - - \vat{\uy}{\yic, \yjc} - \right) - \vat{ - \dder{\uy}{y} - }{\yic, \yjm} - }{\Delta y} - -They are - -.. math:: - - & - \frac{1}{2} - \frac{1}{\Delta x_{\xic}} - \vat{ - \diffe{\ux}{x} - }{\xip, \xjc} - \vat{ - \dder{\ux}{x} - }{\xip, \xjc} - + - \frac{1}{2} - \frac{1}{\Delta x_{\xic}} - \vat{ - \diffe{\ux}{x} - }{\xim, \xjc} - \vat{ - \dder{\ux}{x} - }{\xim, \xjc} \\ - + & - \frac{1}{2} - \frac{1}{\Delta y} - \vat{ - \diffe{\ux}{y} - }{\xic, \xjp} - \vat{ - \dder{\ux}{y} - }{\xic, \xjp} - + - \frac{1}{2} - \frac{1}{\Delta y} - \vat{ - \diffe{\ux}{y} - }{\xic, \xjm} - \vat{ - \dder{\ux}{y} - }{\xic, \xjm} \\ - = & - \frac{1}{2} - \frac{\Delta x_{\xip}}{\Delta x_{\xic}} - \left( - \vat{ - \dder{\ux}{x} - }{\xip, \xjc} - \right)^2 - + - \frac{1}{2} - \frac{\Delta x_{\xim}}{\Delta x_{\xic}} - \left( - \vat{ - \dder{\ux}{x} - }{\xim, \xjc} - \right)^2 \\ - + & - \frac{1}{2} - \left( - \vat{ - \dder{\ux}{y} - }{\xic, \xjp} - \right)^2 - + - \frac{1}{2} - \left( - \vat{ - \dder{\ux}{y} - }{\xic, \xjm} - \right)^2 \\ - = & - \color{red}{ - \dintrpv{ - \left( \dder{\ux}{x} \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{\ux}{y} \right)^2 - }{y} - } - -and - -.. math:: - - & - \frac{1}{2} - \frac{1}{\Delta x_{\yic}} - \vat{C}{\yip} - \vat{ - \diffe{\uy}{x} - }{\yip, \yjc} - \vat{ - \dder{\uy}{x} - }{\yip, \yjc} - + - \frac{1}{2} - \frac{1}{\Delta x_{\yic}} - \vat{C}{\yim} - \vat{ - \diffe{\uy}{x} - }{\yim, \yjc} - \vat{ - \dder{\uy}{x} - }{\yim, \yjc} \\ - + & - \frac{1}{2} - \frac{1}{\Delta y} - \vat{ - \diffe{\uy}{y} - }{\yic, \yjp} - \vat{ - \dder{\uy}{y} - }{\yic, \yjp} - + - \frac{1}{2} - \frac{1}{\Delta y} - \vat{ - \diffe{\uy}{y} - }{\yic, \yjm} - \vat{ - \dder{\uy}{y} - }{\yic, \yjm} \\ - = & - \frac{1}{2} - \frac{\Delta x_{\yip}}{\Delta x_{\yic}} - \vat{C}{\yip} - \left( - \vat{ - \dder{\uy}{x} - }{\yip, \yjc} - \right)^2 - + - \frac{1}{2} - \frac{\Delta x_{\yim}}{\Delta x_{\yic}} - \vat{C}{\yim} - \left( - \vat{ - \dder{\uy}{x} - }{\yim, \yjc} - \right)^2 \\ - + & - \frac{1}{2} - \left( - \vat{ - \dder{\uy}{y} - }{\yic, \yjp} - \right)^2 - + - \frac{1}{2} - \left( - \vat{ - \dder{\uy}{y} - }{\yic, \yjm} - \right)^2 \\ - = & - \color{red}{ - \dintrpv{ - C \left( \dder{\uy}{x} \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{\uy}{y} \right)^2 - }{y} - }. - -Here :math:`C` is a coefficient to correct the boundary values: - -.. math:: - - C - = - \begin{cases} - \text{wall} & 2 \\ - \text{otherwise} & 1 - \end{cases}. - -This is necessary because :math:`\uy` is defined on the walls and the value is directly used rather than being interpolated. - -.. seealso:: - - The reddish terms are computed to check :ref:`the instantaneous Nusselt number `. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/external_forcing_term.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/external_forcing_term.rst deleted file mode 100644 index c1747204..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/external_forcing_term.rst +++ /dev/null @@ -1,20 +0,0 @@ -###################### -External forcing terms -###################### - -Under the Boussinesq approximation, in the momentum balance, I have - -.. math:: - - \dintrpu{T}{x} - -in the :math:`x` direction. - -As a result, in the kinetic energy balance, I have - -.. math:: - - \ux \dintrpu{T}{x}. - -At this moment, I do not have enough information to replace :ref:`the placeholder `, which will be given later. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/introduction.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/introduction.rst deleted file mode 100644 index f96eb707..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/introduction.rst +++ /dev/null @@ -1,61 +0,0 @@ -In this part, I consider to discretise - -.. math:: - - \der{\ux}{t} - + - u_j \der{\ux}{x_j} - = - - - \der{p}{x} - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \der{}{x_j} \der{\ux}{x_j} - + - T - -at the :math:`x` cell faces :math:`\left( \xic, \xjc, \xkc \right)`, and - -.. math:: - - \der{\uy}{t} - + - u_j \der{\uy}{x_j} - = - - - \der{p}{y} - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \der{}{x_j} \der{\uy}{x_j} - -at the :math:`y` cell faces :math:`\left( \yic, \yjc, \ykc \right)`, and - -.. math:: - - \der{\uz}{t} - + - u_j \der{\uz}{x_j} - = - - - \der{p}{z} - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \der{}{x_j} \der{\uz}{x_j} - -at the :math:`z` cell faces :math:`\left( \zic, \zjc, \zkc \right)`. - -Also the kinetic energy balance - -.. math:: - - \der{k}{t} - + - u_i \der{k}{x_i} - = - - - u_i \der{p}{x_i} - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \der{}{x_j} \left( u_i \der{u_i}{x_j} \right) - - - \frac{\sqrt{Pr}}{\sqrt{Ra}} \der{u_i}{x_j} \der{u_i}{x_j} - + u_i T \delta_{ix} - -is considered, which is dependent on the momentum equation in the continuous domain (see :ref:`the continuous space `). - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/main.rst deleted file mode 100644 index 3cb3b4a2..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/main.rst +++ /dev/null @@ -1,33 +0,0 @@ - -.. _momentum_balance: - -################ -Momentum balance -################ - -************ -Introduction -************ - -.. include:: introduction.rst - -******* -Summary -******* - -.. include:: summary.rst - -********** -Derivation -********** - -In this part, I focus on how each term should be discretised to make things consistent, namely to mimic the global (integrated in the volume) properties of the equations. - -.. toctree:: - :maxdepth: 1 - - advective_terms/main - pressure_gradient_terms - diffusive_terms - external_forcing_term - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/pressure_gradient_terms.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/pressure_gradient_terms.rst deleted file mode 100644 index 2561ea32..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/pressure_gradient_terms.rst +++ /dev/null @@ -1,129 +0,0 @@ -####################### -Pressure-gradient terms -####################### - -******** -Momentum -******** - -Since they are the simple gradients, I discretise them as - -.. math:: - - \vat{\dder{p}{x}}{\xic, \xjc}, - \vat{\dder{p}{y}}{\yic, \yjc} - -in both directions, respectively. - -**************** -Squared velocity -**************** - -I have - -.. math:: - - \vat{\ux \dder{p}{x}}{\xic, \xjc} - -and - -.. math:: - - \vat{\uy \dder{p}{y}}{\yic, \yjc} - -in each direction. - -To see the global effect on the squared velocity balances, I consider to discretely integrate these terms in the whole volume. -Fist I focus on the :math:`x` contribution: - -.. math:: - - \sum_{\xjc} \sum_{\xic} - \vat{\ux \dder{p}{x}}{\xic, \xjc} - \Delta x_{\xic} - \Delta y_{\xjc} - = - \sum_{\xjc} \sum_{\xic} - \vat{\ux}{\xic, \xjc} - \left( - \vat{p}{\xip, \xjc} - - - \vat{p}{\xim, \xjc} - \right) - \Delta y_{\xjc}. - -Although this equation is written as the sum of each :math:`\ux`, I can write it as the sum of each :math:`p`: - -.. math:: - - - - \sum_{\pjc} \sum_{\pic} - \vat{p}{\pic, \pjc} - \left( - \vat{\ux}{\pip, \pjc} - - - \vat{\ux}{\pim, \pjc} - \right) - \Delta y_{\pjc}, - -where I use the impermeable condition to eliminate the boundary contributions. - -Similarly, the :math:`y` contribution yields - -.. math:: - - - - \sum_{\pjc} \sum_{\pic} - \vat{p}{\pic, \pjc} - \left( - \vat{\uy}{\pic, \pjp} - - - \vat{\uy}{\pic, \pjm} - \right) - \Delta x_{\pjc}. - -Although they do not vanish independently, when added: - -.. math:: - - & - - - \sum_{\pjc} \sum_{\pic} - \vat{p}{\pic, \pjc} - \left( - \vat{\ux}{\pip, \pjc} - - - \vat{\ux}{\pim, \pjc} - \right) - \Delta y_{\pjc} - - - \sum_{\pjc} \sum_{\pic} - \vat{p}{\pic, \pjc} - \left( - \vat{\uy}{\pic, \pjp} - - - \vat{\uy}{\pic, \pjm} - \right) - \Delta x_{\pjc} \\ - = - & - - - \sum_{\pjc} \sum_{\pic} - \vat{p}{\pic, \pjc} - \left[ - \frac{ - \vat{\ux}{\pip, \pjc} - - - \vat{\ux}{\pim, \pjc} - }{\Delta x_{\pic}} - + - \frac{ - \vat{\uy}{\pic, \pjp} - - - \vat{\uy}{\pic, \pjm} - }{\Delta y_{\pjc}} - \right] - \Delta x_{\pic} \Delta y_{\pjc}, - -they disappear because of the discrete incompressibility constraint. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/summary.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/summary.rst deleted file mode 100644 index 5fa8a82c..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/momentum_balance/summary.rst +++ /dev/null @@ -1,61 +0,0 @@ -The following discrete equations are directly implemented in the code. - -Momentum balance in the :math:`x` direction at :math:`\left( \xic, \xjc \right)`: - -.. math:: - - \der{\ux}{t} - + - \dintrpv{ - \dintrpa{\ux}{x} - \dder{\ux}{x} - }{x} - + - \dintrpa{ - \dintrpv{\uy}{x} - \dder{\ux}{y} - }{y} - = - -\dder{p}{x} - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{\ux}{x} \right) - + - \dder{}{y} \left( \dder{\ux}{y} \right) - \right\} - + - \dintrpu{T}{x}. - -Momentum balance in the :math:`y` direction at :math:`\left( \yic, \yjc \right)`: - -.. math:: - - \der{\uy}{t} - + - \dintrpv{ - \dintrpa{\ux}{y} - \dder{\uy}{x} - }{x} - + - \dintrpa{ - \dintrpa{\uy}{y} - \dder{\uy}{y} - }{y} - = - -\dder{p}{y} - + - \frac{\sqrt{Pr}}{\sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{\uy}{x} \right) - + - \dder{}{y} \left( \dder{\uy}{y} \right) - \right\}. - -.. seealso:: - - :ref:`src/fluid/integrate/compute_rhs.c `. - -.. note:: - - I cannot define the thermal forcing term in the :math:`x` direction :math:`\dintrpu{T}{x}` here (recall that the *widetilde* symbol is a placeholder). - To decide the correct interpolation, I need to take into account the equation of the internal energy and :ref:`the Nusselt number relations `. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/heat_flux.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/heat_flux.rst deleted file mode 100644 index a70c4890..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/heat_flux.rst +++ /dev/null @@ -1,386 +0,0 @@ - -.. _nu_heat_flux_discrete: - -############################ -Heat flux and Nusselt number -############################ - -.. seealso:: - - :ref:`Continuous counterpart `. - -********* -Heat flux -********* - -I consider :ref:`the discrete equation of the internal energy `: - -.. math:: - - \der{T}{t} - + - \dder{ - \ux - \dintrpa{T}{x} - }{x} - + - \dder{ - \uy - \dintrpa{T}{y} - }{y} - = - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left\{ - \dder{}{x} \left( \dder{T}{x} \right) - + - \dder{}{y} \left( \dder{T}{y} \right) - \right\}, - -which is defined at the cell center :math:`\left( \pic, \pjc \right)`, whose time average yields - -.. math:: - - \ave{ - \dder{ - \ux - \dintrpa{T}{x} - }{x} - }{t} - & + - \ave{ - \dder{ - \uy - \dintrpa{T}{y} - }{y} - }{t} \\ - & = - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left\{ - \ave{ - \dder{}{x} \left( \dder{T}{x} \right) - }{t} - + - \ave{ - \dder{}{y} \left( \dder{T}{y} \right) - }{t} - \right\}. - -Now I consider to integrate this equation in the homogeneous direction (:math:`y`), giving - -.. math:: - - \sum_{\pjc} - \ave{ - \dder{ - \ux - \dintrpa{T}{x} - }{x} - }{t} - \Delta y - = - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \dder{}{x} \left( \dder{T}{x} \right) - }{t} - \Delta y - -since the discretely-conservative terms in the :math:`y` direction - -.. math:: - - \dder{}{y} \left( \cdots \right) - -disappear. - -I further integrate this equation in the wall-normal direction from one place :math:`x = x_0` to the other :math:`x = x_1`: - -.. math:: - - \sum_{\pjc} \sum_{\pic} - \ave{ - \dder{ - \ux - \dintrpa{T}{x} - }{x} - }{t} - \Delta x - \Delta y - = - \sum_{\pjc} \sum_{\pic} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \dder{}{x} \left( \dder{T}{x} \right) - }{t} - \Delta x - \Delta y, - -which is - -.. math:: - - \sum_{\pjc} - \ave{ - \vat{ - \ux - \dintrpa{T}{x} - }{x_1} - - - \vat{ - \ux - \dintrpa{T}{x} - }{x_0} - }{t} - \Delta y - = - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \left\{ - \vat{ - \ave{ - \dder{T}{x} - }{t} - }{x_1} - - - \vat{ - \ave{ - \dder{T}{x} - }{t} - }{x_0} - \right\} - \Delta y, - -or equivalently - -.. math:: - - & \sum_{\pjc} - \left[ - \ave{ - \ux - \dintrpa{T}{x} - }{t} - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \dder{T}{x} - }{t} - \right]_{x_1} - \Delta y \\ - & = \\ - & \sum_{\pjc} - \left[ - \ave{ - \ux - \dintrpa{T}{x} - }{t} - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \dder{T}{x} - }{t} - \right]_{x_0} - \Delta y. - -Notice that, since the integrand is defined at cell centers, :math:`x_0` and :math:`x_1` should be the :math:`x` cell faces. - -Finally, I define the discrete time-averaged net heat flux: - -.. _eq_heat_flux_discrete: - -.. math:: - - \vat{J_{T}^D}{\xic} - \equiv - \sum_{\pjc} - \left[ - \ave{ - \ux - \dintrpa{T}{x} - }{t} - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \dder{T}{x} - }{t} - \right]_{\xic} - \Delta y, - -which is constant for all :math:`x` faces. - -.. note:: - - In the continuous space, the net heat flux is constant at any :math:`x`. - In the discrete space, on the other hand, the above relation holds only at each :math:`x` cell face, i.e. there is no guarantee that the above relation holds at cell centers. - Also the same interpolating and the differential operators as the equation of the internal energy should be used to compute the above relation to rigorously satisfy the relation. - -************** -Nusselt number -************** - -To define the Nusselt number in the discrete space, I consider the discrete convective heat flux, which is - -.. math:: - - \vat{J_{T,ref}^D}{\xic} - \equiv - \sum_{\pjc} - \left[ - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \dder{T_{ref}}{x} - }{t} - \right]_{\xic} - \Delta y, - -where :math:`T_{ref}` is the temperature field without the convective effect and simply given by solving the discrete second-order ordinary differential equation - -.. math:: - - \dder{}{x} \dder{\vat{T_{ref}}{\pic}}{x} = 0 - -under the Dirichlet boundary conditions: - -.. math:: - - \vat{T_{ref}}{x = 0} - \vat{T_{ref}}{x = 1} = 1. - -.. note:: - - I normalise the governing equations such that the temperature difference between the walls is unity. - See :ref:`the governing equations `. - -giving - -.. math:: - - \vat{T_{ref}}{\pic} - = - C - \vat{x}{\pic}, - -or - -.. math:: - - \vat{\dder{T_{ref}}{x}}{\xic} - = - -1, - -and thus - -.. math:: - - \vat{J_{T,ref}^D}{\xic} - = - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \Delta y - = - const. - -Now I can define the discrete Nusselt number as the ratio of the time-averaged discrete heat flux defined at the :math:`x` cell face: - -.. math:: - - \vat{J_{T}^D}{\xic} - -to the discrete reference value without convection: - -.. math:: - - J_{T,ref}^D - = - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \Delta y, - -namely - -.. _eq_nu_definition_discrete: - -.. math:: - - Nu - \equiv - \frac{ - \vat{J_{T}^D}{\xic} - }{ - J_{T,ref}^D - } - = - \frac{ - \sum_{\pjc} - \left[ - \ave{ - \ux - \dintrpa{T}{x} - }{t} - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \dder{T}{x} - }{t} - \right]_{\xic} - \Delta y - }{ - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \Delta y - }. - -Note again that this relation is only valid where :math:`\ux` is defined. - -To compute this quantity easily, I consider the above equation at the walls, giving - -.. math:: - - Nu_{wall} - = - \frac{ - J_{T,wall}^D - }{ - J_{T,ref}^D - } - = - \frac{ - \sum_{\pjc} - \left[ - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \dder{T}{x} - }{t} - \right]_{wall} - \Delta y - }{ - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \Delta y - }. - -************** -Implementation -************** - -Numerically, I monitor the instantaneous value - -.. math:: - - Nu_{wall} \left( t \right) - = - \frac{ - \sum_{\pjc} - \left[ - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \dder{T}{x} \left( t \right) - \right]_{wall} - \Delta y - }{ - J_{T,ref}^D - } - -by assuming that the temporal and the spatial treatments are commutative, since I cannot perform the time average when the simulation is running. - -.. myliteralinclude:: /../../src/logging/nusselt/heat_flux.c - :language: c - :tag: heat flux on the walls - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/kinetic_energy_dissipation.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/kinetic_energy_dissipation.rst deleted file mode 100644 index 01834aea..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/kinetic_energy_dissipation.rst +++ /dev/null @@ -1,292 +0,0 @@ - -.. _nu_kinetic_energy_dissipation_discrete: - -###################################################### -Nusselt number based on the kinetic energy dissipation -###################################################### - -.. seealso:: - - :ref:`Continuous counterpart `. - -********** -Derivation -********** - -I focus on :ref:`the discrete and volume-integrated equation of the squared velocity `. - -.. math:: - - & - \der{}{t} \left( - \sum_{\xjc} \sum_{\xic} \frac{1}{2} \ux^2 \Delta x \Delta y - + - \sum_{\yjc} \sum_{\yic} \frac{1}{2} \uy^2 \Delta x \Delta y - \right) \\ - = - & - - - \sum_{\xjc} \sum_{\xic} - \frac{\sqrt{Pr}}{\sqrt{Ra}} - \left[ - \dintrpv{ - \left( \dder{\ux}{x} \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{\ux}{y} \right)^2 - }{y} - \right] - \Delta x \Delta y \\ - & - - - \sum_{\yjc} \sum_{\yic} - \frac{\sqrt{Pr}}{\sqrt{Ra}} - \left[ - \dintrpv{ - \left\{ - C - \left( \dder{\uy}{x} \right)^2 - \right\} - }{x} - + - \dintrpa{ - \left( \dder{\uy}{y} \right)^2 - }{y} - \right] - \Delta x \Delta y \\ - & - + - \sum_{\xjc} \sum_{\xic} - \ux \dintrpu{T}{x} - \Delta x \Delta y. - -Since I am interested in the statistically-steady state, the left-hand-side term is zero and I have - -.. math:: - - & - \sum_{\xjc} \sum_{\xic} - \frac{\sqrt{Pr}}{\sqrt{Ra}} - \ave{ - \dintrpv{ - \left( \dder{\ux}{x} \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{\ux}{y} \right)^2 - }{y} - }{t} - \Delta x \Delta y \\ - + - & - \sum_{\yjc} \sum_{\yic} - \frac{\sqrt{Pr}}{\sqrt{Ra}} - \ave{ - \dintrpv{ - \left\{ - C - \left( \dder{\uy}{x} \right)^2 - \right\} - }{x} - + - \dintrpa{ - \left( \dder{\uy}{y} \right)^2 - }{y} - }{t} - \Delta x \Delta y \\ - = - & - \sum_{\xjc} \sum_{\xic} - \ave{\ux \dintrpu{T}{x}}{t} - \Delta x \Delta y. - -The right-hand-side term is the kinetic energy injection which is discussed in :ref:`the previous page `. -To equate them, finally I notice that the unknown interpolation - -.. math:: - - \dintrpu{T}{x} - -should be - -.. math:: - - \dintrpa{T}{x}. - -Also, from :ref:`the previous page `, I have - -.. math:: - - Nu - = - \frac{ - \sum_{\pjc} \sum_{\xic} - \ave{ - \ux - \dintrpa{T}{x} - }{t} - \Delta x - \Delta y - }{ - J_{T,ref}^D - } - + - 1, - -and by replacing the kinetic energy injection by the kinetic energy dissipation, I obtain - -.. math:: - - Nu - & = - \frac{ - \sum_{\xjc} \sum_{\xic} - \frac{\sqrt{Pr}}{\sqrt{Ra}} - \ave{ - \dintrpv{ - \left( \dder{\ux}{x} \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{\ux}{y} \right)^2 - }{y} - }{t} - \Delta x \Delta y - }{ - J_{T,ref}^D - } \\ - & + - \frac{ - \sum_{\yjc} \sum_{\yic} - \frac{\sqrt{Pr}}{\sqrt{Ra}} - \ave{ - \dintrpv{ - \left\{ - C - \left( \dder{\uy}{x} \right)^2 - \right\} - }{x} - + - \dintrpa{ - \left( \dder{\uy}{y} \right)^2 - }{y} - }{t} - \Delta x \Delta y - }{ - J_{T,ref}^D - } \\ - & + - 1, - -which is the discrete analogue of the Nusselt number based on the kinetic energy dissipation. - -************** -Implementation -************** - -I monitor the instantaneous value: - -.. math:: - - Nu \left( t \right) - & = - \frac{ - \sum_{\xjc} \sum_{\xic} - \frac{\sqrt{Pr}}{\sqrt{Ra}} - \left[ - \dintrpv{ - \left( \dder{\ux}{x} \left( t \right) \right)^2 - }{x} - + - \dintrpa{ - \left( \dder{\ux}{y} \left( t \right) \right)^2 - }{y} - \right] - \Delta x \Delta y - }{ - J_{T,ref}^D - } \\ - & + - \frac{ - \sum_{\yjc} \sum_{\yic} - \frac{\sqrt{Pr}}{\sqrt{Ra}} - \left[ - \dintrpv{ - \left\{ - C - \left( \dder{\uy}{x} \left( t \right) \right)^2 - \right\} - }{x} - + - \dintrpa{ - \left( \dder{\uy}{y} \left( t \right) \right)^2 - }{y} - \right] - \Delta x \Delta y - }{ - J_{T,ref}^D - } \\ - & + - 1. - -The numerator (dissipation of the discrete kinetic energy) is summed (for each velocity component, for each direction), which is normalised by the reference value and the laminar contribution is added: - -.. myliteralinclude:: /../../src/logging/nusselt/kinetic_energy_dissipation.c - :language: c - :tag: compute kinetic energy dissipation - -* :math:`\ux-x` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/kinetic_energy_dissipation.c - :language: c - :tag: ux-x contribution - -* :math:`\ux-y` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/kinetic_energy_dissipation.c - :language: c - :tag: ux-y contribution - -* :math:`\ux-z` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/kinetic_energy_dissipation.c - :language: c - :tag: ux-z contribution - -* :math:`\uy-x` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/kinetic_energy_dissipation.c - :language: c - :tag: uy-x contribution - -* :math:`\uy-y` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/kinetic_energy_dissipation.c - :language: c - :tag: uy-y contribution - -* :math:`\uy-z` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/kinetic_energy_dissipation.c - :language: c - :tag: uy-z contribution - -* :math:`\uz-x` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/kinetic_energy_dissipation.c - :language: c - :tag: uz-x contribution - -* :math:`\uz-y` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/kinetic_energy_dissipation.c - :language: c - :tag: uz-y contribution - -* :math:`\uz-z` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/kinetic_energy_dissipation.c - :language: c - :tag: uz-z contribution - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/kinetic_energy_injection.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/kinetic_energy_injection.rst deleted file mode 100644 index 43f57f77..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/kinetic_energy_injection.rst +++ /dev/null @@ -1,167 +0,0 @@ - -.. _nu_kinetic_energy_injection_discrete: - -#################################################### -Nusselt number based on the kinetic energy injection -#################################################### - -.. seealso:: - - :ref:`Continuous counterpart `. - -********** -Derivation -********** - -Recall that :ref:`the definition of the discrete Nusselt number ` is - -.. math:: - - \vat{J_{T}^D}{\xic} - = - Nu - \times - J_{T,ref}^D. - -Now I consider to integrate this equation from the bottom to the top walls. - -The right-hand-side term yields - -.. math:: - - \sum_{\xic} - Nu - \times - J_{T,ref}^D - \Delta x - & = - Nu - \times - J_{T,ref}^D - \sum_{\xic} - \Delta x \\ - & = - Nu - \times - J_{T,ref}^D \\ - & \left( \because l_x \equiv 1 \right), - -while the left-hand side leads to - -.. math:: - - \sum_{\xic} - J_{T}^D - \Delta x - & = - \sum_{\xic} - \sum_{\pjc} - \left[ - \ave{ - \ux - \dintrpa{T}{x} - }{t} - - - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \dder{T}{x} - }{t} - \right] - \Delta y - \Delta x \\ - & = - \sum_{\pjc} \sum_{\xic} - \ave{ - \ux - \dintrpa{T}{x} - }{t} - \Delta x - \Delta y \\ - & - - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \left( - \ave{\vat{T}{x = 1}}{t} - - - \ave{\vat{T}{x = 0}}{t} - \right) - \Delta y. - -The second term yields - -.. math:: - - + - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \Delta y - -because of the boundary condition of the temperature field, and this is equal to :math:`J_{T,ref}^D`. - -Thus I notice - -.. math:: - - Nu - \times - J_{T,ref}^D - = - \sum_{\pjc} \sum_{\xic} - \ave{ - \ux - \dintrpa{T}{x} - }{t} - \Delta x - \Delta y - + - J_{T,ref}^D, - -or equivalently - - -.. math:: - - Nu - = - \frac{ - \sum_{\pjc} \sum_{\xic} - \ave{ - \ux - \dintrpa{T}{x} - }{t} - \Delta x - \Delta y - }{ - J_{T,ref}^D - } - + - 1, - -which is the discrete analogue of the Nusselt number based on the kinetic energy injection. - -************** -Implementation -************** - -I monitor the instantaneous value: - -.. math:: - - Nu \left( t \right) - = - \frac{ - \sum_{\pjc} \sum_{\xic} - \ux \left( t \right) - \dintrpa{T}{x} \left( t \right) - \Delta x - \Delta y - }{ - J_{T,ref}^D - } - + - 1. - -.. myliteralinclude:: /../../src/logging/nusselt/kinetic_energy_injection.c - :language: c - :tag: energy injection - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/main.rst deleted file mode 100644 index c2841824..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/main.rst +++ /dev/null @@ -1,28 +0,0 @@ - -.. _nusselt_number_relations: - -######################## -Nusselt number relations -######################## - -In this part, I consider the discrete Nusselt number relations, whose continuous version is derived in :ref:`the governing equations `. -In the continuous space, I first define the (temporally-averaged) heat flux, which is followed by the definition of the Nusselt number :math:`Nu`. -Then I derive other several relations to compute :math:`Nu`, all of which are equivalent to each other. -In this section, I apply the same procedure to derive the discrete analogues. - -.. note:: - - In general, the following relations assume that the system is in the statistically-steady state, i.e. they are satisfied after averaged in time :math:`\ave{q}{t}`. - -.. toctree:: - :maxdepth: 1 - - heat_flux - kinetic_energy_injection - kinetic_energy_dissipation - thermal_energy_dissipation - -.. seealso:: - - These relations are used to monitor the simulations, which are implemented in :ref:`src/logging/nusselt `. - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/thermal_energy_dissipation.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/thermal_energy_dissipation.rst deleted file mode 100644 index cafe1b3a..00000000 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/derivation/nusselt/thermal_energy_dissipation.rst +++ /dev/null @@ -1,207 +0,0 @@ - -.. _nu_thermal_energy_dissipation_discrete: - -###################################################### -Nusselt number based on the thermal energy dissipation -###################################################### - -.. seealso:: - - :ref:`Continuous counterpart `. - -********** -Derivation -********** - -I focus on :ref:`the discrete and volume-integrated equation of the squared temperature `. - -.. math:: - - & - \der{}{t} \left( - \sum_{\pjc} \sum_{\pic} \frac{1}{2} T^2 \Delta x \Delta y - \right) \\ - = - & - - - \sum_{\pjc} \sum_{\pic} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \left[ - \dintrpv{ - \left\{ - C - \left( \dder{T}{x} \right)^2 - \right\} - }{x} - + - \dintrpa{ - \left( \dder{T}{y} \right)^2 - }{y} - \right] - \Delta x \Delta y \\ - & - + - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \left( - \vat{ - T \dder{T}{x} - }{x = 1} - - - \vat{ - T \dder{T}{x} - }{x = 0} - \right) - \Delta y. - -Since I am interested in the statistically-steady state, the left-hand-side term is zero and I have - -.. math:: - - & - \sum_{\pjc} \sum_{\pic} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \dintrpv{ - \left\{ - C - \left( \dder{T}{x} \right)^2 - \right\} - }{x} - + - \dintrpa{ - \left( \dder{T}{y} \right)^2 - }{y} - }{t} - \Delta x \Delta y \\ - & - = - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \vat{ - T \dder{T}{x} - }{x = 1} - - - \vat{ - T \dder{T}{x} - }{x = 0} - }{t} - \Delta y. - -The right-hand side yields - -.. math:: - - & - \vat{T}{x = 1} - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \vat{ - \dder{T}{x} - }{x = 1} - }{t} - \Delta y - - - \vat{T}{x = 0} - \sum_{\pjc} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \vat{ - T \dder{T}{x} - }{x = 0} - }{t} - \Delta y \\ - = - & - - - \vat{T}{x = 1} - Nu J_{T,ref}^D - + - \vat{T}{x = 0} - Nu J_{T,ref}^D \\ - = - & - Nu J_{T,ref}^D, - -and thus - -.. math:: - - Nu - = - \frac{ - \sum_{\pjc} \sum_{\pic} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \ave{ - \dintrpv{ - \left\{ - C - \left( \dder{T}{x} \right)^2 - \right\} - }{x} - + - \dintrpa{ - \left( \dder{T}{y} \right)^2 - }{y} - }{t} - \Delta x \Delta y - }{ - J_{T,ref}^D - }. - -************** -Implementation -************** - -I monitor the instantaneous value: - -.. math:: - - Nu \left( t \right) - = - \frac{ - \sum_{\pjc} \sum_{\pic} - \frac{1}{\sqrt{Pr} \sqrt{Ra}} - \left[ - \dintrpv{ - \left\{ - C - \left( \dder{T}{x} \left( t \right) \right)^2 - \right\} - }{x} - + - \dintrpa{ - \left( \dder{T}{y} \left( t \right) \right)^2 - }{y} - \right] - \Delta x \Delta y - }{ - J_{T,ref}^D - }. - -All contributions in the numerator are added first, which is normalised by the reference value: - -.. myliteralinclude:: /../../src/logging/nusselt/thermal_energy_dissipation.c - :language: c - :tag: compute thermal energy dissipation - -* :math:`x` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/thermal_energy_dissipation.c - :language: c - :tag: x contribution - -* :math:`y` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/thermal_energy_dissipation.c - :language: c - :tag: y contribution - -* :math:`z` contribution - - .. myliteralinclude:: /../../src/logging/nusselt/thermal_energy_dissipation.c - :language: c - :tag: z contribution - diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/figures/plot.gp b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/figures/plot.gp new file mode 100644 index 00000000..9db422d3 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/figures/plot.gp @@ -0,0 +1,125 @@ +# physical coordinate +{ + reset + xmin = - 0.5 + xmax = + 4.0 + ymin = - 0.5 + ymax = + 4.0 + lx = xmax-xmin + ly = ymax-ymin + set terminal epslatex standalone color size lx,ly font ',12' + set output 'car.tex' + unset border + set lmargin 0 + set rmargin 0 + set bmargin 0 + set tmargin 0 + unset xlabel + unset ylabel + set xrange [xmin:xmax] + set yrange [ymin:ymax] + unset xtics + unset ytics + set size ratio -1 + set samples 10000 + set style line 1 lc rgb '#000000' lw 10 + num = 8 + xmin = 0. + xmax = 3.5 + ymin = 0. + ymax = 3.5 + deltax = (xmax - xmin) / num + deltay = (ymax - ymin) / num + do for [i = 0 : num : 1] { + if (0 == i) { + x = xmin + } else if (num == i) { + x = xmax + } else { + x = i - num / 2 + x = xmin + (xmax - xmin) * 1. / (1. + exp(- 1. * x)) + } + set arrow from first x, first ymin to first x, first ymax nohead ls 1 + y = ymin + i * deltay + set arrow from first xmin, first y to first xmax, first y nohead ls 1 + } + plot \ + NaN notitle +} + +# computational coordinate +{ + reset + xmin = - 0.5 + xmax = + 4.0 + ymin = - 0.5 + ymax = + 4.0 + lx = xmax-xmin + ly = ymax-ymin + set terminal epslatex standalone color size lx,ly font ',12' + set output 'comp.tex' + unset border + set lmargin 0 + set rmargin 0 + set bmargin 0 + set tmargin 0 + unset xlabel + unset ylabel + set xrange [xmin:xmax] + set yrange [ymin:ymax] + unset xtics + unset ytics + set size ratio -1 + set samples 10000 + set style line 1 lc rgb '#000000' lw 10 + num = 8 + xmin = 0. + xmax = 3.5 + ymin = 0. + ymax = 3.5 + deltax = (xmax - xmin) / num + deltay = (ymax - ymin) / num + do for [i = 0 : num : 1] { + x = xmin + i * deltax + set arrow from first x, first ymin to first x, first ymax nohead ls 1 + y = ymin + i * deltay + set arrow from first xmin, first y to first xmax, first y nohead ls 1 + } + plot \ + NaN notitle +} + +# merge above elements +{ + reset + xmin = 0.0 + xmax = 9.0 + ymin = 0.0 + ymax = 5.25 + lx = xmax - xmin + ly = ymax - ymin + set terminal epslatex standalone color size lx,ly font ',20' + set output 'result.tex' + unset border + set lmargin 0 + set rmargin 0 + set bmargin 0 + set tmargin 0 + unset xlabel + unset ylabel + set xrange [xmin:xmax] + set yrange [ymin:ymax] + unset xtics + unset ytics + set size ratio -1 + ref = 4.5 + set label 'Physical coordinate' center at first 0.5 * ref, first 1. * ref + 0.35 textcolor rgb '#000000' + set label 'Computational coordinate' center at first 1.5 * ref, first 1. * ref + 0.35 textcolor rgb '#000000' + set label '$\left( x, y \right)$' center at first 0.5 * ref, first 1. * ref - 0.05 textcolor rgb '#000000' + set label '$\left( \xi^1, \xi^2 \right)$' center at first 1.5 * ref, first 1. * ref - 0.05 textcolor rgb '#000000' + set label '\includegraphics[width=4.500in, height=4.500in]{car.pdf}' center at first 0.5 * ref, first 0.5 * ref + set label '\includegraphics[width=4.500in, height=4.500in]{comp.pdf}' center at first 1.5 * ref, first 0.5 * ref + plot \ + NaN notitle +} + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/figures/result.png b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/figures/result.png new file mode 100644 index 0000000000000000000000000000000000000000..fb1a5096fb17900bb8e05f05709c5ae18ce8e3ff GIT binary patch literal 13926 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 literal 0 HcmV?d00001 diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/main.rst index 4beeab1c..b9a12be1 100644 --- a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/main.rst +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/main.rst @@ -4,98 +4,39 @@ .. include:: /references.txt ############################ -Discrete governing equations +Discrete Governing Equations ############################ -************ -Introduction -************ - -:ref:`The SMAC method ` adopted in :ref:`the temporal discretisation ` is a *de facto standard* to integrate the momentum balance in time while keeping the incompressibility constraint. -Spatial treatments are, on the other hand, more flexible and a *proper* scheme is totally case-dependent. -In this section, I focus on the following two features and derive the *discrete* governing equations :ref:`which are implemented in this project `. +:ref:`The SMAC method ` adopted in :ref:`the temporal discretization ` is a *de facto standard* to integrate the momentum balance in time while keeping the incompressibility constraint. +Spatial treatments are, on the other hand, more flexible, and a proper scheme is totally case-dependent. +In this section, we focus on the following two features and derive the discrete governing equations which are implemented in this project. * Being simple - In this project, I only consider the second-order-accurate central-difference schemes, where three points in one direction (one and its two neighbouring points) are used to evaluate the differentiations. - Although its accuracy is not at all comparable to the spectral methods, it is versatile and robust. - -* Mimicking the equations in the continuous domain - - Since I discretise and approximate the equations, it is impossible to keep all properties which the original equations have in the continuous domain. - However, as reviewed in :ref:`the governing equations `, there are several important relations among others, e.g. - - * The conservative terms do not alter the net amount of quantity, namely :math:`K \equiv \int k dV` and :math:`H \equiv \int h dV` are conserved in the absence of the dissipations. - - * The dissipative terms in the equations of the squared quantity (:math:`k` and :math:`h`) reduce the total amount of quantity. - - * Multiple Nusselt number descriptions yield statistically equivalent results. - - I focus on keeping these properties in the discrete domain. - -.. note:: - - In short, the conclusive treatment for the advective terms here is known as the energy-conserving scheme. - To derive it, the equations are usually once mapped to a general coordinate system where the grid sizes in all directions are equidistant, which is followed by the discretisation (e.g. |KAJISHIMA1999|). - - Although it is a consistent and a powerful way, it tends to be fairly complicated (see the appendix, where I do that). - In this section, I try to deduce the same conclusion without using this transform but by focusing on some simple relations of the discrete derivatives and integrals. - Also the same concept is adopted to the internal energy equation. + In this project, we only consider second-order-accurate central-difference schemes, where three points in one direction (one and its two neighboring points) are used to evaluate the differentiations. + Although its accuracy is not at all comparable to spectral methods, it is versatile, robust, and also applicable to multiphase flows due to its locality. -*********** -Derivations -*********** +* Preserving properties of the original equations -.. note:: + Since we approximate the equations by discretizations, it is impossible to keep all properties that the original equations have in the continuous domain. + However, as reviewed in :ref:`the governing equations `, there are several important relations, among others, e.g., - Hereafter I use notations like + * The conservative terms do not alter the net amount of quadratic quantities, namely :math:`\int k dV` and :math:`\int h dV` are conserved in the absence of viscous effects. - .. math:: + * The dissipative terms in the equations of the squared quantities (:math:`k` and :math:`h`) reduce the total amount of the quantity. - \sum_{\pic} q + We focus on replicating these properties in the discrete domain. - indicating that the quantity :math:`q`, which is defined at the cell-center, is summed in the :math:`x` direction. - - Similarly I define - - .. math:: - - \sum_{\xic} q - - to imply that the quantity :math:`q`, which is defined at the cell-face, is summed in the :math:`x` direction. +In short, the conclusive treatment for the advective terms here is known as the energy-conserving scheme, and we refer to the overall scheme which treat all other terms in a consistent manner as *energy-consistent* scheme. .. toctree:: - :caption: Details and derivations - :maxdepth: 1 + :maxdepth: 1 - derivation/basic_operators/main.rst - derivation/incompressibility_and_poisson_equation - derivation/momentum_balance/main - derivation/internal_energy_balance/main - derivation/nusselt/main + strong_conservation_form + resulting_schemes/main -.. toctree:: - :caption: Appendix - :maxdepth: 1 +.. seealso:: - appendix/inconsistent_results/main - appendix/strong_conservation_form/main - -********** -Conclusion -********** - -The following equations are implemented in the code, which are designed to keep the properties of the original governing equations discussed above. -See :ref:`the basic operators ` for details about the used symbols. - -.. toctree:: - :maxdepth: 1 - - conclusion/incompressibility - conclusion/x_momentum/main - conclusion/y_momentum/main - conclusion/z_momentum/main - conclusion/internal_energy/main - conclusion/squared_velocity - conclusion/squared_temperature + * |KAJISHIMA1999| + * |KAJISHIMA2017| diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/main.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/main.rst new file mode 100644 index 00000000..961dc7e5 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/main.rst @@ -0,0 +1,174 @@ + +.. include:: /references.txt + +################# +Resulting schemes +################# + +We discretize the mentioned equations as follows. +Note that we define several symbols for notational convenience. + +.. toctree:: + :maxdepth: 1 + + symbols/average + symbols/differentiation + symbols/summation + +The advective terms are implemented in tri-diagonal-like ways to insist the advective operators are skew-symmetric (c.f., |VERSTAPPEN2003|). +Since the diffusive terms are given by the vector Laplacian of the velocity vector, the discrete Laplace operators are pre-computed. + +.. _discrete_incompressibility: + +**************************** +Incompressibility constraint +**************************** + +We define this relation for each cell center :math:`\left( \ccidx{i}, \ccidx{j}, \ccidx{k} \right)`: + +.. math:: + + \ddiv{1} + + + \ddiv{2} + + + \ddiv{3} + = + 0. + +The left-hand side is monitored to confirm if the maximum divergence of the flow field is sufficiently small (:math:`\approx 0`): + +.. myliteralinclude:: /../../src/logging/max_divergence.c + :language: c + :tag: check max local divergence + +.. _discrete_momentum_balance: + +**************** +Momentum balance +**************** + +The following relations are defined at the corresponding cell faces. + +.. math:: + + \pder{\vel{1}}{t} + = + & + - + \dmomadv{1}{1} + - + \dmomadv{2}{1} + - + \dmomadv{3}{1} + + & + - + \dmompre{1} + + & + + + \dmomdif{1}{1} + + + \dmomdif{2}{1} + + + \dmomdif{3}{1} + + & + + + \dmombuo. + +.. math:: + + \pder{\vel{2}}{t} + = + & + - + \dmomadv{1}{2} + - + \dmomadv{2}{2} + - + \dmomadv{3}{2} + + & + - + \dmompre{2} + + & + + + \dmomdif{1}{2} + + + \dmomdif{2}{2} + + + \dmomdif{3}{2}. + +.. math:: + + \pder{\vel{3}}{t} + = + & + - + \dmomadv{1}{3} + - + \dmomadv{2}{3} + - + \dmomadv{3}{3} + + & + - + \dmompre{3} + + & + + + \dmomdif{1}{3} + + + \dmomdif{2}{3} + + + \dmomdif{3}{3}. + +The implementations are elaborated below. + +.. toctree:: + :maxdepth: 1 + + momentum/adv + momentum/pre + momentum/dif + momentum/buo + +.. _discrete_internal_energy_balance: + +*********************** +Internal energy balance +*********************** + +We define this relation for each cell center :math:`\left( \ccidx{i}, \ccidx{j}, \ccidx{k} \right)`: + +.. math:: + + \pder{T}{t} + = + & + - + \dtempadv{1} + - + \dtempadv{2} + - + \dtempadv{3} + + & + + + \dtempdif{1} + + + \dtempdif{2} + + + \dtempdif{3}. + +The implementations are elaborated below. + +.. toctree:: + :maxdepth: 1 + + t/adv + t/dif + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/adv.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/adv.rst new file mode 100644 index 00000000..ffebec3a --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/adv.rst @@ -0,0 +1,97 @@ +######### +Advection +######### + +******************** +Wall-normal relation +******************** + +.. math:: + + - + \dmomadv{1}{1} + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: advected in x + +.. math:: + + - + \dmomadv{2}{1} + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: advected in y + +.. math:: + + - + \dmomadv{3}{1} + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: advected in z + +******************** +Stream-wise relation +******************** + +.. math:: + + - + \dmomadv{1}{2} + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: advected in x + +.. math:: + + - + \dmomadv{2}{2} + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: advected in y + +.. math:: + + - + \dmomadv{3}{2} + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: advected in z + +****************** +Span-wise relation +****************** + +.. math:: + + - + \dmomadv{1}{3} + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: advected in x + +.. math:: + + - + \dmomadv{2}{3} + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: advected in y + +.. math:: + + - + \dmomadv{3}{3} + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: advected in z + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/buo.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/buo.rst new file mode 100644 index 00000000..22fb5831 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/buo.rst @@ -0,0 +1,12 @@ +######## +Buoyancy +######## + +.. math:: + + \dmombuo + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: buoyancy effect + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/dif.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/dif.rst new file mode 100644 index 00000000..e7c64b2a --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/dif.rst @@ -0,0 +1,124 @@ +######### +Diffusion +######### + +******************** +Wall-normal relation +******************** + +.. math:: + + \dmomdif{1}{1} + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: vector laplacian in x + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: diffused in x + +.. math:: + + \dmomdif{2}{1} + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: vector laplacian in y + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: diffused in y + +.. math:: + + \dmomdif{3}{1} + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: vector laplacian in z + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: diffused in z + +******************** +Stream-wise relation +******************** + +.. math:: + + \dmomdif{1}{2} + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: vector laplacian in x + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: diffused in x + +.. math:: + + \dmomdif{2}{2} + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: vector laplacian in y + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: diffused in y + +.. math:: + + \dmomdif{3}{2} + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: vector laplacian in z + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: diffused in z + +****************** +Span-wise relation +****************** + +.. math:: + + \dmomdif{1}{3} + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: diffused in x + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: vector laplacian in x + +.. math:: + + \dmomdif{2}{3} + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: diffused in y + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: vector laplacian in y + +.. math:: + + \dmomdif{3}{3} + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: vector laplacian in z + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: diffused in z + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/pre.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/pre.rst new file mode 100644 index 00000000..0d67d0a5 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/momentum/pre.rst @@ -0,0 +1,43 @@ +################# +Pressure-gradient +################# + +******************** +Wall-normal relation +******************** + +.. math:: + + - + \dmompre{1} + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: pressure gradient effect + +******************** +Stream-wise relation +******************** + +.. math:: + + - + \dmompre{2} + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: pressure gradient effect + +****************** +Span-wise relation +****************** + +.. math:: + + - + \dmompre{3} + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: pressure gradient effect + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/average.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/average.rst new file mode 100644 index 00000000..a2ad586f --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/average.rst @@ -0,0 +1,92 @@ +####### +Average +####### + +At :math:`\gcs{1}` cell faces: + +.. math:: + + \vat{ + \ave{q}{\gcs{1}} + }{ + i + \frac{1}{2} + } + = + \left\{ + \begin{alignedat}{2} + & \text{Negative wall:} & \vat{q}{\frac{1}{2}}, \\ + & \text{Positive wall:} & \vat{q}{\ngp{1} + \frac{1}{2}}, \\ + & \text{Otherwise:} & \frac{1}{2} \vat{q}{i} + \frac{1}{2} \vat{q}{i + 1}. + \end{alignedat} + \right. + +At :math:`\gcs{1}` cell centers: + +.. math:: + + \vat{ + \ave{q}{\gcs{1}} + }{ + i + } + = + \frac{1}{2} \vat{q}{i - \frac{1}{2}} + + + \frac{1}{2} \vat{q}{i + \frac{1}{2}}. + +At :math:`\gcs{2}` cell faces: + +.. math:: + + \vat{ + \ave{q}{\gcs{2}} + }{ + j + \frac{1}{2} + } + = + \frac{1}{2} \vat{q}{j} + + + \frac{1}{2} \vat{q}{j + 1}. + +At :math:`\gcs{2}` cell centers: + +.. math:: + + \vat{ + \ave{q}{\gcs{2}} + }{ + j + } + = + \frac{1}{2} \vat{q}{j - \frac{1}{2}} + + + \frac{1}{2} \vat{q}{j + \frac{1}{2}}. + +At :math:`\gcs{3}` cell faces: + +.. math:: + + \vat{ + \ave{q}{\gcs{3}} + }{ + k + \frac{1}{2} + } + = + \frac{1}{2} \vat{q}{k} + + + \frac{1}{2} \vat{q}{k + 1}. + +At :math:`\gcs{3}` cell centers: + +.. math:: + + \vat{ + \ave{q}{\gcs{3}} + }{ + k + } + = + \frac{1}{2} \vat{q}{k - \frac{1}{2}} + + + \frac{1}{2} \vat{q}{k + \frac{1}{2}}. + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/differentiation.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/differentiation.rst new file mode 100644 index 00000000..7345a0e4 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/differentiation.rst @@ -0,0 +1,95 @@ +############### +Differentiation +############### + +At :math:`\gcs{1}` cell faces: + +.. math:: + + \vat{ + \dif{q}{\gcs{1}} + }{ + i + \frac{1}{2} + } + = + \left\{ + \begin{alignedat}{2} + & \text{Negative wall:} & - \vat{q}{\frac{1}{2}} + \vat{q}{1}, \\ + & \text{Positive wall:} & - \vat{q}{\ngp{1}} + \vat{q}{\ngp{1} + \frac{1}{2}}, \\ + & \text{Otherwise:} & - \vat{q}{i} + \vat{q}{i + 1}. + \end{alignedat} + \right. + +At :math:`\gcs{1}` cell centers: + +.. math:: + + \vat{ + \dif{q}{\gcs{1}} + }{ + i + } + = + - \vat{q}{i - \frac{1}{2}} + + \vat{q}{i + \frac{1}{2}}. + +At :math:`\gcs{2}` cell faces: + +.. math:: + + \vat{ + \dif{q}{\gcs{2}} + }{ + j + \frac{1}{2} + } + = + - + \vat{q}{j} + + + \vat{q}{j + 1}. + +At :math:`\gcs{2}` cell centers: + +.. math:: + + \vat{ + \dif{q}{\gcs{2}} + }{ + j + } + = + - + \vat{q}{j - \frac{1}{2}} + + + \vat{q}{j + \frac{1}{2}}. + +At :math:`\gcs{3}` cell faces: + +.. math:: + + \vat{ + \dif{q}{\gcs{3}} + }{ + k + \frac{1}{2} + } + = + - + \vat{q}{k} + + + \vat{q}{k + 1}. + +At :math:`\gcs{3}` cell centers: + +.. math:: + + \vat{ + \dif{q}{\gcs{3}} + }{ + k + } + = + - + \vat{q}{k - \frac{1}{2}} + + + \vat{q}{k + \frac{1}{2}}. + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/summation.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/summation.rst new file mode 100644 index 00000000..da941780 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/symbols/summation.rst @@ -0,0 +1,100 @@ +######### +Summation +######### + +At :math:`\gcs{1}` cell faces: + +.. math:: + + \sumxf q + \equiv + \vat{q}{\frac{1}{2}} + + + \vat{q}{\frac{3}{2}} + + + \cdots + + + \vat{q}{\ngp{1} - \frac{1}{2}} + + + \vat{q}{\ngp{1} + \frac{1}{2}}. + +At :math:`\gcs{1}` cell centers: + +.. math:: + + \sumxc q + \equiv + \vat{q}{1} + + + \vat{q}{2} + + + \cdots + + + \vat{q}{\ngp{1} - 1} + + + \vat{q}{\ngp{1}}. + +At :math:`\gcs{2}` cell faces: + +.. math:: + + \sumyf q + \equiv + \vat{q}{\frac{1}{2}} + + + \vat{q}{\frac{3}{2}} + + + \cdots + + + \vat{q}{\ngp{2} - \frac{3}{2}} + + + \vat{q}{\ngp{2} - \frac{1}{2}}. + +At :math:`\gcs{2}` cell centers: + +.. math:: + + \sumyc q + \equiv + \vat{q}{1} + + + \vat{q}{2} + + + \cdots + + + \vat{q}{\ngp{2} - 1} + + + \vat{q}{\ngp{2}}. + +At :math:`\gcs{3}` cell faces: + +.. math:: + + \sumzf q + \equiv + \vat{q}{\frac{1}{2}} + + + \vat{q}{\frac{3}{2}} + + + \cdots + + + \vat{q}{\ngp{3} - \frac{3}{2}} + + + \vat{q}{\ngp{3} - \frac{1}{2}}. + +At :math:`\gcs{3}` cell centers: + +.. math:: + + \sumzc q + \equiv + \vat{q}{1} + + + \vat{q}{2} + + + \cdots + + + \vat{q}{\ngp{3} - 1} + + + \vat{q}{\ngp{3}}. + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/t/adv.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/t/adv.rst new file mode 100644 index 00000000..c6c6d35a --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/t/adv.rst @@ -0,0 +1,31 @@ +######### +Advection +######### + +.. math:: + + - + \dtempadv{1} + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: advected in x + +.. math:: + + - + \dtempadv{2} + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: advected in y + +.. math:: + + - + \dtempadv{3} + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: advected in z + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/t/dif.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/t/dif.rst new file mode 100644 index 00000000..6ccc2841 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/resulting_schemes/t/dif.rst @@ -0,0 +1,40 @@ +######### +Diffusion +######### + +.. math:: + + \dtempdif{1} + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: diffused in x + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: scalar laplacian in x + +.. math:: + + \dtempdif{2} + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: diffused in y + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: scalar laplacian in y + +.. math:: + + \dtempdif{3} + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: diffused in z + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: scalar laplacian in z + diff --git a/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/strong_conservation_form.rst b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/strong_conservation_form.rst new file mode 100644 index 00000000..4aa5cf46 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/discrete_governing_equations/strong_conservation_form.rst @@ -0,0 +1,289 @@ + +.. _strong_conservation_form: + +.. include:: /references.txt + +##################################### +Equations in Strong Conservation Form +##################################### + +As explained in :ref:`the domain setup `, non-uniform grid spacings are adopted in the wall-normal direction to resolve boundary layers. +This makes interpolations of quantities ambiguous, as linear interpolations, arithmetic averages, and volumetric averages all yield different results. +As |HAM2002| pointed out, to obtain discrete advective terms that are energy-conserving, we need to utilize the volumetric averages to average some quantities, while arithmetic averages should be used for others, which is cumbersome and error-prone to implement. +To mitigate this issue, we perform a coordinate transform from the original coordinate (physical coordinate) to a new coordinate (computational coordinate): + +.. image:: figures/result.png + :width: 800 + :align: center + +Since the cell-face distances are always unity in the new coordinate system, arithmetic averages suffice for all interpolations. +This is a versatile approach and is applicable to `the other coordinate systems as well `_. + +Note, however, that the new coordinate system has different basis vectors compared to the original system. +The wall-normal basis vectors in the physical coordinate system are :math:`\basis{1}` with a unique length everywhere, while :math:`\gbasis{1}` varies in size. +We need to take this kind of *stretching* of the coordinates into account. +To this end, we write down the governing equations in a more general way, incorporating the spatial changes in the basis vectors (strong conservation form for rectilinear coordinates), giving the following relations: + +.. math:: + + \frac{1}{J} + \pder{}{\gcs{j}} + \left( + J + \gvel{j} + \right) + = + 0, + +.. math:: + + \gbasis{i} + \left[ + \pder{\gvel{i}}{t} + + + \frac{\gvel{j}}{\sfact{i}} + \pder{}{\gcs{j}} + \left( + \sfact{i} + \gvel{i} + \right) + + + \frac{1}{\sfact{i}} + \frac{1}{\sfact{i}} + \pder{p}{\gcs{i}} + - + \frac{\sqrt{Pr}}{\sqrt{Ra}} + \frac{1}{\sfact{i}} + \frac{1}{J} + \pder{}{\gcs{j}} + \left\{ + \frac{J}{\sfact{j}} + \frac{1}{\sfact{j}} + \pder{}{\gcs{j}} + \left( + \sfact{i} + \gvel{i} + \right) + \right\} + - + \frac{T}{\sfact{1}} + \delta_{i1} + \right] + = + 0_i, + +.. math:: + + \pder{T}{t} + + + \gvel{j} + \pder{T}{\gcs{j}} + - + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{1}{J} + \pder{}{\gcs{j}} + \left( + \frac{J}{\sfact{j}} + \frac{1}{\sfact{j}} + \pder{T}{\gcs{j}} + \right) + = + 0. + +:math:`\gvel{i}` are the contra-variant components of the velocity vector with co-variant basis vectors, i.e., + +.. math:: + + \vec{u} + \equiv + \basis{i} u_i + \equiv + \gbasis{i} \gvel{i}. + +The basis vectors and the components of the two coordinate systems are related by + +.. math:: + + \basis{i} + & + = + \pder{\gcs{j}}{x_i} + \gbasis{j}, + + u_i + & + = + \pder{x_i}{\gcs{j}} + \gvel{j}, + +or for rectilinear coordinates simply + +.. math:: + + \basis{i} + & + = + \pder{\gcs{i}}{x_i} + \gbasis{i} + \equiv + \frac{1}{\sfact{i}} + \gbasis{i} + \,\, + (\text{no summation rule over}\,i), + + u_i + & + = + \pder{x_i}{\gcs{i}} + \gvel{i} + \equiv + \sfact{i} + \gvel{i} + \,\, + (\text{no summation rule over}\,i), + +where :math:`\sfact{i}` is the scale factor. +Since we design the computational coordinate systems such that the grid sizes are unitary, the scale factors are given by the grid sizes in the physical coordinates: + +.. math:: + + \sfact{1} + & + \equiv + \Delta x, + + \sfact{2} + & + \equiv + \Delta y, + + \sfact{3} + & + \equiv + \Delta z, + +which are implemented in the code as follows. + +Note that, since the wall-normal grid sizes may differ, the wall-normal scale factors :math:`\sfact{1}` are stored at every wall-normal cell face and center. +At the faces, we calculate them using the cell-center distance + +.. math:: + + \vat{\sfact{1}}{\cmidx{i}} + = + \vat{x}{i} + - + \vat{x}{i-1}: + +.. myliteralinclude:: /../../src/domain.c + :language: c + :tag: x scale factors at x cell faces + +At the centers, we calculate them using the cell-face distance + +.. math:: + + \vat{\sfact{1}}{\ccidx{i}} + = + \vat{x}{\cpidx{1}} + - + \vat{x}{\cmidx{1}}: + +.. myliteralinclude:: /../../src/domain.c + :language: c + :tag: x scale factors at x cell centers + +The scale factors in the other directions (:math:`\sfact{2}, \sfact{3}`) are constant across the whole domain as we fix the grid sizes equidistant: + +.. myliteralinclude:: /../../src/domain.c + :language: c + :tag: y scale factor + +.. myliteralinclude:: /../../src/domain.c + :language: c + :tag: z scale factor + +Note that, by definition, the Jacobian determinant is given by + +.. math:: + + J + = + \Pi_i + \sfact{i}, + +which are implemented in the code as follows. +Note that they are also defined at the wall-normal cell faces and centers: + +.. myliteralinclude:: /../../src/domain.c + :language: c + :tag: jacobian determinants at x cell faces + +.. myliteralinclude:: /../../src/domain.c + :language: c + :tag: jacobian determinants at x cell centers + +Finally, since we usually discuss everything on the physical coordinate systems rather than on the computational coordinate systems, it is convenient to keep the velocity vector descriptions using :math:`\vel{i}` rather than :math:`\gvel{i}`, giving + +.. math:: + + \frac{1}{J} + \pder{}{\gcs{j}} + \left( + \frac{J}{\sfact{j}} + \vel{j} + \right) + = + 0, + +.. math:: + + \basis{i} + \left\{ + \pder{\vel{i}}{t} + + + \frac{\vel{j}}{\sfact{j}} + \pder{\vel{i}}{\gcs{j}} + + + \frac{1}{\sfact{i}} + \pder{p}{\gcs{i}} + - + \frac{\sqrt{Pr}}{\sqrt{Ra}} + \frac{1}{J} + \pder{}{\gcs{j}} + \left( + \frac{J}{\sfact{j}} + \frac{1}{\sfact{j}} + \pder{\vel{i}}{\gcs{j}} + \right) + - + T + \delta_{i1} + \right\} + = + 0_i, + +.. math:: + + \pder{T}{t} + + + \frac{\vel{j}}{\sfact{j}} + \pder{T}{\gcs{j}} + - + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{1}{J} + \pder{}{\gcs{j}} + \left( + \frac{J}{\sfact{j}} + \frac{1}{\sfact{j}} + \pder{T}{\gcs{j}} + \right) + = + 0, + +as a set of conclusive equations. + +.. seealso:: + + For detailed derivation of the mentioned relations, visit `this page `_. + diff --git a/docs/source/numerical_method/spatial_discretisation/domain_setup/images/domain.png b/docs/source/numerical_method/spatial_discretisation/domain_setup/images/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..7968fbe6aa768587263a0b01c13a28630595874f GIT binary patch literal 33599 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 V+j7ZrrYs7+)@tf#Tvapk|1Z2%p>O~I literal 0 HcmV?d00001 diff --git a/docs/source/numerical_method/spatial_discretisation/domain_setup/images/grid1.png b/docs/source/numerical_method/spatial_discretisation/domain_setup/images/grid1.png new file mode 100644 index 0000000000000000000000000000000000000000..7895e92dcc7903dab46e917ca189d5bb835c69eb GIT binary patch literal 15675 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 LihS<1yHEcI(u4rj literal 0 HcmV?d00001 diff --git a/docs/source/numerical_method/spatial_discretisation/domain_setup/images/grid2.png b/docs/source/numerical_method/spatial_discretisation/domain_setup/images/grid2.png new file mode 100644 index 0000000000000000000000000000000000000000..91b7204996d96b89ea462d4ddf1687766997cd5a GIT binary patch literal 20810 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 Z%&)E+lRM!5mp~v;w^VQDU%&VGzW@x`8Lj{T literal 0 HcmV?d00001 diff --git a/docs/source/numerical_method/spatial_discretisation/domain_setup/images/grid4.png b/docs/source/numerical_method/spatial_discretisation/domain_setup/images/grid4.png new file mode 100644 index 0000000000000000000000000000000000000000..7eff1a77126dcc35ddb3ceec26912e63b0008d53 GIT binary patch literal 17888 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 literal 0 HcmV?d00001 diff --git a/docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered1.png b/docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered1.png new file mode 100644 index 0000000000000000000000000000000000000000..1ba0b674166aa1353cd6d596b53eeb0fa24dc350 GIT binary patch literal 26038 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 literal 0 HcmV?d00001 diff --git a/docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered2.png b/docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered2.png new file mode 100644 index 0000000000000000000000000000000000000000..7f9fc95b03a5030e355bd2c3499c502b5ecd011b GIT binary patch literal 43546 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 CscmEc literal 0 HcmV?d00001 diff --git a/docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered3.png b/docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered3.png new file mode 100644 index 0000000000000000000000000000000000000000..05443a8230a594314c10488f7addb3f8c6644be7 GIT binary patch literal 39128 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 literal 0 HcmV?d00001 diff --git a/docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered4.png b/docs/source/numerical_method/spatial_discretisation/domain_setup/images/staggered4.png new file mode 100644 index 0000000000000000000000000000000000000000..ab29c98ae38f0e1ee5d2bff3b9ec85e98d697fb9 GIT binary patch literal 22068 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 literal 0 HcmV?d00001 diff --git a/docs/source/numerical_method/spatial_discretisation/domain_setup/main.rst b/docs/source/numerical_method/spatial_discretisation/domain_setup/main.rst index 92cc127e..f85c1fb7 100644 --- a/docs/source/numerical_method/spatial_discretisation/domain_setup/main.rst +++ b/docs/source/numerical_method/spatial_discretisation/domain_setup/main.rst @@ -2,66 +2,75 @@ .. _domain_setup: ############ -Domain setup +Domain Setup ############ +.. include:: /references.txt + .. note:: - For simplicity I neglect the :math:`z` direction in this page. - Note that the same condition applies as for the :math:`y` direction. + For simplicity, we omit the :math:`z` direction on this page. + Practically, it is treated in the same manner as the :math:`y` direction. *************************************** -Staggered grid and domain decomposition +Staggered Grid and Domain Decomposition *************************************** -In this project, I use the staggered grid arrangement, i.e. the pressure and all velocity components are defined at different locations: +In this project, we utilize the staggered grid arrangement, i.e., all velocity components, pressure, and temperature are defined at different locations: .. image:: images/staggered1.png - :width: 800 + :width: 800 + :align: center -where the left figure shows the locations and indices of the variables whose notations are used in the equations, while the descriptions found in the right figure are used in the code. +Here the left panel shows the locations and indices of the variables in the equations, while the right schematic describes notations used in the code. -In the current project, I assume that the domain is wall-bounded in the :math:`x` direction, while periodic boundary conditions are imposed in the :math:`y` direction. -The spatial resolution is defined in terms of the number of cell centers (where the pressure and the temperature are defined): ``glisize = domain->glsizes[0]`` and ``gljsize = domain->glsizes[1]`` in the :math:`x` and the :math:`y` directions, respectively. -Here the prefix **gl** is used to emphasise they are **global** sizes. +We assume that the domain is wall-bounded in the :math:`x` direction, while periodic boundary conditions are imposed in the other directions. +The spatial resolution denotes the number of cell centers (where pressure and temperature are defined): e.g., ``glisize = domain->glsizes[0]`` and ``gljsize = domain->glsizes[1]`` in the :math:`x` and :math:`y` directions, respectively. -This library supports the process parallelisation (in particular by means of ``MPI``), which is achieved by splitting the whole domain into smaller blocks, and each process is responsible for ``myisize = domain->mysizes[0]`` times ``myjsize = domain->mysizes[1]`` cell centers: +Note that the prefix **gl** is used to emphasize they are **global**. +This library supports process parallelization (in particular by means of ``Message Passing Interface: MPI``), splitting the whole domain into smaller blocks, and each process is only responsible for ``myisize x myjsize = domain->mysizes[0] x domain->mysizes[1]`` cell centers: .. image:: images/domain.png - :width: 800 + :width: 800 + :align: center .. note:: - * No decomposition in the :math:`x` direction + * No decomposition in the wall-normal direction - In this project, the wall-normal direction is not decomposed (so-called `pencil decomposition `_ is adopted). - Thus ``glisize = glsizes[0]`` and ``myisize = mysizes[0]`` are equal. + In this project, the wall-normal direction is not decomposed, and we adopt the so-called `pencil decomposition `_. + Thus ``glisize = glsizes[0]`` and ``myisize = mysizes[0]`` are equal. - * Pencil sizes can be different + * Pencil sizes can be different - Although the domain is decomposed so that each process has a similar workload, ``myjsize`` can be different for each process, epsecially when the domain size is not divisible by the number of processes in the direction. + Although domains are decomposed such that each process has a similar workload, ``myjsize`` can be different for each process, especially when the domain size is not divisible by the number of processes in that direction. -In each process, each variable is positioned as follows: +For each process, variables are positioned as follows: * ``UX(i, j)`` - .. image:: images/staggered2.png - :width: 600 + .. image:: images/staggered2.png + :width: 600 + :align: center * ``UY(i, j)`` - .. image:: images/staggered3.png - :width: 600 + .. image:: images/staggered3.png + :width: 600 + :align: center * ``P(i, j)`` and ``T(i, j)`` - .. image:: images/staggered4.png - :width: 600 + .. image:: images/staggered4.png + :width: 600 + :align: center + +Notice the boundary treatments, where :math:`u_y, p, T` are exceptionally positioned to directly impose the wall boundary conditions. -Sizes of the two-dimensional arrays are summarised as follows: +Sizes of the two-dimensional arrays are as follows: ============ =============== =============== -Name ``i`` range ``j`` range +Variable ``i`` range ``j`` range ============ =============== =============== ``UX`` ``1:myisize+1`` ``0:myjsize+1`` ``UY`` ``0:myisize+1`` ``0:myjsize+1`` @@ -70,98 +79,52 @@ Name ``i`` range ``j`` range .. note:: - * Cell-face positions and cell-center positions - - Although two different positions are adopted, only the cell faces are the free parameter since the cell centers are assumed to be positioned in the middle of the neighbouring cell faces. + * Cell-face positions and cell-center positions - * Halo cells + Cell centers are positioned in the middle of the two neighboring cell faces. - In order to evaluate the differentiations in the :math:`y` direction close to the domain edges, additional cells (halo cells) are attached in the :math:`y` boundaries. + * Halo cells - * Boundary points + To evaluate differentiations in the homogeneous directions at the domain boundaries, additional cells (halo cells) are appended in the :math:`y, z` directions. - ``UY(0, j)``, ``UY(glisize+1, j)`` are shifted towards the near wall locations so that I can impose the velocity boundary conditions directly. - Same holds for the temperature and the pressure. +*************************** +Uniform and Stretched Grids +*************************** - Another way is to locate a cell inside one wall, whose values are extrapolated from the interior values (ghost cells). - It is easy to confirm that this extrapolation and the current implementation are identical. - -***************************************** -Uniform and stretched grid configurations -***************************************** - -In the :math:`y` direction, distance between two neighbouring points should be equal. -In the :math:`x` direction, non-uniform grid arrangement can be adopted, which could be useful to resolve boundary layers close to the walls. -To identify the positions, it is necessary to use two variables which are in ``domain`` structure, whose definitions are described below. +In the homogeneous directions, the distance between two neighboring points should be equal. +In the wall-normal direction, a non-uniform grid arrangement can be used, which could be beneficial to resolve boundary layers close to the walls (c.f., |VANDERPOEL2015|). +To identify this, two variables are defined in the ``domain`` structure. #. Cell-face positions ``domain->xf`` - ``XF(i)`` is used to describe the position of the cell faces in :math:`x` direction (**f** comes from face), i.e. the locations where :math:`\ux` is defined. - - .. note:: + ``XF(i)`` is used to locate the wall-normal cell faces (**f** denotes face), i.e., where :math:`\ux` are defined. - This should be given as the initial condition. - - .. image:: images/grid1.png - :width: 800 + .. image:: images/grid1.png + :width: 800 + :align: center #. Cell-center positions ``domain->xc`` - ``XC(i)`` is used to describe the position of the cell centers in :math:`x` direction (**c** comes from center), i.e. the locations where :math:`p`, :math:`T`, and :math:`\uy` are defined. - - Note again that the first and the last points are defined at cell-faces. - Except these two points, the following relation gives the relation between the cell faces and the center: - - .. math:: - - XC \left( i \right) - = - \frac{1}{2} XF \left( i \right) - + - \frac{1}{2} XF \left( i + 1 \right). - - .. image:: images/grid2.png - :width: 800 + ``XC(i)`` is used to locate the wall-normal cell centers (**c** denotes center), i.e., where :math:`p`, :math:`T`, and :math:`\uy` are defined. - .. note:: + Note again that the first and the last points are exceptionally positioned at the boundaries. + In the bulk, the following relation holds: - Although this is not a free parameter, this should also be given as the initial condition as well as ``xf``. + .. math:: -#. Cell-face distance ``domain->dxf`` + XC \left( i \right) + = + \frac{1}{2} XF \left( i \right) + + + \frac{1}{2} XF \left( i + 1 \right), - ``DXF(i)`` is used to describe the distance between two neighbouring cell faces: + i.e., the cell center is in the middle of the two neighboring cell faces. - .. math:: + .. image:: images/grid2.png + :width: 800 + :align: center - DXF \left( i \right) - = - XF \left( i + 1 \right) - - - XF \left( i \right). - - .. image:: images/grid3.png - :width: 800 - - .. note:: - - This is computed in :ref:`src/domain/init.c `. - -#. Cell-center distance ``domain->dxc`` - - ``DXC(i)`` is used to describe the distance between two neighbouring cell centers: - - .. math:: - - DXC \left( i \right) - = - XC \left( i \right) - - - XC \left( i - 1 \right). - - .. image:: images/grid4.png - :width: 800 - - .. note:: +.. note:: - This is computed in :ref:`src/domain/init.c `. + The cell-face and cell-center positions should be given along with a flow field to launch the simulator. diff --git a/docs/source/numerical_method/spatial_discretisation/main.rst b/docs/source/numerical_method/spatial_discretisation/main.rst index 6e13522b..1c428c16 100644 --- a/docs/source/numerical_method/spatial_discretisation/main.rst +++ b/docs/source/numerical_method/spatial_discretisation/main.rst @@ -1,25 +1,37 @@ -.. _spatial_discretisation: +.. _spatial_discretization: + +.. include:: /references.txt ###################### -Spatial discretisation +Spatial Discretization ###################### -In this section, spatial treatments are extensively discussed. +In this section, we extensively discuss spatial discretizations. -* :ref:`Domain setup ` +First, we describe how variables (coordinates, velocities, pressure, and temperature) are located within the computational domain using staggered grids. +We also briefly explain the domain decomposition technique used for parallelizing the domain. - How variables (coordinates, velocities, pressure, and temperature) are located in the computational domain is described. - Also domain decomposition technique to parallelise the whole procedure is considered. +Second, we address the numerical handling of equations, focusing on ambiguities in interpolations. +To achieve proper discretizations, we consider a computational coordinate system with uniform grid spacings, using equations in general rectilinear orthogonal coordinates. +We provide appropriate discretizations for the :ref:`governing equations `: incompressibility, momentum balance, and internal energy balance. -* :ref:`Discrete governing equations ` +Third, we prove that the resulting relations satisfy the desired properties in terms of the quadratic quantities :math:`k` and :math:`h`. +Specifically, the advective terms conserve the net :math:`k` and :math:`h`, while the diffusive terms conduct and dissipate them, as derived :ref:`here `. +Additionally, we focus on the global energy balance. +For the squared velocity and the squared temperature, the source and sink terms must exactly balance in a statistically steady manner, which is not achieved unless energy consistency is fulfilled. +We show that with our proper treatment, all relations are indeed satisfied numerically. - How :ref:`the governing equations ` should be discretised is presented. - In particular, how the original properties in the continuous space is kept after being discretised is focused. +Finally, the squared velocity and temperature are linked via the Nusselt number: a non-dimensional number measuring the heat transfer enhancement due to the convective effects. +There are several ways to calculate the Nusselt number, which all give an identical result in theory. +This relation is, not necessarily preserved from a numerical standpoint (c.f., |OSTILLAMONICO2015|). +We show that this exact relation can be replicated with our energy-consistent treatment. .. toctree:: - :hidden: + :maxdepth: 1 - domain_setup/main - discrete_governing_equations/main + domain_setup/main + discrete_governing_equations/main + quadratic_quantities/main + nusselt diff --git a/docs/source/numerical_method/spatial_discretisation/nusselt.rst b/docs/source/numerical_method/spatial_discretisation/nusselt.rst new file mode 100644 index 00000000..4dbcd63a --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/nusselt.rst @@ -0,0 +1,301 @@ + +.. _discrete_nusselt: + +############## +Nusselt number +############## + +************* +Heat transfer +************* + +To start, we define the heat transfer: surface-integrating :ref:`the internal energy balance ` in the homogeneous directions yields + +.. math:: + + \sumzc + \sumyc + \frac{J}{\sfact{1}} + \pder{T}{t} + = + \sumzc + \sumyc + \frac{1}{\sfact{1}} + \dif{}{\gcs{1}} + \left( + - + \frac{J}{\sfact{1}} + \vel{1} + \ave{T}{\gcs{1}} + + + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{J}{\sfact{1}} + \frac{1}{\sfact{1}} + \dif{T}{\gcs{1}} + \right). + +Assuming the flow field achieves a statistically-steady state + +.. math:: + + \pder{T}{t} + \rightarrow + 0 + +leads to + +.. math:: + + \frac{1}{\sfact{1}} + \dif{}{\gcs{1}} + \left\{ + \sumzc + \sumyc + \left( + \frac{J}{\sfact{1}} + \vel{1} + \ave{T}{\gcs{1}} + - + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{J}{\sfact{1}} + \frac{1}{\sfact{1}} + \dif{T}{\gcs{1}} + \right) + \right\} + = + 0, + +where the wall-normal differentiation and the homogeneous summations are interchanged, which is justified for rectilinear coordinates. +We introduce + +.. _eq_heat_transfer: + +.. math:: + + \heattransfer + \equiv + \sumzc + \sumyc + \left( + \frac{J}{\sfact{1}} + \vel{1} + \ave{T}{\gcs{1}} + - + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{J}{\sfact{1}} + \frac{1}{\sfact{1}} + \dif{T}{\gcs{1}} + \right), + +which is the internal energy going through a specific wall-normal position per unit time (heat transfer). +Note that the sign is decided such that *normal* cases :math:`\vat{T}{\frac{1}{2}} > \vat{T}{\ngp{1} + \frac{1}{2}}` give positive value. + +The computation of the heat transfer on the walls :math:`\heattransfer` are implemented as follows: + +.. myliteralinclude:: /../../src/logging/heat_transfer.c + :language: c + :tag: compute heat transfer on the walls + +By further integrating the differential equation in the wall-normal direction, we obtain + +.. math:: + + \sum_{i = 1}^{\chi} + \sfact{1} + \frac{1}{\sfact{1}} + \dif{\heattransfer}{\gcs{1}} + = + - + \vat{\heattransfer}{\frac{1}{2}} + + + \vat{\heattransfer}{\xi + \frac{1}{2}} + = + 0, + +indicating that :math:`\heattransfer` is constant for all wall-normal cell faces (:math:`\frac{1}{2}, \frac{3}{2}, \cdots, \ngp{1} - \frac{1}{2}, \ngp{1} + \frac{1}{2}`). + +************** +Nusselt number +************** + +We focus on how much the heat transfer is enhanced compared to the reference case :math:`\heattransfer_{ref}` if the flow fields were stationary with the given :math:`Ra` and :math:`Pr`. +For stationary flow fields, heat is purely conducted diffusively (i.e., :math:`\vel{1} \equiv 0`), and the temperature profile is linear in the wall-normal direction: + +.. math:: + + \frac{1}{\sfact{1}} + \dif{T}{\gcs{1}} + = + - + 1 + \,\, + (\because \text{boundary conditions}), + +and thus the reference heat transfer is given by + +.. math:: + + \heattransfer_{ref} + = + \sumzc + \sumyc + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{J}{\sfact{1}}. + +The Nusselt number is defined as the ratio of them: + +.. math:: + + Nu + \equiv + \frac{\heattransfer}{\heattransfer_{ref}}. + +As proved in :ref:`the global balance of squared temperature `, :math:`\heattransfer` is linked to the source and sink of the squared temperature relation. + +******************* +Squared Temperature +******************* + +Now we revisit the relations derived in :ref:`the global balance of squared temperature `: + +.. math:: + + \dhinjall. + +Due to the Dirichlet boundary condition with respect to the temperature, we can extract :math:`T` out of summation symbols to obtain + +.. math:: + + \vat{ + T + }{\frac{1}{2}} + \vat{ + \heattransfer + }{\frac{1}{2}} + - + \vat{ + T + }{\ngp{1} + \frac{1}{2}} + \vat{ + \heattransfer + }{\ngp{1} + \frac{1}{2}}. + +Additionally, since we fix the temperature difference of the two walls + +.. math:: + + \vat{T}{\frac{1}{2}} + - + \vat{T}{\ngp{1} + \frac{1}{2}} + \equiv + 1, + +and :math:`\heattransfer` is equal at every wall-normal cell faces + +.. math:: + + \heattransfer + \equiv + \vat{\heattransfer}{\frac{1}{2}} + = + \vat{\heattransfer}{\ngp{1} + \frac{1}{2}}, + +we notice + +.. math:: + + \heattransfer + = + \dhinjall, + +and of course + +.. math:: + + \heattransfer + = + \dhdisall. + +**************** +Squared Velocity +**************** + +Integrating :ref:`the definition of heat transfer ` in the wall-normal direction yields + +.. math:: + + \sumxc + \sfact{1} + \heattransfer + = + \sumzc + \sumyc + \sumxc + J + \vel{1} + \ave{T}{\gcs{1}} + - + \sumzc + \sumyc + \sumxc + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{J}{\sfact{1}} + \dif{T}{\gcs{1}}. + +The left-hand side is equal to :math:`\heattransfer` since :math:`\sumxc \sfact{1} \equiv 1` (recall that we assume the wall-normal length of the domain is unity). +The second term in the right-hand side leads to + +.. math:: + + & + - + \sumzc + \sumyc + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{J}{\sfact{1}} + \sumxc + \dif{T}{\gcs{1}} + + = + & + - + \sumzc + \sumyc + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{J}{\sfact{1}} + \left( + \vat{T}{\ngp{1} + \frac{1}{2}} + - + \vat{T}{\frac{1}{2}} + \right) + + = + & + \sumzc + \sumyc + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{J}{\sfact{1}} + + = + & + \heattransfer_{ref}, + +since :math:`J / \sfact{1}` is independent to the homogeneous directions. + +Thus we notice that + +.. math:: + + \heattransfer + = + \sumzc + \sumyc + \sumxc + J + \vel{1} + \ave{T}{\gcs{1}} + + + \heattransfer_{ref}, + +which relates the Nusselt number with the squared velocity relations. + diff --git a/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/advective.rst b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/advective.rst new file mode 100644 index 00000000..01d408df --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/advective.rst @@ -0,0 +1,308 @@ +############### +Advective terms +############### + +**************** +Squared velocity +**************** + +To begin with, we consider + +.. math:: + + \newcommand{\tmp}[1]{ + \dmomadv{#1}{1} + \dmomadv{#1}{2} + \dmomadv{#1}{3} + - + \frac{1}{J} + \vel{#1} + \ave{ + \dif{ + \left( + \frac{J}{\sfact{1}} + \vel{1} + \right) + }{\gcs{1}} + + + \dif{ + \left( + \frac{J}{\sfact{2}} + \vel{2} + \right) + }{\gcs{2}} + + + \dif{ + \left( + \frac{J}{\sfact{3}} + \vel{3} + \right) + }{\gcs{3}} + }{\gcs{#1}} + } + \tmp{1}, + + \tmp{2}, + + \tmp{3}, + +where the first three terms for each direction are the advective terms in the momentum balance, while the last terms are the product of the corresponding velocity and the averaged incompressibility constraint and thus zero. +With some algebra, we find that they are equal to + +.. math:: + + \newcommand{\tmp}[1]{ + - + \frac{1}{J} + \dif{ + \left( + \ave{ + \frac{J}{\sfact{#1}} \vel{1} + }{\gcs{#1}} + \ave{\vel{#1}}{\gcs{1}} + \right) + }{\gcs{1}} + - + \frac{1}{J} + \dif{ + \left( + \ave{ + \frac{J}{\sfact{#1}} \vel{2} + }{\gcs{#1}} + \ave{\vel{#1}}{\gcs{2}} + \right) + }{\gcs{2}} + - + \frac{1}{J} + \dif{ + \left( + \ave{ + \frac{J}{\sfact{#1}} \vel{3} + }{\gcs{#1}} + \ave{\vel{#1}}{\gcs{3}} + \right) + }{\gcs{3}} + } + \tmp{1}, + + \tmp{2}, + + \tmp{3}, + +respectively. + +In short, the above relation is the discrete description of + +.. math:: + + - + \vel{j} \pder{\vel{i}}{x_j} + - + \vel{i} \pder{\vel{j}}{x_j} + = + - + \pder{\vel{j} \vel{i}}{x_j}, + +i.e., the advective and the divergence forms of the advective terms are equivalent. + +Now the global energy balance attributed to the advective terms are considered: + +.. math:: + + \newcommand{\tmp}[1]{ + J + \vel{#1} + \left( + \dmomadv{#1}{1} + \dmomadv{#1}{2} + \dmomadv{#1}{3} + \right) + } + \sumzc + \sumyc + \sumxf + \tmp{1}, + + \sumzc + \sumyf + \sumxc + \tmp{2}, + + \sumzf + \sumyc + \sumxc + \tmp{3}. + +Our objective is to prove they are all zero, indicating that the advective terms do not alter the net amount of the quadratic quantities. + +By using the relations derived in :ref:`the prerequisite `, each term leads to + +.. math:: + + \newcommand{\tmp}[1]{ + J + \vel{#1} + \left\{ + \frac{1}{J} + \dif{ + \left( + \ave{ + \frac{J}{\sfact{#1}} \vel{1} + }{\gcs{#1}} + \ave{\vel{#1}}{\gcs{1}} + \right) + }{\gcs{1}} + + + \frac{1}{J} + \dif{ + \left( + \ave{ + \frac{J}{\sfact{#1}} \vel{2} + }{\gcs{#1}} + \ave{\vel{#1}}{\gcs{2}} + \right) + }{\gcs{2}} + + + \frac{1}{J} + \dif{ + \left( + \ave{ + \frac{J}{\sfact{#1}} \vel{3} + }{\gcs{#1}} + \ave{\vel{#1}}{\gcs{3}} + \right) + }{\gcs{3}} + \right\} + } + \sumzc + \sumyc + \sumxf + \tmp{1}, + + \sumzc + \sumyf + \sumxc + \tmp{2}, + + \sumzf + \sumyc + \sumxc + \tmp{3}. + +The resulting terms are product of the velocity and the advective terms of the divergence form whose sign is flipped, and as a result the aforementioned conclusion is obtained. + +******************* +Squared Temperature +******************* + +We consider + +.. math:: + + \dtempadv{1} + \dtempadv{2} + \dtempadv{3} + - + \frac{1}{J} + T + \left\{ + \dif{ + \left( + \frac{J}{\sfact{1}} + \vel{1} + \right) + }{\gcs{1}} + + + \dif{ + \left( + \frac{J}{\sfact{2}} + \vel{2} + \right) + }{\gcs{2}} + + + \dif{ + \left( + \frac{J}{\sfact{3}} + \vel{3} + \right) + }{\gcs{3}} + \right\}, + +giving + +.. math:: + + - + \frac{1}{J} + \dif{}{\gcs{1}} + \left( + \frac{J}{\sfact{1}} + \vel{1} + \ave{ + T + }{\gcs{1}} + \right) + - + \frac{1}{J} + \dif{}{\gcs{2}} + \left( + \frac{J}{\sfact{2}} + \vel{2} + \ave{ + T + }{\gcs{2}} + \right) + - + \frac{1}{J} + \dif{}{\gcs{3}} + \left( + \frac{J}{\sfact{3}} + \vel{3} + \ave{ + T + }{\gcs{3}} + \right). + +Now we focus on the global balance of the quadratic quantity: + +.. math:: + + \sumzc + \sumyc + \sumxc + J + T + \left( + \dtempadv{1} + \dtempadv{2} + \dtempadv{3} + \right), + +giving + +.. math:: + + \newcommand{\tmp}[1]{ + \frac{1}{J} + \dif{}{\gcs{#1}} + \left( + \frac{J}{\sfact{#1}} + \vel{#1} + \ave{T}{\gcs{#1}} + \right) + } + \sumzc + \sumyc + \sumxc + J + T + \left\{ + \tmp{1} + + + \tmp{2} + + + \tmp{3} + \right\}. + +Thus the advective contribution vanishes. + diff --git a/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/diffusive.rst b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/diffusive.rst new file mode 100644 index 00000000..66759708 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/diffusive.rst @@ -0,0 +1,142 @@ +############### +Diffusive terms +############### + +We utilise :ref:`the relations listed in the prerequisite ` to derive the following relations. + +**************** +Squared velocity +**************** + +From the wall-normal momentum balance, we obtain + +.. math:: + + - + \sumzc + \sumyc + \sumxc + \dkdis{1}{1} + - + \sumzc + \sumyf + \sumxf + \dkdis{2}{1} + - + \sumzf + \sumyc + \sumxf + \dkdis{3}{1}. + +From the stream-wise momentum balance, we obtain + +.. math:: + + - + \sumzc + \sumyf + \sumxf + \dkdis{1}{2} + - + \sumzc + \sumyc + \sumxc + \dkdis{2}{2} + - + \sumzf + \sumyf + \sumxc + \dkdis{3}{2}. + +From the span-wise momentum balance, we obtain + +.. math:: + + - + \sumzf + \sumyc + \sumxf + \dkdis{1}{3} + - + \sumzf + \sumyf + \sumxc + \dkdis{2}{3} + - + \sumzc + \sumyc + \sumxc + \dkdis{3}{3}. + +In total, all terms work to dissipate :math:`k`. + +******************* +Squared temperature +******************* + +We obtain + +.. math:: + + - + \sumzc + \sumyc + \sumxf + J + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \left( + \frac{1}{\sfact{1}} + \dif{T}{\gcs{1}} + \right)^2 + - + \sumzc + \sumyf + \sumxc + J + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \left( + \frac{1}{\sfact{2}} + \dif{T}{\gcs{2}} + \right)^2 + - + \sumzf + \sumyc + \sumxc + J + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \left( + \frac{1}{\sfact{3}} + \dif{T}{\gcs{3}} + \right)^2 + +as the dissipative terms, while + +.. math:: + + - + \sumzc + \sumyc + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \vat{ + \left( + \frac{J}{\sfact{1}} + T + \frac{1}{\sfact{1}} + \dif{T}{\gcs{1}} + \right) + }{\frac{1}{2}} + + + \sumzc + \sumyc + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \vat{ + \left( + \frac{J}{\sfact{1}} + T + \frac{1}{\sfact{1}} + \dif{T}{\gcs{1}} + \right) + }{\ngp{1} + \frac{1}{2}} + +as the conduction on the walls. + diff --git a/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/main.rst b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/main.rst new file mode 100644 index 00000000..31e3ad22 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/main.rst @@ -0,0 +1,17 @@ + +.. _energy_prerequisite: + +############ +Prerequisite +############ + +Discrete operators which are frequently used to derive the discrete energy relations are listed. + +.. toctree:: + :maxdepth: 1 + + x + y + z + t + diff --git a/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/t.rst b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/t.rst new file mode 100644 index 00000000..e5ddd94a --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/t.rst @@ -0,0 +1,113 @@ +############################### +Relations involving temperature +############################### + +**************** +Differentiations +**************** + +.. math:: + + \sumzc + \sumyc + \sumxc + T + \dif{q}{\gcs{1}} + = + - + \sumzc + \sumyc + \left( + \vat{\left(T q\right)}{\frac{1}{2}} + + + \sumxf + \dif{T}{\gcs{1}} + q + - + \vat{\left(T q\right)}{\ngp{1} + \frac{1}{2}} + \right). + +.. math:: + + \sumzc + \sumyc + \sumxc + T + \dif{q}{\gcs{2}} + = + - + \sumzc + \sumyf + \sumxc + \dif{T}{\gcs{2}} + q. + +.. math:: + + \sumzc + \sumyc + \sumxc + T + \dif{q}{\gcs{3}} + = + - + \sumzf + \sumyc + \sumxc + \dif{T}{\gcs{3}} + q. + +******** +Averages +******** + +.. math:: + + \sumzc + \sumyc + \sumxc + T + \ave{q}{\gcs{1}} + = + \sumzc + \sumyc + \left( + \vat{T}{1} + \frac{\vat{q}{\frac{1}{2}}}{2} + + + \sum_{i = \frac{3}{2}}^{\ngp{1} - \frac{1}{2}} + \ave{T}{\gcs{1}} + q + + + \vat{T}{\ngp{1}} + \frac{\vat{q}{\ngp{1} + \frac{1}{2}}}{2} + \right). + +.. math:: + + \sumzc + \sumyc + \sumxc + T + \ave{q}{\gcs{2}} + = + \sumzc + \sumyf + \sumxc + \ave{T}{\gcs{2}} + q. + +.. math:: + + \sumzc + \sumyc + \sumxc + T + \ave{q}{\gcs{3}} + = + \sumzf + \sumyc + \sumxc + \ave{T}{\gcs{3}} + q. + diff --git a/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/x.rst b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/x.rst new file mode 100644 index 00000000..c17d612f --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/x.rst @@ -0,0 +1,101 @@ +######################################## +Relations involving wall-normal velocity +######################################## + +**************** +Differentiations +**************** + +.. math:: + + \sumzc + \sumyc + \sumxf + \vel{1} + \dif{q}{\gcs{1}} + = + - + \sumzc + \sumyc + \sumxc + \dif{\vel{1}}{\gcs{1}} + q, + +where :math:`\vat{\vel{1}}{\frac{1}{2}} = \vat{\vel{1}}{\ngp{1} + \frac{1}{2}} = 0` is used. + +.. math:: + + \sumzc + \sumyc + \sumxf + \vel{1} + \dif{q}{\gcs{2}} + = + - + \sumzc + \sumyf + \sumxf + \dif{\vel{1}}{\gcs{2}} + q. + +.. math:: + + \sumzc + \sumyc + \sumxf + \vel{1} + \dif{q}{\gcs{3}} + = + - + \sumzf + \sumyc + \sumxf + \dif{\vel{1}}{\gcs{3}} + q. + +******** +Averages +******** + +.. math:: + + \sumzc + \sumyc + \sumxf + \vel{1} + \ave{q}{\gcs{1}} + = + \sumzc + \sumyc + \sumxc + \ave{\vel{1}}{\gcs{1}} + q. + +.. math:: + + \sumzc + \sumyc + \sumxf + \vel{1} + \ave{q}{\gcs{2}} + = + \sumzc + \sumyf + \sumxf + \ave{\vel{1}}{\gcs{2}} + q. + +.. math:: + + \sumzc + \sumyc + \sumxf + \vel{1} + \ave{q}{\gcs{3}} + = + \sumzf + \sumyc + \sumxf + \ave{\vel{1}}{\gcs{3}} + q. + diff --git a/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/y.rst b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/y.rst new file mode 100644 index 00000000..f8a45a58 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/y.rst @@ -0,0 +1,113 @@ +######################################## +Relations involving stream-wise velocity +######################################## + +**************** +Differentiations +**************** + +.. math:: + + \sumzc + \sumyf + \sumxc + \vel{2} + \dif{q}{\gcs{1}} + = + - + \sumzc + \sumyf + \left( + \vat{\left(\vel{2} q\right)}{\frac{1}{2}} + + + \sumxf + \dif{\vel{2}}{\gcs{1}} + q + - + \vat{\left(\vel{2} q\right)}{\ngp{1} + \frac{1}{2}} + \right). + +.. math:: + + \sumzc + \sumyf + \sumxc + \vel{2} + \dif{q}{\gcs{2}} + = + - + \sumzc + \sumyc + \sumxc + \dif{\vel{2}}{\gcs{2}} + q. + +.. math:: + + \sumzc + \sumyf + \sumxc + \vel{2} + \dif{q}{\gcs{3}} + = + - + \sumzf + \sumyf + \sumxc + \dif{\vel{2}}{\gcs{3}} + q. + +******** +Averages +******** + +.. math:: + + \sumzc + \sumyf + \sumxc + \vel{2} + \ave{q}{\gcs{1}} + = + \sumzc + \sumyf + \left( + \vat{\vel{2}}{1} + \frac{\vat{q}{\frac{1}{2}}}{2} + + + \sum_{i = \frac{3}{2}}^{\ngp{1} - \frac{1}{2}} + \ave{\vel{2}}{\gcs{1}} + q + + + \vat{\vel{2}}{\ngp{1}} + \frac{\vat{q}{\ngp{1} + \frac{1}{2}}}{2} + \right). + +.. math:: + + \sumzc + \sumyf + \sumxc + \vel{2} + \ave{q}{\gcs{2}} + = + \sumzc + \sumyc + \sumxc + \ave{\vel{2}}{\gcs{2}} + q. + +.. math:: + + \sumzc + \sumyf + \sumxc + \vel{2} + \ave{q}{\gcs{3}} + = + \sumzf + \sumyf + \sumxc + \ave{\vel{2}}{\gcs{3}} + q. + diff --git a/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/z.rst b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/z.rst new file mode 100644 index 00000000..6b779d53 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/prerequisite/z.rst @@ -0,0 +1,110 @@ +###################################### +Relations involving span-wise velocity +###################################### + +**************** +Differentiations +**************** + +.. math:: + + \sumzf + \sumyc + \sumxc + \vel{3} + \dif{q}{\gcs{1}} + = + - + \sumzf + \sumyc + \sumxf + \dif{\vel{3}}{\gcs{1}} + q, + +where :math:`\vat{\vel{3}}{\frac{1}{2},\ccindex{j},\cpindex{k}} = \vat{\vel{3}}{\ngp{1} + \frac{1}{2},\ccindex{j},\cpindex{k}} = 0` is assumed (i.e., the walls do not move in the :math:`z` direction). + +.. math:: + + \sumzf + \sumyc + \sumxc + \vel{3} + \dif{q}{\gcs{2}} + = + - + \sumzf + \sumyf + \sumxc + \dif{\vel{3}}{\gcs{2}} + q. + +.. math:: + + \sumzf + \sumyc + \sumxc + \vel{3} + \dif{q}{\gcs{3}} + = + - + \sumzc + \sumyc + \sumxc + \dif{\vel{3}}{\gcs{3}} + q. + +******** +Averages +******** + +.. math:: + + \sumzf + \sumyc + \sumxc + \vel{3} + \ave{q}{\gcs{1}} + = + \sumzf + \sumyc + \left( + \vat{\vel{3}}{1} + \frac{\vat{q}{\frac{1}{2}}}{2} + + + \sum_{i = \frac{3}{2}}^{\ngp{1} - \frac{1}{2}} + \ave{\vel{3}}{\gcs{1}} + q + + + \vat{\vel{3}}{\ngp{1}} + \frac{\vat{q}{\ngp{1} + \frac{1}{2}}}{2} + \right). + + +.. math:: + + \sumzf + \sumyc + \sumxc + \vel{3} + \ave{q}{\gcs{2}} + = + \sumzf + \sumyf + \sumxc + \ave{\vel{3}}{\gcs{2}} + q. + +.. math:: + + \sumzf + \sumyc + \sumxc + \vel{3} + \ave{q}{\gcs{3}} + = + \sumzc + \sumyc + \sumxc + \ave{\vel{3}}{\gcs{3}} + q. + diff --git a/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/pressure_gradient.rst b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/pressure_gradient.rst new file mode 100644 index 00000000..5fadc22d --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/derivation/pressure_gradient.rst @@ -0,0 +1,72 @@ +####################### +Pressure-gradient terms +####################### + +The pressure-gradient terms: + +.. math:: + + - + \frac{1}{\sfact{i}} + \dif{p}{\gcs{i}} + +contribute to the energy balance as follows: + +.. math:: + + \newcommand{\tmp}[1]{ + J + \vel{#1} + \frac{1}{\sfact{#1}} + \dif{p}{\gcs{#1}} + = + \sumzc + \sumyc + \sumxc + J + p + \frac{1}{J} + \dif{ + \left( + \frac{J}{\sfact{#1}} + \vel{#1} + \right) + }{\gcs{#1}} + } + - + \sumzc + \sumyc + \sumxf + \tmp{1}, + + - + \sumzc + \sumyf + \sumxc + \tmp{2}, + + - + \sumzf + \sumyc + \sumxc + \tmp{3}. + +The sum of these three relations is + +.. math:: + + \sumzc + \sumyc + \sumxc + J + p + \left\{ + \ddiv{1} + + + \ddiv{2} + + + \ddiv{3} + \right\}, + +which is zero because the component inside the wavy parentheses is :ref:`the incompressibility constraint `. + diff --git a/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/main.rst b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/main.rst new file mode 100644 index 00000000..ee6d2a96 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/main.rst @@ -0,0 +1,432 @@ + +.. _quadratic_quantities: + +#################### +Quadratic Quantities +#################### + +Here we aim at confirming if :ref:`the theoretical relations of the quadratic quantities ` are satisfied even after discretized. + +Specifically, as discrete counterparts, we consider :ref:`the discrete momentum balance ` multiplied by the Jacobian determinant (incorporating the volume integrals) and the velocity: + +.. math:: + + \sumzc + \sumyc + \sumxf + J + \vel{1} + \pder{\vel{1}}{t} + = + & + - + \sumzc + \sumyc + \sumxf + J + \vel{1} + \dmomadv{1}{1} + - + \sumzc + \sumyc + \sumxf + J + \vel{1} + \dmomadv{2}{1} + - + \sumzc + \sumyc + \sumxf + J + \vel{1} + \dmomadv{3}{1} + + & + - + \sumzc + \sumyc + \sumxf + J + \vel{1} + \dmompre{1} + + & + + + \sumzc + \sumyc + \sumxf + J + \vel{1} + \dmomdif{1}{1} + + + \sumzc + \sumyc + \sumxf + J + \vel{1} + \dmomdif{2}{1} + + + \sumzc + \sumyc + \sumxf + J + \vel{1} + \dmomdif{3}{1} + + & + + + \sumzc + \sumyc + \sumxf + J + \vel{1} + \ave{T}{\gcs{1}}, + +.. math:: + + \sumzc + \sumyf + \sumxc + J + \vel{2} + \pder{\vel{2}}{t} + = + & + - + \sumzc + \sumyf + \sumxc + J + \vel{2} + \dmomadv{1}{2} + - + \sumzc + \sumyf + \sumxc + J + \vel{2} + \dmomadv{2}{2} + - + \sumzc + \sumyf + \sumxc + J + \vel{2} + \dmomadv{3}{2} + + & + - + \sumzc + \sumyf + \sumxc + J + \vel{2} + \dmompre{2} + + & + + + \sumzc + \sumyf + \sumxc + J + \vel{2} + \dmomdif{1}{2} + + + \sumzc + \sumyf + \sumxc + J + \vel{2} + \dmomdif{2}{2} + + + \sumzc + \sumyf + \sumxc + J + \vel{2} + \dmomdif{3}{2}, + +.. math:: + + \sumzf + \sumyc + \sumxc + J + \vel{3} + \pder{\vel{3}}{t} + = + & + - + \sumzf + \sumyc + \sumxc + J + \vel{3} + \dmomadv{1}{3} + - + \sumzf + \sumyc + \sumxc + J + \vel{3} + \dmomadv{2}{3} + - + \sumzf + \sumyc + \sumxc + J + \vel{3} + \dmomadv{3}{3} + + & + - + \sumzf + \sumyc + \sumxc + J + \vel{3} + \dmompre{3} + + & + + + \sumzf + \sumyc + \sumxc + J + \vel{3} + \dmomdif{1}{3} + + + \sumzf + \sumyc + \sumxc + J + \vel{3} + \dmomdif{2}{3} + + + \sumzf + \sumyc + \sumxc + J + \vel{3} + \dmomdif{3}{3}. + +Similarly, multiplying :ref:`the discrete internal energy balance ` by temperature and volume-integrating it yield + +.. math:: + + \sumzc + \sumyc + \sumxc + J + T + \pder{T}{t} + = + & + - + \sumzc + \sumyc + \sumxc + J + T + \dtempadv{1} + - + \sumzc + \sumyc + \sumxc + J + T + \dtempadv{2} + - + \sumzc + \sumyc + \sumxc + J + T + \dtempadv{3} + + & + + + \sumzc + \sumyc + \sumxc + J + T + \dtempdif{1} + + + \sumzc + \sumyc + \sumxc + J + T + \dtempdif{2} + + + \sumzc + \sumyc + \sumxc + J + T + \dtempdif{3}. + +It should be noted that, since the velocities are defined at different positions due to :ref:`the staggered grid arrangement `, quadratic quantities are also evaluated at different locations, which is the reason why we consider three components separately. + +With some algebra, we obtain + +.. math:: + + & + \sumzc + \sumyc + \sumxf + J + \pder{k_1}{t} + + + \sumzc + \sumyf + \sumxc + J + \pder{k_2}{t} + + + \sumzf + \sumyc + \sumxc + J + \pder{k_3}{t} + + = + & + - + \sumzc + \sumyc + \sumxc + \dkdis{1}{1} + - + \sumzc + \sumyf + \sumxf + \dkdis{2}{1} + - + \sumzf + \sumyc + \sumxf + \dkdis{3}{1} + + & + - + \sumzc + \sumyf + \sumxf + \dkdis{1}{2} + - + \sumzc + \sumyc + \sumxc + \dkdis{2}{2} + - + \sumzf + \sumyf + \sumxc + \dkdis{3}{2} + + & + - + \sumzf + \sumyc + \sumxf + \dkdis{1}{3} + - + \sumzf + \sumyf + \sumxc + \dkdis{2}{3} + - + \sumzc + \sumyc + \sumxc + \dkdis{3}{3} + + & + + + \sumzc + \sumyc + \sumxf + J + \vel{1} + \ave{T}{\gcs{1}}, + +and + +.. math:: + + & + \sumzc + \sumyc + \sumxc + J + \pder{h}{t} + + = + & + - + \sumzc + \sumyc + \sumxf + \dhdis{1} + - + \sumzc + \sumyf + \sumxc + \dhdis{2} + - + \sumzf + \sumyc + \sumxc + \dhdis{3} + + & + - + \sumzc + \sumyc + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \vat{ + \left( + \frac{J}{\sfact{1}} + T + \frac{1}{\sfact{1}} + \dif{T}{\gcs{1}} + \right) + }{\frac{1}{2}} + + & + + + \sumzc + \sumyc + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \vat{ + \left( + \frac{J}{\sfact{1}} + T + \frac{1}{\sfact{1}} + \dif{T}{\gcs{1}} + \right) + }{\ngp{1} + \frac{1}{2}}, + +with respect to the squared velocity and the squared temperature, respectively. + +To derive them, the individual components are focused below. + +.. toctree:: + :maxdepth: 1 + + derivation/prerequisite/main + derivation/advective + derivation/pressure_gradient + derivation/diffusive + +The derived two relations have source terms and sink terms, which are elaborated below as well as their implementations. + +.. toctree:: + :maxdepth: 1 + + squared_velocity + squared_temperature + diff --git a/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/squared_temperature.rst b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/squared_temperature.rst new file mode 100644 index 00000000..f2c23ca7 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/squared_temperature.rst @@ -0,0 +1,56 @@ + +.. _global_balance_squared_temperature: + +##################################### +Global Balance of Squared Temperature +##################################### + +****************** +Sink (Dissipation) +****************** + +There are 3 terms dissipating the squared temperature, which are computed separately. + +.. math:: + + \sumzc + \sumyc + \sumxf + \dhdis{1} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_temperature.c + :language: c + :tag: dtdx component + +.. math:: + + \sumzc + \sumyf + \sumxc + \dhdis{2} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_temperature.c + :language: c + :tag: dtdy component + +.. math:: + + \sumzf + \sumyc + \sumxc + \dhdis{3} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_temperature.c + :language: c + :tag: dtdz component + +****************** +Source (Injection) +****************** + +.. math:: + + \dhinjall + +injects squared temperature, which will be elaborated :ref:`later `. + diff --git a/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/squared_velocity.rst b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/squared_velocity.rst new file mode 100644 index 00000000..94168644 --- /dev/null +++ b/docs/source/numerical_method/spatial_discretisation/quadratic_quantities/squared_velocity.rst @@ -0,0 +1,131 @@ +################################## +Global Balance of Squared Velocity +################################## + +****************** +Sink (Dissipation) +****************** + +There are 9 terms dissipating the squared velocity, which are computed separately. + +.. math:: + + \sumzc + \sumyc + \sumxc + \dkdis{1}{1} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_velocity.c + :language: c + :tag: duxdx component + +.. math:: + + \sumzc + \sumyf + \sumxf + \dkdis{2}{1} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_velocity.c + :language: c + :tag: duxdy component + +.. math:: + + \sumzf + \sumyc + \sumxf + \dkdis{3}{1} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_velocity.c + :language: c + :tag: duxdz component + +.. math:: + + \sumzc + \sumyf + \sumxf + \dkdis{1}{2} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_velocity.c + :language: c + :tag: duydx component + +.. math:: + + \sumzc + \sumyc + \sumxc + \dkdis{2}{2} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_velocity.c + :language: c + :tag: duydy component + +.. math:: + + \sumzf + \sumyf + \sumxc + \dkdis{3}{2} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_velocity.c + :language: c + :tag: duydz component + +.. math:: + + \sumzf + \sumyc + \sumxf + \dkdis{1}{3} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_velocity.c + :language: c + :tag: duzdx component + +.. math:: + + \sumzf + \sumyf + \sumxc + \dkdis{2}{3} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_velocity.c + :language: c + :tag: duzdy component + +.. math:: + + \sumzc + \sumyc + \sumxc + \dkdis{3}{3} + +.. myliteralinclude:: /../../src/logging/dissipated_squared_velocity.c + :language: c + :tag: duzdz component + +****************** +Source (Injection) +****************** + +.. math:: + + \sumzc + \sumyc + \sumxc + J + \vel{1} + \ave{T}{\gcs{1}} + +increases the total amount of the squared velocity. +Physically this term accounts for the kinetic energy injection due to the buoyancy effects. + +This quantity is implemented to monitor as follows: + +.. myliteralinclude:: /../../src/logging/injected_squared_velocity.c + :language: c + :tag: compute injected squared velocity + diff --git a/docs/source/numerical_method/tdm.rst b/docs/source/numerical_method/tdm.rst new file mode 100644 index 00000000..a8e884f3 --- /dev/null +++ b/docs/source/numerical_method/tdm.rst @@ -0,0 +1,657 @@ + +.. _tdm: + +########################## +Tri-diagonal Matrix Solver +########################## + +****************** +Non-Periodic Cases +****************** + +We consider a linear system + +.. math:: + + \vat{l}{i} + \vat{p}{i - 1} + + + \vat{c}{i} + \vat{p}{i} + + + \vat{u}{i} + \vat{p}{i + 1} + = + \vat{q}{i}, + +which appears as a consequence of the Laplace operators in the wall-normal direction. +This linear system is written as a tri-diagonal matrix: + +.. math:: + + \begin{bmatrix} + c_0 & u_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ + l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ + 0 & l_2 & c_2 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ + \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & c_{i-1} & u_{i-1} & 0 & \cdots & 0 & 0 & 0 \\ + 0 & 0 & 0 & \cdots & l_{i } & c_{i } & u_{i } & \cdots & 0 & 0 & 0 \\ + 0 & 0 & 0 & \cdots & 0 & l_{i+1} & c_{i+1} & \cdots & 0 & 0 & 0 \\ + \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & c_{n-3} & u_{n-3} & 0 \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & l_{n-2} & c_{n-2} & u_{n-2} \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & l_{n-1} & c_{n-1} + \end{bmatrix} + \begin{bmatrix} + x_0 \\ + x_1 \\ + x_2 \\ + \vdots \\ + x_{i-1} \\ + x_{i } \\ + x_{i+1} \\ + \vdots \\ + x_{n-3} \\ + x_{n-2} \\ + x_{n-1} + \end{bmatrix} + = + \begin{bmatrix} + q_0 \\ + q_1 \\ + q_2 \\ + \vdots \\ + q_{i-1} \\ + q_{i } \\ + q_{i+1} \\ + \vdots \\ + q_{n-3} \\ + q_{n-2} \\ + q_{n-1} + \end{bmatrix}, + +where :math:`x` is the answer of the system and to be computed. +In the code, a buffer ``q`` is used to store the solution :math:`x` as well as to store the input (right-hand-side terms). +Thus the input array is overwritten by the solver. + +.. note:: + + Although ``l[0]`` and ``u[n-1]`` are not used in this case, ``l``, ``c``, and ``u`` all have the length :math:`n` for simplicity. + In particular these values are used for periodic systems. + +We first look at how this matrix is solved. +For notational simplicity, we concatenate the tri-diagonal matrix and the right-hand-side vector: + +.. math:: + + \begin{bmatrix} + c_0 & u_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_0 \\ + l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_1 \\ + 0 & l_2 & c_2 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_2 \\ + \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & c_{i-1} & u_{i-1} & 0 & \cdots & 0 & 0 & 0 & q_{i-1} \\ + 0 & 0 & 0 & \cdots & l_{i } & c_{i } & u_{i } & \cdots & 0 & 0 & 0 & q_{i } \\ + 0 & 0 & 0 & \cdots & 0 & l_{i+1} & c_{i+1} & \cdots & 0 & 0 & 0 & q_{i+1} \\ + \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & c_{n-3} & u_{n-3} & 0 & q_{n-3} \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & l_{n-2} & c_{n-2} & u_{n-2} & q_{n-2} \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & l_{n-1} & c_{n-1} & q_{n-1} + \end{bmatrix}, + +and consider `the Gaussian elimination `_. + +Our objective is to convert the tri-diagonal matrix to an identity matrix (i.e., matrix inversion): + +.. math:: + + \begin{bmatrix} + 1 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & x_0 \\ + 0 & 1 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & x_1 \\ + 0 & 0 & 1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & x_2 \\ + \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & 1 & 0 & 0 & \cdots & 0 & 0 & 0 & x_{i-1} \\ + 0 & 0 & 0 & \cdots & 0 & 1 & 0 & \cdots & 0 & 0 & 0 & x_{i } \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 1 & \cdots & 0 & 0 & 0 & x_{i+1} \\ + \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 1 & 0 & 0 & x_{n-3} \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 1 & 0 & x_{n-2} \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 1 & x_{n-1} + \end{bmatrix}. + +============= +Forward Sweep +============= + +First we try to eliminate the lower-diagonal components :math:`l_1, l_2, \cdots, l_{n-2}, l_{n-1}`, which is called the forward sweep. + +--------- +First row +--------- + +To get started, we look at the top two rows: + +.. math:: + + \begin{bmatrix} + c_0 & u_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_0 \\ + l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_1 + \end{bmatrix}. + +Dividing the first row by :math:`c_0` yields + +.. math:: + + & + \begin{bmatrix} + 1 & u_0 / c_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_0 / c_0 \\ + l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_1 + \end{bmatrix} \\ + & + = \\ + & + \begin{bmatrix} + 1 & v_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & r_0 \\ + l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_1 + \end{bmatrix}, + +namely + +.. math:: + + v_0 \leftarrow \frac{u_0}{c_0}, + +.. math:: + + r_0 \leftarrow \frac{q_0}{c_0}. + +In the code, we have + +.. myliteralinclude:: /../../src/tdm.c + :language: c + :tag: divide the first row by center-diagonal term + +.. note:: + + One may notice that ``u`` is modified in the above equation. + If we implement in this way, we need to re-initialise :math:`u_i` for each right-hand-side term, which is redundant. + To avoid this, we use a buffer ``v``, in which the modified ``u`` is stored instead of overwriting ``u``. + +Next, to eliminate :math:`l_1`, we subtract *the first row times* :math:`l_1` from *the second row*, yielding + +.. math:: + + \begin{bmatrix} + 1 & v_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & r_0 \\ + 0 & c_1 - l_1 v_0 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & q_1 - l_1 r_0 + \end{bmatrix}, + +or + +.. math:: + + & + \begin{bmatrix} + 1 & v_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & r_0 \\ + 0 & 1 & \frac{u_1}{c_1 - l_1 v_0} & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \frac{q_1 - l_1 r_0}{c_1 - l_1 v_0} + \end{bmatrix}, \\ + & + = \\ + & + \begin{bmatrix} + 1 & v_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & r_0 \\ + 0 & 1 & v_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 & r_1 + \end{bmatrix}. + +------------- +General Cases +------------- + +We consider to extend the above process to a general :math:`i-1`-th and :math:`i`-th rows: + +.. math:: + + \begin{bmatrix} + 0 & 0 & 0 & \cdots & 1 & v_{i-1} & 0 & \cdots & 0 & 0 & 0 & r_{i-1} \\ + 0 & 0 & 0 & \cdots & l_i & c_i & u_i & \cdots & 0 & 0 & 0 & q_{i } + \end{bmatrix}, + +where the upper row (:math:`i-1`-th row) has already been updated, while the bottom row (:math:`i`-th row) is to be updated now. + +Now let us consider to eliminate :math:`l_i`: + +.. math:: + + \begin{bmatrix} + 0 & 0 & 0 & \cdots & 1 & v_{i-1} & 0 & \cdots & 0 & 0 & 0 & r_{i-1} \\ + 0 & 0 & 0 & \cdots & 0 & c_i - l_i v_{i-1} & u_i & \cdots & 0 & 0 & 0 & q_{i} - l_i r_{i-1} + \end{bmatrix}, + +or + +.. math:: + + & + \begin{bmatrix} + 0 & 0 & 0 & \cdots & 1 & v_{i-1} & 0 & \cdots & 0 & 0 & 0 & r_{i-1} \\ + 0 & 0 & 0 & \cdots & 0 & 1 & \frac{u_i}{c_i - l_i v_{i-1}} & \cdots & 0 & 0 & 0 & \frac{q_{i} - l_i r_{i-1}}{c_i - l_i v_{i-1}} + \end{bmatrix} \\ + & + = \\ + & + \begin{bmatrix} + 0 & 0 & 0 & \cdots & 1 & v_{i-1} & 0 & \cdots & 0 & 0 & 0 & r_{i-1} \\ + 0 & 0 & 0 & \cdots & 0 & 1 & v_{i} & \cdots & 0 & 0 & 0 & r_{i } + \end{bmatrix}, + +namely, + +.. math:: + + v_i \leftarrow \frac{u_i}{c_i - l_i v_{i-1}}, + +.. math:: + + r_i \leftarrow \frac{q_i - l_i r_{i-1}}{c_i - l_i v_{i-1}}. + +This is repeated from :math:`i = 1` to :math:`n - 2`: + +.. myliteralinclude:: /../../src/tdm.c + :language: c + :tag: forward sweep + +-------- +Last Row +-------- + +Basically we can do the same thing. +For the last row :math:`i = n-1`, however, the denominator + +.. math:: + + c_{n-1} - l_{n-1} v_{n-2} + +can be :math:`0`, namely the rank of the matrix is :math:`n-1`. + +This is expected, since we often impose the Neumann or the periodic boundary conditions, which can solve the differential equations only up to a constant. + +In order to take into account the singularity and to avoid the resulting zero divisions, we need a special treatment: + +.. myliteralinclude:: /../../src/tdm.c + :language: c + :tag: last row, do the same thing but consider singularity + +===================== +Backward Substitution +===================== + +After the forward sweep, we are left with the following system: + +.. math:: + + \begin{bmatrix} + 1 & v_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ + 0 & 1 & v_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ + 0 & 0 & 1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ + \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & 1 & v_{i-1} & 0 & \cdots & 0 & 0 & 0 \\ + 0 & 0 & 0 & \cdots & 0 & 1 & v_i & \cdots & 0 & 0 & 0 \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 1 & \cdots & 0 & 0 & 0 \\ + \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 1 & v_{n-3} & 0 \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 1 & v_{n-2} \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 1 + \end{bmatrix} + \begin{bmatrix} + x_0 \\ + x_1 \\ + x_2 \\ + \vdots \\ + x_{i-1} \\ + x_{i } \\ + x_{i+1} \\ + \vdots \\ + x_{n-3} \\ + x_{n-2} \\ + x_{n-1} + \end{bmatrix} + = + \begin{bmatrix} + r_0 \\ + r_1 \\ + r_2 \\ + \vdots \\ + r_{i-1} \\ + r_{i } \\ + r_{i+1} \\ + \vdots \\ + r_{n-3} \\ + r_{n-2} \\ + r_{n-1} + \end{bmatrix}. + +In the last row, we have + +.. math:: + + x_{n-1} = r_{n-1}, + +which has already been computed in the forward sweep. + +Also, since we have + +.. math:: + + x_i = r_i - v_i x_{i+1}, + +we can compute :math:`x_i` one after another (sequentially from :math:`i = n-2` to :math:`i = 0`): + +.. myliteralinclude:: /../../src/tdm.c + :language: c + :tag: backward substitution + +Note again that ``q`` is shared among :math:`x_i` (output) and :math:`q_i` (input) in the code. + +************** +Periodic Cases +************** + +The original Thomas algorithm only considers the tri-diagonal matrix. +With periodic boundary conditions, right-top and left-bottom corners have non-zero values (see below). + +Fortunately, by using `the Sherman-Morrison formula `_, we can handle this minor correction in the framework of the Thomas algorithm. + +Now, we consider the following system: + +.. math:: + + \begin{bmatrix} + c_0 & u_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & l_0 \\ + l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ + 0 & l_2 & c_2 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ + \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & c_{i-1} & u_{i-1} & 0 & \cdots & 0 & 0 & 0 \\ + 0 & 0 & 0 & \cdots & l_{i } & c_{i } & u_{i } & \cdots & 0 & 0 & 0 \\ + 0 & 0 & 0 & \cdots & 0 & l_{i+1} & c_{i+1} & \cdots & 0 & 0 & 0 \\ + \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & c_{n-3} & u_{n-3} & 0 \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & l_{n-2} & c_{n-2} & u_{n-2} \\ + u_{n-1} & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & l_{n-1} & c_{n-1} + \end{bmatrix} + \begin{bmatrix} + x_0 \\ + x_1 \\ + x_2 \\ + \vdots \\ + x_{i-1} \\ + x_{i } \\ + x_{i+1} \\ + \vdots \\ + x_{n-3} \\ + x_{n-2} \\ + x_{n-1} + \end{bmatrix} + = + \begin{bmatrix} + q_0 \\ + q_1 \\ + q_2 \\ + \vdots \\ + q_{i-1} \\ + q_{i } \\ + q_{i+1} \\ + \vdots \\ + q_{n-3} \\ + q_{n-2} \\ + q_{n-1} + \end{bmatrix}, + +where one may notice that the top-right and the bottom-left corners have non-zero values. + +Since we have + +.. math:: + + \begin{alignat}{5} + & c_0 x_0 & & + u_0 x_1 & & + l_0 x_{n-1} & & = q_0 & \,\,\, & 0 \text{-th row} \\ + & l_{n-2} x_{n-3} & & + c_{n-2} x_{n-2} & & + u_{n-2} x_{n-1} & & = q_{n-2} & \,\,\, & \left( n-2 \right) \text{-th row} + \end{alignat} + +or + +.. math:: + + \begin{alignat}{3} + & c_0 x_0 & & + u_0 x_1 & & = q_0 - l_0 x_{n-1} \\ + & l_{n-2} x_{n-3} & & + c_{n-2} x_{n-2} & & = q_{n-2} - u_{n-2} x_{n-1}, + \end{alignat} + +we can *shrink* the system: + +.. math:: + + \begin{bmatrix} + c_0 & u_0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ + l_1 & c_1 & u_1 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ + 0 & l_2 & c_2 & \cdots & 0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ + \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & c_{i-1} & u_{i-1} & 0 & \cdots & 0 & 0 & 0 \\ + 0 & 0 & 0 & \cdots & l_{i } & c_{i } & u_{i } & \cdots & 0 & 0 & 0 \\ + 0 & 0 & 0 & \cdots & 0 & l_{i+1} & c_{i+1} & \cdots & 0 & 0 & 0 \\ + \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & c_{n-4} & u_{n-4} & 0 \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & l_{n-3} & c_{n-3} & u_{n-3} \\ + 0 & 0 & 0 & \cdots & 0 & 0 & 0 & \cdots & 0 & l_{n-2} & c_{n-2} + \end{bmatrix} + \begin{bmatrix} + x_0 \\ + x_1 \\ + x_2 \\ + \vdots \\ + x_{i-1} \\ + x_{i } \\ + x_{i+1} \\ + \vdots \\ + x_{n-4} \\ + x_{n-3} \\ + x_{n-2} + \end{bmatrix} + = + \begin{bmatrix} + q_0 - l_0 x_{n-1} \\ + q_1 \\ + q_2 \\ + \vdots \\ + q_{i-1} \\ + q_{i } \\ + q_{i+1} \\ + \vdots \\ + q_{n-4} \\ + q_{n-3} \\ + q_{n-2} - u_{n-2} x_{n-1} + \end{bmatrix}, + +i.e., we moved :math:`x_{n-1}` from the left-hand side to the right-hand side, and the size of the system is now :math:`n - 1`. + +For notational simplicity, hereafter we write this as + +.. math:: + + \underline{\underline{A}} \, \underline{x} = \underline{q}. + +One may notice that this treatment has removed the additional components in the original matrix coming from the periodicity, and as a result we go back to the tri-diagonal system. + +The new system, however, includes an unknown :math:`x_{n-1}`. +As soon as we try to start the forward sweep, we would be in trouble since the first row in the right-hand side includes unknown value. +To resolve this situation, we consider to split the system into two problems: + +.. math:: + + {\underline{\underline{A}}} \, {\underline{x}}^0 & = {\underline{q}}^0, \\ + {\underline{\underline{A}}} \, {\underline{x}}^1 & = {\underline{q}}^1, + +where we define + +.. math:: + + \underline{q}^0 + = + \begin{bmatrix} + q_0 \\ + q_1 \\ + q_2 \\ + \vdots \\ + q_{i-1} \\ + q_{i } \\ + q_{i+1} \\ + \vdots \\ + q_{n-4} \\ + q_{n-3} \\ + q_{n-2} + \end{bmatrix}, + \underline{q}^1 + = + \begin{bmatrix} + - l_0 \\ + 0 \\ + 0 \\ + \vdots \\ + 0 \\ + 0 \\ + 0 \\ + \vdots \\ + 0 \\ + 0 \\ + - u_{n-2} + \end{bmatrix}, + +which satisfies + +.. math:: + + {\underline{q}} + = + {\underline{q}}^0 + + + x_{n-1} + {\underline{q}}^1. + +Note that the superscripts are used to distinguish the two problems (not the exponents). + +Since these two systems: + +.. math:: + + {\underline{\underline{A}}} \, {\underline{x}}^0 & = {\underline{q}}^0, \\ + {\underline{\underline{A}}} \, {\underline{x}}^1 & = {\underline{q}}^1, + +do not contain any unknown, we can solve them as two independent tri-diagonal systems: + +.. math:: + + {\underline{x}} + = + {\underline{x}}^0 + + + x_{n-1} + \times + {\underline{x}}^1, + +indicating that, the solution of the original system is the superposition of the solutions of the two tri-diagonal systems. + +The last piece is how to find :math:`x_{n-1}`, which is obtained by looking at the relation: + +.. math:: + + u_{n-1} x_0 + l_{n-1} x_{n-2} + c_{n-1} x_{n-1} = q_{n-1}, + +which appears in the last row of the original (:math:`n \times n`) system. + +Since we have + +.. math:: + + x_0 & = x_0^0 + x_{n-1} \times x_0^1, \\ + x_{n-2} & = x_{n-2}^0 + x_{n-1} \times x_{n-2}^1, \\ + +we notice + +.. math:: + + \left( u_{n-1} x_0^0 + l_{n-1} x_{n-2}^0 \right) + + \left( u_{n-1} x_0^1 + l_{n-1} x_{n-2}^1 + c_{n-1} \right) x_{n-1} + = q_{n-1}, + +and thus + +.. math:: + + x_{n-1} = + \frac{q_{n-1} - u_{n-1} x_0^0 - l_{n-1} x_{n-2}^0} + {c_{n-1} + u_{n-1} x_0^1 + l_{n-1} x_{n-2}^1}. + +This relation indicates that :math:`x_{n-1}` can be computed after solving the two shrunk linear systems + +.. math:: + + {\underline{\underline{A}}} \, {\underline{x}}^0 = {\underline{q}}^0`, + +.. math:: + + {\underline{\underline{A}}} \, {\underline{x}}^1 = {\underline{q}}^1`. + +Here is the summary and the corresponding implementation: + +#. Solve :math:`{\underline{\underline{A}}} \, {\underline{x}}^1 = {\underline{q}}^1`: + + .. myliteralinclude:: /../../src/tdm.c + :language: c + :tag: solve additional system coming from periodicity + +#. Solve :math:`{\underline{\underline{A}}} \, {\underline{x}}^0 = {\underline{q}}^0`: + + .. myliteralinclude:: /../../src/tdm.c + :language: text + :tag: solve normal system + + .. note:: + + The input argument ``q`` includes multiple (``nrhs``, corresponding to the loop whose index is ``j``) right-hand-side terms. + We assign the pointer of each right-hand side to ``q0`` here. + +#. Find :math:`x_{n-1}` + + :math:`x_{n-1}` is updated following + + .. math:: + + x_{n-1} = \frac{q_{n-1} - u_{n-1} x_0^0 - l_{n-1} x_{n-2}^0}{c_{n-1} + u_{n-1} x_0^1 + l_{n-1} x_{n-2}^1}: + + .. myliteralinclude:: /../../src/tdm.c + :language: c + :tag: find x_{n-1} + +#. Compute the solution of the original system :math:`{\underline{\underline{A}}} \, {\underline{x}} = {\underline{q}}` + + We use + + .. math:: + + {\underline{x}} = {\underline{x}}^0 + x_{n-1} \times {\underline{x}}^1: + + .. myliteralinclude:: /../../src/tdm.c + :language: c + :tag: solve original system + +.. note:: + + Although the function implemented in this project behaves similarly as the function implemented in `LAPACK `_, there are mainly two differences: + + * No pivoting + + Although functions in ``LAPACK`` include pivoting operations to stabilise the system, functions implemented in this source do not since all systems to be solved in this project are (semi-) diagonally dominant. + + * Treatment of the singularity + + When a singularity is detected, functions in ``LAPACK`` terminate. + This is not preferable in this project, since singular systems appear because of the Neumann or the periodic boundary conditions. + diff --git a/docs/source/numerical_method/temporal_discretisation/derivation/cn.rst b/docs/source/numerical_method/temporal_discretisation/derivation/cn.rst new file mode 100644 index 00000000..7a19144d --- /dev/null +++ b/docs/source/numerical_method/temporal_discretisation/derivation/cn.rst @@ -0,0 +1,31 @@ +Because of + +.. math:: + + g^{n+1} + = + \frac{d\newvar{f}}{dt} + = + \frac{df^n}{dt} + \frac{d^2f^n}{dt^2} \Delta t + \frac{1}{2} \frac{d^3f^n}{dt^3} \Delta t^2 + \oerror{3}, + +we find + +.. math:: + + \newvar{f} + & + = + f^n + + + \frac{1}{2} + \frac{df^n}{dt} + \Delta t + + + \frac{1}{2} \left( \frac{df^n}{dt} + \frac{d^2f^n}{dt^2} \Delta t + \frac{1}{2} \frac{d^3f^n}{dt^3} \Delta t^2 + \frac{df^n}{dt} \right) \Delta t + + & + = + f^n + \frac{df^n}{dt} \Delta t + \frac{1}{2} \frac{d^2f^n}{dt^2} \Delta t^2 + \oerror{3}, + +indicating that this scheme has the third-order accuracy (locally) and the second-order accuracy (globally). + diff --git a/docs/source/numerical_method/temporal_discretisation/derivation/rk.rst b/docs/source/numerical_method/temporal_discretisation/derivation/rk.rst new file mode 100644 index 00000000..e6ca7515 --- /dev/null +++ b/docs/source/numerical_method/temporal_discretisation/derivation/rk.rst @@ -0,0 +1,124 @@ +Because of + +.. math:: + + f^{k+1} + = + f^{k } + + + \alpha^k g^{k } \Delta t + + + \beta^k g^{k-1} \Delta t, + +we find + +.. math:: + + g^{k+1} + = + \frac{df^{k+1}}{dt} + = + \frac{df^k}{dt} + + + \alpha^k \frac{d^2f^k}{dt^2} \Delta t + + + \beta^k \frac{d^2f^{k-1}}{dt^2} \Delta t. + +We use this relation repeatedly. +For :math:`k = 1`, we have + +.. math:: + + f^1 + = + f^n + + + \alpha^0 g^n \Delta t + = + f^n + \alpha^0 \frac{df^n}{dt} \Delta t, + +whose derivation leads to + +.. math:: + + g^1 + = + \frac{df^n}{dt} + + + \alpha^0 \frac{d^2f^n}{dt^2} \Delta t. + +Note that :math:`k = 0` corresponds to :math:`n` (old information). +For :math:`k = 2`, we have + +.. math:: + + f^2 + & + = + f^1 + + + \alpha^1 g^1 \Delta t + \beta^1 g^0 \Delta t + + & + = + f^n + + + \alpha^0 \frac{df^n}{dt} \Delta t + \alpha^1 \left( \frac{df^n}{dt} + \alpha^0 \frac{d^2f^n}{dt^2} \Delta t \right) \Delta t + \beta^1 \frac{df^n}{dt} \Delta t, + + & + = + f^n + \left( \alpha^0 + \alpha^1 + \beta^1 \right) \frac{df^n}{dt} \Delta t + \alpha^0 \alpha^1 \frac{d^2f^n}{dt^2} \Delta t^2, + +whose derivation leads to + +.. math:: + + g^2 + = + \frac{df^n}{dt} + \left( \alpha^0 + \alpha^1 + \beta^1 \right) \frac{d^2f^n}{dt^2} \Delta t + \alpha^0 \alpha^1 \frac{d^3f^n}{dt^3} \Delta t^2. + +For :math:`k = 3`, we have + +.. math:: + + f^3 + & + = + f^2 + \alpha^2 g^2 \Delta t + \beta^2 g^1 \Delta t + + & + = + f^n + \left( \alpha^0 + \alpha^1 + \beta^1 \right) \frac{df^n}{dt} \Delta t + \alpha^0 \alpha^1 \frac{d^2f^n}{dt^2} \Delta t^2 + + + \alpha^2 \left( \frac{df^n}{dt} + \left( \alpha^0 + \alpha^1 + \beta^1 \right) \frac{d^2f^n}{dt^2} \Delta t + \alpha^0 \alpha^1 \frac{d^3f^n}{dt^3} \Delta t^2 \right) \Delta t + + + \beta^2 \left( \frac{df^n}{dt} + \alpha^0 \frac{d^2f^n}{dt^2} \Delta t \right) \Delta t, + + & + = + f^n + \left( \alpha^0 + \alpha^1 + \beta^1 + \alpha^2 + \beta^2 \right) \frac{df^n}{dt} \Delta t + + + \left( \alpha^0 \alpha^1 + \alpha^0 \alpha^2 + \alpha^1 \alpha^2 + \alpha^2 \beta^1 + \alpha^0 \beta^2 \right) \frac{d^2f^n}{dt^2} \Delta t^2 + + + \oerror{3} + + & + = + f^n + \frac{df^n}{dt} \Delta t + \frac{1}{2} \frac{d^2f^n}{dt^2} \Delta t^2 + \oerror{3}. + +Since :math:`k = 3` corresponds to :math:`n + 1` (new information), we find + +.. math:: + + \newvar{f} + = + f^n + + + \frac{df^n}{dt} \Delta t + + + \frac{1}{2} \frac{d^2f^n}{dt^2} \left( \Delta t^2 \right) + + + \oerror{3}, + +indicating that this scheme has the third-order accuracy (locally) and the second-order accuracy (globally). + diff --git a/docs/source/numerical_method/temporal_discretisation/implicit.rst b/docs/source/numerical_method/temporal_discretisation/implicit.rst index c899e29a..a844caac 100644 --- a/docs/source/numerical_method/temporal_discretisation/implicit.rst +++ b/docs/source/numerical_method/temporal_discretisation/implicit.rst @@ -1,244 +1,313 @@ -.. include:: /references.txt - .. _implicit_treatment: +.. include:: /references.txt + ################## -Implicit treatment +Implicit Treatment ################## -**************************** -Time scales and restrictions -**************************** +************************************** +Time Scales and Time-Step Restrictions +************************************** -Numerically, I should not allow any information to propagate longer distance than the grid size in one time step. -To use a larger time step size :math:`\Delta t`, the information should be treated implicitly which is capable of capturing infinite propagation speed. -There are following three time scales in the governing equations which are considered in this project, which give the corresponding three restrictions on the time step size :math:`\Delta t`. +In the Navier-Stokes equations, there are advective, pressure-gradient, and diffusive terms, each with different time scales. +Numerically, when a term is treated explicitly in time, information associated with it should not propagate a distance greater than the grid size in one time step. +In other words, to allow the information to travel further, the term should be treated implicitly. +Here, we elaborate on the three terms and their time scales to understand their effects on the overall time-marching process. #. Advective terms - One comes from the advective terms in the governing equations, where the information should not travel longer distance than the local grid size. - This constraint yields - - .. math:: - - \Delta t_{adv} - < - \Delta x / \ux, + By using the fluid velocity and the grid size as the reference velocity and length scales, we find that the advective terms impose a constraint: - where :math:`\Delta x` and :math:`\ux` are the local grid size and the velocity, respectively. - Note that, although written only in :math:`x` direction here, the same condition is applied to the other directions. + .. math:: - I see that the time step size should be reduced as the resolution gets finer, and the refinement speed is proportional to the grid size: + & + \Delta t_{adv} + = + C + \frac{\sfact{i}}{\vel{i}}, - .. math:: + & + C + < + 1, - \Delta t_{adv} - = - C \Delta x, + where :math:`C` is a non-dimensional number known as the Courant number. - where :math:`C` is called Courant number. + Recall that the grid sizes are unity in the computational coordinate system, while the velocity is divided by the scale factor. + See :ref:`the equations in strong conservation forms `. - Since the advective terms are non-linear, it is impractical to treat them implicitly, and I cannot elimitate this constraint. + Due to advective effects, :math:`\Delta t` should be reduced as the resolution becomes finer, being proportional to the spatial resolution. + Since the advective terms are non-linear, treating them implicitly is not straightforward, thus this constraint is always present in this project. #. Diffusive terms - The other one is imposed by the diffusive terms in the governing equations, where the time scale yields + By adopting the diffusivities and the grid size as reference scales, we find that the diffusive terms impose another constraint: - .. math:: + .. math:: - & \Delta t_{dif, fluid} \propto \frac{\sqrt{Ra}}{\sqrt{Pr}} \Delta x^2, \\ - & \Delta t_{dif, temperature} \propto \sqrt{Pr} \sqrt{Ra} \Delta x^2, + & + \Delta t_{dif} + = + F + \min + \left( + \frac{\sqrt{Ra}}{\sqrt{Pr}} , + \sqrt{Pr} \sqrt{Ra} + \right) + \sfact{i}^2, - in non-dimensional form. - Note that the kinematic viscosity has the unit of :math:`\left[ L^2 T^{-1} \right]`, giving the characteristic time scale above. + & + F + < + 1, - Now I see that the time step size should be reduced quadratically: + where :math:`F` is a non-dimensional number known as the Fourier number. + Note that the momentum and temperature fields have different diffusivities. - .. math:: + As the spatial resolution is refined, :math:`\Delta t` should be reduced quadratically. + This criterion can make computational costs prohibitive, especially for wall-bounded turbulent flows where the wall-normal grid sizes must be extremely small close to the walls to resolve boundary layers. - \Delta t_{dif} - = - F \Delta x^2, - - where :math:`F` is called Fourier number. - - This criterion is often very severe and easily makes computations impractical, especially for wall-bounded turbulent flows where the wall-normal grid sizes should be extremely small close to the walls to resolve the boundary layers. - - Since the diffusive terms are linear, this restriction can be eliminated by treating it implicitly, which is the central focus of this section. + Since the diffusive terms are linear, this restriction can be eliminated by treating them implicitly, as elaborated later on this page. #. Pressure-gradient terms - Incompressible liquids is the limited condition whose speed of sound is infinity. - To avoid :math:`\Delta t = 0`, the pressure field should always be treated implicitly. - - .. seealso:: - - In other words, for incompressible flows, the pressure field is determined to satisfy the incompressibility, which is the central idea of :ref:`the SMAC method `. - -************************************************* -Approximate factorisation for the diffusive terms -************************************************* + Assuming the liquids are incompressible, i.e., the speed of sound is infinite, the pressure-gradient term must be treated implicitly. + See :ref:`the temporal integration of the momentum balance `. -To treat the diffusive terms implicitly, I need to solve the following N-dimensional Helmholtz equations: +******************************************** +Approximate Factorization of Diffusive Terms +******************************************** -.. math:: - - \left( 1 - \frac{\Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} \right) u_i^{n+1} - = - - - \dder{p^n}{x_i} \Delta t - - - u_j^n \dder{u_i^n}{x_j} \Delta t - + - \left( 1 + \frac{\Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} \right) u_i^n - -for the momentum field, and +Here we focus on the implicit treatment of the diffusive terms. +Applying :ref:`the combined scheme ` to :ref:`the momentum and internal energy balances ` yields, for each Runge-Kutta sub-step: .. math:: - \left( 1 - \frac{\Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} \right) T^{n+1} - = - - - u_j^n \dder{T^n}{x_j} \Delta t - + - \left( 1 + \frac{\Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} \right) T^n - -for the temperature field. - -In this project, I can simplify these equations as follows. - -First, I re-write the equations as + \vel{i}^{k+1} + - + \vel{i}^{k} + = + \gamma^k + \Delta t + \frac{\sqrt{Pr}}{\sqrt{Ra}} + \frac{1}{J} + \dif{}{\gcs{j}} + \left[ + \frac{J}{\sfact{j}} + \frac{1}{\sfact{j}} + \dif{}{\gcs{j}} + \left\{ + c + \vel{i}^{k+1} + + + \left( 1 - c \right) + \vel{i}^k + \right\} + \right] + + + \left( + \text{others} + \right)_i, + +and .. math:: - \left( 1 - \frac{\Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} \right) \Delta u_i - = - - - \dder{p^n}{x_i} \Delta t - - - u_j^n \dder{u_i^n}{x_j} \Delta t - + - \Delta t \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} u_i^n, + T^{k+1} + - + T^{k} + = + \gamma^k + \Delta t + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{1}{J} + \dif{}{\gcs{j}} + \left[ + \frac{J}{\sfact{j}} + \frac{1}{\sfact{j}} + \dif{}{\gcs{j}} + \left\{ + c + T^{k+1} + + + \left( 1 - c \right) + T^k + \right\} + \right] + + + \left( + \text{others} + \right), + +respectively, where :math:`c` is a coefficient specifying the implicit treatment: .. math:: - \left( 1 - \frac{\Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} \right) \Delta T - = - - - u_j^n \dder{T^n}{x_j} \Delta t - + - \Delta t \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} T^n, + c + = + \begin{cases} + \text{Euler explicit} & 0, \\ + \text{Crank-Nicolson} & \frac{1}{2}, \\ + \text{Euler implicit} & 1. + \end{cases} -where +Here the last terms include the advective, pressure-gradient, and buoyancy terms which are not important here. +Since they are almost identical, we only focus on the temperature relation, which yields a Helmholtz equation: .. math:: - \Delta u_i - \equiv - u_i^{n+1} - - - u_i^{n }, + \left\{ + 1 + - + c \gamma^k \Delta t + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{1}{J} + \dif{}{\gcs{j}} + \left( + \frac{J}{\sfact{j}} + \frac{1}{\sfact{j}} + \dif{}{\gcs{j}} + \right) + \right\} + T^{k+1} + = + \left\{ + 1 + + + \left( + 1 + - + c + \right) + \gamma^k \Delta t + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{1}{J} + \dif{}{\gcs{j}} + \left( + \frac{J}{\sfact{j}} + \frac{1}{\sfact{j}} + \dif{}{\gcs{j}} + \right) + \right\} + T^{k} + + + \left( + \text{others} + \right). + +Although this equation can be solved in a similar way as :ref:`solving Poisson equations `, we simplify it by utilising the approximate factorisation (|DUKOWICZ1992|) as follows. + +First, we rewrite the equations as .. math:: - \Delta T - \equiv - T^{n+1} - - - T^{n }. + \newcommand{\lap}[2]{ + {#2} \gamma^k \Delta t + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{1}{J} + \dif{}{\gcs{#1}} + \left( + \frac{J}{\sfact{#1}} + \frac{1}{\sfact{#1}} + \dif{}{\gcs{#1}} + \right) + } + \left\{ + 1 + - + \lap{j}{c} + \right\} + \Delta T + = + \lap{j}{} + T^k + + + \left( + \text{others} + \right), -Then I approximate the left-hand-side terms as +where .. math:: - \left( 1 - \frac{\Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} \right) \Delta u_i - & - = - \left( - 1 - - - \frac{\Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x} \dder{}{x} - - - \frac{\Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{y} \dder{}{y} - \right) \Delta u_i \\ - & - \approx - \left( 1 - \frac{\Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x} \dder{}{x} \right) - \left( 1 - \frac{\Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{y} \dder{}{y} \right) - \Delta u_i, - -.. math:: + \Delta T + \equiv + T^{n+1} + - + T^{n }. - \left( 1 - \frac{\Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} \right) \Delta T - & - = - \left( - 1 - - - \frac{\Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x} \dder{}{x} - - - \frac{\Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{y} \dder{}{y} - \right) \Delta T \\ - & - \approx - \left( 1 - \frac{\Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x} \dder{}{x} \right) - \left( 1 - \frac{\Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{y} \dder{}{y} \right) - \Delta T. - -By assuming +We approximate the left-hand side .. math:: - \Delta u_i - \sim - \Delta t, - \Delta T - \sim - \Delta t, + \left\{ + 1 + - + \lap{1}{c} + - + \lap{2}{c} + - + \lap{3}{c} + \right\} + \Delta T -I notice that the splitting errors +as .. math:: - \frac{\Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x} \dder{}{x} - \times - \frac{\Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{y} \dder{}{y} - \times - \Delta u_i, + \left\{ + 1 + - + \lap{1}{c} + \right\} + \left\{ + 1 + - + \lap{2}{c} + \right\} + \left\{ + 1 + - + \lap{3}{c} + \right\} + \Delta T. + +The leading-order error induced by this approximation is .. math:: - \frac{\Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x} \dder{}{x} - \times - \frac{\Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{y} \dder{}{y} - \times - \Delta T, - -are :math:`\sim \Delta t^3`, which is comparable to the dominant error of :ref:`the used Runge-Kutta scheme `. + \lap{1}{c} + \Delta T + \times + \lap{2}{c} + \Delta T + = + \mathcal{O} \left( \Delta t^3 \right) -In summary, the N-dimensional Helmholtz equations are approximated by the linear system for each direction and much easier to solve. -This splitting method is called approximate factorisation (|DUKOWICZ1992|). -Obviously this technique is not accepted if +by assuming - * I solve the equation with respect to the variable itself (i.e. :math:`u_i` instead of :math:`\Delta u_i`) +.. math:: - * I use higher-order scheme to integrate the equation in time. + \Delta T + \sim + \Delta t. -Here, I need to solve :ref:`a linear system ` to find :math:`\left( \cdots \right)^{-1}`. -Since they are independent linear systems in each direction and I adopt the second-order-accurate central-difference scheme in space, I can solve them by :ref:`the tri-diagonal matrix algorithm `. +Since :math:`\mathcal{O} \left( \Delta t^3 \right)` is comparable to the dominant error of :ref:`the three-step explicit Runge-Kutta scheme `, this treatment is justified. +(Note that this is not accepted if we adopt a more accurate scheme to integrate the equation in time.) +Note that we need to solve :ref:`a linear system ` here. .. note:: - * Overhead + * Overhead - In this project, the implicit treatment in the :math:`x` (wall-normal) direction can be easily achieved, whose cost is up to a few percent. - The implicit treatments in the other directions, however, require certain amount of MPI communication, whose overhead can be more than :math:`100` percent. + In this project, the implicit treatment in the :math:`x` (wall-normal) direction can be easily achieved, whose cost is up to a few percent. + The implicit treatments in the other directions, however, require certain amount of MPI communication, whose overhead can be more than :math:`100` percent. - * Monotonicity + * Monotonicity - Although the Crank-Nicolson scheme can eliminate the stability restriction, monotonicity (i.e. temperature is bounded between the two boundary values) is not guaranteed. - Unfortunately, in order to guarantee the monotonicity, :math:`\Delta t \propto \Delta x^2` should be satisfied again (see e.g. |HORVATH2000|). - Although this restriction disappears by adopting the Euler backward scheme (at the expense of the temporal accuracy), this fact is neglected in this project for now. - To minimises this issue, using a sufficiently fine spatial resolution to resolve the highest frequency is important. + Although the Crank-Nicolson scheme can eliminate the stability restriction, monotonicity (i.e., temperature is bounded between the two boundary values) is not guaranteed. + Unfortunately, to guarantee the monotonicity, :math:`\Delta t \propto \left( \sfact{i} \right)^2` should be satisfied (see e.g., |HORVATH2000|). + Although this restriction disappears by adopting the Euler implicit scheme (at the expense of the temporal accuracy), this issue is neglected in this project for now. diff --git a/docs/source/numerical_method/temporal_discretisation/main.rst b/docs/source/numerical_method/temporal_discretisation/main.rst index 019cd53a..b621f0c0 100644 --- a/docs/source/numerical_method/temporal_discretisation/main.rst +++ b/docs/source/numerical_method/temporal_discretisation/main.rst @@ -1,17 +1,19 @@ -.. include:: /references.txt +.. _temporal_discretization: -.. _temporal_discretisation: +.. include:: /references.txt ####################### -Temporal discretisation +Temporal Discretization ####################### -In this section, I discuss how :ref:`the governing equations ` are integrated in time. +In this section, we discuss how :ref:`the governing equations ` are integrated over time. + +First, we briefly introduce several explicit and implicit schemes for integrating general ordinary differential equations over time. + +We then elaborate on the implicit treatment applied to the diffusive terms to avoid severe restrictions on time-step sizes. -To begin with, general remarks (the overall time marching schemes and the implicit treatment of the diffusive terms) are discussed. -The treatment of the temperature field is presented in the first part as it is simpler. -Special attention is needed for the conservation of the mass and the balance of the momentum, which is discussed in the second part. +These fundamental techniques are first applied to the internal energy equation (evolution of temperature) due to its simplicity, and are then extended to the integration of the velocity field, where the momentum balance is integrated while keeping the incompressibility constraint. .. toctree:: :maxdepth: 1 @@ -19,5 +21,5 @@ Special attention is needed for the conservation of the mass and the balance of time_marcher implicit temperature - smac_method + momentum diff --git a/docs/source/numerical_method/temporal_discretisation/momentum.rst b/docs/source/numerical_method/temporal_discretisation/momentum.rst new file mode 100644 index 00000000..2f7f9cf1 --- /dev/null +++ b/docs/source/numerical_method/temporal_discretisation/momentum.rst @@ -0,0 +1,571 @@ + +.. _momentum_integration: + +.. include:: /references.txt + +################ +Momentum Balance +################ + +The momentum balance is + +.. math:: + + \pder{u_i}{t} + = + P_i + + + A_i + + + D_{i1} + + + D_{i2} + + + D_{i3}, + +where :math:`P_i` and :math:`A_i` are the pressure-gradient terms: + +.. math:: + + P_i + \equiv + - + \dmompre{i}, + +and the advective terms: + +.. math:: + + A_i + \equiv + - + \dmomadv{i}{1} + - + \dmomadv{i}{2} + - + \dmomadv{i}{3}, + +respectively. + +:math:`D_{ij}` is the diffusive term involving spatial differentiation in the :math:`j`-th direction: + +.. math:: + + D_{ij} + \equiv + \dmomdif{j}{i} + \,\, + (\text{no summation over}\,j). + +See :ref:`the spatial discretization `. + +The temporal discretization for each Runge-Kutta iteration leads to + +.. math:: + + \Delta u_i + & + = + \gamma^k \Delta t P_i^k + + + \alpha^k \Delta t \left( A_i^{k } + D_i^{k } \right) + + + \beta^k \Delta t \left( A_i^{k-1} + D_i^{k-1} \right), + + u_i^* + & + = + u_i^k + + + \Delta u_i, + +when all advective and diffusive terms are treated explicitly, while + +.. math:: + + \newcommand{\lap}[2]{ + {#2} \gamma^k \Delta t + \frac{\sqrt{Pr}}{\sqrt{Ra}} + \frac{1}{J} + \dif{}{\gcs{#1}} + \left( + \frac{J}{\sfact{#1}} + \frac{1}{\sfact{#1}} + \dif{}{\gcs{#1}} + \right) + } + \Delta u_i + & + = + \gamma^k \Delta t P_i^k + + + \alpha^k \Delta t A_i^{k } + + + \beta^k \Delta t A_i^{k-1} + + + \gamma^k \Delta t \left( D_{i1}^k + D_{i2}^k + D_{i3}^k \right), + + u_i^* + & + = + u_i^k + + + \left\{ + 1 + - + \lap{3}{c} + \right\}^{-1} + \left\{ + 1 + - + \lap{2}{c} + \right\}^{-1} + \left\{ + 1 + - + \lap{1}{c} + \right\}^{-1} + \Delta u_i, + +when diffusive terms are treated implicitly. + +Although we obtain a new velocity field, this does not satisfy :ref:`the incompressibility ` in general, which necessitates the additional procedure below. + +*********** +SMAC method +*********** + +In addition to the incompressibility constraint, we need to somehow update the pressure field as well, which we do not have any equation such as: + +.. math:: + + \pder{p}{t} + = + \cdots. + +To overcome these issues, we adopt the Simplified Marker And Cell (SMAC) method (|AMSDEN1970|), which is a two-step method. + +=============== +Prediction Step +=============== + +In the first step (prediction step), momentum equation is integrated in time, without taking care of the mass conservation, as discussed above. +Basically the procedure is identical to :ref:`how we handle the temperature field `. +First, the explicit and implicit terms are calculated and stored to the corresponding buffers: + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: compute right-hand-side terms, which are added to buffers + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: compute right-hand-side terms, which are added to buffers + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: compute right-hand-side terms, which are added to buffers + +The stored values are used to compute :math:`\Delta u_i`: + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: compute increments + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: compute increments + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: compute increments + +When necessary, linear systems are solved to take care of the implicit treatments: + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: solve linear systems in x + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: solve linear systems in y + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: solve linear systems in z + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: solve linear systems in x + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: solve linear systems in y + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: solve linear systems in z + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: solve linear systems in x + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: solve linear systems in y + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: solve linear systems in z + +Finally the velocity field is updated: + +.. myliteralinclude:: /../../src/fluid/predict/ux.c + :language: c + :tag: update velocity field + +.. myliteralinclude:: /../../src/fluid/predict/uy.c + :language: c + :tag: update velocity field + +.. myliteralinclude:: /../../src/fluid/predict/uz.c + :language: c + :tag: update velocity field + +=============== +Correction Step +=============== + +The updated velocity field :math:`u_i^*`, which in general violates the incompressibility, is corrected in the second step (correction step). +The idea is mathematically written as + +.. math:: + + u_i^{k+1} + = + u_i^* + - + \gamma^k \Delta t + \frac{1}{\sfact{i}} + \dif{\psi}{\gcs{i}}, + +where :math:`\psi` is a scalar potential to be given. +By taking the (discrete) divergence, we obtain + +.. math:: + + \frac{1}{J} + \dif{}{\gcs{i}} + \left( + \frac{J}{\sfact{i}} + \vel{i}^{k+1} + \right) + = + \frac{1}{J} + \dif{}{\gcs{i}} + \left( + \frac{J}{\sfact{i}} + \vel{i}^* + \right) + - + \gamma^k \Delta t + \frac{1}{J} + \dif{}{\gcs{i}} + \left( + \frac{J}{\sfact{i}} + \frac{1}{\sfact{i}} + \dif{\psi}{\gcs{i}} + \right). + +By requesting the (discrete) incompressibility constraint on the new velocity field (namely the left-hand-side term to be zero), we obtain + +.. math:: + + \frac{1}{J} + \dif{}{\gcs{i}} + \left( + \frac{J}{\sfact{i}} + \frac{1}{\sfact{i}} + \dif{\psi}{\gcs{i}} + \right) + = + \frac{1}{\gamma^k \Delta t} + \frac{1}{J} + \dif{}{\gcs{i}} + \left( + \frac{J}{\sfact{i}} + \vel{i}^* + \right), + +which is a Poisson equation with respect to :math:`\psi`. + +The right-hand-side term is computed as follows in the code: + +.. myliteralinclude:: /../../src/fluid/compute_potential.c + :language: c + :tag: compute right-hand side of Poisson equation + +After :ref:`solving the Poisson equation `, the velocity field is corrected as follows in the code: + +.. myliteralinclude:: /../../src/fluid/correct/ux.c + :language: c + :tag: correct x velocity + +.. myliteralinclude:: /../../src/fluid/correct/uy.c + :language: c + :tag: correct y velocity + +.. myliteralinclude:: /../../src/fluid/correct/uz.c + :language: c + :tag: correct z velocity + +============================= +Pressure and Scalar Potential +============================= + +Finally we relate the pressure field with the scalar potential to close the system. +Each Runge-Kutta step when all diffusive terms are treated explicitly is given by + +.. math:: + + \Delta u_i + & + = + \gamma^k \Delta t P_i^k + + + \alpha^k \Delta t \left( A_i^{k } + D_{i1}^{k } + D_{i2}^{k } + D_{i3}^{k } \right) + + + \beta^k \Delta t \left( A_i^{k-1} + D_{i1}^{k-1} + D_{i2}^{k-1} + D_{i3}^{k-1} \right), + + u_i^* + & + = + u_i^k + + + \Delta u_i, + + u_i^{k+1} + & + = + u_i^* + - + \gamma^k \Delta t + \frac{1}{\sfact{i}} + \dif{\psi}{\gcs{i}}. + +Summing all three steps yield + +.. math:: + + u_i^{k+1} + = + u_i^k + - + \gamma^k \Delta t + \frac{1}{\sfact{i}} + \dif{\psi}{\gcs{i}} + + + \gamma^k \Delta t P_i^k + + + \alpha^k \Delta t \left( A_i^{k } + D_{i1}^{k } + D_{i2}^{k } + D_{i3}^{k } \right) + + + \beta^k \Delta t \left( A_i^{k-1} + D_{i1}^{k-1} + D_{i2}^{k-1} + D_{i3}^{k-1} \right). + +Since :ref:`the pressure field should be treated implicitly in time `, we find a requirement: + +.. math:: + + - + \gamma^k \Delta t + \frac{1}{\sfact{i}} + \dif{\psi}{\gcs{i}} + + + \gamma^k \Delta t P_i^k + = + \gamma^k \Delta t P_i^{k+1}, + +or equivalently + +.. math:: + + p^{k+1} + = + p^k + + + \psi. + +Next, we consider cases where the diffusive terms are partially treated implicitly in time; for instance, the following relation holds when the diffusive terms are treated implicitly only in :math:`x` direction: + +.. math:: + + \Delta u_i + & + = + \gamma^k \Delta t P_i^k + + + \alpha^k \Delta t \left( A_i^{k } + D_{i2}^{k } + D_{i3}^{k } \right) + + + \beta^k \Delta t \left( A_i^{k-1} + D_{i2}^{k-1} + D_{i3}^{k-1} \right) + + + \gamma^k \Delta t D_{i1}^{k }, + + u_i^* + & + = + u_i^k + + + \left\{ + 1 + - + \lap{1}{c} + \right\}^{-1} + \Delta u_i, + + u_i^{k+1} + & + = + u_i^* + - + \gamma^k \Delta t + \frac{1}{\sfact{i}} + \dif{\psi}{\gcs{i}}. + +Note that, in the second step, the left-hand side is :math:`u_i^*` while it should be :math:`u_i^{k+1}` but is unknown. +By requesting the pressure-gradient term to be implicit in time and with some algebra, we obtain + +.. math:: + + \gamma^k \Delta t + \left\{ + 1 + - + \lap{1}{c} + \right\} + \frac{1}{\sfact{i}} + \dif{\psi}{\gcs{i}} + - + \gamma^k \Delta t + P_i^k + = + - + \gamma^k \Delta t + P_i^{k+1}, + +or equivalently + +.. math:: + + \frac{1}{\sfact{i}} + \dif{p^{k+1}}{\gcs{i}} + = + \frac{1}{\sfact{i}} + \dif{p^k}{\gcs{i}} + + + \left\{ + 1 + - + \lap{1}{c} + \right\} + \frac{1}{\sfact{i}} + \dif{\psi}{\gcs{i}}. + +Since the spatial-differential operators are interchangeable, we simplify the relation to + +.. math:: + + p^{k+1} + = + p^k + + + \psi + - + \lap{1}{c} + \psi. + +Similarly, when all diffusive terms are treated implicitly, we have + +.. math:: + + \Delta u_i + & + = + \gamma^k \Delta t P_i^k + + + \alpha^k \Delta t A_i^{k } + + + \beta^k \Delta t A_i^{k-1} + + + \gamma^k \Delta t \left( D_{i1}^{k } + D_{i2}^{k } + D_{i3}^{k } \right), + + u_i^* + & + = + u_i^k + + + \left\{ + 1 + - + \lap{3}{c} + \right\}^{-1} + \left\{ + 1 + - + \lap{2}{c} + \right\}^{-1} + \left\{ + 1 + - + \lap{1}{c} + \right\}^{-1} + \Delta u_i, + + u_i^{k+1} + & + = + u_i^* + - + \gamma^k \Delta t + \frac{1}{\sfact{i}} + \dif{\psi}{\gcs{i}}, + + p^{k+1} + & + = + p^k + + + \psi + - + \lap{1}{c} + \psi + - + \lap{2}{c} + \psi + - + \lap{3}{c} + \psi. + +Updating pressure field using the scalar potential is implemented as follows: + +.. myliteralinclude:: /../../src/fluid/update_pressure.c + :language: c + :tag: update pressure field using scalar potential + +The explicit contribution, which is always present, is given here: + +.. myliteralinclude:: /../../src/fluid/update_pressure.c + :language: c + :tag: explicit contribution + +The implicit contributions, which is needed when the Laplace operator in the direction is implicitly treated, are given here: + +.. myliteralinclude:: /../../src/fluid/update_pressure.c + :language: c + :tag: x implicit contribution + +.. myliteralinclude:: /../../src/fluid/update_pressure.c + :language: c + :tag: y implicit contribution + +.. myliteralinclude:: /../../src/fluid/update_pressure.c + :language: c + :tag: z implicit contribution + diff --git a/docs/source/numerical_method/temporal_discretisation/smac_method.rst b/docs/source/numerical_method/temporal_discretisation/smac_method.rst deleted file mode 100644 index 91605e02..00000000 --- a/docs/source/numerical_method/temporal_discretisation/smac_method.rst +++ /dev/null @@ -1,499 +0,0 @@ - -.. include:: /references.txt - -.. _smac_method: - -################################################## -Integrating mass conservation and momentum balance -################################################## - -The momentum equation in non-dimensional form is - -.. math:: - - \der{u_i}{t} - = - P_i - + - A_i - + - D_i, - -where I introduce some symbols for notational convenience: - -.. math:: - - P_i^k - \equiv - - - \dder{p^k}{x_i}, - -.. math:: - - A_i^k - \equiv - - - u_j^k \dder{u_i^k}{x_j}, - -.. math:: - - D_i^k - \equiv - \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{u_i^k}{x_j}. - -Here the spatial derivatives are approximated by proper finite-difference schemes discussed in :ref:`the other part `. - -************** -Discretisation -************** - -A naive temporal discretisation of the momentum equation would be - -.. math:: - - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\, - \Delta u_i - = - \gamma^k \Delta t P_i^{k+1} - + - \alpha^k \Delta t g_i^k - + - \beta^k \Delta t g_i^{k-1}, \\ - &\,\,\,\, - u_i^{k+1} - = - u_i^k - + - \Delta u_i, \\ - &\text{enddo} - -when all advective and diffusive terms are treated explicitly, while - -.. math:: - - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\, - \Delta u_i - = - \gamma^k \Delta t P_i^{k+1} - + - \alpha^k \Delta t g_i^k - + - \beta^k \Delta t g_i^{k-1} - + - \gamma^k \Delta t h_i^{k }, \\ - &\,\,\,\, - u_i^{k+1} - = - u_i^k - + - \left( - 1 - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{y} \dder{}{y} - \right)^{-1} - \left( - 1 - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x} \dder{}{x} - \right)^{-1} - \Delta u_i, \\ - &\text{enddo} - -when diffusive terms are treated implicitly. - -Here :math:`g_i^k` and :math:`h_i^k` are used to denote terms which are treated explicitly and implicitly, respectively. -Since the advective terms :math:`A_i^k` are always treated explicitly in time in this project, they are included in :math:`g_i^k`. -Diffusive terms can be fully or partially in :math:`g_i^k` or :math:`h_i^k`, depending on the user specification. - -Recall that I regard the three Runge-Kutta sub-steps as the combination of the three Euler-forward schemes having smaller time steps :math:`\gamma^k \Delta t` (see :ref:`the time-marching schemes `). -As discussed in :ref:`the implicit treatment `, as long as I assume the fluid is incompressible, I need to handle the pressure term implicitly, which is the reason why :math:`P_i^{k+1}` is used. - -=========== -SMAC method -=========== - -------------------------------------------- -When diffusive terms are explicitly treated -------------------------------------------- - -First, I consider :math:`h_i^k = 0`, i.e. all diffusive terms are treated explicitly. -Compared to the temporal integration of :ref:`the temperature field `, I have two additional problems to integrate :ref:`the momentum equation `: - - * enforcing the mass conservation while integrating the momentum equation, - - * coupling the pressure, which is independent of time. - -One solution to resolve these two challenges is known as Simplified Marker And Cell (SMAC) method (|AMSDEN1970|), which splits the momentum equation into two parts. -In the first step (prediction step), momentum equation is integrated in time, without taking care of the mass conservation. -This velocity field, which in general violates the incompressibility, is corrected by adjusting the pressure field in the second step (correction step). - -By introducing a prediction velocity :math:`u_i^*`, this idea to update the velocity field can be written as - -.. math:: - - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\, - \Delta u_i - = - \gamma^k \Delta t P_i^k - + - \alpha^k \Delta t g_i^k - + - \beta^k \Delta t g_i^{k-1}, \\ - &\,\,\,\, - u_i^* - = - u_i^k - + - \Delta u_i, \\ - &\,\,\,\, - u_i^{k+1} - = - u_i^* - - - \gamma^k \Delta t \dder{\psi}{x_i}. \\ - &\text{enddo} - -In the above equation, I have :math:`\psi`, which is an unknown scalar potential to be given now. - -By adding the above three equations, I have - -.. math:: - - u_i^{k+1} - = - u_i^k - - - \gamma^k \Delta t \dder{\left( p^k + \psi \right)}{x_i} - + - \alpha^k \Delta t g_i^k - + - \beta^k \Delta t g_i^{k-1}. - -As discussed above, since the pressure field is treated implicitly, I obtain - -.. math:: - - p^{k+1} - \equiv - p^{k } - + - \psi. - -To compute :math:`\psi` from the known information, I take the (discrete) divergence of the correction step: - -.. math:: - - u_i^{k+1} - = - u_i^* - - - \gamma^k \Delta t \dder{\psi}{x_i} - -to have - -.. math:: - - \dder{u_i^{k+1}}{x_i} - = - \dder{u_i^{* }}{x_i} - - - \gamma^k \Delta t \dder{}{x_i} \dder{\psi}{x_i}. - -By requesting the (discrete) incompressibility constraint on the new velocity field: - -.. math:: - - \dder{u_i^{k+1}}{x_i} - = - 0, - -I find - -.. math:: - - \dder{}{x_i} \dder{\psi}{x_i} - = - \frac{1}{\gamma^k \Delta t} \dder{u_i^*}{x_i}, - -which is a Poisson equation with respect to :math:`\psi`. - -In summary, the conclusive scheme is - -.. math:: - - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\, - \Delta u_i - = - \gamma^k \Delta t P_i^k - + - \alpha^k \Delta t g_i^k - + - \beta^k \Delta t g_i^{k-1}, \\ - &\,\,\,\, - u_i^* - = - u_i^k - + - \Delta u_i, \\ - &\,\,\,\, - \dder{}{x_i} \dder{\psi}{x_i} - = - \frac{1}{\gamma^k \Delta t} \dder{u_i^*}{x_i}, \\ - &\,\,\,\, - u_i^{k+1} - = - u_i^* - - - \gamma^k \Delta t \dder{\psi}{x_i}, \\ - &\,\,\,\, - p^{k+1} - \equiv - p^{k } - + - \psi. \\ - &\text{enddo} - -.. seealso:: - - :ref:`fluid/compute_potential ` for the Poisson solver. - -------------------------------------------- -When diffusive terms are implicitly treated -------------------------------------------- - -I consider :math:`h_i^k \ne 0`, i.e. all or some diffusive terms are treated implicitly, when minor correction is needed for :math:`\psi`. -The prediction and the correction steps are - -.. math:: - - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\, - \Delta u_i - = - \gamma^k \Delta t P_i^k - + - \alpha^k \Delta t g_i^k - + - \beta^k \Delta t g_i^{k-1} - + - \gamma^k \Delta t \frac{ - h_i^{k+1} - + - h_i^{k } - }{2}, \\ - &\,\,\,\, - u_i^* - = - u_i^k - + - \Delta u_i, \\ - &\,\,\,\, - u_i^{k+1} - = - u_i^* - - - \gamma^k \Delta t \dder{\psi}{x_i}. \\ - &\text{enddo} - -From the correction step, I obtain the same Poisson equation - -.. math:: - - \dder{}{x_i} \dder{\psi}{x_i} - = - \frac{1}{\gamma^k \Delta t} \dder{u_i^*}{x_i}. - -Since the diffusive terms are treated implicitly now, I have - -.. math:: - - h_i^k - = - D_i^k - = - \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{u_i^k}{x_j}. - -Since :math:`h_i^{k+1}`, which is a function of :math:`u_i^{k+1}`, is unknown in the prediction step, I instead use - -.. math:: - - \Delta u_i - = - \gamma^k \Delta t P_i^k - + - \alpha^k \Delta t g_i^k - + - \beta^k \Delta t g_i^{k-1} - + - \gamma^k \Delta t \frac{ - h_i^{* } - + - h_i^{k } - }{2} - -as the prediction step. -Note that :math:`h_i^{* }` is used instead of :math:`h_i^{k+1}`. - -Using the relation coming from the correction step - -.. math:: - - u_i^* - = - u_i^{k+1} - + - \gamma^k \Delta t \dder{\psi}{x_i}, - -I consider to absorb this change in :math:`\psi` by eliminating :math:`u_i^*`: - -.. math:: - - u_i^{k+1} - & = - u_i^k - - - \gamma^k \Delta t \dder{}{x_i} \left( \psi + p^k \right) - + - \frac{\gamma^k \Delta t^2}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} \gamma^k \dder{\psi}{x_i} - + - \cdots \\ - & = - u_i^k - - - \gamma^k \Delta t \dder{p^{k+1}}{x_i} - + - \cdots, - -giving - -.. math:: - - \dder{p^{k+1}}{x_i} - = - \dder{p^{k }}{x_i} - + - \dder{\psi}{x_i} - - - \frac{\gamma^k}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{}{x_j} \Delta t \dder{\psi}{x_i}. - -When the discrete gradient operator - -.. math:: - - \dder{}{x_i} - -and the discrete Laplace operator - -.. math:: - - \dder{}{x_j} \dder{}{x_j} - -are commutative, which holds in this project, I have - -.. math:: - - \dder{p^{k+1}}{x_i} - = - \dder{p^{k }}{x_i} - + - \dder{\psi}{x_i} - - - \dder{}{x_i} \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{\psi}{x_j} - -and thus - -.. math:: - - p^{k+1} - = - p^{k } - + - \psi - - - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{\psi}{x_j}. - -In summary, the conclusive scheme is - -.. math:: - - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\, - \Delta u_i - = - \gamma^k \Delta t P_i^k - + - \alpha^k \Delta t g_i^k - + - \beta^k \Delta t g_i^{k-1} - + - \gamma^k \Delta t h_i^{k }, \\ - &\,\,\,\, - u_i^* - = - u_i^k - + - \left( - 1 - - - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} - \dder{}{y} \dder{}{y} - \right)^{-1} - \left( - 1 - - - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} - \dder{}{x} \dder{}{x} - \right)^{-1} - \Delta u_i, \\ - &\,\,\,\, - \dder{}{x_i} \dder{\psi}{x_i} - = - \frac{1}{\gamma^k \Delta t} \dder{u_i^*}{x_i}, \\ - &\,\,\,\, - u_i^{k+1} - = - u_i^* - - - \gamma^k \dder{\psi}{x_i}, \\ - &\,\,\,\, - p^{k+1} - \equiv - p^{k } - + - \psi - - - \frac{\gamma^k \Delta t}{2} \frac{\sqrt{Pr}}{\sqrt{Ra}} \dder{}{x_j} \dder{\psi}{x_j}. \\ - &\text{enddo} - -.. seealso:: - - :ref:`Time-marching schemes `. - -************** -Implementation -************** - -#. Compute right-hand-side terms - - :ref:`fluid_compute_rhs ` - -#. Compute :math:`\Delta u_i` - - :ref:`fluid_predict_field ` - -#. Solve linear systems and update velocity field - - :ref:`fluid_predict_field ` - -#. Solve Poisson equation - - :ref:`fluid_compute_potential ` - -#. Correct velocity field - - :ref:`fluid_correct_velocity ` - -#. Update pressure field - - :ref:`fluid_update_pressure.c ` - diff --git a/docs/source/numerical_method/temporal_discretisation/temperature.rst b/docs/source/numerical_method/temporal_discretisation/temperature.rst index 3bb4962e..3bcb1ef7 100644 --- a/docs/source/numerical_method/temporal_discretisation/temperature.rst +++ b/docs/source/numerical_method/temporal_discretisation/temperature.rst @@ -1,145 +1,172 @@ .. _temperature_integration: -#################################### -Integrating internal energy equation -#################################### +.. include:: /references.txt -The equation of the internal energy in non-dimensional form is +####################### +Internal Energy Balance +####################### -.. math:: - - \der{T}{t} - = - A - + - D, - -where I introduce some symbols for notational convenience: - -.. math:: - - A^k - \equiv - - - u_j^k \dder{T^k}{x_j}, +The equation of the internal energy is .. math:: - D^k - \equiv - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x_j} \dder{T^k}{x_j}. - -Here the spatial derivatives are approximated by proper finite-difference schemes discussed in :ref:`the other part `. - -************** -Discretisation -************** + \pder{T}{t} + = + A + + + D_x + + + D_y + + + D_z, -The temporal discretisation of the above equation leads to +where :math:`A` is the advective terms: .. math:: - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\, - \Delta T - = - \alpha^k \Delta t \left( A^{k } + D^{k } \right) - + - \beta^k \Delta t \left( A^{k-1} + D^{k-1} \right), \\ - &\,\,\,\, - T^{k+1} - = - T^k - + - \Delta T. \\ - &\text{enddo} + A + \equiv + - + \dtempadv{1} + - + \dtempadv{2} + - + \dtempadv{3}, -when all diffusive terms are treated explicitly, while +while :math:`D_i` is the diffusive term involving spatial differentiation in the :math:`i`-th direction: .. math:: - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\,\Delta T - = - \alpha^k \Delta t A^{k } - + - \beta^k \Delta t A^{k-1} - + - \gamma^k \Delta t D^{k }, \\ - &\,\,\,\,T^{k+1} - = - T^k - + - \left( - 1 - - - \frac{\gamma^k \Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{y} \dder{}{y} - \right)^{-1} - \left( - 1 - - - \frac{\gamma^k \Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x} \dder{}{x} - \right)^{-1} - \Delta T, \\ - &\text{enddo} + D_i + \equiv + \dtempdif{i} + \,\, + (\text{no summation over}\,i). -when all diffusive terms are treated implicitly. +See :ref:`the spatial discretization `. -Diffusive terms are sometimes partially treated implicitly. -For example, when only the diffusive term in :math:`x` direction is implicitly treated, I have +The temporal discretization for each Runge-Kutta iteration leads to .. math:: - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\,\Delta T - = - \alpha^k \Delta t \left( A^{k } + D_y^{k } \right) - + - \beta^k \Delta t \left( A^{k-1} + D_y^{k-1} \right) - + - \gamma^k \Delta t D_x^{k }, \\ - &\,\,\,\,T^{k+1} - = - T^k - + - \left( - 1 - \frac{\gamma^k \Delta t}{2} \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x} \dder{}{x} - \right)^{-1} - \Delta T, \\ - &\text{enddo} - -where + \Delta T + & + = + \alpha^k \Delta t \left( A^{k } + D^{k } \right) + + + \beta^k \Delta t \left( A^{k-1} + D^{k-1} \right), -.. math:: + T^{k+1} + & + = + T^k + + + \Delta T, - D_x^k - \equiv - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{x} \dder{T^k}{x}, +when all diffusive terms are treated explicitly, while .. math:: - D_y^k - \equiv - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \dder{}{y} \dder{T^k}{y}. - -.. seealso:: - - :ref:`Time-marching schemes `. - -************** -Implementation -************** + \newcommand{\lap}[2]{ + {#2} \gamma^k \Delta t + \frac{1}{\sqrt{Pr} \sqrt{Ra}} + \frac{1}{J} + \dif{}{\gcs{#1}} + \left( + \frac{J}{\sfact{#1}} + \frac{1}{\sfact{#1}} + \dif{}{\gcs{#1}} + \right) + } + \Delta T + & + = + \alpha^k \Delta t A^{k } + + + \beta^k \Delta t A^{k-1} + + + \gamma^k \Delta t D^{k }, + + T^{k+1} + & + = + T^k + + + \left\{ + 1 + - + \lap{3}{c} + \right\}^{-1} + \left\{ + 1 + - + \lap{2}{c} + \right\}^{-1} + \left\{ + 1 + - + \lap{1}{c} + \right\}^{-1} + \Delta T, -#. Compute right-hand-side terms - - :ref:`fluid_compute_rhs ` - -#. Compute :math:`\Delta T` +when all diffusive terms are treated implicitly. - :ref:`fluid_predict_field ` +Diffusive terms are sometimes partially treated implicitly (c.f., |VANDERPOEL2015|). +When only the diffusive term in :math:`x` direction is implicitly treated (the default configuration in this project), we have -#. Solve linear systems and update temperature field +.. math:: - :ref:`fluid_predict_field ` + \Delta T + & + = + \alpha^k \Delta t \left( A^{k } + D_2^{k } + D_3^{k } \right) + + + \beta^k \Delta t \left( A^{k-1} + D_2^{k-1} + D_3^{k-1} \right) + + + \gamma^k \Delta t D_1^{k }, + + T^{k+1} + & + = + T^k + + + \left\{ + 1 + - + \lap{1}{c} + \right\}^{-1} + \Delta T. + +First, the explicit and implicit terms are calculated and stored to the corresponding buffers: + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: compute right-hand-side terms, which are added to buffers + +The buffers are used to compute :math:`\Delta T`: + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: compute increments + +When necessary, linear systems are solved to take care of the implicit treatments: + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: solve linear systems in x + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: solve linear systems in y + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: solve linear systems in z + +Finally the temperature field is updated: + +.. myliteralinclude:: /../../src/fluid/predict/t.c + :language: c + :tag: update temperature field diff --git a/docs/source/numerical_method/temporal_discretisation/time_marcher.rst b/docs/source/numerical_method/temporal_discretisation/time_marcher.rst index 353cae28..6d64f739 100644 --- a/docs/source/numerical_method/temporal_discretisation/time_marcher.rst +++ b/docs/source/numerical_method/temporal_discretisation/time_marcher.rst @@ -1,211 +1,224 @@ -.. include:: /references.txt - .. _time_marchers: +.. include:: /references.txt + ##################### -Time-marching schemes +Time-Marching Schemes ##################### ************* -Problem setup +Problem Setup ************* -I am interested in integrating several partial differential equations in this project. -When only the temporal derivatives are focused, I can write these equations as +We focus on integrating several partial differential equations involving both temporal and spatial derivatives in this project. +When focusing only on the temporal derivatives, all equations can be written as .. math:: - \frac{df}{dt} - = - g - -in general. + \newcommand{\oldvar}[1]{{#1^{n}}} + \newcommand{\newvar}[1]{{#1^{n+1}}} + \newcommand{\oerror}[1]{\mathcal{O} \left( \Delta t^{#1} \right)} + \frac{df}{dt} + = + g. .. note:: - I assume :math:`g` is not a function of :math:`t` explicitly, i.e. the following discussion is limited for `autonomous systems `_. - For instance, + We assume the right-hand-side term :math:`g` is not a function of time explicitly, i.e., the following discussion is limited for `autonomous systems `_. + This applies to all equations discussed in this project. - .. math:: +This is equivalent to - \der{T}{t} - = - - - u_j \der{T}{x_j} - + - \frac{1}{\sqrt{Pr} \sqrt{Ra}} \der{}{x_j} \der{T}{x_j} +.. math:: - includes no explicit :math:`t` dependency in the right-hand-side terms. + \int_\oldvar{t}^\newvar{t} \frac{df}{dt} dt + = + \int_\oldvar{t}^\newvar{t} g dt, -This is equal to +or .. math:: - \int_{t^{n }}^{t^{n+1}} \frac{df}{dt} dt - = - \int_{t^{n }}^{t^{n+1}} g dt, + \newvar{f} + - + \oldvar{f} + = + \int_\oldvar{t}^\newvar{t} g dt, -or +where we introduce .. math:: - f^{n+1} - - - f^{n } - = - \int_{t^{n }}^{t^{n+1}} g dt, + \oldvar{f} + & + \equiv + f \left( t = \oldvar{t} \right), -where I introduce + \newvar{f} + & + \equiv + f \left( t = \newvar{t} \right), + +and .. math:: - f^{n } - \equiv - f \left( t^{n } \right). + \Delta t + \equiv + \newvar{t} + - + \oldvar{t}. -The main focus of this page is how to approximate the right-hand side. -In other words, the objective is to approximate the Taylor-series expansion around :math:`t^{n }`: +To obtain :math:`\newvar{f}` from :math:`\oldvar{f}`, we need to evaluate the right-hand-side integral. +In this page, we aim at approximating the right-hand-side term using the Taylor-series expansion around :math:`\oldvar{t}`: .. math:: - f^{n+1} - & - = - f^{n } - + - \frac{df^n}{dt} \Delta t - + - \frac{1}{2} \frac{d^2f^n}{dt^2} \left( \Delta t \right)^2 - + - \mathcal{O} \left( \Delta t^3 \right) \\ - & - = - f^{n } - + - g^{n } \Delta t - + - \frac{1}{2} \left( \frac{dg}{dt} \right)^n \left( \Delta t \right)^2 - + - \mathcal{O} \left( \Delta t^3 \right). + \newvar{f} + & + = + \oldvar{f} + + + \frac{d\oldvar{f}}{dt} \Delta t + + + \frac{1}{2} \frac{d^2\oldvar{f}}{dt^2} \left( \Delta t \right)^2 + + + \oerror{3} + + & + = + \oldvar{f} + + + \oldvar{g} \Delta t + + + \frac{1}{2} \frac{d\oldvar{g}}{dt} \left( \Delta t \right)^2 + + + \oerror{3}. ******************* -Zeroth-order scheme +Zeroth-Order Scheme ******************* -The simplest way to approximate the above series expansion is +The simplest but useless way to approximate the above Taylor series expansion is .. math:: - f^{n+1} - \approx - f^{n }, + \newvar{f} + \approx + \oldvar{f}, -where only the first term in the right-hand side is extracted. +where we only pick-up the first term. The leading-order error (local truncation error) is .. math:: - g^{n } \Delta t - = - \mathcal{O} \left( \Delta t \right), + \oldvar{g} \Delta t + = + \oerror{1}, which indicates the first-order accuracy. - -Since I am interested in studying the system after it has been integrated for a long time, I should focus on the global truncation error, which is :math:`\mathcal{O} \left( 1 \right)`. -This means that, even when I make :math:`\Delta t` smaller, the solution never converges and thus this scheme is useless. +Since our objective is integrate the equation for a long time, we should consider the global truncation error, giving a reduced order of accuracy: :math:`\oerror{0}`. +This indicates that the error never shrinks even with a smaller :math:`\Delta t`, which is useless. .. note:: - Hereafter temporal accuracy is not based on the lobal truncation errors but the global ones. + Hereafter we only focus on the global truncation errors. ******************** -Euler-forward scheme +Euler-Forward Scheme ******************** -A simple and practical scheme to integrate the above equation in time would be to extract up to the second term of the series expansion: +We obtain a simple but (to some extent) practical scheme by extracting the first two terms: .. math:: - f^{n+1} - = - f^{n } - + - g^{n } - \Delta t, + \newvar{f} + = + \oldvar{f} + + + \oldvar{g} + \Delta t, or equivalently .. math:: - \Delta f - & = - g^{n } - \Delta t, \\ - f^{n+1} - & = - f^{n } - + - \Delta f, + \Delta f + & + = + \oldvar{g} + \Delta t, -which is the Euler-forward (or Euler-explicit) scheme. + \newvar{f} + & + = + \oldvar{f} + + + \Delta f, -Since the deviation from the series expansion is :math:`\mathcal{O} \left( \Delta t^2 \right)`, this scheme has the first-order accuracy in time. +which is known as the Euler-forward (or Euler-explicit) scheme. -Although it is very simple and easy to understand, there are mainly two issues in the above scheme: +Since the deviation from the series expansion is :math:`\oerror{2}` (locally), this scheme has the first-order accuracy (globally). - #. It only has the first-order accuracy in time. +Although it is very simple and easy to use, two clear issues exist: - #. All terms are treated explicitly. + * It only has the first-order accuracy in time, -They are elaborated in the following sections. + * All terms are treated explicitly. ********************************* -Fully-explicit Runge-Kutta scheme +Fully-Explicit Runge-Kutta Scheme ********************************* -To make the temporal accuracy second-order, I need to take three terms in the series expansion above. -In this project, I adopt the explicit Runge-Kutta scheme. -Since the system is autonomous, I use a three-step and low-storage scheme to achieve the second-order accuracy in time: +To achieve a higher-order accuracy, we need to extract more than two terms from the series expansion above. +In this project, we adopt the explicit Runge-Kutta scheme. +Since the system is autonomous, we use a three-step and low-storage scheme to achieve the second-order accuracy in time: .. math:: - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\, - \Delta f - = - \left( - \alpha^k g^{k } - + - \beta^k g^{k-1} - \right) \Delta t, \\ - &\,\,\,\, - f^{k+1} - = - f^{k } - + - \Delta f. \\ - &\text{enddo} + & + \text{do}\,\,k = 0, 2 + + & + \,\,\,\, + \Delta f + = + \left( + \alpha^k g^{k } + + + \beta^k g^{k-1} + \right) \Delta t, + + & + \,\,\,\, + f^{k+1} + = + f^{k } + + + \Delta f. -Although :math:`\alpha^k` and :math:`\beta^k`, which are coefficients to achieve the second-order accuracy in time, `have several possible combinations `_, in this project, I adopt + & + \text{enddo} + +Although the coefficients :math:`\alpha^k,\beta^k` have `several possibilities `_, we adopt .. math:: - \left(\alpha^0, \alpha^1, \alpha^2 \right) + \left( \alpha^0, \alpha^1, \alpha^2 \right) = - \left(32/60, 25/60, 45/60 \right), + \left( \frac{32}{60}, \frac{25}{60}, \frac{45}{60} \right), and .. math:: - \left(\beta^0, \beta^1, \beta^2 \right) + \left( \beta^0, \beta^1, \beta^2 \right) = - \left(0, -17/60, -25/60 \right), - -following e.g. |RAI1991|, |VERZICCO1996|, |COSTA2018|. + \left( \frac{0}{60}, -\frac{17}{60}, -\frac{25}{60} \right), -Although not used in the above equation, I introduce +following e.g., |RAI1991|, |VERZICCO1996|, |COSTA2018|. +For later convenience, we also introduce .. math:: @@ -219,216 +232,87 @@ or explicitly .. math:: - \left(\gamma^0, \gamma^1, \gamma^2 \right) + \left( \gamma^0, \gamma^1, \gamma^2 \right) = - \left(32/60, 8/60, 20/60 \right), - -which is used in the next section. - -.. note:: - - An important takeaway here is that, since - - .. math:: - - \sum_{k = 0}^{2} \gamma^k - = - 1, - - I can regard the above three-step Runge-Kutta scheme as the combination of the three Euler-forward scheme having different and smaller time steps :math:`\gamma^k \Delta t`. - -.. mydetails:: Proof of the second-order accuracy + \left( \frac{32}{60}, \frac{8}{60}, \frac{20}{60} \right). - Because - - .. math:: - - f^{k+1} - = - f^{k } - + - \alpha^k g^{k } \Delta t - + - \beta^k g^{k-1} \Delta t, - - I find - - .. math:: - - g^{k+1} - = - \frac{df^{k+1}}{dt} - = - \frac{df^k}{dt} - + - \alpha^k \frac{d^2f^k}{dt^2} \Delta t - + - \beta^k \frac{d^2f^{k-1}}{dt^2} \Delta t. - - By using this relation repeatedly, I have - - .. math:: - - f^1 &= f^n + \alpha^0 g^n \Delta t \\ - &= f^n + \alpha^0 \frac{df^n}{dt} \Delta t, \\ - g^1 &= \frac{df^n}{dt} + \alpha^0 \frac{d^2f^n}{dt^2} \Delta t, \\ - f^2 &= f^1 + \alpha^1 g^1 \Delta t + \beta^1 g^0 \Delta t \\ - &= f^n + \alpha^0 \frac{df^n}{dt} \Delta t + \alpha^1 \left( \frac{df^n}{dt} + \alpha^0 \frac{d^2f^n}{dt^2} \Delta t \right) \Delta t + \beta^1 \frac{df^n}{dt} \Delta t, \\ - &= f^n + \left( \alpha^0 + \alpha^1 + \beta^1 \right) \frac{df^n}{dt} \Delta t + \alpha^0 \alpha^1 \frac{d^2f^n}{dt^2} \Delta t^2, \\ - g^2 &= \frac{df^n}{dt} + \left( \alpha^0 + \alpha^1 + \beta^1 \right) \frac{d^2f^n}{dt^2} \Delta t + \alpha^0 \alpha^1 \frac{d^3f^n}{dt^3} \Delta t^2, \\ - f^3 &= f^{n+1} \\ - &= f^2 + \alpha^2 g^2 \Delta t + \beta^2 g^1 \Delta t \\ - &= f^n + \left( \alpha^0 + \alpha^1 + \beta^1 \right) \frac{df^n}{dt} \Delta t + \alpha^0 \alpha^1 \frac{d^2f^n}{dt^2} \Delta t^2 \\ - &+ \alpha^2 \left( \frac{df^n}{dt} + \left( \alpha^0 + \alpha^1 + \beta^1 \right) \frac{d^2f^n}{dt^2} \Delta t + \alpha^0 \alpha^1 \frac{d^3f^n}{dt^3} \Delta t^2 \right) \Delta t \\ - &+ \beta^2 \left( \frac{df^n}{dt} + \alpha^0 \frac{d^2f^n}{dt^2} \Delta t \right) \Delta t, \\ - &= f^n + \left( \alpha^0 + \alpha^1 + \beta^1 + \alpha^2 + \beta^2 \right) \frac{df^n}{dt} \Delta t \\ - &+ \left( \alpha^0 \alpha^1 + \alpha^0 \alpha^2 + \alpha^1 \alpha^2 + \alpha^2 \beta^1 + \alpha^0 \beta^2 \right) \frac{d^2f^n}{dt^2} \Delta t^2 \\ - &+ \mathcal{O} \left( \Delta t^3 \right) \\ - &= f^n + \frac{df^n}{dt} \Delta t + \frac{1}{2} \frac{d^2f^n}{dt^2} \Delta t^2 + \mathcal{O} \left( \Delta t^3 \right), - - i.e. - - .. math:: - - f^{n+1} - = - f^n - + - \frac{df^n}{dt} \Delta t - + - \frac{1}{2} \frac{d^2f^n}{dt^2} \left( \Delta t^2 \right) - + - \mathcal{O} \left( \Delta t^3 \right), - - and thus this scheme has the second-order accuracy in time. - -*********************** -Semi-implicit treatment -*********************** - -As discussed in :ref:`the implicit treatment of the diffusive terms `, I often need to treat some terms implicitly in time, i.e. +Note that, since we have .. math:: - \Delta f - \equiv - f^{n+1} - - - f^{n } - = - \frac{1}{2} - g^{n } - \Delta t - + - \frac{1}{2} - g^{n+1} - \Delta t, + \sum_{k = 0}^{2} \gamma^k + = + 1, -where `Crank-Nicolson scheme `_ is adopted to keep the second-order accuracy in time. +the above three-step Runge-Kutta scheme is essentially a combination of three Euler-forward scheme with :math:`\gamma^k \Delta t` as time-step sizes. .. mydetails:: Proof of the second-order accuracy - By using + .. include:: derivation/rk.rst - .. math:: +****************** +Implicit Treatment +****************** - g^{n+1} - = \frac{df^{n+1}}{dt} - = \frac{df^n}{dt} + \frac{d^2f^n}{dt^2} \Delta t + \frac{1}{2} \frac{d^3f^n}{dt^3} \Delta t^2 + \mathcal{O} \left( \Delta t^3 \right), - - I find - - .. math:: - - f^{n+1} - &= f^n + \frac{1}{2} \left( \frac{df^n}{dt} + \frac{d^2f^n}{dt^2} \Delta t + \frac{1}{2} \frac{d^3f^n}{dt^3} \Delta t^2 + \frac{df^n}{dt} \right) \Delta t \\ - &= f^n + \frac{df^n}{dt} \Delta t + \frac{1}{2} \frac{d^2f^n}{dt^2} \Delta t^2 + \mathcal{O} \left( \Delta t^3 \right), - - i.e. second-order accuracy in time. - -To embed this in the above Runge-Kutta iterations, I regard the above three-step Runge-Kutta method as the combination of three Euler-forward iterations with smaller time step sizes, i.e. +As justified :ref:`later `, we treat some terms implicitly in time; among others we adopt `Crank-Nicolson scheme `_: .. math:: - f^{1} - - - f^{0} - = - \left( \gamma^0 \Delta t \right) g^{0}, + \Delta f + & + = + \frac{1}{2} + \oldvar{g} + \Delta t + + + \frac{1}{2} + g^{n+1} + \Delta t, + + \newvar{f} + - + \oldvar{f} + & + = + \Delta f, + +which has the second-order accuracy in time as well. -.. math:: - - f^{2} - - - f^{1} - = - \left( \gamma^1 \Delta t \right) g^{1}, - -.. math:: - - f^{3} - - - f^{2} - = - \left( \gamma^2 \Delta t \right) g^{2}, - -since - -.. math:: - - \sum_{k = 0}^{2} \gamma^k - = - 1. +.. mydetails:: Proof of the second-order accuracy -Then I replace the Euler-forward scheme with the Crank-Nicolson scheme, giving + .. include:: derivation/cn.rst -.. math:: +*************** +Combined scheme +*************** - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\, - \Delta f - = - \frac{1}{2} - \left( - g^{k } - + - g^{k+1} - \right) \gamma^k \Delta t, \\ - &\,\,\,\, - f^{k+1} - = - f^{k } - + - \Delta f. \\ - &\text{enddo} - -In summary, the conclusive scheme leads to +We embed the implicit treatment (Crank-Nicolson scheme) into the Runge-Kutta iterations: .. math:: - &\text{do}\,\,\,\, k = 0, 2 \\ - &\,\,\,\, - \Delta f - = - \left( + &\text{do}\,\, k = 0, 2 \\ + &\,\,\,\, + \Delta f + = + \left( \alpha^k g^{k } + \beta^k g^{k-1} - \right) \Delta t - + - \frac{1}{2} - \left( + \right) \Delta t + + + \frac{1}{2} + \left( h^{k } + h^{k+1} - \right) \gamma^k \Delta t, \\ - &\,\,\,\, - f^{k+1} - = - f^{k } - + - \Delta f, \\ - &\text{enddo} + \right) \gamma^k \Delta t, \\ + &\,\,\,\, + f^{k+1} + = + f^{k } + + + \Delta f, \\ + &\text{enddo} where :math:`g` and :math:`h` are terms treated explicitly and implicitly in time, respectively. diff --git a/docs/source/references.rst b/docs/source/references.rst index 32520d91..c5b1ced3 100644 --- a/docs/source/references.rst +++ b/docs/source/references.rst @@ -11,9 +11,11 @@ References * |MORINISHI1998| * |KAJISHIMA1999| * |HORVATH2000| -* |GROSSMANN2000| * |HAM2002| +* |VERSTAPPEN2003| * |VANDERPOEL2013| +* |VANDERPOEL2015| +* |OSTILLAMONICO2015| +* |KAJISHIMA2017| * |COSTA2018| * |COPPOLA2019| -* |KOOLOTH2021| diff --git a/docs/source/references.txt b/docs/source/references.txt index d8a6fb6b..7588bc94 100644 --- a/docs/source/references.txt +++ b/docs/source/references.txt @@ -1,13 +1,15 @@ -.. |AMSDEN1970| replace:: Amsden and Harlow, *J. Comput. Phys.* (**6**), 1970 -.. |RAI1991| replace:: Rai and Moin, *J. Comput. Phys.* (**96**), 1991 -.. |DUKOWICZ1992| replace:: Dukowicz and Dvinsky, *J. Comput. Phys.* (**102**), 1992 -.. |VERZICCO1996| replace:: Verzicco and Orlandi, *J. Comput. Phys.* (**123**), 1996 -.. |MORINISHI1998| replace:: Morinishi et al., *J. Comput. Phys.* (**143**), 1998 -.. |KAJISHIMA1999| replace:: Kajishima, Trans. *JSME* (**65-633, 1607**), 1999 (in Japanese) -.. |HORVATH2000| replace:: Horváth, *RANA* (**0015**), 2000 -.. |GROSSMANN2000| replace:: Grossmann and Lohse, *J. Fluid Mech.* (**407**), 2000 -.. |HAM2002| replace:: Ham et al., *J. Comput. Phys.* (**177**), 2002 -.. |VANDERPOEL2013| replace:: van der Poel et al., *J. Fluid Mech.* (**736**), 2013 -.. |COSTA2018| replace:: Costa, *Comput. Math Appl.* (**76**), 2018 -.. |COPPOLA2019| replace:: Coppola et al., *Appl. Mech. Rev.* (**71**), 2019 -.. |KOOLOTH2021| replace:: Kooloth et al., *Phys. Rev. Fluids* (**6**), 2021 +.. |AMSDEN1970| replace:: Amsden and Harlow, *J. Comput. Phys.* (**6**), 1970 +.. |RAI1991| replace:: Rai and Moin, *J. Comput. Phys.* (**96**), 1991 +.. |DUKOWICZ1992| replace:: Dukowicz and Dvinsky, *J. Comput. Phys.* (**102**), 1992 +.. |VERZICCO1996| replace:: Verzicco and Orlandi, *J. Comput. Phys.* (**123**), 1996 +.. |MORINISHI1998| replace:: Morinishi et al., *J. Comput. Phys.* (**143**), 1998 +.. |KAJISHIMA1999| replace:: Kajishima, Trans. *JSME* (**65-633, 1607**), 1999 (in Japanese) +.. |HORVATH2000| replace:: Horváth, *RANA* (**0015**), 2000 +.. |HAM2002| replace:: Ham et al., *J. Comput. Phys.* (**177**), 2002 +.. |VERSTAPPEN2003| replace:: Verstappen and Veldman, *J. Comput. Phys.* (**187**), 2003 +.. |VANDERPOEL2013| replace:: van der Poel et al., *J. Fluid Mech.* (**736**), 2013 +.. |VANDERPOEL2015| replace:: van der Poel et al., *Comput. Fluids* (**116**), 2015 +.. |OSTILLAMONICO2015| replace:: Ostilla-Monico et al., *J. Comput. Phys* (**301**), 2015 +.. |KAJISHIMA2017| replace:: Kajishima and Taira, Springer International Publishing, 2017 +.. |COSTA2018| replace:: Costa, *Comput. Math Appl.* (**76**), 2018 +.. |COPPOLA2019| replace:: Coppola et al., *Appl. Mech. Rev.* (**71**), 2019 diff --git a/include/array.h b/include/array.h index 9db0e072..0065fc92 100644 --- a/include/array.h +++ b/include/array.h @@ -24,7 +24,7 @@ typedef struct { typedef struct { // allocate array and store its size information - int (* const prepare)( + int (* const create)( const domain_t * domain, const int nadds[NDIMS][2], const size_t size, diff --git a/include/array_macros/domain/dxc.h b/include/array_macros/domain/dxc.h deleted file mode 100644 index 42ae9148..00000000 --- a/include/array_macros/domain/dxc.h +++ /dev/null @@ -1,10 +0,0 @@ -#if !defined(INCLUDE_ARRAY_MACROS_DOMAIN_DXC_H) -#define INCLUDE_ARRAY_MACROS_DOMAIN_DXC_H - -// This file is generated by tools/define_arrays.py - -// [1 : isize+1] -#define DXC(I) (dxc[(I-1)]) -#define DXC_NADDS (int [2]){0, 1} - -#endif // INCLUDE_ARRAY_MACROS_DOMAIN_DXC_H diff --git a/include/array_macros/domain/dxf.h b/include/array_macros/domain/dxf.h deleted file mode 100644 index 1e4b7295..00000000 --- a/include/array_macros/domain/dxf.h +++ /dev/null @@ -1,10 +0,0 @@ -#if !defined(INCLUDE_ARRAY_MACROS_DOMAIN_DXF_H) -#define INCLUDE_ARRAY_MACROS_DOMAIN_DXF_H - -// This file is generated by tools/define_arrays.py - -// [1 : isize+0] -#define DXF(I) (dxf[(I-1)]) -#define DXF_NADDS (int [2]){0, 0} - -#endif // INCLUDE_ARRAY_MACROS_DOMAIN_DXF_H diff --git a/include/array_macros/domain/hxxc.h b/include/array_macros/domain/hxxc.h new file mode 100644 index 00000000..0ec589ea --- /dev/null +++ b/include/array_macros/domain/hxxc.h @@ -0,0 +1,10 @@ +#if !defined(INCLUDE_ARRAY_MACROS_DOMAIN_HXXC_H) +#define INCLUDE_ARRAY_MACROS_DOMAIN_HXXC_H + +// This file is generated by tools/define_arrays.py + +// [1 : isize+0] +#define HXXC(I) (hxxc[(I-1)]) +#define HXXC_NADDS (int [2]){0, 0} + +#endif // INCLUDE_ARRAY_MACROS_DOMAIN_HXXC_H diff --git a/include/array_macros/domain/hxxf.h b/include/array_macros/domain/hxxf.h new file mode 100644 index 00000000..5be5c330 --- /dev/null +++ b/include/array_macros/domain/hxxf.h @@ -0,0 +1,10 @@ +#if !defined(INCLUDE_ARRAY_MACROS_DOMAIN_HXXF_H) +#define INCLUDE_ARRAY_MACROS_DOMAIN_HXXF_H + +// This file is generated by tools/define_arrays.py + +// [1 : isize+1] +#define HXXF(I) (hxxf[(I-1)]) +#define HXXF_NADDS (int [2]){0, 1} + +#endif // INCLUDE_ARRAY_MACROS_DOMAIN_HXXF_H diff --git a/include/array_macros/domain/jdxc.h b/include/array_macros/domain/jdxc.h new file mode 100644 index 00000000..5c567d29 --- /dev/null +++ b/include/array_macros/domain/jdxc.h @@ -0,0 +1,10 @@ +#if !defined(INCLUDE_ARRAY_MACROS_DOMAIN_JDXC_H) +#define INCLUDE_ARRAY_MACROS_DOMAIN_JDXC_H + +// This file is generated by tools/define_arrays.py + +// [1 : isize+0] +#define JDXC(I) (jdxc[(I-1)]) +#define JDXC_NADDS (int [2]){0, 0} + +#endif // INCLUDE_ARRAY_MACROS_DOMAIN_JDXC_H diff --git a/include/array_macros/domain/jdxf.h b/include/array_macros/domain/jdxf.h new file mode 100644 index 00000000..24123a44 --- /dev/null +++ b/include/array_macros/domain/jdxf.h @@ -0,0 +1,10 @@ +#if !defined(INCLUDE_ARRAY_MACROS_DOMAIN_JDXF_H) +#define INCLUDE_ARRAY_MACROS_DOMAIN_JDXF_H + +// This file is generated by tools/define_arrays.py + +// [1 : isize+1] +#define JDXF(I) (jdxf[(I-1)]) +#define JDXF_NADDS (int [2]){0, 1} + +#endif // INCLUDE_ARRAY_MACROS_DOMAIN_JDXF_H diff --git a/include/array_macros/statistics/adv.h b/include/array_macros/statistics/adv.h new file mode 100644 index 00000000..a3ba0582 --- /dev/null +++ b/include/array_macros/statistics/adv.h @@ -0,0 +1,18 @@ +#if !defined(INCLUDE_ARRAY_MACROS_STATISTICS_ADV_H) +#define INCLUDE_ARRAY_MACROS_STATISTICS_ADV_H + +// This file is generated by tools/define_arrays.py + +#if NDIMS == 2 +// [1 : isize+1], [1 : jsize+0] +#define ADV(I, J) (adv[(I-1) + (isize+1) * (J-1)]) +#define ADV_NADDS (int [NDIMS][2]){ {0, 1}, {0, 0}, } +#endif + +#if NDIMS == 3 +// [1 : isize+1], [1 : jsize+0], [1 : ksize+0] +#define ADV(I, J, K) (adv[(I-1) + (isize+1) * ((J-1) + (jsize+0) * (K-1))]) +#define ADV_NADDS (int [NDIMS][2]){ {0, 1}, {0, 0}, {0, 0}, } +#endif + +#endif // INCLUDE_ARRAY_MACROS_STATISTICS_ADV_H diff --git a/include/array_macros/statistics/dif.h b/include/array_macros/statistics/dif.h new file mode 100644 index 00000000..6469d353 --- /dev/null +++ b/include/array_macros/statistics/dif.h @@ -0,0 +1,18 @@ +#if !defined(INCLUDE_ARRAY_MACROS_STATISTICS_DIF_H) +#define INCLUDE_ARRAY_MACROS_STATISTICS_DIF_H + +// This file is generated by tools/define_arrays.py + +#if NDIMS == 2 +// [1 : isize+1], [1 : jsize+0] +#define DIF(I, J) (dif[(I-1) + (isize+1) * (J-1)]) +#define DIF_NADDS (int [NDIMS][2]){ {0, 1}, {0, 0}, } +#endif + +#if NDIMS == 3 +// [1 : isize+1], [1 : jsize+0], [1 : ksize+0] +#define DIF(I, J, K) (dif[(I-1) + (isize+1) * ((J-1) + (jsize+0) * (K-1))]) +#define DIF_NADDS (int [NDIMS][2]){ {0, 1}, {0, 0}, {0, 0}, } +#endif + +#endif // INCLUDE_ARRAY_MACROS_STATISTICS_DIF_H diff --git a/include/array_macros/statistics/uxt.h b/include/array_macros/statistics/uxt.h deleted file mode 100644 index d0c4ef63..00000000 --- a/include/array_macros/statistics/uxt.h +++ /dev/null @@ -1,18 +0,0 @@ -#if !defined(INCLUDE_ARRAY_MACROS_STATISTICS_UXT_H) -#define INCLUDE_ARRAY_MACROS_STATISTICS_UXT_H - -// This file is generated by tools/define_arrays.py - -#if NDIMS == 2 -// [1 : isize+1], [1 : jsize+0] -#define UXT(I, J) (uxt[(I-1) + (isize+1) * (J-1)]) -#define UXT_NADDS (int [NDIMS][2]){ {0, 1}, {0, 0}, } -#endif - -#if NDIMS == 3 -// [1 : isize+1], [1 : jsize+0], [1 : ksize+0] -#define UXT(I, J, K) (uxt[(I-1) + (isize+1) * ((J-1) + (jsize+0) * (K-1))]) -#define UXT_NADDS (int [NDIMS][2]){ {0, 1}, {0, 0}, {0, 0}, } -#endif - -#endif // INCLUDE_ARRAY_MACROS_STATISTICS_UXT_H diff --git a/include/domain.h b/include/domain.h index 6d48f35b..0626fbb5 100644 --- a/include/domain.h +++ b/include/domain.h @@ -7,14 +7,14 @@ /** * @struct domain_t * @brief struct storing parameters relevant to spatial domain - * @var info : MPI domain decomposition - * @var glsizes : global number of grid points in each direction - * @var mysizes : local (my) number of grid points in each direction - * @var offsets : offsets to my starting index in each direction - * @var lengths : domain size in each direction - * @var xf, xc : cell-face and cell-center locations in x direction - * @var dxf, dxc : face-to-face and center-to-center distances in x direction - * @var dy, dz : grid sizes in homogeneous directions + * @var info : MPI domain decomposition + * @var glsizes : global number of grid points in each direction + * @var mysizes : local (my) number of grid points in each direction + * @var offsets : offsets to my starting index in each direction + * @var lengths : domain size in each direction + * @var xf, xc : cell-face and cell-center locations in x direction + * @var hxxf, hxxc : wall-normal scale factors at faces and centers + * @var hy, hz : scale factors in the homogeneous directions */ typedef struct { sdecomp_info_t * info; @@ -23,11 +23,12 @@ typedef struct { size_t offsets[NDIMS]; double lengths[NDIMS]; double * restrict xf, * restrict xc; - double * restrict dxf, * restrict dxc; - double dy; + double * restrict hxxf, * restrict hxxc; + double hy; #if NDIMS == 3 - double dz; + double hz; #endif + double * restrict jdxf, * restrict jdxc; } domain_t; // constructor @@ -42,10 +43,4 @@ extern int domain_save( const domain_t * domain ); -// check grid size uniformity -extern int domain_check_x_grid_is_uniform( - const domain_t * domain, - bool * x_grid_is_uniform -); - #endif // DOMAIN_H diff --git a/include/fluid.h b/include/fluid.h index 91ba697e..555d737d 100644 --- a/include/fluid.h +++ b/include/fluid.h @@ -33,7 +33,20 @@ typedef struct { #endif array_t srct[3]; double Ra, Pr; - double m_dif, t_dif; } fluid_t; +// initialiser of fluid_t +extern int fluid_init( + const char dirname_ic[], + const domain_t * domain, + fluid_t * fluid +); + +// save flow field +extern int fluid_save( + const char dirname[], + const domain_t * domain, + const fluid_t * fluid +); + #endif // FLUID_H diff --git a/include/fluid_solver.h b/include/fluid_solver.h index bffe1192..adfac0e0 100644 --- a/include/fluid_solver.h +++ b/include/fluid_solver.h @@ -5,33 +5,15 @@ #include "domain.h" #include "fluid.h" -// initialiser of fluid_t -extern int fluid_init( - const char dirname_ic[], - const domain_t * domain, - fluid_t * fluid +extern double fluid_compute_momentum_diffusivity ( + const fluid_t * fluid ); -// save flow field -extern int fluid_save( - const char dirname[], - const domain_t * domain, +extern double fluid_compute_temperature_diffusivity ( const fluid_t * fluid ); // predict the new velocity field and update the temperature field -extern int fluid_compute_rhs( - const domain_t * domain, - fluid_t * fluid -); - -// couple external forces -extern int fluid_couple_external_force( - const domain_t * domain, - fluid_t * fluid -); - -// update fields using the previously-computed RK source terms extern int fluid_predict_field( const domain_t * domain, const size_t rkstep, @@ -47,7 +29,7 @@ extern int fluid_compute_potential( fluid_t * fluid ); -// correct velocity field using scalar potential +// correct velocity field using scalar potential to enforce divergence zero extern int fluid_correct_velocity( const domain_t * domain, const size_t rkstep, @@ -55,7 +37,6 @@ extern int fluid_correct_velocity( fluid_t * fluid ); -// update pressure extern int fluid_update_pressure( const domain_t * domain, const size_t rkstep, @@ -63,8 +44,6 @@ extern int fluid_update_pressure( fluid_t * fluid ); -// exchange halos and impose boundary conditions - extern int fluid_update_boundaries_ux( const domain_t * domain, array_t * ux diff --git a/include/runge_kutta.h b/include/runge_kutta.h index cbf3a876..64353c79 100644 --- a/include/runge_kutta.h +++ b/include/runge_kutta.h @@ -3,13 +3,14 @@ #include -// Runge-Kutta configurations | 9 +// Runge-Kutta configurations | 10 +// NOTE: three buffers: alpha, beta, gamma +#define RKNBUFFERS 3 // indices extern const uint_fast8_t rk_a; // 0 extern const uint_fast8_t rk_b; // 1 extern const uint_fast8_t rk_g; // 2 -// NOTE: alpha, beta, gamma and thus three here -typedef double rkcoef_t[3]; +typedef double rkcoef_t[RKNBUFFERS]; // NOTE: only three-step Wray is allowed #define RKSTEPMAX 3 extern const rkcoef_t rkcoefs[RKSTEPMAX]; diff --git a/include/save.h b/include/save.h index d2e127d4..dc57aee0 100644 --- a/include/save.h +++ b/include/save.h @@ -10,11 +10,12 @@ typedef struct save_t_ { const domain_t * domain, const double time ); - // make space to save flow fields, save some scalars - int (* const prepare)( + // save a instantaneous flow field to files + int (* const output)( const domain_t * domain, - const int step, - char ** dirname + const size_t step, + const double time, + const fluid_t * fluid ); // getter, next timing to call "output" double (* const get_next_time)( diff --git a/initial_condition/2d.py b/initial_condition/2d.py deleted file mode 100644 index 52ca40e8..00000000 --- a/initial_condition/2d.py +++ /dev/null @@ -1,85 +0,0 @@ -import os -import sys -import numpy as np - - -def init_time(dest): - # iterator and time - step = np.array(0, dtype=np.uint64) - time = np.array(0, dtype=np.float64) - np.save(f"{dest}/step.npy", step) - np.save(f"{dest}/time.npy", time) - return - - -def init_domain(lengths, glsizes, uniformx, dest): - # NOTE: cell face has +1 elements - if uniformx: - # uniform grid in x, - # which is advantageous to solve Poisson equation more efficiently - xf = np.linspace(0., lengths[0], glsizes[0] + 1, endpoint=True) - else: - # stretched grid, clipped Chebyshev just as an example - # number of grid points to be clipped at the edges - nclip = 3 - # generate equidistant sequence - xf = np.arange(0, glsizes[0] + 1, 1) - # gather close to the boundaries - xf = np.cos(np.pi * (xf + 1. * nclip) / (glsizes[0] + 2. * nclip)) - # make the descending order ascending - xf *= -1. - # normalse to force it changing from 0 to lx - xf = lengths[0] * (xf - np.min(xf)) / (np.max(xf) - np.min(xf)) - # cell centers are located at the center - # of the two neighbouring cell faces, - # which are appended by the boundaries - xc = 0. - xc = np.append(xc, 0.5 * xf[:-1] + 0.5 * xf[1:]) - xc = np.append(xc, lengths[0]) - np.save(f"{dest}/xf.npy", np.array(xf, dtype=np.float64)) - np.save(f"{dest}/xc.npy", np.array(xc, dtype=np.float64)) - np.save(f"{dest}/glsizes.npy", np.array(glsizes, dtype=np.uint64)) - np.save(f"{dest}/lengths.npy", np.array(lengths, dtype=np.float64)) - return xf, xc - - -def init_fluid(lengths, glsizes, xf, xc, dest): - shape = (glsizes[1], glsizes[0] + 1) - ux = np.zeros(shape, dtype=np.float64) - shape = (glsizes[1], glsizes[0] + 2) - uy = np.zeros(shape, dtype=np.float64) - shape = (glsizes[1], glsizes[0] + 2) - p = np.zeros(shape, dtype=np.float64) - shape = (glsizes[1], glsizes[0] + 2) - t = -0.5 + np.random.random_sample(shape) - np.save(f"{dest}/ux.npy", ux) - np.save(f"{dest}/uy.npy", uy) - np.save(f"{dest}/p.npy", p) - np.save(f"{dest}/t.npy", t) - - -def main(): - lengths = list() - lengths.append(float(os.environ["lx"])) - lengths.append(float(os.environ["ly"])) - glsizes = list() - glsizes.append(int(os.environ["glisize"])) - glsizes.append(int(os.environ["gljsize"])) - uniformx = os.environ["uniformx"] - if "True" == uniformx: - uniformx = True - elif "true" == uniformx: - uniformx = True - else: - uniformx = False - dest = sys.argv[1] - # sanitise - ndims = len(lengths) - assert 2 == ndims - # init and save - init_time(dest) - xf, xc = init_domain(lengths, glsizes, uniformx, dest) - init_fluid(lengths, glsizes, xf, xc, dest) - - -main() diff --git a/initial_condition/3d.py b/initial_condition/3d.py deleted file mode 100644 index 40ccdb63..00000000 --- a/initial_condition/3d.py +++ /dev/null @@ -1,90 +0,0 @@ -import os -import sys -import numpy as np - - -def init_time(dest): - # iterator and time - step = np.array(0, dtype=np.uint64) - time = np.array(0, dtype=np.float64) - np.save(f"{dest}/step.npy", step) - np.save(f"{dest}/time.npy", time) - return - - -def init_domain(lengths, glsizes, uniformx, dest): - # NOTE: cell face has +1 elements - if uniformx: - # uniform grid in x, - # which is advantageous to solve Poisson equation more efficiently - xf = np.linspace(0., lengths[0], glsizes[0] + 1, endpoint=True) - else: - # stretched grid, clipped Chebyshev just as an example - # number of grid points to be clipped at the edges - nclip = 3 - # generate equidistant sequence - xf = np.arange(0, glsizes[0] + 1, 1) - # gather close to the boundaries - xf = np.cos(np.pi * (xf + 1. * nclip) / (glsizes[0] + 2. * nclip)) - # make the descending order ascending - xf *= -1. - # normalse to force it changing from 0 to lx - xf = lengths[0] * (xf - np.min(xf)) / (np.max(xf) - np.min(xf)) - # cell centers are located at the center - # of the two neighbouring cell faces, - # which are appended by the boundaries - xc = 0. - xc = np.append(xc, 0.5 * xf[:-1] + 0.5 * xf[1:]) - xc = np.append(xc, lengths[0]) - np.save(f"{dest}/xf.npy", np.array(xf, dtype=np.float64)) - np.save(f"{dest}/xc.npy", np.array(xc, dtype=np.float64)) - np.save(f"{dest}/glsizes.npy", np.array(glsizes, dtype=np.uint64)) - np.save(f"{dest}/lengths.npy", np.array(lengths, dtype=np.float64)) - return xf, xc - - -def init_fluid(lengths, glsizes, xf, xc, dest): - shape = (glsizes[2], glsizes[1], glsizes[0] + 1) - ux = np.zeros(shape, dtype=np.float64) - shape = (glsizes[2], glsizes[1], glsizes[0] + 2) - uy = np.zeros(shape, dtype=np.float64) - shape = (glsizes[2], glsizes[1], glsizes[0] + 2) - uz = np.zeros(shape, dtype=np.float64) - shape = (glsizes[2], glsizes[1], glsizes[0] + 2) - p = np.zeros(shape, dtype=np.float64) - shape = (glsizes[2], glsizes[1], glsizes[0] + 2) - t = -0.5 + np.random.random_sample(shape) - np.save(f"{dest}/ux.npy", ux) - np.save(f"{dest}/uy.npy", uy) - np.save(f"{dest}/uz.npy", uz) - np.save(f"{dest}/p.npy", p) - np.save(f"{dest}/t.npy", t) - - -def main(): - lengths = list() - lengths.append(float(os.environ["lx"])) - lengths.append(float(os.environ["ly"])) - lengths.append(float(os.environ["lz"])) - glsizes = list() - glsizes.append(int(os.environ["glisize"])) - glsizes.append(int(os.environ["gljsize"])) - glsizes.append(int(os.environ["glksize"])) - uniformx = os.environ["uniformx"] - if "True" == uniformx: - uniformx = True - elif "true" == uniformx: - uniformx = True - else: - uniformx = False - dest = sys.argv[1] - # sanitise - ndims = len(lengths) - assert 3 == ndims - # init and save - init_time(dest) - xf, xc = init_domain(lengths, glsizes, uniformx, dest) - init_fluid(lengths, glsizes, xf, xc, dest) - - -main() diff --git a/initial_condition/main.py b/initial_condition/main.py new file mode 100644 index 00000000..5f5c636e --- /dev/null +++ b/initial_condition/main.py @@ -0,0 +1,109 @@ +import os +import sys +import numpy as np + + +rng = np.random.default_rng() + + +def get_lengths(): + lx = os.environ.get("lx") + ly = os.environ.get("ly") + lz = os.environ.get("lz") + if lz: + lengths = [lx, ly, lz] + else: + lengths = [lx, ly] + return list(map(float, lengths)) + + +def get_glsizes(): + glisize = os.environ.get("glisize") + gljsize = os.environ.get("gljsize") + glksize = os.environ.get("glksize") + if glksize: + glsizes = [glisize, gljsize, glksize] + else: + glsizes = [glisize, gljsize] + return list(map(int, glsizes)) + + +def init_time(dest): + # iterator and time + step = np.array(0, dtype=np.uint64) + time = np.array(0, dtype=np.float64) + np.save(f"{dest}/step.npy", step) + np.save(f"{dest}/time.npy", time) + return + + +def init_domain(lengths, glsizes, dest): + # generate equidistant sequence + # NOTE: cell face has +1 elements + xf = np.arange(0, glsizes[0] + 1, 1) + # stretched grid, clipped Chebyshev just as an example + is_uniform = False + if not is_uniform: + # number of grid points to be clipped at the edges + nclip = 3 + # gather close to the boundaries + xf = np.cos(np.pi * (xf + 1. * nclip) / (glsizes[0] + 2. * nclip)) + # make the descending order ascending + xf *= -1. + # normalse to enforce [0 : lx] + xf = lengths[0] * (xf - np.min(xf)) / (np.max(xf) - np.min(xf)) + # cell centers are located at the center + # of the two neighbouring cell faces, + # which are appended by the boundaries + xc = 0. + xc = np.append(xc, 0.5 * xf[:-1] + 0.5 * xf[1:]) + xc = np.append(xc, lengths[0]) + np.save(f"{dest}/xf.npy", np.array(xf, dtype=np.float64)) + np.save(f"{dest}/xc.npy", np.array(xc, dtype=np.float64)) + np.save(f"{dest}/glsizes.npy", np.array(glsizes, dtype=np.uint64)) + np.save(f"{dest}/lengths.npy", np.array(lengths, dtype=np.float64)) + return xf, xc + + +def init_fluid(is_3d, lengths, glsizes, xf, xc, dest): + if is_3d: + ux = np.zeros((glsizes[2], glsizes[1], glsizes[0] + 1), dtype=np.float64) + uy = np.zeros((glsizes[2], glsizes[1], glsizes[0] + 2), dtype=np.float64) + uz = np.zeros((glsizes[2], glsizes[1], glsizes[0] + 2), dtype=np.float64) + p = np.zeros((glsizes[2], glsizes[1], glsizes[0] + 2), dtype=np.float64) + t = rng.random((glsizes[2], glsizes[1], glsizes[0] + 2), dtype=np.float64) + t -= 0.5 + np.save(f"{dest}/ux.npy", ux) + np.save(f"{dest}/uy.npy", uy) + np.save(f"{dest}/uz.npy", uz) + np.save(f"{dest}/p.npy", p) + np.save(f"{dest}/t.npy", t) + else: + ux = np.zeros((glsizes[1], glsizes[0] + 1), dtype=np.float64) + uy = np.zeros((glsizes[1], glsizes[0] + 2), dtype=np.float64) + p = np.zeros((glsizes[1], glsizes[0] + 2), dtype=np.float64) + t = rng.random((glsizes[1], glsizes[0] + 2), dtype=np.float64) + t -= 0.5 + np.save(f"{dest}/ux.npy", ux) + np.save(f"{dest}/uy.npy", uy) + np.save(f"{dest}/p.npy", p) + np.save(f"{dest}/t.npy", t) + + +def main(): + lengths = get_lengths() + glsizes = get_glsizes() + assert len(lengths) == len(glsizes) + dest = sys.argv[1] + is_3d = 3 == len(lengths) + if is_3d: + print("A 3D field is initialised") + else: + print("A 2D field is initialised") + # init and save + init_time(dest) + xf, xc = init_domain(lengths, glsizes, dest) + init_fluid(is_3d, lengths, glsizes, xf, xc, dest) + + +main() diff --git a/initial_condition/exec.sh b/initial_condition/main.sh similarity index 73% rename from initial_condition/exec.sh rename to initial_condition/main.sh index f6d2de08..b8e89fc2 100644 --- a/initial_condition/exec.sh +++ b/initial_condition/main.sh @@ -7,8 +7,6 @@ export ly=2.0e+0 # number of cell centers export glisize=128 export gljsize=256 -# grid is uniformly distributed in x (true) or not (false) -export uniformx=false ## where to write resulting NPY files dirname="output" diff --git a/src/README.rst b/src/README.rst deleted file mode 100644 index 665585b9..00000000 --- a/src/README.rst +++ /dev/null @@ -1,51 +0,0 @@ -#### -src/ -#### - -All functions are implemented here. -For the directories, see the corresponding README file. - -* array.c - - Function to initialise, destruct, load, and save multi-dimensional arrays including halo cells are implemented. - -* config.c - - Environment variable loader which is called when the solver is launched to acquire the runtime-parameters by the user is implemented. - -* domain.c - - Information about the coordinate systems, the grid configuration, and the domain parallelisation is included. - -* linear_system.c - - Functions to initialise and destruct the tri-diagonal linear system solver are included. - -* main.c - - Main function is here. - -* memory.c - - Utility functions and global parameters are defined. - -* runge_kutta.c - - Runge-Kutta coefficients are defined. - -* save.c - - Functions to save flow field and parameters for restart are included. - -* statistics.c - - Routines to collect statistical data are implemented. - -* tdm.c - - Kernel functions to solve tri-diagonal matrices are implemented. - -* timer.c - - A function to obtain the current wall time is implemented. - diff --git a/src/array.c b/src/array.c index 2ae63f66..c7e02b3d 100644 --- a/src/array.c +++ b/src/array.c @@ -7,7 +7,7 @@ #include "array.h" #include "fileio.h" -static int prepare( +static int create( const domain_t * domain, const int nadds[NDIMS][2], size_t size, @@ -288,7 +288,7 @@ static int dump( } const array_method_t array = { - .prepare = prepare, + .create = create, .destroy = destroy, .load = load, .dump = dump, diff --git a/src/decide_dt.c b/src/decide_dt.c index 124c4579..31443207 100644 --- a/src/decide_dt.c +++ b/src/decide_dt.c @@ -9,18 +9,23 @@ #include "sdecomp.h" #include "domain.h" #include "fluid.h" +#include "fluid_solver.h" #include "array_macros/fluid/ux.h" #include "array_macros/fluid/uy.h" #if NDIMS == 3 #include "array_macros/fluid/uz.h" #endif -#include "array_macros/domain/dxc.h" +#include "array_macros/domain/hxxf.h" // overriden later using environment variables static bool coefs_are_initialised = false; static double coef_dt_adv = 0.; static double coef_dt_dif = 0.; +// maximum time step size, allowed for all cases +// NOTE: makes sense to set e.g., 0.1 free-fall time units +static const double dt_max = 1.e-1; + /** * @brief decide time step size restricted by the advective terms * @param[in] domain : information about domain decomposition and size @@ -32,7 +37,7 @@ static int decide_dt_adv( const domain_t * domain, const fluid_t * fluid, double * restrict dt -){ +) { MPI_Comm comm_cart = MPI_COMM_NULL; sdecomp.get_comm_cart(domain->info, &comm_cart); const int isize = domain->mysizes[0]; @@ -40,10 +45,10 @@ static int decide_dt_adv( #if NDIMS == 3 const int ksize = domain->mysizes[2]; #endif - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; + const double * restrict hxxf = domain->hxxf; + const double hy = domain->hy; #if NDIMS == 3 - const double dz = domain->dz; + const double hz = domain->hz; #endif const double * restrict ux = fluid->ux.data; const double * restrict uy = fluid->uy.data; @@ -52,52 +57,50 @@ static int decide_dt_adv( #endif // sufficiently small number to avoid zero division const double small = 1.e-8; - *dt = 1.; // max possible dt - // compute grid-size over velocity in x | 19 + *dt = dt_max; + // compute grid size over velocity in x | 19 #if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 2; i <= isize; i++){ - const double dx = DXC(i ); - double vel = fabs(UX(i, j)) + small; - *dt = fmin(*dt, dx / vel); + for (int j = 1; j <= jsize; j++) { + for (int i = 2; i <= isize; i++) { + const double vel = fabs(UX(i, j)) + small; + *dt = fmin(*dt, HXXF(i) / vel); } } #else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 2; i <= isize; i++){ - const double dx = DXC(i ); - double vel = fabs(UX(i, j, k)) + small; - *dt = fmin(*dt, dx / vel); + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 2; i <= isize; i++) { + const double vel = fabs(UX(i, j, k)) + small; + *dt = fmin(*dt, HXXF(i) / vel); } } } #endif - // compute grid-size over velocity in y | 17 + // compute grid size over velocity in y | 17 #if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - double vel = fabs(UY(i, j)) + small; - *dt = fmin(*dt, dy / vel); + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double vel = fabs(UY(i, j)) + small; + *dt = fmin(*dt, hy / vel); } } #else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - double vel = fabs(UY(i, j, k)) + small; - *dt = fmin(*dt, dy / vel); + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double vel = fabs(UY(i, j, k)) + small; + *dt = fmin(*dt, hy / vel); } } } #endif - // compute grid-size over velocity in z | 10 #if NDIMS == 3 - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - double vel = fabs(UZ(i, j, k)) + small; - *dt = fmin(*dt, dz / vel); + // compute grid size over velocity in z | 10 + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double vel = fabs(UZ(i, j, k)) + small; + *dt = fmin(*dt, hz / vel); } } } @@ -115,30 +118,29 @@ static int decide_dt_adv( * @param[out] dt : time step size * @return : error code */ -static int decide_dt_dif( +static int decide_dt_dif ( const domain_t * domain, const double diffusivity, double * restrict dt -){ +) { const int isize = domain->mysizes[0]; - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; + const double * restrict hxxf = domain->hxxf; + const double hy = domain->hy; #if NDIMS == 3 - const double dz = domain->dz; + const double hz = domain->hz; #endif double grid_sizes[NDIMS] = {0.}; // find minimum grid size in x direction grid_sizes[0] = DBL_MAX; - for(int i = 2; i <= isize; i++){ - const double dx = DXC(i ); - grid_sizes[0] = fmin(grid_sizes[0], dx); + for (int i = 2; i <= isize; i++) { + grid_sizes[0] = fmin(grid_sizes[0], HXXF(i)); } - grid_sizes[1] = dy; + grid_sizes[1] = hy; #if NDIMS == 3 - grid_sizes[2] = dz; + grid_sizes[2] = hz; #endif // compute diffusive constraints | 3 - for(int dim = 0; dim < NDIMS; dim++){ + for (size_t dim = 0; dim < NDIMS; dim++) { dt[dim] = coef_dt_dif / diffusivity * 0.5 / NDIMS * pow(grid_sizes[dim], 2.); } return 0; @@ -156,15 +158,15 @@ int decide_dt( const domain_t * domain, const fluid_t * fluid, double * restrict dt -){ - if(!coefs_are_initialised){ - if(0 != config.get_double("coef_dt_adv", &coef_dt_adv)) return 1; - if(0 != config.get_double("coef_dt_dif", &coef_dt_dif)) return 1; +) { + if (!coefs_are_initialised) { + if (0 != config.get_double("coef_dt_adv", &coef_dt_adv)) return 1; + if (0 != config.get_double("coef_dt_dif", &coef_dt_dif)) return 1; coefs_are_initialised = true; const int root = 0; int myrank = root; sdecomp.get_comm_rank(domain->info, &myrank); - if(root == myrank){ + if (root == myrank) { printf("coefs: (adv) % .3e, (dif) % .3e\n", coef_dt_adv, coef_dt_dif); } } @@ -172,33 +174,34 @@ int decide_dt( double dt_adv[1] = {0.}; double dt_dif_m[NDIMS] = {0.}; double dt_dif_t[NDIMS] = {0.}; - decide_dt_adv(domain, fluid, dt_adv ); - decide_dt_dif(domain, fluid->m_dif, dt_dif_m); - decide_dt_dif(domain, fluid->t_dif, dt_dif_t); + decide_dt_adv(domain, fluid, dt_adv); + decide_dt_dif(domain, fluid_compute_momentum_diffusivity(fluid), dt_dif_m); + decide_dt_dif(domain, fluid_compute_temperature_diffusivity(fluid), dt_dif_t); // choose smallest value as dt | 26 + *dt = dt_max; // advection - *dt = dt_adv[0]; + *dt = fmin(*dt, dt_adv[0]); // diffusion, momentum - if(!param_m_implicit_x){ + if (!param_m_implicit_x) { *dt = fmin(*dt, dt_dif_m[0]); } - if(!param_m_implicit_y){ + if (!param_m_implicit_y) { *dt = fmin(*dt, dt_dif_m[1]); } #if NDIMS == 3 - if(!param_m_implicit_z){ + if (!param_m_implicit_z) { *dt = fmin(*dt, dt_dif_m[2]); } #endif // diffusion, temperature - if(!param_t_implicit_x){ + if (!param_t_implicit_x) { *dt = fmin(*dt, dt_dif_t[0]); } - if(!param_t_implicit_y){ + if (!param_t_implicit_y) { *dt = fmin(*dt, dt_dif_t[1]); } #if NDIMS == 3 - if(!param_t_implicit_z){ + if (!param_t_implicit_z) { *dt = fmin(*dt, dt_dif_t[2]); } #endif diff --git a/src/domain.c b/src/domain.c index dccbc347..ebdbaf97 100644 --- a/src/domain.c +++ b/src/domain.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #if NDIMS == 3 #include @@ -13,8 +14,10 @@ #include "fileio.h" #include "array_macros/domain/xf.h" #include "array_macros/domain/xc.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/hxxc.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" /** * @brief load members in domain_t @@ -76,31 +79,6 @@ int domain_save( return 0; } -int domain_check_x_grid_is_uniform( - const domain_t * domain, - bool * x_grid_is_uniform -){ - static bool is_checked = false; - static bool is_uniform = false; - if(!is_checked){ - const size_t isize = domain->glsizes[0]; - const double * dxf = domain->dxf; - double extrema[2] = {+1. * DBL_MAX, -1. * DBL_MAX}; - for(size_t i = 0; i < isize; i++){ - extrema[0] = fmin(extrema[0], dxf[i]); - extrema[1] = fmax(extrema[1], dxf[i]); - } - if(fabs(extrema[1] - extrema[0]) < 1.e-15){ - is_uniform = true; - }else{ - is_uniform = false; - } - is_checked = true; - } - *x_grid_is_uniform = is_uniform; - return 0; -} - #if NDIMS == 3 static int get_ndigits( int num @@ -274,48 +252,72 @@ static int optimise_sdecomp_init( } #endif -/** - * @brief define face-to-face distances in x direction - * @param[in] isize : number of cell-centers in x direction (boundary excluded) - * @param[in] xf : cell-face positions in x direction - * @return : face-to-face distances in x direction - */ -static double * allocate_and_init_dxf( +// x scale factors at x cell faces | 10 +static double * allocate_and_init_hxxf ( + const int isize, + const double * xc +) { + double * hxxf = memory_calloc(isize + 1, sizeof(double)); + for (int i = 1; i <= isize + 1; i++) { + HXXF(i ) = XC(i ) - XC(i-1); + } + return hxxf; +} + +// x scale factors at x cell centers | 10 +static double * allocate_and_init_hxxc ( const int isize, const double * xf -){ - // dxf: distance from cell face to cell face | 8 - // NOTE: since xf has "isize + 1" items, - // dxf, which tells the distance of the two neighbouring cell faces, - // has "isize" elements, whose index starts from 1 - const size_t nitems = isize; - double * dxf = memory_calloc(nitems, sizeof(double)); - for(size_t i = 1; i <= nitems; i++){ - DXF(i ) = XF(i+1) - XF(i ); +) { + double * hxxc = memory_calloc(isize, sizeof(double)); + for (int i = 1; i <= isize; i++) { + HXXC(i ) = XF(i+1) - XF(i ); } - return dxf; + return hxxc; } -/** - * @brief define center-to-center distances in x direction - * @param[in] isize : number of cell-centers in x direction (boundary excluded) - * @param[in] xc : cell-center positions in x direction - * @return : center-to-center distances in x direction - */ -static double * allocate_and_init_dxc( +// jacobian determinants at x cell faces | 20 +static double * allocate_and_init_jdxf ( const int isize, - const double * xc -){ - // dxc: distance from cell center to cell center (generally) | 8 - // NOTE: since xc has "isize + 2" items, - // dxc, which tells the distance of the two neighbouring cell centers, - // has "isize + 1" elements, whose index starts from 1 - const size_t nitems = isize + 1; - double * dxc = memory_calloc(nitems, sizeof(double)); - for(size_t i = 1; i <= nitems; i++){ - DXC(i ) = XC(i ) - XC(i-1); + const double * hxxf, +#if NDIMS == 2 + const double hy +#else + const double hy, + const double hz +#endif +) { + double * jdxf = memory_calloc(isize + 1, sizeof(double)); + for (int i = 1; i <= isize + 1; i++) { +#if NDIMS == 2 + JDXF(i ) = HXXF(i ) * hy; +#else + JDXF(i ) = HXXF(i ) * hy * hz; +#endif } - return dxc; + return jdxf; +} + +// jacobian determinants at x cell centers | 20 +static double * allocate_and_init_jdxc ( + const int isize, + const double * hxxc, +#if NDIMS == 2 + const double hy +#else + const double hy, + const double hz +#endif +) { + double * jdxc = memory_calloc(isize, sizeof(double)); + for (int i = 1; i <= isize; i++) { +#if NDIMS == 2 + JDXC(i ) = HXXC(i ) * hy; +#else + JDXC(i ) = HXXC(i ) * hy * hz; +#endif + } + return jdxc; } static void report( @@ -354,24 +356,34 @@ int domain_init( double * restrict lengths = domain->lengths; double * restrict * xf = &domain->xf; double * restrict * xc = &domain->xc; - double * restrict * dxf = &domain->dxf; - double * restrict * dxc = &domain->dxc; - double * restrict dy = &domain->dy; + double * restrict * hxxf = &domain->hxxf; + double * restrict * hxxc = &domain->hxxc; + double * restrict hy = &domain->hy; #if NDIMS == 3 - double * restrict dz = &domain->dz; + double * restrict hz = &domain->hz; #endif + double * restrict * jdxf = &domain->jdxf; + double * restrict * jdxc = &domain->jdxc; // load spatial information | 3 if(0 != domain_load(dirname_ic, domain)){ return 1; } - // compute grid sizes | 8 - // allocate and initialise x coordinates - *dxf = allocate_and_init_dxf(glsizes[0], *xf); - *dxc = allocate_and_init_dxc(glsizes[0], *xc); - // grid sizes in homogeneous directions - *dy = lengths[1] / glsizes[1]; + // allocate and initialise scale factors + *hxxf = allocate_and_init_hxxf(glsizes[0], *xc); + *hxxc = allocate_and_init_hxxc(glsizes[0], *xf); + // y scale factor | 1 + *hy = lengths[1] / glsizes[1]; #if NDIMS == 3 - *dz = lengths[2] / glsizes[2]; + // z scale factor | 1 + *hz = lengths[2] / glsizes[2]; +#endif + // allocate and initialise Jacobian determinants +#if NDIMS == 2 + *jdxf = allocate_and_init_jdxf(glsizes[0], *hxxf, *hy); + *jdxc = allocate_and_init_jdxc(glsizes[0], *hxxc, *hy); +#else + *jdxf = allocate_and_init_jdxf(glsizes[0], *hxxf, *hy, *hz); + *jdxc = allocate_and_init_jdxc(glsizes[0], *hxxc, *hy, *hz); #endif // initialise sdecomp to distribute the domain | 14 #if NDIMS == 2 diff --git a/src/fluid/README.rst b/src/fluid/README.rst deleted file mode 100644 index bf2df260..00000000 --- a/src/fluid/README.rst +++ /dev/null @@ -1,46 +0,0 @@ -###### -fluid/ -###### - -Fluid solver. - -* arrays - - Macros to access flow field. - -* boundary_conditions - - Impose boundary conditions and exchange halo cells. - -* compute_potential - - Poisson solver. - -* compute_rhs - - Evaluate right-hand-side of the momentum equation. - -* correct_velocity - - Project velocity from non-solenoidal to divergence-free field. - -* decide_dt - - Decide time step size in the next step. - -* init.c - - Allocator and flow-field loader. - -* internal.h - - Private functions only used in this directory is declared. - -* update_pressure.c - - Function to update pressure field. - -* update_field - - Update velocity and temperature field using what is computed by ``compute_rhs``. - diff --git a/src/fluid/compute_diffusivity.c b/src/fluid/compute_diffusivity.c new file mode 100644 index 00000000..b1ec3037 --- /dev/null +++ b/src/fluid/compute_diffusivity.c @@ -0,0 +1,15 @@ +#include +#include "fluid.h" + +double fluid_compute_momentum_diffusivity ( + const fluid_t * fluid +) { + return sqrt(fluid->Pr) / sqrt(fluid->Ra); +} + +double fluid_compute_temperature_diffusivity ( + const fluid_t * fluid +) { + return 1. / sqrt(fluid->Pr) / sqrt(fluid->Ra); +} + diff --git a/src/fluid/compute_potential/dft.c b/src/fluid/compute_potential.c similarity index 59% rename from src/fluid/compute_potential/dft.c rename to src/fluid/compute_potential.c index 63f77736..97fce29e 100644 --- a/src/fluid/compute_potential/dft.c +++ b/src/fluid/compute_potential.c @@ -11,9 +11,10 @@ #include "tdm.h" #include "fluid.h" #include "fluid_solver.h" -#include "internal.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" +#include "array_macros/domain/xc.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" #include "array_macros/fluid/ux.h" #include "array_macros/fluid/uy.h" #if NDIMS == 3 @@ -21,26 +22,29 @@ #endif #include "array_macros/fluid/psi.h" +static const double g_pi = 3.14159265358979324; + // structure only used to solve Poisson equation -// NOTE: this is shared among normal and efficient solvers -// thus some variables may not be used typedef struct { bool is_initialised; void * restrict buf0; void * restrict buf1; - fftw_plan fftw_plan_x[2]; fftw_plan fftw_plan_y[2]; #if NDIMS == 3 fftw_plan fftw_plan_z[2]; #endif - size_t tdm_sizes[2]; + size_t tdm_sizes[NDIMS]; tdm_info_t * tdm_info; - double * evals; + double * restrict eval_ys; +#if NDIMS == 3 + double * restrict eval_zs; +#endif sdecomp_transpose_plan_t * r_transposer_x1_to_y1; sdecomp_transpose_plan_t * r_transposer_y1_to_x1; +#if NDIMS == 2 sdecomp_transpose_plan_t * c_transposer_x1_to_y1; sdecomp_transpose_plan_t * c_transposer_y1_to_x1; -#if NDIMS == 3 +#else sdecomp_transpose_plan_t * c_transposer_y1_to_z1; sdecomp_transpose_plan_t * c_transposer_z1_to_y1; sdecomp_transpose_plan_t * c_transposer_z1_to_x2; @@ -80,20 +84,20 @@ static size_t c_z1pncl_sizes[NDIMS] = {0}; static size_t c_x2pncl_sizes[NDIMS] = {0}; #endif -static size_t prod( +static size_t prod ( const size_t sizes[NDIMS] -){ +) { // compute the product of the given vector size_t nitems = 1; - for(size_t dim = 0; dim < NDIMS; dim++){ + for (size_t dim = 0; dim < NDIMS; dim++) { nitems *= sizes[dim]; } return nitems; } -static int report_failure( +static int report_failure ( const char type[] -){ +) { // function to just dump error message and abort FILE * stream = stderr; fprintf(stream, "Poisson solver, initialisation failed: %s\n", type); @@ -106,22 +110,22 @@ static int report_failure( return 0; } -static size_t max( +static size_t max ( const size_t val0, const size_t val1 -){ - if(val0 > val1){ +) { + if (val0 > val1) { return val0; }else{ return val1; } } -static int compute_pencil_sizes( +static int compute_pencil_sizes ( const domain_t * domain -){ +) { // NOTE: those variables are defined globally at the top of this file - // to reduce the nhumber of arguments which functions take + // to reduce the number of arguments which functions take // global domain size in real space const sdecomp_info_t * info = domain->info; r_gl_sizes[0] = domain->glsizes[0]; @@ -137,23 +141,23 @@ static int compute_pencil_sizes( c_gl_sizes[2] = domain->glsizes[2]; #endif // local domain sizes - for(sdecomp_dir_t dim = 0; dim < NDIMS; dim++){ - if(0 != sdecomp.get_pencil_mysize(info, SDECOMP_X1PENCIL, dim, r_gl_sizes[dim], r_x1pncl_sizes + dim)) return 1; - if(0 != sdecomp.get_pencil_mysize(info, SDECOMP_Y1PENCIL, dim, r_gl_sizes[dim], r_y1pncl_sizes + dim)) return 1; - if(0 != sdecomp.get_pencil_mysize(info, SDECOMP_Y1PENCIL, dim, c_gl_sizes[dim], c_y1pncl_sizes + dim)) return 1; + for (sdecomp_dir_t dim = 0; dim < NDIMS; dim++) { + if (0 != sdecomp.get_pencil_mysize(info, SDECOMP_X1PENCIL, dim, r_gl_sizes[dim], r_x1pncl_sizes + dim)) return 1; + if (0 != sdecomp.get_pencil_mysize(info, SDECOMP_Y1PENCIL, dim, r_gl_sizes[dim], r_y1pncl_sizes + dim)) return 1; + if (0 != sdecomp.get_pencil_mysize(info, SDECOMP_Y1PENCIL, dim, c_gl_sizes[dim], c_y1pncl_sizes + dim)) return 1; #if NDIMS == 2 - if(0 != sdecomp.get_pencil_mysize(info, SDECOMP_X1PENCIL, dim, c_gl_sizes[dim], c_x1pncl_sizes + dim)) return 1; + if (0 != sdecomp.get_pencil_mysize(info, SDECOMP_X1PENCIL, dim, c_gl_sizes[dim], c_x1pncl_sizes + dim)) return 1; #else - if(0 != sdecomp.get_pencil_mysize(info, SDECOMP_Z1PENCIL, dim, c_gl_sizes[dim], c_z1pncl_sizes + dim)) return 1; - if(0 != sdecomp.get_pencil_mysize(info, SDECOMP_X2PENCIL, dim, c_gl_sizes[dim], c_x2pncl_sizes + dim)) return 1; + if (0 != sdecomp.get_pencil_mysize(info, SDECOMP_Z1PENCIL, dim, c_gl_sizes[dim], c_z1pncl_sizes + dim)) return 1; + if (0 != sdecomp.get_pencil_mysize(info, SDECOMP_X2PENCIL, dim, c_gl_sizes[dim], c_x2pncl_sizes + dim)) return 1; #endif } return 0; } -static int allocate_buffers( +static int allocate_buffers ( poisson_solver_t * poisson_solver -){ +) { // although there are bunch of pencils involved, // two buffers are enough to do the job, // which are allocated here @@ -182,13 +186,13 @@ static int allocate_buffers( #endif // allocate them using fftw_malloc to enforce them 16bit-aligned for SIMD *buf0 = fftw_malloc(buf0_bytes); - if(NULL == *buf0){ + if (NULL == *buf0) { fprintf(stderr, "FATAL: fftw_malloc failed (requested %zu bytes)\n", buf0_bytes); fflush(stderr); return 1; } *buf1 = fftw_malloc(buf1_bytes); - if(NULL == *buf1){ + if (NULL == *buf1) { fprintf(stderr, "FATAL: fftw_malloc failed (requested %zu bytes)\n", buf1_bytes); fflush(stderr); return 1; @@ -196,13 +200,12 @@ static int allocate_buffers( return 0; } -static int init_tri_diagonal_solver( +static int init_tri_diagonal_solver ( const domain_t * domain, poisson_solver_t * poisson_solver -){ - // N x N tri-diagonal matrix, - // which are solved for M times - // tdm_sizes[0] = N, tdm_sizes[1] = M +) { + // tri-diagonal matrice, whose size is Nx, + // are solved for Ny x Nz times // since lower- and upper-diagonal components are // independent to y, z directions and time, // we compute here and re-use them @@ -211,15 +214,15 @@ static int init_tri_diagonal_solver( // in the solver size_t * restrict tdm_sizes = poisson_solver->tdm_sizes; tdm_info_t ** tdm_info = &poisson_solver->tdm_info; - // in x: d^2p / dx^2 = q #if NDIMS == 2 tdm_sizes[0] = c_x1pncl_sizes[0]; tdm_sizes[1] = c_x1pncl_sizes[1]; #else tdm_sizes[0] = c_x2pncl_sizes[0]; - tdm_sizes[1] = c_x2pncl_sizes[1] * c_x2pncl_sizes[2]; + tdm_sizes[1] = c_x2pncl_sizes[1]; + tdm_sizes[2] = c_x2pncl_sizes[2]; #endif - if(0 != tdm.construct( + if (0 != tdm.construct( /* size of system */ tdm_sizes[0], /* number of rhs */ 1, /* is periodic */ false, @@ -231,55 +234,55 @@ static int init_tri_diagonal_solver( double * tdm_u = NULL; tdm.get_l(*tdm_info, &tdm_l); tdm.get_u(*tdm_info, &tdm_u); - const double * dxf = domain->dxf; - const double * dxc = domain->dxc; - for(size_t i = 1; i <= tdm_sizes[0]; i++){ - // N.B. loop from i = 1 to use DXC and DXF macros, - // which should be assigned to tdm_[lu] at i = 0 - tdm_l[i-1] = 1. / DXC(i ) / DXF(i ); - tdm_u[i-1] = 1. / DXC(i+1) / DXF(i ); + const double * hxxf = domain->hxxf; + const double * jdxf = domain->jdxf; + const double * jdxc = domain->jdxc; + for (int i = 1; i <= (int)tdm_sizes[0]; i++) { + // N.B. iterated from i = 1 to use macros + tdm_l[i-1] = 1. / JDXC(i ) * JDXF(i ) / HXXF(i ) / HXXF(i ); + tdm_u[i-1] = 1. / JDXC(i ) * JDXF(i+1) / HXXF(i+1) / HXXF(i+1); } return 0; } -static int init_pencil_rotations( +static int init_pencil_rotations ( const domain_t * domain, poisson_solver_t * poisson_solver -){ +) { const sdecomp_info_t * info = domain->info; const size_t r_dsize = sizeof(double); const size_t c_dsize = sizeof(fftw_complex); - if(0 != sdecomp.transpose.construct(info, SDECOMP_X1PENCIL, SDECOMP_Y1PENCIL, r_gl_sizes, r_dsize, &poisson_solver->r_transposer_x1_to_y1)){ + if (0 != sdecomp.transpose.construct(info, SDECOMP_X1PENCIL, SDECOMP_Y1PENCIL, r_gl_sizes, r_dsize, &poisson_solver->r_transposer_x1_to_y1)) { report_failure("SDECOMP x1 to y1 for real"); return 1; } - if(0 != sdecomp.transpose.construct(info, SDECOMP_Y1PENCIL, SDECOMP_X1PENCIL, r_gl_sizes, r_dsize, &poisson_solver->r_transposer_y1_to_x1)){ + if (0 != sdecomp.transpose.construct(info, SDECOMP_Y1PENCIL, SDECOMP_X1PENCIL, r_gl_sizes, r_dsize, &poisson_solver->r_transposer_y1_to_x1)) { report_failure("SDECOMP y1 to x1 for real"); return 1; } #if NDIMS == 2 - if(0 != sdecomp.transpose.construct(info, SDECOMP_X1PENCIL, SDECOMP_Y1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_x1_to_y1)){ - report_failure("SDECOMP x1 to y1 for complex"); + if (0 != sdecomp.transpose.construct(info, SDECOMP_Y1PENCIL, SDECOMP_X1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_y1_to_x1)) { + report_failure("SDECOMP y1 to x1 for complex"); return 1; } - if(0 != sdecomp.transpose.construct(info, SDECOMP_Y1PENCIL, SDECOMP_X1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_y1_to_x1)){ - report_failure("SDECOMP y1 to x1 for complex"); + if (0 != sdecomp.transpose.construct(info, SDECOMP_X1PENCIL, SDECOMP_Y1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_x1_to_y1)) { + report_failure("SDECOMP x1 to y1 for complex"); return 1; } #else - if(0 != sdecomp.transpose.construct(info, SDECOMP_Y1PENCIL, SDECOMP_Z1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_y1_to_z1)){ + if (0 != sdecomp.transpose.construct(info, SDECOMP_Y1PENCIL, SDECOMP_Z1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_y1_to_z1)) { report_failure("SDECOMP y1 to z1 for complex"); return 1; } - if(0 != sdecomp.transpose.construct(info, SDECOMP_Z1PENCIL, SDECOMP_Y1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_z1_to_y1)){ + if (0 != sdecomp.transpose.construct(info, SDECOMP_Z1PENCIL, SDECOMP_Y1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_z1_to_y1)) { report_failure("SDECOMP z1 to y1 for complex"); return 1; } - if(0 != sdecomp.transpose.construct(info, SDECOMP_Z1PENCIL, SDECOMP_X2PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_z1_to_x2)){ + if (0 != sdecomp.transpose.construct(info, SDECOMP_Z1PENCIL, SDECOMP_X2PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_z1_to_x2)) { report_failure("SDECOMP z1 to x2 for complex"); return 1; } - if(0 != sdecomp.transpose.construct(info, SDECOMP_X2PENCIL, SDECOMP_Z1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_x2_to_z1)){ + if (0 != sdecomp.transpose.construct(info, SDECOMP_X2PENCIL, SDECOMP_Z1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_x2_to_z1)) { report_failure("SDECOMP x2 to z1 for complex"); return 1; } @@ -287,10 +290,10 @@ static int init_pencil_rotations( return 0; } -static int init_ffts( +static int init_ffts ( poisson_solver_t * poisson_solver -){ - const unsigned flags = FFTW_PATIENT | FFTW_DESTROY_INPUT; +) { + const unsigned flags = FFTW_MEASURE; // NOTE: two buffers should be properly given // see "allocate_buffers" above // y, real / complex @@ -316,11 +319,11 @@ static int init_ffts( poisson_solver->buf1, NULL, 1, r_signal_length, flags ); - if(NULL == *fplan){ + if (NULL == *fplan) { report_failure("FFTW y-forward"); return 1; } - if(NULL == *bplan){ + if (NULL == *bplan) { report_failure("FFTW y-backward"); return 1; } @@ -344,11 +347,11 @@ static int init_ffts( poisson_solver->buf1, NULL, 1, signal_length, FFTW_BACKWARD, flags ); - if(NULL == *fplan){ + if (NULL == *fplan) { report_failure("FFTW z-forward"); return 1; } - if(NULL == *bplan){ + if (NULL == *bplan) { report_failure("FFTW z-backward"); return 1; } @@ -357,63 +360,49 @@ static int init_ffts( return 0; } -static int init_eigenvalues( +static int init_eigenvalues ( const domain_t * domain, poisson_solver_t * poisson_solver -){ - const double pi = 3.14159265358979324; +) { const sdecomp_info_t * info = domain->info; - double ** evals = &poisson_solver->evals; #if NDIMS == 2 - // x1 pencil, DFT in y + // x1 pencil const sdecomp_pencil_t pencil = SDECOMP_X1PENCIL; - const double signal_lengths[NDIMS - 1] = { - r_gl_sizes[SDECOMP_YDIR], - }; - size_t mysizes[NDIMS - 1] = {0}; - sdecomp.get_pencil_mysize(info, pencil, SDECOMP_YDIR, c_gl_sizes[SDECOMP_YDIR], mysizes); - size_t offsets[NDIMS - 1] = {0}; - sdecomp.get_pencil_offset(info, pencil, SDECOMP_YDIR, c_gl_sizes[SDECOMP_YDIR], offsets); - const double gridsizes[NDIMS - 1] = { - domain->lengths[SDECOMP_YDIR] / r_gl_sizes[SDECOMP_YDIR], - }; - // initialise eigenvalues in homogeneous directions | 8 - *evals = memory_calloc(mysizes[0], sizeof(double)); - for(size_t cnt = 0, j = offsets[0]; j < mysizes[0] + offsets[0]; j++, cnt++){ - (*evals)[cnt] = - - 4. / pow(gridsizes[0], 2.) * pow( - sin( pi * j / signal_lengths[0] ), - 2. - ); - } #else - // x2 pencil, DFTs in y and z directions + // x2 pencil const sdecomp_pencil_t pencil = SDECOMP_X2PENCIL; - const double signal_lengths[NDIMS - 1] = { - 1. * r_gl_sizes[SDECOMP_YDIR], - 1. * r_gl_sizes[SDECOMP_ZDIR], - }; - size_t mysizes[NDIMS - 1] = {0}; - sdecomp.get_pencil_mysize(info, pencil, SDECOMP_YDIR, c_gl_sizes[SDECOMP_YDIR], mysizes + 0); - sdecomp.get_pencil_mysize(info, pencil, SDECOMP_ZDIR, c_gl_sizes[SDECOMP_ZDIR], mysizes + 1); - size_t offsets[NDIMS - 1] = {0}; - sdecomp.get_pencil_offset(info, pencil, SDECOMP_YDIR, c_gl_sizes[SDECOMP_YDIR], offsets + 0); - sdecomp.get_pencil_offset(info, pencil, SDECOMP_ZDIR, c_gl_sizes[SDECOMP_ZDIR], offsets + 1); - const double gridsizes[NDIMS - 1] = { - domain->lengths[SDECOMP_YDIR] / r_gl_sizes[SDECOMP_YDIR], - domain->lengths[SDECOMP_ZDIR] / r_gl_sizes[SDECOMP_ZDIR], - }; - // initialise eigenvalues in homogeneous directions | 14 - *evals = memory_calloc(mysizes[0] * mysizes[1], sizeof(double)); - for(size_t cnt = 0, j = offsets[1]; j < mysizes[1] + offsets[1]; j++){ - for(size_t i = offsets[0]; i < mysizes[0] + offsets[0]; i++, cnt++){ - (*evals)[cnt] = - - 4. / pow(gridsizes[0], 2.) * pow( - sin( pi * i / signal_lengths[0] ), +#endif + // y eigenvalues | 16 + { + double * restrict * evals = &poisson_solver->eval_ys; + size_t mysize = 0; + size_t myoffs = 0; + sdecomp.get_pencil_mysize(info, pencil, SDECOMP_YDIR, c_gl_sizes[SDECOMP_YDIR], &mysize); + sdecomp.get_pencil_offset(info, pencil, SDECOMP_YDIR, c_gl_sizes[SDECOMP_YDIR], &myoffs); + *evals = memory_calloc(mysize, sizeof(double)); + for (size_t local_n = 0; local_n < mysize; local_n++) { + const size_t global_n = local_n + myoffs; + (*evals)[local_n] = + - 4. * pow( + sin( g_pi * global_n / r_gl_sizes[SDECOMP_YDIR] ), 2. - ) - - 4. / pow(gridsizes[1], 2.) * pow( - sin( pi * j / signal_lengths[1] ), + ); + } + } +#if NDIMS == 3 + // z eigenvalues | 16 + { + double * restrict * evals = &poisson_solver->eval_zs; + size_t mysize = 0; + size_t myoffs = 0; + sdecomp.get_pencil_mysize(info, pencil, SDECOMP_ZDIR, c_gl_sizes[SDECOMP_ZDIR], &mysize); + sdecomp.get_pencil_offset(info, pencil, SDECOMP_ZDIR, c_gl_sizes[SDECOMP_ZDIR], &myoffs); + *evals = memory_calloc(mysize, sizeof(double)); + for (size_t local_n = 0; local_n < mysize; local_n++) { + const size_t global_n = local_n + myoffs; + (*evals)[local_n] = + - 4. * pow( + sin( g_pi * global_n / r_gl_sizes[SDECOMP_ZDIR] ), 2. ); } @@ -422,45 +411,66 @@ static int init_eigenvalues( return 0; } -static int init_poisson_solver( +static int init_poisson_solver ( const domain_t * domain, poisson_solver_t * poisson_solver -){ +) { // check domain size (global, local, pencils) - if(0 != compute_pencil_sizes(domain)) return 1; + if (0 != compute_pencil_sizes(domain)) return 1; // initialise each part of poisson_solver_t - if(0 != allocate_buffers(poisson_solver)) return 1; - if(0 != init_tri_diagonal_solver(domain, poisson_solver)) return 1; - if(0 != init_pencil_rotations(domain, poisson_solver)) return 1; - if(0 != init_ffts(poisson_solver)) return 1; - if(0 != init_eigenvalues(domain, poisson_solver)) return 1; + if (0 != allocate_buffers(poisson_solver)) return 1; + if (0 != init_tri_diagonal_solver(domain, poisson_solver)) return 1; + if (0 != init_pencil_rotations(domain, poisson_solver)) return 1; + if (0 != init_ffts(poisson_solver)) return 1; + if (0 != init_eigenvalues(domain, poisson_solver)) return 1; poisson_solver->is_initialised = true; const int root = 0; int myrank = root; sdecomp.get_comm_rank(domain->info, &myrank); - if(root == myrank){ + if (root == myrank) { printf("DFT-based solver is used\n"); } return 0; } -static int assign_input( +#if NDIMS == 2 +#define BEGIN \ + for (int cnt = 0, j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++, cnt++) { +#define END \ + } \ + } +#else +#define BEGIN \ + for (int cnt = 0, k = 1; k <= ksize; k++) { \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++, cnt++) { +#define END \ + } \ + } \ + } +#endif + +// compute right-hand side of Poisson equation | 61 +static int assign_input ( const domain_t * domain, const size_t rkstep, const double dt, const fluid_t * fluid, double * restrict rhs -){ +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; #if NDIMS == 3 const int ksize = domain->mysizes[2]; #endif - const double * restrict dxf = domain->dxf; - const double dy = domain->dy; + const double * restrict hxxf = domain->hxxf; + const double hy = domain->hy; #if NDIMS == 3 - const double dz = domain->dz; + const double hz = domain->hz; #endif + const double * restrict jdxf = domain->jdxf; + const double * restrict jdxc = domain->jdxc; const double * restrict ux = fluid->ux.data; const double * restrict uy = fluid->uy.data; #if NDIMS == 3 @@ -473,151 +483,164 @@ static int assign_input( const double norm = 1. * domain->glsizes[1] * domain->glsizes[2]; #endif const double prefactor = 1. / (rkcoefs[rkstep][rk_g] * dt) / norm; + BEGIN + const double hx_xm = HXXF(i ); + const double hx_xp = HXXF(i+1); + const double jd_xm = JDXF(i ); + const double jd_x0 = JDXC(i ); + const double jd_xp = JDXF(i+1); +#if NDIMS == 2 + const double ux_xm = UX(i , j ); + const double ux_xp = UX(i+1, j ); + const double uy_ym = UY(i , j ); + const double uy_yp = UY(i , j+1); +#else + const double ux_xm = UX(i , j , k ); + const double ux_xp = UX(i+1, j , k ); + const double uy_ym = UY(i , j , k ); + const double uy_yp = UY(i , j+1, k ); + const double uz_zm = UZ(i , j , k ); + const double uz_zp = UZ(i , j , k+1); +#endif + const double div = 1. / jd_x0 * ( + - jd_xm / hx_xm * ux_xm + jd_xp / hx_xp * ux_xp + - jd_x0 / hy * uy_ym + jd_x0 / hy * uy_yp +#if NDIMS == 3 + - jd_x0 / hz * uz_zm + jd_x0 / hz * uz_zp +#endif + ); + rhs[cnt] = prefactor * div; + END + return 0; +} + +static int solve_linear_systems ( + const domain_t * domain, + poisson_solver_t * poisson_solver +) { + const double hy = domain->hy; +#if NDIMS == 3 + const double hz = domain->hz; +#endif + const size_t * restrict tdm_sizes = poisson_solver->tdm_sizes; + const size_t isize = tdm_sizes[0]; + const size_t jsize = tdm_sizes[1]; +#if NDIMS == 3 + const size_t ksize = tdm_sizes[2]; +#endif + // tri-diagonal matrix + tdm_info_t * tdm_info = poisson_solver->tdm_info; + double * restrict tdm_l = NULL; + double * restrict tdm_u = NULL; + double * restrict tdm_c = NULL; + tdm.get_l(tdm_info, &tdm_l); + tdm.get_u(tdm_info, &tdm_u); + tdm.get_c(tdm_info, &tdm_c); + // eigenvalues coming from Fourier projection + const double * restrict eval_ys = poisson_solver->eval_ys; +#if NDIMS == 3 + const double * restrict eval_zs = poisson_solver->eval_zs; +#endif + fftw_complex * restrict rhs = poisson_solver->buf1; + // solve tri-diagonal matrices | 37 #if NDIMS == 2 - for(int cnt = 0, j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++, cnt++){ - const double dx = DXF(i ); - const double ux_xm = UX(i , j ); - const double ux_xp = UX(i+1, j ); - const double uy_ym = UY(i , j ); - const double uy_yp = UY(i , j+1); - rhs[cnt] = prefactor * ( - + (ux_xp - ux_xm) / dx - + (uy_yp - uy_ym) / dy - ); + for (size_t j = 0; j < jsize; j++) { + const double eval_y = eval_ys[j]; + // set center diagonal components + for (size_t i = 0; i < isize; i++) { + tdm_c[i] = + - tdm_l[i] + - tdm_u[i] + + eval_y / hy / hy; } + // boundary treatment (Neumann boundary condition) + tdm_c[ 0] += tdm_l[ 0]; + tdm_c[isize - 1] += tdm_u[isize - 1]; + // solve + tdm.solve(tdm_info, rhs + j * isize); } #else - for(int cnt = 0, k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++, cnt++){ - const double dx = DXF(i ); - const double ux_xm = UX(i , j , k ); - const double ux_xp = UX(i+1, j , k ); - const double uy_ym = UY(i , j , k ); - const double uy_yp = UY(i , j+1, k ); - const double uz_zm = UZ(i , j , k ); - const double uz_zp = UZ(i , j , k+1); - rhs[cnt] = prefactor * ( - + (ux_xp - ux_xm) / dx - + (uy_yp - uy_ym) / dy - + (uz_zp - uz_zm) / dz - ); + for (size_t k = 0; k < ksize; k++) { + const double eval_z = eval_zs[k]; + for (size_t j = 0; j < jsize; j++) { + const double eval_y = eval_ys[j]; + // set center diagonal components + for (size_t i = 0; i < isize; i++) { + tdm_c[i] = + - tdm_l[i] + - tdm_u[i] + + eval_y / hy / hy + + eval_z / hz / hz; } + // boundary treatment (Neumann boundary condition) + tdm_c[ 0] += tdm_l[ 0]; + tdm_c[isize - 1] += tdm_u[isize - 1]; + // solve + tdm.solve(tdm_info, rhs + (k * jsize + j) * isize); } } #endif return 0; } -static int extract_output( +static int extract_output ( const domain_t * domain, const double * restrict rhs, fluid_t * fluid -){ +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; #if NDIMS == 3 const int ksize = domain->mysizes[2]; #endif double * restrict psi = fluid->psi.data; + BEGIN #if NDIMS == 2 - for(int cnt = 0, j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++, cnt++){ - PSI(i, j) = rhs[cnt]; - } - } + PSI(i, j) = rhs[cnt]; #else - for(int cnt = 0, k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++, cnt++){ - PSI(i, j, k) = rhs[cnt]; - } - } - } + PSI(i, j, k) = rhs[cnt]; #endif - if(0 != fluid_update_boundaries_psi(domain, &fluid->psi)){ + END + if (0 != fluid_update_boundaries_psi(domain, &fluid->psi)) { return 1; } return 0; } -static int solve_linear_systems( - poisson_solver_t * poisson_solver -){ - // size of system (length) and how many such systems to be solved - // NOTE: although size_of_system is the same as tdm.get_size gives, - // repeat_for is different from what tdm.get_nrhs returns (=1) - // here repeat_for is the degree of freedom in the wavespace - const size_t size_of_system = poisson_solver->tdm_sizes[0]; - const size_t repeat_for = poisson_solver->tdm_sizes[1]; - // tri-diagonal matrix - tdm_info_t * tdm_info = poisson_solver->tdm_info; - double * restrict tdm_l = NULL; - double * restrict tdm_u = NULL; - double * restrict tdm_c = NULL; - tdm.get_l(tdm_info, &tdm_l); - tdm.get_u(tdm_info, &tdm_u); - tdm.get_c(tdm_info, &tdm_c); - // eigenvalues coming from Fourier projection - const double * restrict evals = poisson_solver->evals; - fftw_complex * restrict rhs = poisson_solver->buf1; - for(size_t m = 0; m < repeat_for; m++){ - // set center diagonal components | 7 - for(size_t n = 0; n < size_of_system; n++){ - tdm_c[n] = - tdm_l[n] - tdm_u[n] + evals[m]; - } - // boundary treatment (Neumann boundary condition) - tdm_c[ 0] += tdm_l[ 0]; - tdm_c[size_of_system-1] += tdm_u[size_of_system-1]; - tdm.solve(tdm_info, rhs + m * size_of_system); - } - return 0; -} - -/** - * @brief compute scalar potential psi to correct velocity - * @param[in] domain : information about domain decomposition and size - * @param[in] rkstep : Runge-Kutta step - * @param[in] dt : time step size - * @param[in,out] fluid : velocity (in), scalar potential psi (out) - * @return : (success) 0 - * : (failure) 1 - */ -int fluid_compute_potential_dft( +// solve Poisson equation | 105 +int fluid_compute_potential ( const domain_t * domain, const size_t rkstep, const double dt, fluid_t * fluid -){ +) { static poisson_solver_t poisson_solver = { .is_initialised = false, }; - // initialise Poisson solver | 7 - if(!poisson_solver.is_initialised){ - if(0 != init_poisson_solver(domain, &poisson_solver)){ + // initialise Poisson solver + if (!poisson_solver.is_initialised) { + if (0 != init_poisson_solver(domain, &poisson_solver)) { // failed to initialise Poisson solver return 1; } } - // compute right-hand side of Poisson equation | 2 + // compute right-hand side of Poisson equation // assigned to buf0 assign_input(domain, rkstep, dt, fluid, poisson_solver.buf0); - // solve the equation - // transpose real x1pencil to y1pencil | 6 + // transpose real x1pencil to y1pencil // from buf0 to buf1 sdecomp.transpose.execute( poisson_solver.r_transposer_x1_to_y1, poisson_solver.buf0, poisson_solver.buf1 ); - // project y to wave space | 4 + // project y to wave space // f(x, y) -> f(x, k_y) // f(x, y, z) -> f(x, k_y, z) // from buf1 to buf0 fftw_execute(poisson_solver.fftw_plan_y[0]); #if NDIMS == 2 - // transpose complex y1pencil to x1pencil | 6 + // transpose complex y1pencil to x1pencil // from buf0 to buf1 sdecomp.transpose.execute( poisson_solver.c_transposer_y1_to_x1, @@ -625,18 +648,18 @@ int fluid_compute_potential_dft( poisson_solver.buf1 ); #else - // transpose complex y1pencil to z1pencil | 6 + // transpose complex y1pencil to z1pencil // from buf0 to buf1 sdecomp.transpose.execute( poisson_solver.c_transposer_y1_to_z1, poisson_solver.buf0, poisson_solver.buf1 ); - // project z to wave space | 3 + // project z to wave space // f(x, k_y, z) -> f(x, k_y, k_z) // from buf1 to buf0 fftw_execute(poisson_solver.fftw_plan_z[0]); - // transpose complex z1pencil to x2pencil | 6 + // transpose complex z1pencil to x2pencil // from buf0 to buf1 sdecomp.transpose.execute( poisson_solver.c_transposer_z1_to_x2, @@ -644,10 +667,10 @@ int fluid_compute_potential_dft( poisson_solver.buf1 ); #endif - // solve linear systems | 1 - solve_linear_systems(&poisson_solver); + // solve linear systems in x direction + solve_linear_systems(domain, &poisson_solver); #if NDIMS == 2 - // transpose complex x1pencil to y1pencil | 6 + // transpose complex x1pencil to y1pencil // from buf1 to buf0 sdecomp.transpose.execute( poisson_solver.c_transposer_x1_to_y1, @@ -655,18 +678,18 @@ int fluid_compute_potential_dft( poisson_solver.buf0 ); #else - // transpose complex x2pencil to z1pencil | 6 + // transpose complex x2pencil to z1pencil // from buf1 to buf0 sdecomp.transpose.execute( poisson_solver.c_transposer_x2_to_z1, poisson_solver.buf1, poisson_solver.buf0 ); - // project z to physical space | 3 + // project z to physical space // f(x, k_y, k_z) -> f(x, k_y, z) // from buf0 to buf1 fftw_execute(poisson_solver.fftw_plan_z[1]); - // transpose complex z1pencil to y1pencil | 6 + // transpose complex z1pencil to y1pencil // from buf1 to buf0 sdecomp.transpose.execute( poisson_solver.c_transposer_z1_to_y1, @@ -674,18 +697,19 @@ int fluid_compute_potential_dft( poisson_solver.buf0 ); #endif - // project y to physical space | 4 + // project y to physical space // f(x, k_y) -> f(x, y) // f(x, k_y, z) -> f(x, y, z) // from buf0 to buf1 fftw_execute(poisson_solver.fftw_plan_y[1]); - // transpose real y1pencil to x1pencil | 6 + // transpose real y1pencil to x1pencil // from buf1 to buf0 sdecomp.transpose.execute( poisson_solver.r_transposer_y1_to_x1, poisson_solver.buf1, poisson_solver.buf0 ); + // copy result to the original buffer extract_output(domain, poisson_solver.buf0, fluid); return 0; } diff --git a/src/fluid/compute_potential/dct.c b/src/fluid/compute_potential/dct.c deleted file mode 100644 index 7d77de18..00000000 --- a/src/fluid/compute_potential/dct.c +++ /dev/null @@ -1,656 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "sdecomp.h" -#include "memory.h" -#include "runge_kutta.h" -#include "domain.h" -#include "tdm.h" -#include "fluid.h" -#include "fluid_solver.h" -#include "internal.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/ux.h" -#include "array_macros/fluid/uy.h" -#if NDIMS == 3 -#include "array_macros/fluid/uz.h" -#endif -#include "array_macros/fluid/psi.h" - -// structure only used to solve Poisson equation -// NOTE: this is shared among normal and efficient solvers -// thus some variables may not be used -typedef struct { - bool is_initialised; - void * restrict buf0; - void * restrict buf1; - fftw_plan fftw_plan_x[2]; -#if NDIMS == 3 - fftw_plan fftw_plan_y[2]; - fftw_plan fftw_plan_z[2]; -#endif - size_t tdm_sizes[2]; - tdm_info_t * tdm_info; - double * evals; - sdecomp_transpose_plan_t * r_transposer_x1_to_y1; - sdecomp_transpose_plan_t * r_transposer_y1_to_x1; -#if NDIMS == 3 - sdecomp_transpose_plan_t * c_transposer_y1_to_z1; - sdecomp_transpose_plan_t * c_transposer_z1_to_y1; -#endif -} poisson_solver_t; - -/* initialise Poisson solver */ -// several pencils for different data types are treated -// and thus this source is very complicated -// used prefixes are as follows: -// r_: real (double) type -// c_: complex (fftw_complex) type -// -// gl_ : global array size (not pencils) -// x1pncl_: each pencl (x1, y1, ...) - -/* size of domain and pencils */ -// NOTE: define globally to reduce the number of arguments of static functions -// global domain size in real space -static size_t r_gl_sizes[NDIMS] = {0}; -#if NDIMS == 3 -// global domain size in complex space -static size_t c_gl_sizes[NDIMS] = {0}; -#endif -// local domain size (x1 pencil) in real space -static size_t r_x1pncl_sizes[NDIMS] = {0}; -// local domain size (y1 pencil) in real space -static size_t r_y1pncl_sizes[NDIMS] = {0}; -#if NDIMS == 3 -// local domain size (y1 pencil) in complex space -static size_t c_y1pncl_sizes[NDIMS] = {0}; -// local domain size (z1 pencil) in complex space -static size_t c_z1pncl_sizes[NDIMS] = {0}; -#endif - -static size_t prod( - const size_t sizes[NDIMS] -){ - // compute the product of the given vector - size_t nitems = 1; - for(size_t dim = 0; dim < NDIMS; dim++){ - nitems *= sizes[dim]; - } - return nitems; -} - -static int report_failure( - const char type[] -){ - // function to just dump error message and abort - FILE * stream = stderr; - fprintf(stream, "Poisson solver, initialisation failed: %s\n", type); - fprintf(stream, " FFTW: A possible reason is you link Intel-MKL lib\n"); - fprintf(stream, " Make sure you use FFTW3 directly,\n"); - fprintf(stream, " NOT its crazy wrapper offered by MKL\n"); - fprintf(stream, " SDECOMP: Check sdecomp.log and check arguments\n"); - fprintf(stream, " If they are all correct, PLEASE CONTACT ME\n"); - fflush(stream); - return 0; -} - -static size_t max( - const size_t val0, - const size_t val1 -){ - if(val0 > val1){ - return val0; - }else{ - return val1; - } -} - -static int compute_pencil_sizes( - const domain_t * domain -){ - // NOTE: those variables are defined globally at the top of this file - // to reduce the nhumber of arguments which functions take - // global domain size in real space - const sdecomp_info_t * info = domain->info; - r_gl_sizes[0] = domain->glsizes[0]; - r_gl_sizes[1] = domain->glsizes[1]; -#if NDIMS == 3 - r_gl_sizes[2] = domain->glsizes[2]; -#endif -#if NDIMS == 3 - // global domain size in complex space - // NOTE: Hermite symmetry in y - c_gl_sizes[0] = domain->glsizes[0]; - c_gl_sizes[1] = domain->glsizes[1] / 2 + 1; - c_gl_sizes[2] = domain->glsizes[2]; -#endif - // local domain sizes - for(sdecomp_dir_t dim = 0; dim < NDIMS; dim++){ - if(0 != sdecomp.get_pencil_mysize(info, SDECOMP_X1PENCIL, dim, r_gl_sizes[dim], r_x1pncl_sizes + dim)) return 1; - if(0 != sdecomp.get_pencil_mysize(info, SDECOMP_Y1PENCIL, dim, r_gl_sizes[dim], r_y1pncl_sizes + dim)) return 1; -#if NDIMS == 3 - if(0 != sdecomp.get_pencil_mysize(info, SDECOMP_Y1PENCIL, dim, c_gl_sizes[dim], c_y1pncl_sizes + dim)) return 1; - if(0 != sdecomp.get_pencil_mysize(info, SDECOMP_Z1PENCIL, dim, c_gl_sizes[dim], c_z1pncl_sizes + dim)) return 1; -#endif - } - return 0; -} - -static int allocate_buffers( - poisson_solver_t * poisson_solver -){ - // although there are bunch of pencils involved, - // two buffers are enough to do the job, - // which are allocated here - void * restrict * buf0 = &poisson_solver->buf0; - void * restrict * buf1 = &poisson_solver->buf1; - const size_t r_dsize = sizeof(double); -#if NDIMS == 3 - const size_t c_dsize = sizeof(fftw_complex); -#endif - size_t buf0_bytes = 0; - size_t buf1_bytes = 0; -#if NDIMS == 2 - // r_x1pncl -> FFT -> r_x1pncl -> rotate -> r_y1pncl - // buffer0 buffer1 buffer0 - buf0_bytes = max(buf0_bytes, r_dsize * prod(r_x1pncl_sizes)); - buf0_bytes = max(buf0_bytes, r_dsize * prod(r_y1pncl_sizes)); - buf1_bytes = max(buf1_bytes, r_dsize * prod(r_x1pncl_sizes)); -#else - // r_x1pncl -> FFT -> r_x1pncl -> rotate -> r_y1pncl -> FFT -> c_y1pncl -> rotate -> c_z1pncl - // buffer0 buffer1 buffer0 buffer1 buffer0 - buf0_bytes = max(buf0_bytes, r_dsize * prod(r_x1pncl_sizes)); - buf0_bytes = max(buf0_bytes, r_dsize * prod(r_y1pncl_sizes)); - buf0_bytes = max(buf0_bytes, c_dsize * prod(c_z1pncl_sizes)); - buf1_bytes = max(buf1_bytes, r_dsize * prod(r_x1pncl_sizes)); - buf1_bytes = max(buf1_bytes, c_dsize * prod(c_y1pncl_sizes)); -#endif - // allocate them using fftw_malloc to enforce them 16bit-aligned for SIMD - *buf0 = fftw_malloc(buf0_bytes); - if(NULL == *buf0){ - fprintf(stderr, "FATAL: fftw_malloc failed (requested %zu bytes)\n", buf0_bytes); - fflush(stderr); - return 1; - } - *buf1 = fftw_malloc(buf1_bytes); - if(NULL == *buf1){ - fprintf(stderr, "FATAL: fftw_malloc failed (requested %zu bytes)\n", buf1_bytes); - fflush(stderr); - return 1; - } - return 0; -} - -static int init_tri_diagonal_solver( - const domain_t * domain, - poisson_solver_t * poisson_solver -){ - // N x N tri-diagonal matrix, - // which are solved for M times - // tdm_sizes[0] = N, tdm_sizes[1] = M - // since lower- and upper-diagonal components are - // independent to y, z directions and time, - // we compute here and re-use them - // center-diagonal components are, on the other hand, - // dependent on time and thus needs to compute everytime - // in the solver - size_t * restrict tdm_sizes = poisson_solver->tdm_sizes; - tdm_info_t ** tdm_info = &poisson_solver->tdm_info; -#if NDIMS == 2 - // in y: d^2p / dy^2 = q - tdm_sizes[0] = r_y1pncl_sizes[1]; - tdm_sizes[1] = r_y1pncl_sizes[0]; - if(0 != tdm.construct( - /* size of system */ tdm_sizes[0], - /* number of rhs */ 1, - /* is periodic */ true, - /* is complex */ false, - /* output */ tdm_info - )) return 1; - // initialise tri-diagonal matrix in y direction | 9 - double * tdm_l = NULL; - double * tdm_u = NULL; - tdm.get_l(*tdm_info, &tdm_l); - tdm.get_u(*tdm_info, &tdm_u); - const double dy = domain->dy; - for(size_t j = 0; j < tdm_sizes[0]; j++){ - tdm_l[j] = 1. / dy / dy; - tdm_u[j] = 1. / dy / dy; - } -#else - // in z: d^2p / dz^2 = q - tdm_sizes[0] = c_z1pncl_sizes[2]; - tdm_sizes[1] = c_z1pncl_sizes[0] * c_z1pncl_sizes[1]; - if(0 != tdm.construct( - /* size of system */ tdm_sizes[0], - /* number of rhs */ 1, - /* is periodic */ true, - /* is complex */ true, - /* output */ tdm_info - )) return 1; - // initialise tri-diagonal matrix in z direction | 9 - double * tdm_l = NULL; - double * tdm_u = NULL; - tdm.get_l(*tdm_info, &tdm_l); - tdm.get_u(*tdm_info, &tdm_u); - const double dz = domain->dz; - for(size_t k = 0; k < tdm_sizes[0]; k++){ - tdm_l[k] = 1. / dz / dz; - tdm_u[k] = 1. / dz / dz; - } -#endif - return 0; -} - -static int init_pencil_rotations( - const domain_t * domain, - poisson_solver_t * poisson_solver -){ - const sdecomp_info_t * info = domain->info; - const size_t r_dsize = sizeof(double); -#if NDIMS == 3 - const size_t c_dsize = sizeof(fftw_complex); -#endif - if(0 != sdecomp.transpose.construct(info, SDECOMP_X1PENCIL, SDECOMP_Y1PENCIL, r_gl_sizes, r_dsize, &poisson_solver->r_transposer_x1_to_y1)){ - report_failure("SDECOMP x1 to y1 for real"); - return 1; - } - if(0 != sdecomp.transpose.construct(info, SDECOMP_Y1PENCIL, SDECOMP_X1PENCIL, r_gl_sizes, r_dsize, &poisson_solver->r_transposer_y1_to_x1)){ - report_failure("SDECOMP y1 to x1 for real"); - return 1; - } -#if NDIMS == 3 - if(0 != sdecomp.transpose.construct(info, SDECOMP_Y1PENCIL, SDECOMP_Z1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_y1_to_z1)){ - report_failure("SDECOMP y1 to z1 for complex"); - return 1; - } - if(0 != sdecomp.transpose.construct(info, SDECOMP_Z1PENCIL, SDECOMP_Y1PENCIL, c_gl_sizes, c_dsize, &poisson_solver->c_transposer_z1_to_y1)){ - report_failure("SDECOMP z1 to y1 for complex"); - return 1; - } -#endif - return 0; -} - -static int init_ffts( - poisson_solver_t * poisson_solver -){ - const unsigned flags = FFTW_PATIENT | FFTW_DESTROY_INPUT; - // NOTE: two buffers should be properly given - // see "allocate_buffers" above - // x, real to real - { - const int signal_length = r_x1pncl_sizes[SDECOMP_XDIR]; -#if NDIMS == 2 - const int repeat_for = r_x1pncl_sizes[SDECOMP_YDIR]; -#else - const int repeat_for = r_x1pncl_sizes[SDECOMP_YDIR] * r_x1pncl_sizes[SDECOMP_ZDIR]; -#endif - fftw_plan * fplan = &poisson_solver->fftw_plan_x[0]; - fftw_plan * bplan = &poisson_solver->fftw_plan_x[1]; - *fplan = fftw_plan_many_r2r( - 1, &signal_length, repeat_for, - poisson_solver->buf0, NULL, 1, signal_length, - poisson_solver->buf1, NULL, 1, signal_length, - (fftw_r2r_kind [1]){FFTW_REDFT10}, flags - ); - *bplan = fftw_plan_many_r2r( - 1, &signal_length, repeat_for, - poisson_solver->buf1, NULL, 1, signal_length, - poisson_solver->buf0, NULL, 1, signal_length, - (fftw_r2r_kind [1]){FFTW_REDFT01}, flags - ); - if(NULL == *fplan){ - report_failure("FFTW x-forward"); - return 1; - } - if(NULL == *bplan){ - report_failure("FFTW x-backward"); - return 1; - } - } -#if NDIMS == 3 - // y, real / complex - { - fftw_plan * fplan = &poisson_solver->fftw_plan_y[0]; - fftw_plan * bplan = &poisson_solver->fftw_plan_y[1]; - const int r_signal_length = r_y1pncl_sizes[SDECOMP_YDIR]; - const int c_signal_length = c_y1pncl_sizes[SDECOMP_YDIR]; - const int repeat_for = r_y1pncl_sizes[SDECOMP_ZDIR] * r_y1pncl_sizes[SDECOMP_XDIR]; - *fplan = fftw_plan_many_dft_r2c( - 1, &r_signal_length, repeat_for, - poisson_solver->buf0, NULL, 1, r_signal_length, - poisson_solver->buf1, NULL, 1, c_signal_length, - flags - ); - *bplan = fftw_plan_many_dft_c2r( - 1, &r_signal_length, repeat_for, - poisson_solver->buf1, NULL, 1, c_signal_length, - poisson_solver->buf0, NULL, 1, r_signal_length, - flags - ); - if(NULL == *fplan){ - report_failure("FFTW y-forward"); - return 1; - } - if(NULL == *bplan){ - report_failure("FFTW y-backward"); - return 1; - } - } -#endif - return 0; -} - -static int init_eigenvalues( - const domain_t * domain, - poisson_solver_t * poisson_solver -){ - const double pi = 3.14159265358979324; - const sdecomp_info_t * info = domain->info; - double ** evals = &poisson_solver->evals; -#if NDIMS == 2 - // y1 pencil, DCT in x - const sdecomp_pencil_t pencil = SDECOMP_Y1PENCIL; - const double signal_lengths[NDIMS - 1] = { - 2. * r_gl_sizes[SDECOMP_XDIR], - }; - size_t mysizes[NDIMS - 1] = {0}; - sdecomp.get_pencil_mysize(info, pencil, SDECOMP_XDIR, r_gl_sizes[SDECOMP_XDIR], mysizes); - size_t offsets[NDIMS - 1] = {0}; - sdecomp.get_pencil_offset(info, pencil, SDECOMP_XDIR, r_gl_sizes[SDECOMP_XDIR], offsets); - const double gridsizes[NDIMS - 1] = { - domain->lengths[SDECOMP_XDIR] / r_gl_sizes[SDECOMP_XDIR], - }; - // initialise eigenvalues in homogeneous directions | 8 - *evals = memory_calloc(mysizes[0], sizeof(double)); - for(size_t cnt = 0, i = offsets[0]; i < mysizes[0] + offsets[0]; i++, cnt++){ - (*evals)[cnt] = - - 4. / pow(gridsizes[0], 2.) * pow( - sin( pi * i / signal_lengths[0] ), - 2. - ); - } -#else - // z1 pencil, DCT in x and DFT in y - const sdecomp_pencil_t pencil = SDECOMP_Z1PENCIL; - const double signal_lengths[NDIMS - 1] = { - 2. * r_gl_sizes[SDECOMP_XDIR], - 1. * r_gl_sizes[SDECOMP_YDIR], - }; - size_t mysizes[NDIMS - 1] = {0}; - sdecomp.get_pencil_mysize(info, pencil, SDECOMP_XDIR, c_gl_sizes[SDECOMP_XDIR], mysizes + 0); - sdecomp.get_pencil_mysize(info, pencil, SDECOMP_YDIR, c_gl_sizes[SDECOMP_YDIR], mysizes + 1); - size_t offsets[NDIMS - 1] = {0}; - sdecomp.get_pencil_offset(info, pencil, SDECOMP_XDIR, c_gl_sizes[SDECOMP_XDIR], offsets + 0); - sdecomp.get_pencil_offset(info, pencil, SDECOMP_YDIR, c_gl_sizes[SDECOMP_YDIR], offsets + 1); - const double gridsizes[NDIMS - 1] = { - domain->lengths[SDECOMP_XDIR] / r_gl_sizes[SDECOMP_XDIR], - domain->lengths[SDECOMP_YDIR] / r_gl_sizes[SDECOMP_YDIR], - }; - // initialise eigenvalues in homogeneous directions | 14 - *evals = memory_calloc(mysizes[0] * mysizes[1], sizeof(double)); - for(size_t cnt = 0, j = offsets[1]; j < mysizes[1] + offsets[1]; j++){ - for(size_t i = offsets[0]; i < mysizes[0] + offsets[0]; i++, cnt++){ - (*evals)[cnt] = - - 4. / pow(gridsizes[0], 2.) * pow( - sin( pi * i / signal_lengths[0] ), - 2. - ) - - 4. / pow(gridsizes[1], 2.) * pow( - sin( pi * j / signal_lengths[1] ), - 2. - ); - } - } -#endif - return 0; -} - -static int init_poisson_solver( - const domain_t * domain, - poisson_solver_t * poisson_solver -){ - // check domain size (global, local, pencils) - if(0 != compute_pencil_sizes(domain)) return 1; - // initialise each part of poisson_solver_t - if(0 != allocate_buffers(poisson_solver)) return 1; - if(0 != init_tri_diagonal_solver(domain, poisson_solver)) return 1; - if(0 != init_pencil_rotations(domain, poisson_solver)) return 1; - if(0 != init_ffts(poisson_solver)) return 1; - if(0 != init_eigenvalues(domain, poisson_solver)) return 1; - poisson_solver->is_initialised = true; - const int root = 0; - int myrank = root; - sdecomp.get_comm_rank(domain->info, &myrank); - if(root == myrank){ - printf("DCT-based solver is used\n"); - } - return 0; -} - -static int assign_input( - const domain_t * domain, - const size_t rkstep, - const double dt, - const fluid_t * fluid, - double * restrict rhs -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict ux = fluid->ux.data; - const double * restrict uy = fluid->uy.data; -#if NDIMS == 3 - const double * restrict uz = fluid->uz.data; -#endif - // normalise FFT beforehand -#if NDIMS == 2 - const double norm = 2. * domain->glsizes[0]; -#else - const double norm = 2. * domain->glsizes[0] * domain->glsizes[1]; -#endif - const double prefactor = 1. / (rkcoefs[rkstep][rk_g] * dt) / norm; -#if NDIMS == 2 - for(int cnt = 0, j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++, cnt++){ - const double dx = DXF(i ); - const double ux_xm = UX(i , j ); - const double ux_xp = UX(i+1, j ); - const double uy_ym = UY(i , j ); - const double uy_yp = UY(i , j+1); - rhs[cnt] = prefactor * ( - + (ux_xp - ux_xm) / dx - + (uy_yp - uy_ym) / dy - ); - } - } -#else - for(int cnt = 0, k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++, cnt++){ - const double dx = DXF(i ); - const double ux_xm = UX(i , j , k ); - const double ux_xp = UX(i+1, j , k ); - const double uy_ym = UY(i , j , k ); - const double uy_yp = UY(i , j+1, k ); - const double uz_zm = UZ(i , j , k ); - const double uz_zp = UZ(i , j , k+1); - rhs[cnt] = prefactor * ( - + (ux_xp - ux_xm) / dx - + (uy_yp - uy_ym) / dy - + (uz_zp - uz_zm) / dz - ); - } - } - } -#endif - return 0; -} - -static int extract_output( - const domain_t * domain, - const double * restrict rhs, - fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - double * restrict psi = fluid->psi.data; -#if NDIMS == 2 - for(int cnt = 0, j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++, cnt++){ - PSI(i, j) = rhs[cnt]; - } - } -#else - for(int cnt = 0, k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++, cnt++){ - PSI(i, j, k) = rhs[cnt]; - } - } - } -#endif - if(0 != fluid_update_boundaries_psi(domain, &fluid->psi)){ - return 1; - } - return 0; -} - -static int solve_linear_systems( - poisson_solver_t * poisson_solver -){ - // size of system (length) and how many such systems to be solved - // NOTE: although size_of_system is the same as tdm.get_size gives, - // repeat_for is different from what tdm.get_nrhs returns (=1) - // here repeat_for is the degree of freedom in the wavespace - const size_t size_of_system = poisson_solver->tdm_sizes[0]; - const size_t repeat_for = poisson_solver->tdm_sizes[1]; - // tri-diagonal matrix - tdm_info_t * tdm_info = poisson_solver->tdm_info; - double * restrict tdm_l = NULL; - double * restrict tdm_u = NULL; - double * restrict tdm_c = NULL; - tdm.get_l(tdm_info, &tdm_l); - tdm.get_u(tdm_info, &tdm_u); - tdm.get_c(tdm_info, &tdm_c); - // eigenvalues coming from Fourier projection - const double * restrict evals = poisson_solver->evals; -#if NDIMS == 2 - double * restrict rhs = poisson_solver->buf0; -#else - fftw_complex * restrict rhs = poisson_solver->buf0; -#endif - for(size_t m = 0; m < repeat_for; m++){ - // set center diagonal components | 3 - for(size_t n = 0; n < size_of_system; n++){ - tdm_c[n] = - tdm_l[n] - tdm_u[n] + evals[m]; - } - tdm.solve(tdm_info, rhs + m * size_of_system); - } - return 0; -} - -/** - * @brief compute scalar potential psi to correct velocity - * @param[in] domain : information about domain decomposition and size - * @param[in] rkstep : Runge-Kutta step - * @param[in] dt : time step size - * @param[in,out] fluid : velocity (in), scalar potential psi (out) - * @return : (success) 0 - * : (failure) 1 - */ -int fluid_compute_potential_dct( - const domain_t * domain, - const size_t rkstep, - const double dt, - fluid_t * fluid -){ - static poisson_solver_t poisson_solver = { - .is_initialised = false, - }; - // initialise Poisson solver | 7 - if(!poisson_solver.is_initialised){ - if(0 != init_poisson_solver(domain, &poisson_solver)){ - // failed to initialise Poisson solver - return 1; - } - } - // compute right-hand side of Poisson equation | 2 - // assigned to buf0 - assign_input(domain, rkstep, dt, fluid, poisson_solver.buf0); - // solve the equation - // project x to wave space | 4 - // f(x, y) -> f(k_x, y) - // f(x, y, z) -> f(k_x, y, z) - // from buf0 to buf1 - fftw_execute(poisson_solver.fftw_plan_x[0]); - // transpose real x1pencil to y1pencil | 6 - // from buf1 to buf0 - sdecomp.transpose.execute( - poisson_solver.r_transposer_x1_to_y1, - poisson_solver.buf1, - poisson_solver.buf0 - ); -#if NDIMS == 3 - // project y to wave space | 3 - // f(k_x, y, z) -> f(k_x, k_y, z) - // from buf0 to buf1 - fftw_execute(poisson_solver.fftw_plan_y[0]); - // transpose complex y1pencil to z1pencil | 6 - // from buf1 to buf0 - sdecomp.transpose.execute( - poisson_solver.c_transposer_y1_to_z1, - poisson_solver.buf1, - poisson_solver.buf0 - ); -#endif - // solve linear systems | 1 - solve_linear_systems(&poisson_solver); -#if NDIMS == 3 - // transpose complex z1pencil to y1pencil | 6 - // from buf0 to buf1 - sdecomp.transpose.execute( - poisson_solver.c_transposer_z1_to_y1, - poisson_solver.buf0, - poisson_solver.buf1 - ); - // project y to physical space | 3 - // f(k_x, k_y, z) -> f(k_x, y, z) - // from buf1 to buf0 - fftw_execute(poisson_solver.fftw_plan_y[1]); -#endif - // transpose real y1pencil to x1pencil | 6 - // from buf0 to buf1 - sdecomp.transpose.execute( - poisson_solver.r_transposer_y1_to_x1, - poisson_solver.buf0, - poisson_solver.buf1 - ); - // project x to physical space | 4 - // f(k_x, y) -> f(x, y) - // f(k_x, y, z) -> f(x, y, z) - // from buf1 to buf0 - fftw_execute(poisson_solver.fftw_plan_x[1]); - // buf0 to fluid->psi - if(0 != extract_output(domain, poisson_solver.buf0, fluid)){ - return 1; - } - return 0; -} - diff --git a/src/fluid/compute_potential/internal.h b/src/fluid/compute_potential/internal.h deleted file mode 100644 index d6b55e6f..00000000 --- a/src/fluid/compute_potential/internal.h +++ /dev/null @@ -1,25 +0,0 @@ -#if !defined(FLUID_COMPUTE_POTENTIAL_INTERNAL_H) -#define FLUID_COMPUTE_POTENTIAL_INTERNAL_H - -#include "domain.h" -#include "fluid.h" - -// compute scalar potential by solving Poisson equation -// (general-purpose version) -extern int fluid_compute_potential_dft( - const domain_t * domain, - const size_t rkstep, - const double dt, - fluid_t * fluid -); - -// compute scalar potential by solving Poisson equation -// (efficient version) -extern int fluid_compute_potential_dct( - const domain_t * domain, - const size_t rkstep, - const double dt, - fluid_t * fluid -); - -#endif // FLUID_COMPUTE_POTENTIAL_INTERNAL_H diff --git a/src/fluid/compute_potential/main.c b/src/fluid/compute_potential/main.c deleted file mode 100644 index f2a79099..00000000 --- a/src/fluid/compute_potential/main.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include "domain.h" -#include "fluid.h" -#include "fluid_solver.h" -#include "internal.h" - -int fluid_compute_potential( - const domain_t * domain, - const size_t rkstep, - const double dt, - fluid_t * fluid -){ - // check grid in x direction is uniform - // if it is, I can use more efficient algorithm - // to compute the scalar potential; - // otherwise a versatile version is adopted - static bool is_initialised = false; - static bool x_grid_is_uniform = false; - if(!is_initialised){ - if(0 != domain_check_x_grid_is_uniform(domain, &x_grid_is_uniform)){ - return 1; - } - is_initialised = true; - } - // use one of - // DCT version: fluid_compute_potential_dct - // DFT version: fluid_compute_potential_dft - // depending on the x grid configuration - if(x_grid_is_uniform){ - if(0 != fluid_compute_potential_dct(domain, rkstep, dt, fluid)){ - return 1; - } - }else{ - if(0 != fluid_compute_potential_dft(domain, rkstep, dt, fluid)){ - return 1; - } - } - return 0; -} - diff --git a/src/fluid/correct_velocity/internal.h b/src/fluid/correct/internal.h similarity index 100% rename from src/fluid/correct_velocity/internal.h rename to src/fluid/correct/internal.h diff --git a/src/fluid/correct_velocity/main.c b/src/fluid/correct/main.c similarity index 100% rename from src/fluid/correct_velocity/main.c rename to src/fluid/correct/main.c diff --git a/src/fluid/correct/ux.c b/src/fluid/correct/ux.c new file mode 100644 index 00000000..5a29074c --- /dev/null +++ b/src/fluid/correct/ux.c @@ -0,0 +1,63 @@ +#include "domain.h" +#include "fluid.h" +#include "fluid_solver.h" +#include "internal.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/fluid/ux.h" +#include "array_macros/fluid/psi.h" + +#if NDIMS == 2 +#define BEGIN \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 2; i <= isize; i++) { +#define END \ + } \ + } +#else +#define BEGIN \ + for (int k = 1; k <= ksize; k++) { \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 2; i <= isize; i++) { +#define END \ + } \ + } \ + } +#endif + +// correct x velocity | 35 +int fluid_correct_velocity_ux ( + const domain_t * domain, + const double prefactor, + fluid_t * fluid +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict hxxf = domain->hxxf; + const double * restrict psi = fluid->psi.data; + double * restrict ux = fluid->ux.data; + BEGIN + const double hx = HXXF(i ); +#if NDIMS == 2 + const double psi_xm = PSI(i-1, j ); + const double psi_xp = PSI(i , j ); + double * vel = &UX(i, j); +#else + const double psi_xm = PSI(i-1, j , k ); + const double psi_xp = PSI(i , j , k ); + double * vel = &UX(i, j, k); +#endif + *vel -= prefactor / hx * ( + - psi_xm + + psi_xp + ); + END + // update boundary and halo cells + if (0 != fluid_update_boundaries_ux(domain, &fluid->ux)) { + return 1; + } + return 0; +} + diff --git a/src/fluid/correct/uy.c b/src/fluid/correct/uy.c new file mode 100644 index 00000000..158ebbe2 --- /dev/null +++ b/src/fluid/correct/uy.c @@ -0,0 +1,61 @@ +#include "domain.h" +#include "fluid.h" +#include "fluid_solver.h" +#include "internal.h" +#include "array_macros/fluid/uy.h" +#include "array_macros/fluid/psi.h" + +#if NDIMS == 2 +#define BEGIN \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++) { +#define END \ + } \ + } +#else +#define BEGIN \ + for (int k = 1; k <= ksize; k++) { \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++) { +#define END \ + } \ + } \ + } +#endif + +// correct y velocity | 34 +int fluid_correct_velocity_uy ( + const domain_t * domain, + const double prefactor, + fluid_t * fluid +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double hy = domain->hy; + const double * restrict psi = fluid->psi.data; + double * restrict uy = fluid->uy.data; + BEGIN +#if NDIMS == 2 + const double psi_ym = PSI(i , j-1); + const double psi_yp = PSI(i , j ); + double * vel = &UY(i, j); +#else + const double psi_ym = PSI(i , j-1, k ); + const double psi_yp = PSI(i , j , k ); + double * vel = &UY(i, j, k); +#endif + *vel -= prefactor / hy * ( + - psi_ym + + psi_yp + ); + END + // update boundary and halo cells + if (0 != fluid_update_boundaries_uy(domain, &fluid->uy)) { + return 1; + } + return 0; +} + diff --git a/src/fluid/correct_velocity/uz.c b/src/fluid/correct/uz.c similarity index 50% rename from src/fluid/correct_velocity/uz.c rename to src/fluid/correct/uz.c index e3b7c511..98dd33bd 100644 --- a/src/fluid/correct_velocity/uz.c +++ b/src/fluid/correct/uz.c @@ -6,39 +6,32 @@ #include "array_macros/fluid/uz.h" #include "array_macros/fluid/psi.h" -/** - * @brief correct uz using scalar potential psi - * @param[in] domain : information about domain decomposition and size - * @param[in] prefactor : pre-factor in front of grad psi - * @param[in,out] fluid : scalar potential psi (in), uz (out) - * @return : error code - */ -int fluid_correct_velocity_uz( +// correct z velocity | 29 +int fluid_correct_velocity_uz ( const domain_t * domain, const double prefactor, fluid_t * fluid -){ +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; const int ksize = domain->mysizes[2]; - const double dz = domain->dz; + const double hz = domain->hz; const double * restrict psi = fluid->psi.data; double * restrict uz = fluid->uz.data; - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // correct z velocity | 6 + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { const double psi_zm = PSI(i , j , k-1); const double psi_zp = PSI(i , j , k ); - UZ(i, j, k) -= prefactor / dz * ( - + psi_zp + UZ(i, j, k) -= prefactor / hz * ( - psi_zm + + psi_zp ); } } } - // update boundary and halo cells | 3 - if(0 != fluid_update_boundaries_uz(domain, &fluid->uz)){ + // update boundary and halo cells + if (0 != fluid_update_boundaries_uz(domain, &fluid->uz)) { return 1; } return 0; diff --git a/src/fluid/correct_velocity/ux.c b/src/fluid/correct_velocity/ux.c deleted file mode 100644 index eb091ab2..00000000 --- a/src/fluid/correct_velocity/ux.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "domain.h" -#include "fluid.h" -#include "fluid_solver.h" -#include "internal.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/ux.h" -#include "array_macros/fluid/psi.h" - -/** - * @brief correct ux using scalar potential psi - * @param[in] domain : information about domain decomposition and size - * @param[in] prefactor : pre-factor in front of grad psi - * @param[in,out] fluid : scalar potential psi (in), ux (out) - * @return : error code - */ -int fluid_correct_velocity_ux( - const domain_t * domain, - const double prefactor, - fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxc = domain->dxc; - const double * restrict psi = fluid->psi.data; - double * restrict ux = fluid->ux.data; -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 2; i <= isize; i++){ - // correct x velocity | 7 - const double dx = DXC(i ); - const double psi_xm = PSI(i-1, j ); - const double psi_xp = PSI(i , j ); - UX(i, j) -= prefactor / dx * ( - + psi_xp - - psi_xm - ); - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 2; i <= isize; i++){ - // correct x velocity | 7 - const double dx = DXC(i ); - const double psi_xm = PSI(i-1, j , k ); - const double psi_xp = PSI(i , j , k ); - UX(i, j, k) -= prefactor / dx * ( - + psi_xp - - psi_xm - ); - } - } - } -#endif - // update boundary and halo cells | 3 - if(0 != fluid_update_boundaries_ux(domain, &fluid->ux)){ - return 1; - } - return 0; -} - diff --git a/src/fluid/correct_velocity/uy.c b/src/fluid/correct_velocity/uy.c deleted file mode 100644 index bec57fd0..00000000 --- a/src/fluid/correct_velocity/uy.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "domain.h" -#include "fluid.h" -#include "fluid_solver.h" -#include "internal.h" -#include "array_macros/fluid/uy.h" -#include "array_macros/fluid/psi.h" - -/** - * @brief correct uy using scalar potential psi - * @param[in] domain : information about domain decomposition and size - * @param[in] prefactor : pre-factor in front of grad psi - * @param[in,out] fluid : scalar potential psi (in), uy (out) - * @return : error code - */ -int fluid_correct_velocity_uy( - const domain_t * domain, - const double prefactor, - fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double dy = domain->dy; - const double * restrict psi = fluid->psi.data; - double * restrict uy = fluid->uy.data; -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // correct y velocity | 6 - const double psi_ym = PSI(i , j-1); - const double psi_yp = PSI(i , j ); - UY(i, j) -= prefactor / dy * ( - + psi_yp - - psi_ym - ); - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // correct y velocity | 6 - const double psi_ym = PSI(i , j-1, k ); - const double psi_yp = PSI(i , j , k ); - UY(i, j, k) -= prefactor / dy * ( - + psi_yp - - psi_ym - ); - } - } - } -#endif - // update boundary and halo cells | 3 - if(0 != fluid_update_boundaries_uy(domain, &fluid->uy)){ - return 1; - } - return 0; -} - diff --git a/src/fluid/init.c b/src/fluid/init.c index 8d11a8ab..1fc44622 100644 --- a/src/fluid/init.c +++ b/src/fluid/init.c @@ -1,5 +1,6 @@ #include #include "param.h" +#include "runge_kutta.h" #include "memory.h" #include "config.h" #include "domain.h" @@ -21,52 +22,44 @@ #endif #include "array_macros/fluid/srct.h" -/** - * @brief allocate members - * @param[in] domain : information about domain decomposition and size - * @param[out] fluid : structure storing flow fields and auxiliary buffers - * @return : error code - */ -static int allocate( +static int allocate ( const domain_t * domain, fluid_t * fluid -){ +) { // velocity - if(0 != array.prepare(domain, UX_NADDS, sizeof(double), &fluid->ux )) return 1; - if(0 != array.prepare(domain, UY_NADDS, sizeof(double), &fluid->uy )) return 1; + if (0 != array.create(domain, UX_NADDS, sizeof(double), &fluid->ux )) return 1; + if (0 != array.create(domain, UY_NADDS, sizeof(double), &fluid->uy )) return 1; #if NDIMS == 3 - if(0 != array.prepare(domain, UZ_NADDS, sizeof(double), &fluid->uz )) return 1; + if (0 != array.create(domain, UZ_NADDS, sizeof(double), &fluid->uz )) return 1; #endif // pressure and scalar potential - if(0 != array.prepare(domain, P_NADDS, sizeof(double), &fluid->p )) return 1; - if(0 != array.prepare(domain, PSI_NADDS, sizeof(double), &fluid->psi)) return 1; + if (0 != array.create(domain, P_NADDS, sizeof(double), &fluid->p )) return 1; + if (0 != array.create(domain, PSI_NADDS, sizeof(double), &fluid->psi)) return 1; // temperature - if(0 != array.prepare(domain, T_NADDS, sizeof(double), &fluid->t)) return 1; + if (0 != array.create(domain, T_NADDS, sizeof(double), &fluid->t)) return 1; // Runge-Kutta source terms - for(size_t n = 0; n < 3; n++){ - if(0 != array.prepare(domain, SRCUX_NADDS, sizeof(double), &fluid->srcux[n])) return 1; - if(0 != array.prepare(domain, SRCUY_NADDS, sizeof(double), &fluid->srcuy[n])) return 1; + for (size_t n = 0; n < RKNBUFFERS; n++) { + if (0 != array.create(domain, SRCUX_NADDS, sizeof(double), fluid->srcux + n)) return 1; + if (0 != array.create(domain, SRCUY_NADDS, sizeof(double), fluid->srcuy + n)) return 1; #if NDIMS == 3 - if(0 != array.prepare(domain, SRCUZ_NADDS, sizeof(double), &fluid->srcuz[n])) return 1; + if (0 != array.create(domain, SRCUZ_NADDS, sizeof(double), fluid->srcuz + n)) return 1; #endif - if(0 != array.prepare(domain, SRCT_NADDS, sizeof(double), &fluid->srct [n])) return 1; + if (0 != array.create(domain, SRCT_NADDS, sizeof(double), fluid->srct + n)) return 1; } return 0; } -static void report( +static int report ( const sdecomp_info_t * info, const fluid_t * fluid -){ +) { const int root = 0; int myrank = root; sdecomp.get_comm_rank(info, &myrank); - if(root == myrank){ + if (root == myrank) { printf("FLUID\n"); printf("\tRa: % .7e\n", fluid->Ra); printf("\tPr: % .7e\n", fluid->Pr); - printf("\tMomentum diffusivity: % .7e\n", fluid->m_dif); - printf("\tTemperature diffusivity: % .7e\n", fluid->t_dif); printf("\tdiffusive treatment in x: %s\n", param_m_implicit_x ? "implicit" : "explicit"); printf("\tdiffusive treatment in y: %s\n", param_m_implicit_y ? "implicit" : "explicit"); #if NDIMS == 3 @@ -74,6 +67,7 @@ static void report( #endif fflush(stdout); } + return 0; } /** @@ -88,30 +82,28 @@ int fluid_init( const char dirname_ic[], const domain_t * domain, fluid_t * fluid -){ +) { // allocate arrays | 1 - if(0 != allocate(domain, fluid)) return 1; + if (0 != allocate(domain, fluid)) return 1; // load flow fields | 7 - if(0 != array.load(domain, dirname_ic, "ux", fileio.npy_double, &fluid->ux)) return 1; - if(0 != array.load(domain, dirname_ic, "uy", fileio.npy_double, &fluid->uy)) return 1; + if (0 != array.load(domain, dirname_ic, "ux", fileio.npy_double, &fluid->ux)) return 1; + if (0 != array.load(domain, dirname_ic, "uy", fileio.npy_double, &fluid->uy)) return 1; #if NDIMS == 3 - if(0 != array.load(domain, dirname_ic, "uz", fileio.npy_double, &fluid->uz)) return 1; + if (0 != array.load(domain, dirname_ic, "uz", fileio.npy_double, &fluid->uz)) return 1; #endif - if(0 != array.load(domain, dirname_ic, "p", fileio.npy_double, &fluid-> p)) return 1; - if(0 != array.load(domain, dirname_ic, "t", fileio.npy_double, &fluid-> t)) return 1; + if (0 != array.load(domain, dirname_ic, "p", fileio.npy_double, &fluid-> p)) return 1; + if (0 != array.load(domain, dirname_ic, "t", fileio.npy_double, &fluid-> t)) return 1; // impose boundary conditions and communicate halo cells | 7 - if(0 != fluid_update_boundaries_ux(domain, &fluid->ux)) return 1; - if(0 != fluid_update_boundaries_uy(domain, &fluid->uy)) return 1; + if (0 != fluid_update_boundaries_ux(domain, &fluid->ux)) return 1; + if (0 != fluid_update_boundaries_uy(domain, &fluid->uy)) return 1; #if NDIMS == 3 - if(0 != fluid_update_boundaries_uz(domain, &fluid->uz)) return 1; + if (0 != fluid_update_boundaries_uz(domain, &fluid->uz)) return 1; #endif - if(0 != fluid_update_boundaries_p(domain, &fluid->p)) return 1; - if(0 != fluid_update_boundaries_t(domain, &fluid->t)) return 1; - // compute diffusivities | 4 - if(0 != config.get_double("Pr", &fluid->Pr)) return 1; - if(0 != config.get_double("Ra", &fluid->Ra)) return 1; - fluid->m_dif = 1. * sqrt(fluid->Pr) / sqrt(fluid->Ra); - fluid->t_dif = 1. / sqrt(fluid->Pr) / sqrt(fluid->Ra); + if (0 != fluid_update_boundaries_p(domain, &fluid->p)) return 1; + if (0 != fluid_update_boundaries_t(domain, &fluid->t)) return 1; + // load diffusivities | 2 + if (0 != config.get_double("Pr", &fluid->Pr)) return 1; + if (0 != config.get_double("Ra", &fluid->Ra)) return 1; report(domain->info, fluid); return 0; } diff --git a/src/fluid/integrate/couple_external_force.c b/src/fluid/integrate/couple_external_force.c deleted file mode 100644 index a320fc75..00000000 --- a/src/fluid/integrate/couple_external_force.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "param.h" -#include "domain.h" -#include "fluid.h" -#include "fluid_solver.h" -#include "internal.h" - -/** - * @brief couple external forces - * @param[in] domain : information related to MPI domain decomposition - * @param[in,out] fluid : buoyancy force (in), x RK source term (in/out) - * @return : error code - */ -int fluid_couple_external_force( - const domain_t * domain, - fluid_t * fluid -){ - // add buoyancy in x when spcified - if(param_add_buoyancy){ - if(0 != buoyancy_ux(domain, fluid)){ - return 1; - } - } - return 0; -} - diff --git a/src/fluid/integrate/predict_field.c b/src/fluid/integrate/predict_field.c deleted file mode 100644 index ef876e2e..00000000 --- a/src/fluid/integrate/predict_field.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "domain.h" -#include "fluid.h" -#include "fluid_solver.h" -#include "internal.h" - -/** - * @brief update fields using the previously-computed RK source terms - * @param[in] domain : information related to MPI domain decomposition - * @param[in] rkstep : Runge-Kutta step - * @param[in] dt : time step size - * @param[in,out] fluid : RK source terms (in), flow field (out) - * @return : error code - */ -int fluid_predict_field( - const domain_t * domain, - const size_t rkstep, - const double dt, - fluid_t * fluid -){ - if(0 != update_ux(domain, rkstep, dt, fluid)){ - return 1; - } - if(0 != update_uy(domain, rkstep, dt, fluid)){ - return 1; - } -#if NDIMS == 3 - if(0 != update_uz(domain, rkstep, dt, fluid)){ - return 1; - } -#endif - if(0 != update_t (domain, rkstep, dt, fluid)){ - return 1; - } - return 0; -} - diff --git a/src/fluid/integrate/t.c b/src/fluid/integrate/t.c deleted file mode 100644 index 6ac4e86f..00000000 --- a/src/fluid/integrate/t.c +++ /dev/null @@ -1,530 +0,0 @@ -#include "param.h" -#include "memory.h" -#include "runge_kutta.h" -#include "linear_system.h" -#include "tdm.h" -#include "domain.h" -#include "fluid.h" -#include "fluid_solver.h" -#include "internal.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/ux.h" -#include "array_macros/fluid/uy.h" -#if NDIMS == 3 -#include "array_macros/fluid/uz.h" -#endif -#include "array_macros/fluid/t.h" - -// store approximation of laplacian -typedef double laplacian_t[3]; - -typedef struct { - bool is_initialised; - laplacian_t * lapx; - laplacian_t lapy; -#if NDIMS == 3 - laplacian_t lapz; -#endif -} laplacians_t; - -static laplacians_t laplacians = { - .is_initialised = false, -}; - -// [1 : isize] -#define LAPX(I) lapx[(I)-1] - -static int init_lap( - const domain_t * domain -){ - // Laplacian w.r.t. temp in x | 14 - { - const size_t isize = domain->glsizes[0]; - const double * dxf = domain->dxf; - const double * dxc = domain->dxc; - laplacians.lapx = memory_calloc(isize, sizeof(laplacian_t)); - for(size_t i = 1; i <= isize; i++){ - const double l = 1. / DXC(i ) / DXF(i ); - const double u = 1. / DXC(i+1) / DXF(i ); - const double c = - l - u; - laplacians.LAPX(i)[0] = l; - laplacians.LAPX(i)[1] = c; - laplacians.LAPX(i)[2] = u; - } - } - // Laplacian in y | 6 - { - const double dy = domain->dy; - laplacians.lapy[0] = + 1. / dy / dy; - laplacians.lapy[1] = - 2. / dy / dy; - laplacians.lapy[2] = + 1. / dy / dy; - } -#if NDIMS == 3 - // Laplacian in z | 6 - { - const double dz = domain->dz; - laplacians.lapz[0] = + 1. / dz / dz; - laplacians.lapz[1] = - 2. / dz / dz; - laplacians.lapz[2] = + 1. / dz / dz; - } -#endif - laplacians.is_initialised = true; - return 0; -} - -#if NDIMS == 2 -#define BEGIN \ - for(int cnt = 0, j = 1; j <= jsize; j++){ \ - for(int i = 1; i <= isize; i++, cnt++){ -#define END \ - } \ - } -#else -#define BEGIN \ - for(int cnt = 0, k = 1; k <= ksize; k++){ \ - for(int j = 1; j <= jsize; j++){ \ - for(int i = 1; i <= isize; i++, cnt++){ -#define END \ - } \ - } \ - } -#endif - -static int advection_x( - const domain_t * domain, - const double * restrict t, - const double * restrict ux, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; -#if NDIMS == 2 - BEGIN - // T is transported by ux | 7 - const double l = + 0.5 / DXF(i ) * UX(i , j ); - const double u = - 0.5 / DXF(i ) * UX(i+1, j ); - const double c = - l - u; - src[cnt] += - + l * T(i-1, j ) - + c * T(i , j ) - + u * T(i+1, j ); - END -#else - BEGIN - // T is transported by ux | 7 - const double l = + 0.5 / DXF(i ) * UX(i , j , k ); - const double u = - 0.5 / DXF(i ) * UX(i+1, j , k ); - const double c = - l - u; - src[cnt] += - + l * T(i-1, j , k ) - + c * T(i , j , k ) - + u * T(i+1, j , k ); - END -#endif - return 0; -} - -static int advection_y( - const domain_t * domain, - const double * restrict t, - const double * restrict uy, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double dy = domain->dy; -#if NDIMS == 2 - BEGIN - // T is transported by uy | 7 - const double l = + 0.5 / dy * UY(i , j ); - const double u = - 0.5 / dy * UY(i , j+1); - const double c = - l - u; - src[cnt] += - + l * T(i , j-1) - + c * T(i , j ) - + u * T(i , j+1); - END -#else - BEGIN - // T is transported by uy | 7 - const double l = + 0.5 / dy * UY(i , j , k ); - const double u = - 0.5 / dy * UY(i , j+1, k ); - const double c = - l - u; - src[cnt] += - + l * T(i , j-1, k ) - + c * T(i , j , k ) - + u * T(i , j+1, k ); - END -#endif - return 0; -} - -#if NDIMS == 3 -static int advection_z( - const domain_t * domain, - const double * restrict t, - const double * restrict uz, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double dz = domain->dz; - BEGIN - // T is transported by uz | 7 - const double l = + 0.5 / dz * UZ(i , j , k ); - const double u = - 0.5 / dz * UZ(i , j , k+1); - const double c = - l - u; - src[cnt] += - + l * T(i , j , k-1) - + c * T(i , j , k ) - + u * T(i , j , k+1); - END - return 0; -} -#endif - -static int diffusion_x( - const domain_t * domain, - const double diffusivity, - const double * restrict t, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const laplacian_t * restrict lapx = laplacians.lapx; -#if NDIMS == 2 - BEGIN - // T is diffused in x | 5 - src[cnt] += diffusivity * ( - + LAPX(i)[0] * T(i-1, j ) - + LAPX(i)[1] * T(i , j ) - + LAPX(i)[2] * T(i+1, j ) - ); - END -#else - BEGIN - // T is diffused in x | 5 - src[cnt] += diffusivity * ( - + LAPX(i)[0] * T(i-1, j , k ) - + LAPX(i)[1] * T(i , j , k ) - + LAPX(i)[2] * T(i+1, j , k ) - ); - END -#endif - return 0; -} - -static int diffusion_y( - const domain_t * domain, - const double diffusivity, - const double * restrict t, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const laplacian_t * restrict lapy = &laplacians.lapy; -#if NDIMS == 2 - BEGIN - // T is diffused in y | 5 - src[cnt] += diffusivity * ( - + (*lapy)[0] * T(i , j-1) - + (*lapy)[1] * T(i , j ) - + (*lapy)[2] * T(i , j+1) - ); - END -#else - BEGIN - // T is diffused in y | 5 - src[cnt] += diffusivity * ( - + (*lapy)[0] * T(i , j-1, k ) - + (*lapy)[1] * T(i , j , k ) - + (*lapy)[2] * T(i , j+1, k ) - ); - END -#endif - return 0; -} - -#if NDIMS == 3 -static int diffusion_z( - const domain_t * domain, - const double diffusivity, - const double * restrict t, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const laplacian_t * restrict lapz = &laplacians.lapz; - BEGIN - // T is diffused in z | 5 - src[cnt] += diffusivity * ( - + (*lapz)[0] * T(i , j , k-1) - + (*lapz)[1] * T(i , j , k ) - + (*lapz)[2] * T(i , j , k+1) - ); - END - return 0; -} -#endif - -/** - * @brief comute right-hand-side of Runge-Kutta scheme - * @param[in] domain : information related to domain decomposition and size - * @param[in,out] fluid : n-step flow field (in), RK source terms (out) - * @return : error code - */ -int compute_rhs_t( - const domain_t * domain, - fluid_t * fluid -){ - if(!laplacians.is_initialised){ - if(0 != init_lap(domain)){ - return 1; - } - } - const double * restrict ux = fluid->ux.data; - const double * restrict uy = fluid->uy.data; -#if NDIMS == 3 - const double * restrict uz = fluid->uz.data; -#endif - const double * restrict t = fluid-> t.data; - double * restrict srca = fluid->srct[rk_a].data; - double * restrict srcg = fluid->srct[rk_g].data; - const double diffusivity = fluid->t_dif; - // advective contributions, always explicit | 5 - advection_x(domain, t, ux, srca); - advection_y(domain, t, uy, srca); -#if NDIMS == 3 - advection_z(domain, t, uz, srca); -#endif - // diffusive contributions, can be explicit or implicit | 5 - diffusion_x(domain, diffusivity, t, param_t_implicit_x ? srcg : srca); - diffusion_y(domain, diffusivity, t, param_t_implicit_y ? srcg : srca); -#if NDIMS == 3 - diffusion_z(domain, diffusivity, t, param_t_implicit_z ? srcg : srca); -#endif - return 0; -} - -static int solve_in_x( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_x; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapx = laplacians.lapx; - for(int i = 0; i < size; i++){ - tdm_l[i] = - prefactor * lapx[i][0]; - tdm_c[i] = 1. - prefactor * lapx[i][1]; - tdm_u[i] = - prefactor * lapx[i][2]; - } - tdm.solve(tdm_info, linear_system->x1pncl); - return 0; -} - -static int solve_in_y( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_y; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapy = &laplacians.lapy; - for(int j = 0; j < size; j++){ - tdm_l[j] = - prefactor * (*lapy)[0]; - tdm_c[j] = 1. - prefactor * (*lapy)[1]; - tdm_u[j] = - prefactor * (*lapy)[2]; - } - tdm.solve(tdm_info, linear_system->y1pncl); - return 0; -} - -#if NDIMS == 3 -static int solve_in_z( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_z; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapz = &laplacians.lapz; - for(int k = 0; k < size; k++){ - tdm_l[k] = - prefactor * (*lapz)[0]; - tdm_c[k] = 1. - prefactor * (*lapz)[1]; - tdm_u[k] = - prefactor * (*lapz)[2]; - } - tdm.solve(tdm_info, linear_system->z2pncl); - return 0; -} -#endif - -/** - * @brief update temperature field - * @param[in] domain : information about domain decomposition and size - * @param[in] rkstep : Runge-Kutta step - * @param[in] dt : time step size - * @param[in,out] fluid : Runge-Kutta source terms (in), temperature (out) - * @return : error code - */ -int update_t( - const domain_t * domain, - const size_t rkstep, - const double dt, - fluid_t * fluid -){ - static linear_system_t linear_system = { - .is_initialised = false, - }; - if(!linear_system.is_initialised){ - // if not initialised yet, prepare linear solver - // for implicit diffusive term treatment - const bool implicit[NDIMS] = { - param_t_implicit_x, - param_t_implicit_y, -#if NDIMS == 3 - param_t_implicit_z, -#endif - }; - const size_t glsizes[NDIMS] = { - domain->glsizes[0], - domain->glsizes[1], -#if NDIMS == 3 - domain->glsizes[2], -#endif - }; - if(0 != linear_system_init(domain->info, implicit, glsizes, &linear_system)){ - return 1; - } - } - // compute increments | 25 - { - const double coef_a = rkcoefs[rkstep][rk_a]; - const double coef_b = rkcoefs[rkstep][rk_b]; - const double coef_g = rkcoefs[rkstep][rk_g]; - const double * restrict srcta = fluid->srct[rk_a].data; - const double * restrict srctb = fluid->srct[rk_b].data; - const double * restrict srctg = fluid->srct[rk_g].data; - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - double * restrict dtemp = linear_system.x1pncl; -#if NDIMS == 2 - const size_t nitems = isize * jsize; -#else - const size_t nitems = isize * jsize * ksize; -#endif - for(size_t n = 0; n < nitems; n++){ - dtemp[n] = - + coef_a * dt * srcta[n] - + coef_b * dt * srctb[n] - + coef_g * dt * srctg[n]; - } - } - // gamma dt diffusivity / 2 | 2 - const double prefactor = - 0.5 * rkcoefs[rkstep][rk_g] * dt * fluid->t_dif; - // solve linear systems in x - if(param_t_implicit_x){ - solve_in_x( - prefactor, - &linear_system - ); - } - // solve linear systems in y - if(param_t_implicit_y){ - sdecomp.transpose.execute( - linear_system.transposer_x1_to_y1, - linear_system.x1pncl, - linear_system.y1pncl - ); - solve_in_y( - prefactor, - &linear_system - ); - sdecomp.transpose.execute( - linear_system.transposer_y1_to_x1, - linear_system.y1pncl, - linear_system.x1pncl - ); - } -#if NDIMS == 3 - // solve linear systems in z - if(param_t_implicit_z){ - sdecomp.transpose.execute( - linear_system.transposer_x1_to_z2, - linear_system.x1pncl, - linear_system.z2pncl - ); - solve_in_z( - prefactor, - &linear_system - ); - sdecomp.transpose.execute( - linear_system.transposer_z2_to_x1, - linear_system.z2pncl, - linear_system.x1pncl - ); - } -#endif - // the field is actually updated here | 21 - { - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dtemp = linear_system.x1pncl; - double * restrict t = fluid->t.data; -#if NDIMS == 2 - BEGIN - T(i, j) += dtemp[cnt]; - END -#else - BEGIN - T(i, j, k) += dtemp[cnt]; - END -#endif - if(0 != fluid_update_boundaries_t(domain, &fluid->t)){ - return 1; - } - } - return 0; -} - diff --git a/src/fluid/integrate/ux.c b/src/fluid/integrate/ux.c deleted file mode 100644 index b401ca7c..00000000 --- a/src/fluid/integrate/ux.c +++ /dev/null @@ -1,616 +0,0 @@ -#include "param.h" -#include "memory.h" -#include "runge_kutta.h" -#include "linear_system.h" -#include "tdm.h" -#include "domain.h" -#include "fluid.h" -#include "fluid_solver.h" -#include "internal.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/ux.h" -#include "array_macros/fluid/uy.h" -#if NDIMS == 3 -#include "array_macros/fluid/uz.h" -#endif -#include "array_macros/fluid/p.h" -#include "array_macros/fluid/t.h" - -// store approximation of laplacian -typedef double laplacian_t[3]; - -typedef struct { - bool is_initialised; - laplacian_t * lapx; - laplacian_t lapy; -#if NDIMS == 3 - laplacian_t lapz; -#endif -} laplacians_t; - -static laplacians_t laplacians = { - .is_initialised = false, -}; - -// [2 : isize] -#define LAPX(I) lapx[(I)-2] - -static int init_lap( - const domain_t * domain -){ - // Laplacian w.r.t. ux in x | 14 - { - const size_t isize = domain->glsizes[0]; - const double * dxf = domain->dxf; - const double * dxc = domain->dxc; - laplacians.lapx = memory_calloc(isize - 1, sizeof(laplacian_t)); - for(size_t i = 2; i <= isize; i++){ - const double l = 1. / DXF(i-1) / DXC(i ); - const double u = 1. / DXF(i ) / DXC(i ); - const double c = - l - u; - laplacians.LAPX(i)[0] = l; - laplacians.LAPX(i)[1] = c; - laplacians.LAPX(i)[2] = u; - } - } - // Laplacian in y | 6 - { - const double dy = domain->dy; - laplacians.lapy[0] = + 1. / dy / dy; - laplacians.lapy[1] = - 2. / dy / dy; - laplacians.lapy[2] = + 1. / dy / dy; - } -#if NDIMS == 3 - // Laplacian in z | 6 - { - const double dz = domain->dz; - laplacians.lapz[0] = + 1. / dz / dz; - laplacians.lapz[1] = - 2. / dz / dz; - laplacians.lapz[2] = + 1. / dz / dz; - } -#endif - laplacians.is_initialised = true; - return 0; -} - -#if NDIMS == 2 -#define BEGIN \ - for(int cnt = 0, j = 1; j <= jsize; j++){ \ - for(int i = 2; i <= isize; i++, cnt++){ -#define END \ - } \ - } -#else -#define BEGIN \ - for(int cnt = 0, k = 1; k <= ksize; k++){ \ - for(int j = 1; j <= jsize; j++){ \ - for(int i = 2; i <= isize; i++, cnt++){ -#define END \ - } \ - } \ - } -#endif - -static int advection_x( - const domain_t * domain, - const double * restrict ux, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxc = domain->dxc; -#if NDIMS == 2 - BEGIN - // ux is transported by ux | 9 - const double ux_l = + 0.5 * UX(i-1, j ) + 0.5 * UX(i , j ); - const double ux_u = + 0.5 * UX(i , j ) + 0.5 * UX(i+1, j ); - const double l = + 0.5 / DXC(i ) * ux_l; - const double u = - 0.5 / DXC(i ) * ux_u; - const double c = - l - u; - src[cnt] += - + l * UX(i-1, j ) - + c * UX(i , j ) - + u * UX(i+1, j ); - END -#else - BEGIN - // ux is transported by ux | 9 - const double ux_l = + 0.5 * UX(i-1, j , k ) + 0.5 * UX(i , j , k ); - const double ux_u = + 0.5 * UX(i , j , k ) + 0.5 * UX(i+1, j , k ); - const double l = + 0.5 / DXC(i ) * ux_l; - const double u = - 0.5 / DXC(i ) * ux_u; - const double c = - l - u; - src[cnt] += - + l * UX(i-1, j , k ) - + c * UX(i , j , k ) - + u * UX(i+1, j , k ); - END -#endif - return 0; -} - -static int advection_y( - const domain_t * domain, - const double * restrict ux, - const double * restrict uy, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; -#if NDIMS == 2 - BEGIN - // ux is transported by uy | 11 - const double w_xm = 0.5 * DXF(i-1) / DXC(i ); - const double w_xp = 0.5 * DXF(i ) / DXC(i ); - const double uy_l = w_xm * UY(i-1, j ) + w_xp * UY(i , j ); - const double uy_u = w_xm * UY(i-1, j+1) + w_xp * UY(i , j+1); - const double l = + 0.5 / dy * uy_l; - const double u = - 0.5 / dy * uy_u; - const double c = - l - u; - src[cnt] += - + l * UX(i , j-1) - + c * UX(i , j ) - + u * UX(i , j+1); - END -#else - BEGIN - // ux is transported by uy | 11 - const double w_xm = 0.5 * DXF(i-1) / DXC(i ); - const double w_xp = 0.5 * DXF(i ) / DXC(i ); - const double uy_l = w_xm * UY(i-1, j , k ) + w_xp * UY(i , j , k ); - const double uy_u = w_xm * UY(i-1, j+1, k ) + w_xp * UY(i , j+1, k ); - const double l = + 0.5 / dy * uy_l; - const double u = - 0.5 / dy * uy_u; - const double c = - l - u; - src[cnt] += - + l * UX(i , j-1, k ) - + c * UX(i , j , k ) - + u * UX(i , j+1, k ); - END -#endif - return 0; -} - -#if NDIMS == 3 -static int advection_z( - const domain_t * domain, - const double * restrict ux, - const double * restrict uz, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double * restrict dxf = domain->dxf; - const double * restrict dxc = domain->dxc; - const double dz = domain->dz; - BEGIN - // ux is transported by uz | 11 - const double w_xm = 0.5 * DXF(i-1) / DXC(i ); - const double w_xp = 0.5 * DXF(i ) / DXC(i ); - const double uz_l = w_xm * UZ(i-1, j , k ) + w_xp * UZ(i , j , k ); - const double uz_u = w_xm * UZ(i-1, j , k+1) + w_xp * UZ(i , j , k+1); - const double l = + 0.5 / dz * uz_l; - const double u = - 0.5 / dz * uz_u; - const double c = - l - u; - src[cnt] += - + l * UX(i , j , k-1) - + c * UX(i , j , k ) - + u * UX(i , j , k+1); - END - return 0; -} -#endif - -static int diffusion_x( - const domain_t * domain, - const double diffusivity, - const double * restrict ux, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const laplacian_t * restrict lapx = laplacians.lapx; -#if NDIMS == 2 - BEGIN - // ux is diffused in x | 5 - src[cnt] += diffusivity * ( - + LAPX(i)[0] * UX(i-1, j ) - + LAPX(i)[1] * UX(i , j ) - + LAPX(i)[2] * UX(i+1, j ) - ); - END -#else - BEGIN - // ux is diffused in x | 5 - src[cnt] += diffusivity * ( - + LAPX(i)[0] * UX(i-1, j , k ) - + LAPX(i)[1] * UX(i , j , k ) - + LAPX(i)[2] * UX(i+1, j , k ) - ); - END -#endif - return 0; -} - -static int diffusion_y( - const domain_t * domain, - const double diffusivity, - const double * restrict ux, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const laplacian_t * restrict lapy = &laplacians.lapy; -#if NDIMS == 2 - BEGIN - // ux is diffused in y | 5 - src[cnt] += diffusivity * ( - + (*lapy)[0] * UX(i , j-1) - + (*lapy)[1] * UX(i , j ) - + (*lapy)[2] * UX(i , j+1) - ); - END -#else - BEGIN - // ux is diffused in y | 5 - src[cnt] += diffusivity * ( - + (*lapy)[0] * UX(i , j-1, k ) - + (*lapy)[1] * UX(i , j , k ) - + (*lapy)[2] * UX(i , j+1, k ) - ); - END -#endif - return 0; -} - -#if NDIMS == 3 -static int diffusion_z( - const domain_t * domain, - const double diffusivity, - const double * restrict ux, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const laplacian_t * restrict lapz = &laplacians.lapz; - BEGIN - // ux is diffused in z | 5 - src[cnt] += diffusivity * ( - + (*lapz)[0] * UX(i , j , k-1) - + (*lapz)[1] * UX(i , j , k ) - + (*lapz)[2] * UX(i , j , k+1) - ); - END - return 0; -} -#endif - -static int pressure( - const domain_t * domain, - const double * restrict p, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxc = domain->dxc; -#if NDIMS == 2 - BEGIN - src[cnt] -= 1. / DXC(i ) * ( - - P(i-1, j ) - + P(i , j ) - ); - END -#else - BEGIN - src[cnt] -= 1. / DXC(i ) * ( - - P(i-1, j , k ) - + P(i , j , k ) - ); - END -#endif - return 0; -} - -/** - * @brief comute right-hand-side of Runge-Kutta scheme of ux - * @param[in] domain : information related to MPI domain decomposition - * @param[in,out] fluid : n-step flow field (in), RK source terms (inout) - * @return : error code - */ -int compute_rhs_ux( - const domain_t * domain, - fluid_t * fluid -){ - if(!laplacians.is_initialised){ - if(0 != init_lap(domain)){ - return 1; - } - } - const double * restrict ux = fluid->ux.data; - const double * restrict uy = fluid->uy.data; -#if NDIMS == 3 - const double * restrict uz = fluid->uz.data; -#endif - const double * restrict p = fluid-> p.data; - double * restrict srca = fluid->srcux[rk_a].data; - double * restrict srcg = fluid->srcux[rk_g].data; - const double diffusivity = fluid->m_dif; - // advective contributions, always explicit | 5 - advection_x(domain, ux, srca); - advection_y(domain, ux, uy, srca); -#if NDIMS == 3 - advection_z(domain, ux, uz, srca); -#endif - // diffusive contributions, can be explicit or implicit | 5 - diffusion_x(domain, diffusivity, ux, param_m_implicit_x ? srcg : srca); - diffusion_y(domain, diffusivity, ux, param_m_implicit_y ? srcg : srca); -#if NDIMS == 3 - diffusion_z(domain, diffusivity, ux, param_m_implicit_z ? srcg : srca); -#endif - // pressure-gradient contribution, always implicit - pressure(domain, p, srcg); - return 0; -} - -/** - * @brief add buoyancy force (Boussinesq approximation) - * @param[in] domain : information related to MPI domain decomposition - * @param[in,out] fluid : temperature field (in), RK source term (inout) - * @return : error code - */ -int buoyancy_ux( - const domain_t * domain, - fluid_t * fluid -){ - const double * restrict t = fluid->t.data; - double * restrict src = fluid->srcux[rk_a].data; - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - // NOTE: use arithmetic average, not volume average - // to achieve the discrete energy balance -#if NDIMS == 2 - BEGIN - src[cnt] += - + 0.5 * T(i-1, j ) - + 0.5 * T(i , j ); - END -#else - BEGIN - src[cnt] += - + 0.5 * T(i-1, j , k ) - + 0.5 * T(i , j , k ); - END -#endif - return 0; -} - -static int solve_in_x( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_x; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapx = laplacians.lapx; - for(int i = 0; i < size; i++){ - tdm_l[i] = - prefactor * lapx[i][0]; - tdm_c[i] = 1. - prefactor * lapx[i][1]; - tdm_u[i] = - prefactor * lapx[i][2]; - } - tdm.solve(tdm_info, linear_system->x1pncl); - return 0; -} - -static int solve_in_y( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_y; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapy = &laplacians.lapy; - for(int j = 0; j < size; j++){ - tdm_l[j] = - prefactor * (*lapy)[0]; - tdm_c[j] = 1. - prefactor * (*lapy)[1]; - tdm_u[j] = - prefactor * (*lapy)[2]; - } - tdm.solve(tdm_info, linear_system->y1pncl); - return 0; -} - -#if NDIMS == 3 -static int solve_in_z( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_z; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapz = &laplacians.lapz; - for(int k = 0; k < size; k++){ - tdm_l[k] = - prefactor * (*lapz)[0]; - tdm_c[k] = 1. - prefactor * (*lapz)[1]; - tdm_u[k] = - prefactor * (*lapz)[2]; - } - tdm.solve(tdm_info, linear_system->z2pncl); - return 0; -} -#endif - -/** - * @brief update ux - * @param[in] domain : information about domain decomposition and size - * @param[in] rkstep : Runge-Kutta step - * @param[in] dt : time step size - * @param[in,out] fluid : Runge-Kutta source terms (in), velocity (out) - * @return : error code - */ -int update_ux( - const domain_t * domain, - const size_t rkstep, - const double dt, - fluid_t * fluid -){ - static linear_system_t linear_system = { - .is_initialised = false, - }; - if(!linear_system.is_initialised){ - // if not initialised yet, prepare linear solver - // for implicit diffusive term treatment - const bool implicit[NDIMS] = { - param_m_implicit_x, - param_m_implicit_y, -#if NDIMS == 3 - param_m_implicit_z, -#endif - }; - const size_t glsizes[NDIMS] = { - domain->glsizes[0] - 1, - domain->glsizes[1], -#if NDIMS == 3 - domain->glsizes[2], -#endif - }; - if(0 != linear_system_init(domain->info, implicit, glsizes, &linear_system)){ - return 1; - } - } - // compute increments | 25 - { - const double coef_a = rkcoefs[rkstep][rk_a]; - const double coef_b = rkcoefs[rkstep][rk_b]; - const double coef_g = rkcoefs[rkstep][rk_g]; - const double * restrict srcuxa = fluid->srcux[rk_a].data; - const double * restrict srcuxb = fluid->srcux[rk_b].data; - const double * restrict srcuxg = fluid->srcux[rk_g].data; - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - double * restrict dux = linear_system.x1pncl; -#if NDIMS == 2 - const size_t nitems = (isize - 1) * jsize; -#else - const size_t nitems = (isize - 1) * jsize * ksize; -#endif - for(size_t n = 0; n < nitems; n++){ - dux[n] = - + coef_a * dt * srcuxa[n] - + coef_b * dt * srcuxb[n] - + coef_g * dt * srcuxg[n]; - } - } - // gamma dt diffusivity / 2 | 2 - const double prefactor = - 0.5 * rkcoefs[rkstep][rk_g] * dt * fluid->m_dif; - // solve linear systems in x - if(param_m_implicit_x){ - solve_in_x( - prefactor, - &linear_system - ); - } - // solve linear systems in y - if(param_m_implicit_y){ - sdecomp.transpose.execute( - linear_system.transposer_x1_to_y1, - linear_system.x1pncl, - linear_system.y1pncl - ); - solve_in_y( - prefactor, - &linear_system - ); - sdecomp.transpose.execute( - linear_system.transposer_y1_to_x1, - linear_system.y1pncl, - linear_system.x1pncl - ); - } -#if NDIMS == 3 - // solve linear systems in z - if(param_m_implicit_z){ - sdecomp.transpose.execute( - linear_system.transposer_x1_to_z2, - linear_system.x1pncl, - linear_system.z2pncl - ); - solve_in_z( - prefactor, - &linear_system - ); - sdecomp.transpose.execute( - linear_system.transposer_z2_to_x1, - linear_system.z2pncl, - linear_system.x1pncl - ); - } -#endif - // the field is actually updated here | 21 - { - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dux = linear_system.x1pncl; - double * restrict ux = fluid->ux.data; -#if NDIMS == 2 - BEGIN - UX(i, j) += dux[cnt]; - END -#else - BEGIN - UX(i, j, k) += dux[cnt]; - END -#endif - if(0 != fluid_update_boundaries_ux(domain, &fluid->ux)){ - return 1; - } - } - return 0; -} - diff --git a/src/fluid/integrate/uy.c b/src/fluid/integrate/uy.c deleted file mode 100644 index 18a2bbe2..00000000 --- a/src/fluid/integrate/uy.c +++ /dev/null @@ -1,570 +0,0 @@ -#include "param.h" -#include "memory.h" -#include "runge_kutta.h" -#include "linear_system.h" -#include "tdm.h" -#include "domain.h" -#include "fluid.h" -#include "fluid_solver.h" -#include "internal.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/ux.h" -#include "array_macros/fluid/uy.h" -#if NDIMS == 3 -#include "array_macros/fluid/uz.h" -#endif -#include "array_macros/fluid/p.h" - -// store approximation of laplacian -typedef double laplacian_t[3]; - -typedef struct { - bool is_initialised; - laplacian_t * lapx; - laplacian_t lapy; -#if NDIMS == 3 - laplacian_t lapz; -#endif -} laplacians_t; - -static laplacians_t laplacians = { - .is_initialised = false, -}; - -// [1 : isize] -#define LAPX(I) lapx[(I)-1] - -static int init_lap( - const domain_t * domain -){ - // Laplacian w.r.t. uy in x | 14 - { - const size_t isize = domain->glsizes[0]; - const double * dxf = domain->dxf; - const double * dxc = domain->dxc; - laplacians.lapx = memory_calloc(isize, sizeof(laplacian_t)); - for(size_t i = 1; i <= isize; i++){ - const double l = 1. / DXC(i ) / DXF(i ); - const double u = 1. / DXC(i+1) / DXF(i ); - const double c = - l - u; - laplacians.LAPX(i)[0] = l; - laplacians.LAPX(i)[1] = c; - laplacians.LAPX(i)[2] = u; - } - } - // Laplacian in y | 6 - { - const double dy = domain->dy; - laplacians.lapy[0] = + 1. / dy / dy; - laplacians.lapy[1] = - 2. / dy / dy; - laplacians.lapy[2] = + 1. / dy / dy; - } -#if NDIMS == 3 - // Laplacian in z | 6 - { - const double dz = domain->dz; - laplacians.lapz[0] = + 1. / dz / dz; - laplacians.lapz[1] = - 2. / dz / dz; - laplacians.lapz[2] = + 1. / dz / dz; - } -#endif - laplacians.is_initialised = true; - return 0; -} - -#if NDIMS == 2 -#define BEGIN \ - for(int cnt = 0, j = 1; j <= jsize; j++){ \ - for(int i = 1; i <= isize; i++, cnt++){ -#define END \ - } \ - } -#else -#define BEGIN \ - for(int cnt = 0, k = 1; k <= ksize; k++){ \ - for(int j = 1; j <= jsize; j++){ \ - for(int i = 1; i <= isize; i++, cnt++){ -#define END \ - } \ - } \ - } -#endif - -static int advection_x( - const domain_t * domain, - const double * restrict uy, - const double * restrict ux, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; -#if NDIMS == 2 - BEGIN - // uy is transported by ux | 9 - const double ux_l = + 0.5 * UX(i , j-1) + 0.5 * UX(i , j ); - const double ux_u = + 0.5 * UX(i+1, j-1) + 0.5 * UX(i+1, j ); - const double l = + 0.5 / DXF(i ) * ux_l; - const double u = - 0.5 / DXF(i ) * ux_u; - const double c = - l - u; - src[cnt] += - + l * UY(i-1, j ) - + c * UY(i , j ) - + u * UY(i+1, j ); - END -#else - BEGIN - // uy is transported by ux | 9 - const double ux_l = + 0.5 * UX(i , j-1, k ) + 0.5 * UX(i , j , k ); - const double ux_u = + 0.5 * UX(i+1, j-1, k ) + 0.5 * UX(i+1, j , k ); - const double l = + 0.5 / DXF(i ) * ux_l; - const double u = - 0.5 / DXF(i ) * ux_u; - const double c = - l - u; - src[cnt] += - + l * UY(i-1, j , k ) - + c * UY(i , j , k ) - + u * UY(i+1, j , k ); - END -#endif - return 0; -} - -static int advection_y( - const domain_t * domain, - const double * restrict uy, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double dy = domain->dy; -#if NDIMS == 2 - BEGIN - // uy is transported by uy | 9 - const double uy_l = + 0.5 * UY(i , j-1) + 0.5 * UY(i , j ); - const double uy_u = + 0.5 * UY(i , j ) + 0.5 * UY(i , j+1); - const double l = + 0.5 / dy * uy_l; - const double u = - 0.5 / dy * uy_u; - const double c = - l - u; - src[cnt] += - + l * UY(i , j-1) - + c * UY(i , j ) - + u * UY(i , j+1); - END -#else - BEGIN - // uy is transported by uy | 9 - const double uy_l = + 0.5 * UY(i , j-1, k ) + 0.5 * UY(i , j , k ); - const double uy_u = + 0.5 * UY(i , j , k ) + 0.5 * UY(i , j+1, k ); - const double l = + 0.5 / dy * uy_l; - const double u = - 0.5 / dy * uy_u; - const double c = - l - u; - src[cnt] += - + l * UY(i , j-1, k ) - + c * UY(i , j , k ) - + u * UY(i , j+1, k ); - END -#endif - return 0; -} - -#if NDIMS == 3 -static int advection_z( - const domain_t * domain, - const double * restrict uy, - const double * restrict uz, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double dz = domain->dz; - BEGIN - // uy is transported by uz | 9 - const double uz_l = + 0.5 * UZ(i , j-1, k ) + 0.5 * UZ(i , j , k ); - const double uz_u = + 0.5 * UZ(i , j-1, k+1) + 0.5 * UZ(i , j , k+1); - const double l = + 0.5 / dz * uz_l; - const double u = - 0.5 / dz * uz_u; - const double c = - l - u; - src[cnt] += - + l * UY(i , j , k-1) - + c * UY(i , j , k ) - + u * UY(i , j , k+1); - END - return 0; -} -#endif - -static int diffusion_x( - const domain_t * domain, - const double diffusivity, - const double * restrict uy, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const laplacian_t * restrict lapx = laplacians.lapx; -#if NDIMS == 2 - BEGIN - // uy is diffused in x | 5 - src[cnt] += diffusivity * ( - + LAPX(i)[0] * UY(i-1, j ) - + LAPX(i)[1] * UY(i , j ) - + LAPX(i)[2] * UY(i+1, j ) - ); - END -#else - BEGIN - // uy is diffused in x | 5 - src[cnt] += diffusivity * ( - + LAPX(i)[0] * UY(i-1, j , k ) - + LAPX(i)[1] * UY(i , j , k ) - + LAPX(i)[2] * UY(i+1, j , k ) - ); - END -#endif - return 0; -} - -static int diffusion_y( - const domain_t * domain, - const double diffusivity, - const double * restrict uy, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const laplacian_t * restrict lapy = &laplacians.lapy; -#if NDIMS == 2 - BEGIN - // uy is diffused in y | 5 - src[cnt] += diffusivity * ( - + (*lapy)[0] * UY(i , j-1) - + (*lapy)[1] * UY(i , j ) - + (*lapy)[2] * UY(i , j+1) - ); - END -#else - BEGIN - // uy is diffused in y | 5 - src[cnt] += diffusivity * ( - + (*lapy)[0] * UY(i , j-1, k ) - + (*lapy)[1] * UY(i , j , k ) - + (*lapy)[2] * UY(i , j+1, k ) - ); - END -#endif - return 0; -} - -#if NDIMS == 3 -static int diffusion_z( - const domain_t * domain, - const double diffusivity, - const double * restrict uy, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const laplacian_t * restrict lapz = &laplacians.lapz; - BEGIN - // uy is diffused in z | 5 - src[cnt] += diffusivity * ( - + (*lapz)[0] * UY(i , j , k-1) - + (*lapz)[1] * UY(i , j , k ) - + (*lapz)[2] * UY(i , j , k+1) - ); - END - return 0; -} -#endif - -static int pressure( - const domain_t * domain, - const double * restrict p, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double dy = domain->dy; -#if NDIMS == 2 - BEGIN - src[cnt] -= 1. / dy * ( - - P(i , j-1) - + P(i , j ) - ); - END -#else - BEGIN - src[cnt] -= 1. / dy * ( - - P(i , j-1, k ) - + P(i , j , k ) - ); - END -#endif - return 0; -} - -/** - * @brief comute right-hand-side of Runge-Kutta scheme of uy - * @param[in] domain : information related to MPI domain decomposition - * @param[in,out] fluid : n-step flow field (in), RK source terms (inout) - * @return : error code - */ -int compute_rhs_uy( - const domain_t * domain, - fluid_t * fluid -){ - if(!laplacians.is_initialised){ - if(0 != init_lap(domain)){ - return 1; - } - } - const double * restrict ux = fluid->ux.data; - const double * restrict uy = fluid->uy.data; -#if NDIMS == 3 - const double * restrict uz = fluid->uz.data; -#endif - const double * restrict p = fluid-> p.data; - double * restrict srca = fluid->srcuy[rk_a].data; - double * restrict srcg = fluid->srcuy[rk_g].data; - const double diffusivity = fluid->m_dif; - // advective contributions, always explicit | 5 - advection_x(domain, uy, ux, srca); - advection_y(domain, uy, srca); -#if NDIMS == 3 - advection_z(domain, uy, uz, srca); -#endif - // diffusive contributions, can be explicit or implicit | 5 - diffusion_x(domain, diffusivity, uy, param_m_implicit_x ? srcg : srca); - diffusion_y(domain, diffusivity, uy, param_m_implicit_y ? srcg : srca); -#if NDIMS == 3 - diffusion_z(domain, diffusivity, uy, param_m_implicit_z ? srcg : srca); -#endif - // pressure-gradient contribution, always implicit - pressure(domain, p, srcg); - return 0; -} - -static int solve_in_x( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_x; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapx = laplacians.lapx; - for(int i = 0; i < size; i++){ - tdm_l[i] = - prefactor * lapx[i][0]; - tdm_c[i] = 1. - prefactor * lapx[i][1]; - tdm_u[i] = - prefactor * lapx[i][2]; - } - tdm.solve(tdm_info, linear_system->x1pncl); - return 0; -} - -static int solve_in_y( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_y; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapy = &laplacians.lapy; - for(int j = 0; j < size; j++){ - tdm_l[j] = - prefactor * (*lapy)[0]; - tdm_c[j] = 1. - prefactor * (*lapy)[1]; - tdm_u[j] = - prefactor * (*lapy)[2]; - } - tdm.solve(tdm_info, linear_system->y1pncl); - return 0; -} - -#if NDIMS == 3 -static int solve_in_z( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_z; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapz = &laplacians.lapz; - for(int k = 0; k < size; k++){ - tdm_l[k] = - prefactor * (*lapz)[0]; - tdm_c[k] = 1. - prefactor * (*lapz)[1]; - tdm_u[k] = - prefactor * (*lapz)[2]; - } - tdm.solve(tdm_info, linear_system->z2pncl); - return 0; -} -#endif - -/** - * @brief update uy - * @param[in] domain : information about domain decomposition and size - * @param[in] rkstep : Runge-Kutta step - * @param[in] dt : time step size - * @param[in,out] fluid : Runge-Kutta source terms (in), velocity (out) - * @return : error code - */ -int update_uy( - const domain_t * domain, - const size_t rkstep, - const double dt, - fluid_t * fluid -){ - static linear_system_t linear_system = { - .is_initialised = false, - }; - if(!linear_system.is_initialised){ - // if not initialised yet, prepare linear solver - // for implicit diffusive term treatment - const bool implicit[NDIMS] = { - param_m_implicit_x, - param_m_implicit_y, -#if NDIMS == 3 - param_m_implicit_z, -#endif - }; - const size_t glsizes[NDIMS] = { - domain->glsizes[0], - domain->glsizes[1], -#if NDIMS == 3 - domain->glsizes[2], -#endif - }; - if(0 != linear_system_init(domain->info, implicit, glsizes, &linear_system)){ - return 1; - } - } - // compute increments | 25 - { - const double coef_a = rkcoefs[rkstep][rk_a]; - const double coef_b = rkcoefs[rkstep][rk_b]; - const double coef_g = rkcoefs[rkstep][rk_g]; - const double * restrict srcuya = fluid->srcuy[rk_a].data; - const double * restrict srcuyb = fluid->srcuy[rk_b].data; - const double * restrict srcuyg = fluid->srcuy[rk_g].data; - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - double * restrict duy = linear_system.x1pncl; -#if NDIMS == 2 - const size_t nitems = isize * jsize; -#else - const size_t nitems = isize * jsize * ksize; -#endif - for(size_t n = 0; n < nitems; n++){ - duy[n] = - + coef_a * dt * srcuya[n] - + coef_b * dt * srcuyb[n] - + coef_g * dt * srcuyg[n]; - } - } - // gamma dt diffusivity / 2 | 2 - const double prefactor = - 0.5 * rkcoefs[rkstep][rk_g] * dt * fluid->m_dif; - // solve linear systems in x - if(param_m_implicit_x){ - solve_in_x( - prefactor, - &linear_system - ); - } - // solve linear systems in y - if(param_m_implicit_y){ - sdecomp.transpose.execute( - linear_system.transposer_x1_to_y1, - linear_system.x1pncl, - linear_system.y1pncl - ); - solve_in_y( - prefactor, - &linear_system - ); - sdecomp.transpose.execute( - linear_system.transposer_y1_to_x1, - linear_system.y1pncl, - linear_system.x1pncl - ); - } -#if NDIMS == 3 - // solve linear systems in z - if(param_m_implicit_z){ - sdecomp.transpose.execute( - linear_system.transposer_x1_to_z2, - linear_system.x1pncl, - linear_system.z2pncl - ); - solve_in_z( - prefactor, - &linear_system - ); - sdecomp.transpose.execute( - linear_system.transposer_z2_to_x1, - linear_system.z2pncl, - linear_system.x1pncl - ); - } -#endif - // the field is actually updated here | 21 - { - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict duy = linear_system.x1pncl; - double * restrict uy = fluid->uy.data; -#if NDIMS == 2 - BEGIN - UY(i, j) += duy[cnt]; - END -#else - BEGIN - UY(i, j, k) += duy[cnt]; - END -#endif - if(0 != fluid_update_boundaries_uy(domain, &fluid->uy)){ - return 1; - } - } - return 0; -} - diff --git a/src/fluid/integrate/uz.c b/src/fluid/integrate/uz.c deleted file mode 100644 index ad18d45e..00000000 --- a/src/fluid/integrate/uz.c +++ /dev/null @@ -1,453 +0,0 @@ -#if NDIMS == 3 -#include "param.h" -#include "memory.h" -#include "runge_kutta.h" -#include "linear_system.h" -#include "tdm.h" -#include "domain.h" -#include "fluid.h" -#include "fluid_solver.h" -#include "internal.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/ux.h" -#include "array_macros/fluid/uy.h" -#include "array_macros/fluid/uz.h" -#include "array_macros/fluid/p.h" - -// store approximation of laplacian -typedef double laplacian_t[3]; - -typedef struct { - bool is_initialised; - laplacian_t * lapx; - laplacian_t lapy; - laplacian_t lapz; -} laplacians_t; - -static laplacians_t laplacians = { - .is_initialised = false, -}; - -// [1 : isize] -#define LAPX(I) lapx[(I)-1] - -static int init_lap( - const domain_t * domain -){ - // Laplacian w.r.t. uz in x | 14 - { - const size_t isize = domain->glsizes[0]; - const double * dxf = domain->dxf; - const double * dxc = domain->dxc; - laplacians.lapx = memory_calloc(isize, sizeof(laplacian_t)); - for(size_t i = 1; i <= isize; i++){ - const double l = 1. / DXC(i ) / DXF(i ); - const double u = 1. / DXC(i+1) / DXF(i ); - const double c = - l - u; - laplacians.LAPX(i)[0] = l; - laplacians.LAPX(i)[1] = c; - laplacians.LAPX(i)[2] = u; - } - } - // Laplacian in y | 6 - { - const double dy = domain->dy; - laplacians.lapy[0] = + 1. / dy / dy; - laplacians.lapy[1] = - 2. / dy / dy; - laplacians.lapy[2] = + 1. / dy / dy; - } - // Laplacian in z | 6 - { - const double dz = domain->dz; - laplacians.lapz[0] = + 1. / dz / dz; - laplacians.lapz[1] = - 2. / dz / dz; - laplacians.lapz[2] = + 1. / dz / dz; - } - laplacians.is_initialised = true; - return 0; -} - -#define BEGIN \ - for(int cnt = 0, k = 1; k <= ksize; k++){ \ - for(int j = 1; j <= jsize; j++){ \ - for(int i = 1; i <= isize; i++, cnt++){ -#define END \ - } \ - } \ - } - -static int advection_x( - const domain_t * domain, - const double * restrict uz, - const double * restrict ux, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double * restrict dxf = domain->dxf; - BEGIN - // uz is transported by ux | 9 - const double ux_l = + 0.5 * UX(i , j , k-1) + 0.5 * UX(i , j , k ); - const double ux_u = + 0.5 * UX(i+1, j , k-1) + 0.5 * UX(i+1, j , k ); - const double l = + 0.5 / DXF(i ) * ux_l; - const double u = - 0.5 / DXF(i ) * ux_u; - const double c = - l - u; - src[cnt] += - + l * UZ(i-1, j , k ) - + c * UZ(i , j , k ) - + u * UZ(i+1, j , k ); - END - return 0; -} - -static int advection_y( - const domain_t * domain, - const double * restrict uz, - const double * restrict uy, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double dy = domain->dy; - BEGIN - // uz is transported by uy | 9 - const double uy_l = + 0.5 * UY(i , j , k-1) + 0.5 * UY(i , j , k ); - const double uy_u = + 0.5 * UY(i , j+1, k-1) + 0.5 * UY(i , j+1, k ); - const double l = + 0.5 / dy * uy_l; - const double u = - 0.5 / dy * uy_u; - const double c = - l - u; - src[cnt] += - + l * UZ(i , j-1, k ) - + c * UZ(i , j , k ) - + u * UZ(i , j+1, k ); - END - return 0; -} - -static int advection_z( - const domain_t * domain, - const double * restrict uz, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double dz = domain->dz; - BEGIN - // uz is transported by uz | 9 - const double uz_l = + 0.5 * UZ(i , j , k-1) + 0.5 * UZ(i , j , k ); - const double uz_u = + 0.5 * UZ(i , j , k ) + 0.5 * UZ(i , j , k+1); - const double l = + 0.5 / dz * uz_l; - const double u = - 0.5 / dz * uz_u; - const double c = - l - u; - src[cnt] += - + l * UZ(i , j , k-1) - + c * UZ(i , j , k ) - + u * UZ(i , j , k+1); - END - return 0; -} - -static int diffusion_x( - const domain_t * domain, - const double diffusivity, - const double * restrict uz, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const laplacian_t * restrict lapx = laplacians.lapx; - BEGIN - // uz is diffused in x | 5 - src[cnt] += diffusivity * ( - + LAPX(i)[0] * UZ(i-1, j , k ) - + LAPX(i)[1] * UZ(i , j , k ) - + LAPX(i)[2] * UZ(i+1, j , k ) - ); - END - return 0; -} - -static int diffusion_y( - const domain_t * domain, - const double diffusivity, - const double * restrict uz, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const laplacian_t * restrict lapy = &laplacians.lapy; - BEGIN - // uz is diffused in y | 5 - src[cnt] += diffusivity * ( - + (*lapy)[0] * UZ(i , j-1, k ) - + (*lapy)[1] * UZ(i , j , k ) - + (*lapy)[2] * UZ(i , j+1, k ) - ); - END - return 0; -} - -static int diffusion_z( - const domain_t * domain, - const double diffusivity, - const double * restrict uz, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const laplacian_t * restrict lapz = &laplacians.lapz; - BEGIN - // uz is diffused in z | 5 - src[cnt] += diffusivity * ( - + (*lapz)[0] * UZ(i , j , k-1) - + (*lapz)[1] * UZ(i , j , k ) - + (*lapz)[2] * UZ(i , j , k+1) - ); - END - return 0; -} - -static int pressure( - const domain_t * domain, - const double * restrict p, - double * restrict src -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double dz = domain->dz; - BEGIN - src[cnt] -= 1. / dz * ( - - P(i , j , k-1) - + P(i , j , k ) - ); - END - return 0; -} - -/** - * @brief comute right-hand-side of Runge-Kutta scheme of uz - * @param[in] domain : information related to MPI domain decomposition - * @param[in,out] fluid : n-step flow field (in), RK source terms (inout) - * @return : error code - */ -int compute_rhs_uz( - const domain_t * domain, - fluid_t * fluid -){ - if(!laplacians.is_initialised){ - if(0 != init_lap(domain)){ - return 1; - } - } - const double * restrict ux = fluid->ux.data; - const double * restrict uy = fluid->uy.data; - const double * restrict uz = fluid->uz.data; - const double * restrict p = fluid-> p.data; - double * restrict srca = fluid->srcuz[rk_a].data; - double * restrict srcg = fluid->srcuz[rk_g].data; - const double diffusivity = fluid->m_dif; - // advective contributions, always explicit | 3 - advection_x(domain, uz, ux, srca); - advection_y(domain, uz, uy, srca); - advection_z(domain, uz, srca); - // diffusive contributions, can be explicit or implicit | 3 - diffusion_x(domain, diffusivity, uz, param_m_implicit_x ? srcg : srca); - diffusion_y(domain, diffusivity, uz, param_m_implicit_y ? srcg : srca); - diffusion_z(domain, diffusivity, uz, param_m_implicit_z ? srcg : srca); - // pressure-gradient contribution, always implicit - pressure(domain, p, srcg); - return 0; -} - -static int solve_in_x( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_x; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapx = laplacians.lapx; - for(int i = 0; i < size; i++){ - tdm_l[i] = - prefactor * lapx[i][0]; - tdm_c[i] = 1. - prefactor * lapx[i][1]; - tdm_u[i] = - prefactor * lapx[i][2]; - } - tdm.solve(tdm_info, linear_system->x1pncl); - return 0; -} - -static int solve_in_y( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_y; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapy = &laplacians.lapy; - for(int j = 0; j < size; j++){ - tdm_l[j] = - prefactor * (*lapy)[0]; - tdm_c[j] = 1. - prefactor * (*lapy)[1]; - tdm_u[j] = - prefactor * (*lapy)[2]; - } - tdm.solve(tdm_info, linear_system->y1pncl); - return 0; -} - -static int solve_in_z( - const double prefactor, - linear_system_t * linear_system -){ - tdm_info_t * tdm_info = linear_system->tdm_z; - int size = 0; - double * restrict tdm_l = NULL; - double * restrict tdm_c = NULL; - double * restrict tdm_u = NULL; - tdm.get_size(tdm_info, &size); - tdm.get_l(tdm_info, &tdm_l); - tdm.get_c(tdm_info, &tdm_c); - tdm.get_u(tdm_info, &tdm_u); - const laplacian_t * restrict lapz = &laplacians.lapz; - for(int k = 0; k < size; k++){ - tdm_l[k] = - prefactor * (*lapz)[0]; - tdm_c[k] = 1. - prefactor * (*lapz)[1]; - tdm_u[k] = - prefactor * (*lapz)[2]; - } - tdm.solve(tdm_info, linear_system->z2pncl); - return 0; -} - -/** - * @brief update uz - * @param[in] domain : information about domain decomposition and size - * @param[in] rkstep : Runge-Kutta step - * @param[in] dt : time step size - * @param[in,out] fluid : Runge-Kutta source terms (in), velocity (out) - * @return : error code - */ -int update_uz( - const domain_t * domain, - const size_t rkstep, - const double dt, - fluid_t * fluid -){ - static linear_system_t linear_system = { - .is_initialised = false, - }; - if(!linear_system.is_initialised){ - // if not initialised yet, prepare linear solver - // for implicit diffusive term treatment - const bool implicit[NDIMS] = { - param_m_implicit_x, - param_m_implicit_y, - param_m_implicit_z, - }; - const size_t glsizes[NDIMS] = { - domain->glsizes[0], - domain->glsizes[1], - domain->glsizes[2], - }; - if(0 != linear_system_init(domain->info, implicit, glsizes, &linear_system)){ - return 1; - } - } - // compute increments | 19 - { - const double coef_a = rkcoefs[rkstep][rk_a]; - const double coef_b = rkcoefs[rkstep][rk_b]; - const double coef_g = rkcoefs[rkstep][rk_g]; - const double * restrict srcuza = fluid->srcuz[rk_a].data; - const double * restrict srcuzb = fluid->srcuz[rk_b].data; - const double * restrict srcuzg = fluid->srcuz[rk_g].data; - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - double * restrict duz = linear_system.x1pncl; - const size_t nitems = isize * jsize * ksize; - for(size_t n = 0; n < nitems; n++){ - duz[n] = - + coef_a * dt * srcuza[n] - + coef_b * dt * srcuzb[n] - + coef_g * dt * srcuzg[n]; - } - } - // gamma dt diffusivity / 2 | 2 - const double prefactor = - 0.5 * rkcoefs[rkstep][rk_g] * dt * fluid->m_dif; - // solve linear systems in x - if(param_m_implicit_x){ - solve_in_x( - prefactor, - &linear_system - ); - } - // solve linear systems in y - if(param_m_implicit_y){ - sdecomp.transpose.execute( - linear_system.transposer_x1_to_y1, - linear_system.x1pncl, - linear_system.y1pncl - ); - solve_in_y( - prefactor, - &linear_system - ); - sdecomp.transpose.execute( - linear_system.transposer_y1_to_x1, - linear_system.y1pncl, - linear_system.x1pncl - ); - } - // solve linear systems in z - if(param_m_implicit_z){ - sdecomp.transpose.execute( - linear_system.transposer_x1_to_z2, - linear_system.x1pncl, - linear_system.z2pncl - ); - solve_in_z( - prefactor, - &linear_system - ); - sdecomp.transpose.execute( - linear_system.transposer_z2_to_x1, - linear_system.z2pncl, - linear_system.x1pncl - ); - } - // the field is actually updated here | 13 - { - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double * restrict duz = linear_system.x1pncl; - double * restrict uz = fluid->uz.data; - BEGIN - UZ(i, j, k) += duz[cnt]; - END - if(0 != fluid_update_boundaries_uz(domain, &fluid->uz)){ - return 1; - } - } - return 0; -} -#endif diff --git a/src/fluid/integrate/internal.h b/src/fluid/predict/internal.h similarity index 51% rename from src/fluid/integrate/internal.h rename to src/fluid/predict/internal.h index 93d4b567..3aacc945 100644 --- a/src/fluid/integrate/internal.h +++ b/src/fluid/predict/internal.h @@ -1,41 +1,55 @@ -#if !defined(FLUID_INTEGRATE_INTERNAL) -#define FLUID_INTEGRATE_INTERNAL +#if !defined(FLUID_PREDICT_INTERNAL) +#define FLUID_PREDICT_INTERNAL -extern int compute_rhs_ux( +#include + +// store approximation of laplacian +typedef struct { + double l; + double c; + double u; +} laplacian_t; + +// store Laplacian for each directoin +typedef struct { + bool is_initialised; + laplacian_t * lapx; + laplacian_t lapy; +#if NDIMS == 3 + laplacian_t lapz; +#endif +} laplacians_t; + +extern int compute_rhs_ux ( const domain_t * domain, fluid_t * fluid ); -extern int compute_rhs_uy( +extern int compute_rhs_uy ( const domain_t * domain, fluid_t * fluid ); #if NDIMS == 3 -extern int compute_rhs_uz( +extern int compute_rhs_uz ( const domain_t * domain, fluid_t * fluid ); #endif -extern int compute_rhs_t( - const domain_t * domain, - fluid_t * fluid -); - -extern int buoyancy_ux( +extern int compute_rhs_t ( const domain_t * domain, fluid_t * fluid ); -extern int update_ux( +extern int update_ux ( const domain_t * domain, const size_t rkstep, const double dt, fluid_t * fluid ); -extern int update_uy( +extern int update_uy ( const domain_t * domain, const size_t rkstep, const double dt, @@ -43,7 +57,7 @@ extern int update_uy( ); #if NDIMS == 3 -extern int update_uz( +extern int update_uz ( const domain_t * domain, const size_t rkstep, const double dt, @@ -51,11 +65,11 @@ extern int update_uz( ); #endif -extern int update_t( +extern int update_t ( const domain_t * domain, const size_t rkstep, const double dt, fluid_t * fluid ); -#endif // FLUID_INTEGRATE_INTERNAL +#endif // FLUID_PREDICT_INTERNAL diff --git a/src/fluid/integrate/compute_rhs.c b/src/fluid/predict/main.c similarity index 69% rename from src/fluid/integrate/compute_rhs.c rename to src/fluid/predict/main.c index 5df3243d..b10d9035 100644 --- a/src/fluid/integrate/compute_rhs.c +++ b/src/fluid/predict/main.c @@ -1,5 +1,6 @@ #include #include "runge_kutta.h" +#include "domain.h" #include "fluid.h" #include "fluid_solver.h" #include "internal.h" @@ -24,13 +25,17 @@ static int reset_srcs( } /** - * @brief compute right-hand-side terms of the Runge-Kutta scheme + * @brief update fields using the previously-computed RK source terms * @param[in] domain : information related to MPI domain decomposition - * @param[in,out] fluid : flow field (in), RK source terms (out) + * @param[in] rkstep : Runge-Kutta step + * @param[in] dt : time step size + * @param[in,out] fluid : RK source terms (in), flow field (out) * @return : error code */ -int fluid_compute_rhs( +int fluid_predict_field( const domain_t * domain, + const size_t rkstep, + const double dt, fluid_t * fluid ){ // reset buffers @@ -49,7 +54,7 @@ int fluid_compute_rhs( if(0 != reset_srcs(fluid->srct + rk_a, fluid->srct + rk_b, fluid->srct + rk_g)){ return 1; } - // update buffers + // compute right-hand-side terms and store them to the corresponding buffers if(0 != compute_rhs_ux(domain, fluid)){ return 1; } @@ -64,6 +69,21 @@ int fluid_compute_rhs( if(0 != compute_rhs_t (domain, fluid)){ return 1; } + // update flow field + if(0 != update_ux(domain, rkstep, dt, fluid)){ + return 1; + } + if(0 != update_uy(domain, rkstep, dt, fluid)){ + return 1; + } +#if NDIMS == 3 + if(0 != update_uz(domain, rkstep, dt, fluid)){ + return 1; + } +#endif + if(0 != update_t (domain, rkstep, dt, fluid)){ + return 1; + } return 0; } diff --git a/src/fluid/predict/t.c b/src/fluid/predict/t.c new file mode 100644 index 00000000..98f0fbee --- /dev/null +++ b/src/fluid/predict/t.c @@ -0,0 +1,501 @@ +#include "param.h" +#include "memory.h" +#include "runge_kutta.h" +#include "linear_system.h" +#include "tdm.h" +#include "domain.h" +#include "fluid.h" +#include "fluid_solver.h" +#include "internal.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/hxxc.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" +#include "array_macros/fluid/ux.h" +#include "array_macros/fluid/uy.h" +#if NDIMS == 3 +#include "array_macros/fluid/uz.h" +#endif +#include "array_macros/fluid/t.h" + +static laplacians_t laplacians = { + .is_initialised = false, +}; + +// map [1 : isize] in code to [0 : isize - 1] in memory +#define LAPX(I) lapx[(I) - 1] + +static int init_laplacians ( + const domain_t * domain +) { + // scalar laplacian in x | 15 + { + const int isize = domain->glsizes[0]; + const double * hxxf = domain->hxxf; + const double * jdxf = domain->jdxf; + const double * jdxc = domain->jdxc; + laplacians.lapx = memory_calloc(isize, sizeof(laplacian_t)); + for (int i = 1; i <= isize; i++) { + const double l = 1. / JDXC(i ) * JDXF(i ) / HXXF(i ) / HXXF(i ); + const double u = 1. / JDXC(i ) * JDXF(i+1) / HXXF(i+1) / HXXF(i+1); + const double c = - l - u; + laplacians.LAPX(i).l = l; + laplacians.LAPX(i).c = c; + laplacians.LAPX(i).u = u; + } + } + // scalar laplacian in y | 9 + { + const double hy = domain->hy; + const double l = 1. / hy / hy; + const double u = 1. / hy / hy; + const double c = - l - u; + laplacians.lapy.l = l; + laplacians.lapy.c = c; + laplacians.lapy.u = u; + } +#if NDIMS == 3 + // scalar laplacian in z | 9 + { + const double hz = domain->hz; + const double l = 1. / hz / hz; + const double u = 1. / hz / hz; + const double c = - l - u; + laplacians.lapz.l = l; + laplacians.lapz.c = c; + laplacians.lapz.u = u; + } +#endif + laplacians.is_initialised = true; + return 0; +} + +#if NDIMS == 2 +#define BEGIN \ + for (int cnt = 0, j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++, cnt++) { +#define END \ + } \ + } +#else +#define BEGIN \ + for (int cnt = 0, k = 1; k <= ksize; k++) { \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++, cnt++) { +#define END \ + } \ + } \ + } +#endif + +// advected in x | 44 +static int advection_x ( + const domain_t * domain, + const double * restrict t, + const double * restrict ux, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict hxxf = domain->hxxf; + const double * restrict jdxf = domain->jdxf; + const double * restrict jdxc = domain->jdxc; + BEGIN + const double hx_xm = HXXF(i ); + const double hx_xp = HXXF(i+1); + const double jd_xm = JDXF(i ); + const double jd_x0 = JDXC(i ); + const double jd_xp = JDXF(i+1); +#if NDIMS == 2 + const double ux_xm = jd_xm / hx_xm * UX(i , j ); + const double ux_xp = jd_xp / hx_xp * UX(i+1, j ); +#else + const double ux_xm = jd_xm / hx_xm * UX(i , j , k ); + const double ux_xp = jd_xp / hx_xp * UX(i+1, j , k ); +#endif + const double l = - 0.5 * ux_xm; + const double u = + 0.5 * ux_xp; + const double c = - l - u; + src[cnt] -= 1. / jd_x0 * ( +#if NDIMS == 2 + + l * T(i-1, j ) + + c * T(i , j ) + + u * T(i+1, j ) +#else + + l * T(i-1, j , k ) + + c * T(i , j , k ) + + u * T(i+1, j , k ) +#endif + ); + END + return 0; +} + +// advected in y | 39 +static int advection_y ( + const domain_t * domain, + const double * restrict t, + const double * restrict uy, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double hy = domain->hy; + const double * restrict jdxc = domain->jdxc; + BEGIN + const double jd = JDXC(i ); +#if NDIMS == 2 + const double uy_ym = jd / hy * UY(i , j ); + const double uy_yp = jd / hy * UY(i , j+1); +#else + const double uy_ym = jd / hy * UY(i , j , k ); + const double uy_yp = jd / hy * UY(i , j+1, k ); +#endif + const double l = - 0.5 * uy_ym; + const double u = + 0.5 * uy_yp; + const double c = - l - u; + src[cnt] -= 1. / jd * ( +#if NDIMS == 2 + + l * T(i , j-1) + + c * T(i , j ) + + u * T(i , j+1) +#else + + l * T(i , j-1, k ) + + c * T(i , j , k ) + + u * T(i , j+1, k ) +#endif + ); + END + return 0; +} + +#if NDIMS == 3 +// advected in z | 26 +static int advection_z ( + const domain_t * domain, + const double * restrict t, + const double * restrict uz, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double hz = domain->hz; + const double * restrict jdxc = domain->jdxc; + BEGIN + const double jd = JDXC(i ); + const double uz_zm = jd / hz * UZ(i , j , k ); + const double uz_zp = jd / hz * UZ(i , j , k+1); + const double l = - 0.5 * uz_zm; + const double u = + 0.5 * uz_zp; + const double c = - l - u; + src[cnt] -= 1. / jd * ( + + l * T(i , j , k-1) + + c * T(i , j , k ) + + u * T(i , j , k+1) + ); + END + return 0; +} +#endif + +// diffused in x | 27 +static int diffusion_x ( + const domain_t * domain, + const double diffusivity, + const double * restrict t, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const laplacian_t * lapx = laplacians.lapx; + BEGIN + src[cnt] += diffusivity * ( +#if NDIMS == 2 + + LAPX(i).l * T(i-1, j ) + + LAPX(i).c * T(i , j ) + + LAPX(i).u * T(i+1, j ) +#else + + LAPX(i).l * T(i-1, j , k ) + + LAPX(i).c * T(i , j , k ) + + LAPX(i).u * T(i+1, j , k ) +#endif + ); + END + return 0; +} + +// diffused in y | 27 +static int diffusion_y ( + const domain_t * domain, + const double diffusivity, + const double * restrict t, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const laplacian_t * lapy = &laplacians.lapy; + BEGIN + src[cnt] += diffusivity * ( +#if NDIMS == 2 + + (*lapy).l * T(i , j-1) + + (*lapy).c * T(i , j ) + + (*lapy).u * T(i , j+1) +#else + + (*lapy).l * T(i , j-1, k ) + + (*lapy).c * T(i , j , k ) + + (*lapy).u * T(i , j+1, k ) +#endif + ); + END + return 0; +} + +#if NDIMS == 3 +// diffused in z | 19 +static int diffusion_z ( + const domain_t * domain, + const double diffusivity, + const double * restrict t, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const laplacian_t * lapz = &laplacians.lapz; + BEGIN + src[cnt] += diffusivity * ( + + (*lapz).l * T(i , j , k-1) + + (*lapz).c * T(i , j , k ) + + (*lapz).u * T(i , j , k+1) + ); + END + return 0; +} +#endif + +// compute right-hand-side terms, which are added to buffers | 34 +int compute_rhs_t ( + const domain_t * domain, + fluid_t * fluid +) { + if (!laplacians.is_initialised) { + if (0 != init_laplacians(domain)) { + return 1; + } + } + const double * restrict ux = fluid->ux.data; + const double * restrict uy = fluid->uy.data; +#if NDIMS == 3 + const double * restrict uz = fluid->uz.data; +#endif + const double * restrict t = fluid-> t.data; + // buffer for explicit terms + double * restrict srca = fluid->srct[rk_a].data; + // buffer for implicit terms + double * restrict srcg = fluid->srct[rk_g].data; + const double diffusivity = fluid_compute_temperature_diffusivity(fluid); + // advective contributions, always explicit + advection_x(domain, t, ux, srca); + advection_y(domain, t, uy, srca); +#if NDIMS == 3 + advection_z(domain, t, uz, srca); +#endif + // diffusive contributions, can be explicit or implicit + diffusion_x(domain, diffusivity, t, param_t_implicit_x ? srcg : srca); + diffusion_y(domain, diffusivity, t, param_t_implicit_y ? srcg : srca); +#if NDIMS == 3 + diffusion_z(domain, diffusivity, t, param_t_implicit_z ? srcg : srca); +#endif + return 0; +} + +// update temperature field +int update_t ( + const domain_t * domain, + const size_t rkstep, + const double dt, + fluid_t * fluid +) { + static linear_system_t linear_system = { + .is_initialised = false, + }; + if (!linear_system.is_initialised) { + // if not initialised yet, prepare linear solver + // for implicit diffusive treatment + const bool implicit[NDIMS] = { + param_t_implicit_x, + param_t_implicit_y, +#if NDIMS == 3 + param_t_implicit_z, +#endif + }; + const size_t glsizes[NDIMS] = { + domain->glsizes[0], + domain->glsizes[1], +#if NDIMS == 3 + domain->glsizes[2], +#endif + }; + if (0 != linear_system_init(domain->info, implicit, glsizes, &linear_system)) { + return 1; + } + } + // compute increments | 28 + { + // Runge-Kutta coefficients, alpha, beta, gamma + const double coef_a = rkcoefs[rkstep][rk_a]; + const double coef_b = rkcoefs[rkstep][rk_b]; + const double coef_g = rkcoefs[rkstep][rk_g]; + // Runge-Kutta buffers, alpha, beta, gamma + const double * restrict srcta = fluid->srct[rk_a].data; + const double * restrict srctb = fluid->srct[rk_b].data; + const double * restrict srctg = fluid->srct[rk_g].data; + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + double * restrict dtemp = linear_system.x1pncl; +#if NDIMS == 2 + const size_t nitems = isize * jsize; +#else + const size_t nitems = isize * jsize * ksize; +#endif + // compute T(new) - T(old) + for (size_t n = 0; n < nitems; n++) { + dtemp[n] = + + coef_a * dt * srcta[n] + + coef_b * dt * srctb[n] + + coef_g * dt * srctg[n]; + } + } + // solve linear systems if necessary + { + const double prefactor = + 0.5 * rkcoefs[rkstep][rk_g] * dt * fluid_compute_temperature_diffusivity(fluid); + // solve linear systems in x | 20 + if (param_t_implicit_x) { + // prepare tri-diagonal coefficients + tdm_info_t * tdm_info = linear_system.tdm_x; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapx = laplacians.lapx; + for (int i = 0; i < size; i++) { + tdm_l[i] = - prefactor * lapx[i].l; + tdm_c[i] = 1. - prefactor * lapx[i].c; + tdm_u[i] = - prefactor * lapx[i].u; + } + // solve all + tdm.solve(tdm_info, linear_system.x1pncl); + } + // solve linear systems in y | 32 + if (param_t_implicit_y) { + // make all y data accessible + sdecomp.transpose.execute( + linear_system.transposer_x1_to_y1, + linear_system.x1pncl, + linear_system.y1pncl + ); + // prepare tri-diagonal coefficients + tdm_info_t * tdm_info = linear_system.tdm_y; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapy = &laplacians.lapy; + for (int j = 0; j < size; j++) { + tdm_l[j] = - prefactor * (*lapy).l; + tdm_c[j] = 1. - prefactor * (*lapy).c; + tdm_u[j] = - prefactor * (*lapy).u; + } + // solve all + tdm.solve(tdm_info, linear_system.y1pncl); + // recover original memory alignment + sdecomp.transpose.execute( + linear_system.transposer_y1_to_x1, + linear_system.y1pncl, + linear_system.x1pncl + ); + } +#if NDIMS == 3 + // solve linear systems in z | 32 + if (param_t_implicit_z) { + // make all z data accessible + sdecomp.transpose.execute( + linear_system.transposer_x1_to_z2, + linear_system.x1pncl, + linear_system.z2pncl + ); + // prepare tri-diagonal coefficients + tdm_info_t * tdm_info = linear_system.tdm_z; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapz = &laplacians.lapz; + for (int k = 0; k < size; k++) { + tdm_l[k] = - prefactor * (*lapz).l; + tdm_c[k] = 1. - prefactor * (*lapz).c; + tdm_u[k] = - prefactor * (*lapz).u; + } + // solve all + tdm.solve(tdm_info, linear_system.z2pncl); + // recover original memory alignment + sdecomp.transpose.execute( + linear_system.transposer_z2_to_x1, + linear_system.z2pncl, + linear_system.x1pncl + ); + } +#endif + } + // update temperature field | 19 + { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict dtemp = linear_system.x1pncl; + double * restrict t = fluid->t.data; + BEGIN +#if NDIMS == 2 + T(i, j) += dtemp[cnt]; +#else + T(i, j, k) += dtemp[cnt]; +#endif + END + if (0 != fluid_update_boundaries_t(domain, &fluid->t)) { + return 1; + } + } + return 0; +} + diff --git a/src/fluid/predict/ux.c b/src/fluid/predict/ux.c new file mode 100644 index 00000000..c5509f34 --- /dev/null +++ b/src/fluid/predict/ux.c @@ -0,0 +1,566 @@ +#include "param.h" +#include "memory.h" +#include "runge_kutta.h" +#include "linear_system.h" +#include "tdm.h" +#include "domain.h" +#include "fluid.h" +#include "fluid_solver.h" +#include "internal.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/hxxc.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" +#include "array_macros/fluid/ux.h" +#include "array_macros/fluid/uy.h" +#if NDIMS == 3 +#include "array_macros/fluid/uz.h" +#endif +#include "array_macros/fluid/p.h" +#include "array_macros/fluid/t.h" + +static laplacians_t laplacians = { + .is_initialised = false, +}; + +// map [2 : isize] in code to [0 : isize - 2] in memory +#define LAPX(I) lapx[(I) - 2] + +static int init_laplacians ( + const domain_t * domain +) { + // vector laplacian in x | 15 + { + const int isize = domain->glsizes[0]; + const double * hxxc = domain->hxxc; + const double * jdxf = domain->jdxf; + const double * jdxc = domain->jdxc; + laplacians.lapx = memory_calloc(isize - 1, sizeof(laplacian_t)); + for (int i = 2; i <= isize; i++) { + const double l = 1. / JDXF(i ) * JDXC(i-1) / HXXC(i-1) / HXXC(i-1); + const double u = 1. / JDXF(i ) * JDXC(i ) / HXXC(i ) / HXXC(i ); + const double c = - l - u; + laplacians.LAPX(i).l = l; + laplacians.LAPX(i).c = c; + laplacians.LAPX(i).u = u; + } + } + // vector laplacian in y | 9 + { + const double hy = domain->hy; + const double l = 1. / hy / hy; + const double u = 1. / hy / hy; + const double c = - l - u; + laplacians.lapy.l = l; + laplacians.lapy.c = c; + laplacians.lapy.u = u; + } +#if NDIMS == 3 + // vector laplacian in z | 9 + { + const double hz = domain->hz; + const double l = 1. / hz / hz; + const double u = 1. / hz / hz; + const double c = - l - u; + laplacians.lapz.l = l; + laplacians.lapz.c = c; + laplacians.lapz.u = u; + } +#endif + laplacians.is_initialised = true; + return 0; +} + +#if NDIMS == 2 +#define BEGIN \ + for (int cnt = 0, j = 1; j <= jsize; j++) { \ + for (int i = 2; i <= isize; i++, cnt++) { +#define END \ + } \ + } +#else +#define BEGIN \ + for (int cnt = 0, k = 1; k <= ksize; k++) { \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 2; i <= isize; i++, cnt++) { +#define END \ + } \ + } \ + } +#endif + +// advected in x | 47 +static int advection_x ( + const domain_t * domain, + const double * restrict ux, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict hxxf = domain->hxxf; + const double * restrict jdxf = domain->jdxf; + BEGIN + const double hx_xm = HXXF(i-1); + const double hx_x0 = HXXF(i ); + const double hx_xp = HXXF(i+1); + const double jd_xm = JDXF(i-1); + const double jd_x0 = JDXF(i ); + const double jd_xp = JDXF(i+1); +#if NDIMS == 2 + const double ux_xm = + 0.5 * jd_xm / hx_xm * UX(i-1, j ) + + 0.5 * jd_x0 / hx_x0 * UX(i , j ); + const double ux_xp = + 0.5 * jd_x0 / hx_x0 * UX(i , j ) + + 0.5 * jd_xp / hx_xp * UX(i+1, j ); +#else + const double ux_xm = + 0.5 * jd_xm / hx_xm * UX(i-1, j , k ) + + 0.5 * jd_x0 / hx_x0 * UX(i , j , k ); + const double ux_xp = + 0.5 * jd_x0 / hx_x0 * UX(i , j , k ) + + 0.5 * jd_xp / hx_xp * UX(i+1, j , k ); +#endif + const double l = - 0.5 * ux_xm; + const double u = + 0.5 * ux_xp; + const double c = - l - u; + src[cnt] -= 1. / jd_x0 * ( +#if NDIMS == 2 + + l * UX(i-1, j ) + + c * UX(i , j ) + + u * UX(i+1, j ) +#else + + l * UX(i-1, j , k ) + + c * UX(i , j , k ) + + u * UX(i+1, j , k ) +#endif + ); + END + return 0; +} + +// advected in y | 46 +static int advection_y ( + const domain_t * domain, + const double * restrict ux, + const double * restrict uy, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double hy = domain->hy; + const double * restrict jdxf = domain->jdxf; + const double * restrict jdxc = domain->jdxc; + BEGIN + const double jd_xm = JDXC(i-1); + const double jd_x0 = JDXF(i ); + const double jd_xp = JDXC(i ); +#if NDIMS == 2 + const double uy_ym = + 0.5 * jd_xm / hy * UY(i-1, j ) + + 0.5 * jd_xp / hy * UY(i , j ); + const double uy_yp = + 0.5 * jd_xm / hy * UY(i-1, j+1) + + 0.5 * jd_xp / hy * UY(i , j+1); +#else + const double uy_ym = + 0.5 * jd_xm / hy * UY(i-1, j , k ) + + 0.5 * jd_xp / hy * UY(i , j , k ); + const double uy_yp = + 0.5 * jd_xm / hy * UY(i-1, j+1, k ) + + 0.5 * jd_xp / hy * UY(i , j+1, k ); +#endif + const double l = - 0.5 * uy_ym; + const double u = + 0.5 * uy_yp; + const double c = - l - u; + src[cnt] -= 1. / jd_x0 * ( +#if NDIMS == 2 + + l * UX(i , j-1) + + c * UX(i , j ) + + u * UX(i , j+1) +#else + + l * UX(i , j-1, k ) + + c * UX(i , j , k ) + + u * UX(i , j+1, k ) +#endif + ); + END + return 0; +} + +#if NDIMS == 3 +// advected in z | 31 +static int advection_z ( + const domain_t * domain, + const double * restrict ux, + const double * restrict uz, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double hz = domain->hz; + const double * restrict jdxf = domain->jdxf; + const double * restrict jdxc = domain->jdxc; + BEGIN + const double jd_xm = JDXC(i-1); + const double jd_x0 = JDXF(i ); + const double jd_xp = JDXC(i ); + const double uz_zm = + 0.5 * jd_xm / hz * UZ(i-1, j , k ) + + 0.5 * jd_xp / hz * UZ(i , j , k ); + const double uz_zp = + 0.5 * jd_xm / hz * UZ(i-1, j , k+1) + + 0.5 * jd_xp / hz * UZ(i , j , k+1); + const double l = - 0.5 * uz_zm; + const double u = + 0.5 * uz_zp; + const double c = - l - u; + src[cnt] -= 1. / jd_x0 * ( + + l * UX(i , j , k-1) + + c * UX(i , j , k ) + + u * UX(i , j , k+1) + ); + END + return 0; +} +#endif + +// pressure gradient effect | 24 +static int pressure ( + const domain_t * domain, + const double * restrict p, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict hxxf = domain->hxxf; + BEGIN + src[cnt] -= 1. / HXXF(i ) * ( +#if NDIMS == 2 + - P(i-1, j ) + + P(i , j ) +#else + - P(i-1, j , k ) + + P(i , j , k ) +#endif + ); + END + return 0; +} + +// diffused in x | 27 +static int diffusion_x ( + const domain_t * domain, + const double diffusivity, + const double * restrict ux, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const laplacian_t * lapx = laplacians.lapx; + BEGIN + src[cnt] += diffusivity * ( +#if NDIMS == 2 + + LAPX(i).l * UX(i-1, j ) + + LAPX(i).c * UX(i , j ) + + LAPX(i).u * UX(i+1, j ) +#else + + LAPX(i).l * UX(i-1, j , k ) + + LAPX(i).c * UX(i , j , k ) + + LAPX(i).u * UX(i+1, j , k ) +#endif + ); + END + return 0; +} + +// diffused in y | 27 +static int diffusion_y ( + const domain_t * domain, + const double diffusivity, + const double * restrict ux, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const laplacian_t * lapy = &laplacians.lapy; + BEGIN + src[cnt] += diffusivity * ( +#if NDIMS == 2 + + (*lapy).l * UX(i , j-1) + + (*lapy).c * UX(i , j ) + + (*lapy).u * UX(i , j+1) +#else + + (*lapy).l * UX(i , j-1, k ) + + (*lapy).c * UX(i , j , k ) + + (*lapy).u * UX(i , j+1, k ) +#endif + ); + END + return 0; +} + +#if NDIMS == 3 +// diffused in z | 19 +static int diffusion_z ( + const domain_t * domain, + const double diffusivity, + const double * restrict ux, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const laplacian_t * lapz = &laplacians.lapz; + BEGIN + src[cnt] += diffusivity * ( + + (*lapz).l * UX(i , j , k-1) + + (*lapz).c * UX(i , j , k ) + + (*lapz).u * UX(i , j , k+1) + ); + END + return 0; +} +#endif + +// buoyancy effect | 29 +static int buoyancy ( + const domain_t * domain, + const double * restrict t, + double * restrict src +) { + // impose it only when desired + if (!param_add_buoyancy) { + return 0; + } + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif +#if NDIMS == 2 + BEGIN + src[cnt] += + + 0.5 * T(i-1, j ) + + 0.5 * T(i , j ); + END +#else + BEGIN + src[cnt] += + + 0.5 * T(i-1, j , k ) + + 0.5 * T(i , j , k ); + END +#endif + return 0; +} + +// compute right-hand-side terms, which are added to buffers | 39 +int compute_rhs_ux ( + const domain_t * domain, + fluid_t * fluid +) { + if (!laplacians.is_initialised) { + if (0 != init_laplacians(domain)) { + return 1; + } + } + const double * restrict ux = fluid->ux.data; + const double * restrict uy = fluid->uy.data; +#if NDIMS == 3 + const double * restrict uz = fluid->uz.data; +#endif + const double * restrict p = fluid-> p.data; + const double * restrict t = fluid-> t.data; + // buffer for explicit terms + double * restrict srca = fluid->srcux[rk_a].data; + // buffer for implicit terms + double * restrict srcg = fluid->srcux[rk_g].data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); + // advective contributions, always explicit + advection_x(domain, ux, srca); + advection_y(domain, ux, uy, srca); +#if NDIMS == 3 + advection_z(domain, ux, uz, srca); +#endif + // pressure-gradient contribution, always implicit + pressure(domain, p, srcg); + // diffusive contributions, can be explicit or implicit + diffusion_x(domain, diffusivity, ux, param_m_implicit_x ? srcg : srca); + diffusion_y(domain, diffusivity, ux, param_m_implicit_y ? srcg : srca); +#if NDIMS == 3 + diffusion_z(domain, diffusivity, ux, param_m_implicit_z ? srcg : srca); +#endif + // buoyancy contribution, always explicit + buoyancy(domain, t, srca); + return 0; +} + +// update x velocity field +int update_ux ( + const domain_t * domain, + const size_t rkstep, + const double dt, + fluid_t * fluid +) { + static linear_system_t linear_system = { + .is_initialised = false, + }; + if (!linear_system.is_initialised) { + // if not initialised yet, prepare linear solver + // for implicit diffusive term treatment + const bool implicit[NDIMS] = { + param_m_implicit_x, + param_m_implicit_y, +#if NDIMS == 3 + param_m_implicit_z, +#endif + }; + const size_t glsizes[NDIMS] = { + domain->glsizes[0] - 1, + domain->glsizes[1], +#if NDIMS == 3 + domain->glsizes[2], +#endif + }; + if (0 != linear_system_init(domain->info, implicit, glsizes, &linear_system)) { + return 1; + } + } + // compute increments | 25 + { + const double coef_a = rkcoefs[rkstep][rk_a]; + const double coef_b = rkcoefs[rkstep][rk_b]; + const double coef_g = rkcoefs[rkstep][rk_g]; + const double * restrict srcuxa = fluid->srcux[rk_a].data; + const double * restrict srcuxb = fluid->srcux[rk_b].data; + const double * restrict srcuxg = fluid->srcux[rk_g].data; + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + double * restrict dux = linear_system.x1pncl; +#if NDIMS == 2 + const size_t nitems = (isize - 1) * jsize; +#else + const size_t nitems = (isize - 1) * jsize * ksize; +#endif + for (size_t n = 0; n < nitems; n++) { + dux[n] = + + coef_a * dt * srcuxa[n] + + coef_b * dt * srcuxb[n] + + coef_g * dt * srcuxg[n]; + } + } + // solve linear systems if necessary + { + const double prefactor = + 0.5 * rkcoefs[rkstep][rk_g] * dt * fluid_compute_momentum_diffusivity(fluid); + // solve linear systems in x | 18 + if (param_m_implicit_x) { + tdm_info_t * tdm_info = linear_system.tdm_x; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapx = laplacians.lapx; + for (int i = 0; i < size; i++) { + tdm_l[i] = - prefactor * lapx[i].l; + tdm_c[i] = 1. - prefactor * lapx[i].c; + tdm_u[i] = - prefactor * lapx[i].u; + } + tdm.solve(tdm_info, linear_system.x1pncl); + } + // solve linear systems in y | 28 + if (param_m_implicit_y) { + sdecomp.transpose.execute( + linear_system.transposer_x1_to_y1, + linear_system.x1pncl, + linear_system.y1pncl + ); + tdm_info_t * tdm_info = linear_system.tdm_y; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapy = &laplacians.lapy; + for (int j = 0; j < size; j++) { + tdm_l[j] = - prefactor * (*lapy).l; + tdm_c[j] = 1. - prefactor * (*lapy).c; + tdm_u[j] = - prefactor * (*lapy).u; + } + tdm.solve(tdm_info, linear_system.y1pncl); + sdecomp.transpose.execute( + linear_system.transposer_y1_to_x1, + linear_system.y1pncl, + linear_system.x1pncl + ); + } +#if NDIMS == 3 + // solve linear systems in z | 28 + if (param_m_implicit_z) { + sdecomp.transpose.execute( + linear_system.transposer_x1_to_z2, + linear_system.x1pncl, + linear_system.z2pncl + ); + tdm_info_t * tdm_info = linear_system.tdm_z; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapz = &laplacians.lapz; + for (int k = 0; k < size; k++) { + tdm_l[k] = - prefactor * (*lapz).l; + tdm_c[k] = 1. - prefactor * (*lapz).c; + tdm_u[k] = - prefactor * (*lapz).u; + } + tdm.solve(tdm_info, linear_system.z2pncl); + sdecomp.transpose.execute( + linear_system.transposer_z2_to_x1, + linear_system.z2pncl, + linear_system.x1pncl + ); + } +#endif + } + // update velocity field | 19 + { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict dux = linear_system.x1pncl; + double * restrict ux = fluid->ux.data; + BEGIN +#if NDIMS == 2 + UX(i, j) += dux[cnt]; +#else + UX(i, j, k) += dux[cnt]; +#endif + END + if (0 != fluid_update_boundaries_ux(domain, &fluid->ux)) { + return 1; + } + } + return 0; +} + diff --git a/src/fluid/predict/uy.c b/src/fluid/predict/uy.c new file mode 100644 index 00000000..25b160b3 --- /dev/null +++ b/src/fluid/predict/uy.c @@ -0,0 +1,525 @@ +#include "param.h" +#include "memory.h" +#include "runge_kutta.h" +#include "linear_system.h" +#include "tdm.h" +#include "domain.h" +#include "fluid.h" +#include "fluid_solver.h" +#include "internal.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/hxxc.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" +#include "array_macros/fluid/ux.h" +#include "array_macros/fluid/uy.h" +#if NDIMS == 3 +#include "array_macros/fluid/uz.h" +#endif +#include "array_macros/fluid/p.h" + +static laplacians_t laplacians = { + .is_initialised = false, +}; + +// map [1 : isize] in code to [0 : isize - 1] in memory +#define LAPX(I) lapx[(I) - 1] + +static int init_laplacians( + const domain_t * domain +) { + // vector laplacian in x | 15 + { + const int isize = domain->glsizes[0]; + const double * hxxf = domain->hxxf; + const double * jdxf = domain->jdxf; + const double * jdxc = domain->jdxc; + laplacians.lapx = memory_calloc(isize, sizeof(laplacian_t)); + for (int i = 1; i <= isize; i++) { + const double l = 1. / JDXC(i ) * JDXF(i ) / HXXF(i ) / HXXF(i ); + const double u = 1. / JDXC(i ) * JDXF(i+1) / HXXF(i+1) / HXXF(i+1); + const double c = - l - u; + laplacians.LAPX(i).l = l; + laplacians.LAPX(i).c = c; + laplacians.LAPX(i).u = u; + } + } + // vector laplacian in y | 9 + { + const double hy = domain->hy; + const double l = 1. / hy / hy; + const double u = 1. / hy / hy; + const double c = - l - u; + laplacians.lapy.l = l; + laplacians.lapy.c = c; + laplacians.lapy.u = u; + } +#if NDIMS == 3 + // vector laplacian in z | 9 + { + const double hz = domain->hz; + const double l = 1. / hz / hz; + const double u = 1. / hz / hz; + const double c = - l - u; + laplacians.lapz.l = l; + laplacians.lapz.c = c; + laplacians.lapz.u = u; + } +#endif + laplacians.is_initialised = true; + return 0; +} + +#if NDIMS == 2 +#define BEGIN \ + for (int cnt = 0, j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++, cnt++) { +#define END \ + } \ + } +#else +#define BEGIN \ + for (int cnt = 0, k = 1; k <= ksize; k++) { \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++, cnt++) { +#define END \ + } \ + } \ + } +#endif + +// advected in x | 48 +static int advection_x ( + const domain_t * domain, + const double * restrict uy, + const double * restrict ux, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict hxxf = domain->hxxf; + const double * restrict jdxf = domain->jdxf; + const double * restrict jdxc = domain->jdxc; + BEGIN + const double hx_xm = HXXF(i ); + const double hx_xp = HXXF(i+1); + const double jd_xm = JDXF(i ); + const double jd_x0 = JDXC(i ); + const double jd_xp = JDXF(i+1); +#if NDIMS == 2 + const double ux_xm = + 0.5 * jd_xm / hx_xm * UX(i , j-1) + + 0.5 * jd_xm / hx_xm * UX(i , j ); + const double ux_xp = + 0.5 * jd_xp / hx_xp * UX(i+1, j-1) + + 0.5 * jd_xp / hx_xp * UX(i+1, j ); +#else + const double ux_xm = + 0.5 * jd_xm / hx_xm * UX(i , j-1, k ) + + 0.5 * jd_xm / hx_xm * UX(i , j , k ); + const double ux_xp = + 0.5 * jd_xp / hx_xp * UX(i+1, j-1, k ) + + 0.5 * jd_xp / hx_xp * UX(i+1, j , k ); +#endif + const double l = - 0.5 * ux_xm; + const double u = + 0.5 * ux_xp; + const double c = - l - u; + src[cnt] -= 1. / jd_x0 * ( +#if NDIMS == 2 + + l * UY(i-1, j ) + + c * UY(i , j ) + + u * UY(i+1, j ) +#else + + l * UY(i-1, j , k ) + + c * UY(i , j , k ) + + u * UY(i+1, j , k ) +#endif + ); + END + return 0; +} + +// advected in y | 42 +static int advection_y ( + const domain_t * domain, + const double * restrict uy, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double hy = domain->hy; + const double * restrict jdxc = domain->jdxc; + BEGIN + const double jd = JDXC(i ); +#if NDIMS == 2 + const double uy_ym = + 0.5 * jd / hy * UY(i , j-1) + + 0.5 * jd / hy * UY(i , j ); + const double uy_yp = + 0.5 * jd / hy * UY(i , j ) + + 0.5 * jd / hy * UY(i , j+1); +#else + const double uy_ym = + 0.5 * jd / hy * UY(i , j-1, k ) + + 0.5 * jd / hy * UY(i , j , k ); + const double uy_yp = + 0.5 * jd / hy * UY(i , j , k ) + + 0.5 * jd / hy * UY(i , j+1, k ); +#endif + const double l = - 0.5 * uy_ym; + const double u = + 0.5 * uy_yp; + const double c = - l - u; + src[cnt] -= 1. / jd * ( +#if NDIMS == 2 + + l * UY(i , j-1) + + c * UY(i , j ) + + u * UY(i , j+1) +#else + + l * UY(i , j-1, k ) + + c * UY(i , j , k ) + + u * UY(i , j+1, k ) +#endif + ); + END + return 0; +} + +#if NDIMS == 3 +// advected in z | 28 +static int advection_z ( + const domain_t * domain, + const double * restrict uy, + const double * restrict uz, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double hz = domain->hz; + const double * restrict jdxc = domain->jdxc; + BEGIN + const double jd = JDXC(i ); + const double uz_zm = + 0.5 * jd / hz * UZ(i , j-1, k ) + + 0.5 * jd / hz * UZ(i , j , k ); + const double uz_zp = + 0.5 * jd / hz * UZ(i , j-1, k+1) + + 0.5 * jd / hz * UZ(i , j , k+1); + const double l = - 0.5 * uz_zm; + const double u = + 0.5 * uz_zp; + const double c = - l - u; + src[cnt] -= 1. / jd * ( + + l * UY(i , j , k-1) + + c * UY(i , j , k ) + + u * UY(i , j , k+1) + ); + END + return 0; +} +#endif + +// pressure gradient effect | 24 +static int pressure ( + const domain_t * domain, + const double * restrict p, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double hy = domain->hy; + BEGIN + src[cnt] -= 1. / hy * ( +#if NDIMS == 2 + - P(i , j-1) + + P(i , j ) +#else + - P(i , j-1, k ) + + P(i , j , k ) +#endif + ); + END + return 0; +} + +// diffused in x | 27 +static int diffusion_x ( + const domain_t * domain, + const double diffusivity, + const double * restrict uy, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const laplacian_t * lapx = laplacians.lapx; + BEGIN + src[cnt] += diffusivity * ( +#if NDIMS == 2 + + LAPX(i).l * UY(i-1, j ) + + LAPX(i).c * UY(i , j ) + + LAPX(i).u * UY(i+1, j ) +#else + + LAPX(i).l * UY(i-1, j , k ) + + LAPX(i).c * UY(i , j , k ) + + LAPX(i).u * UY(i+1, j , k ) +#endif + ); + END + return 0; +} + +// diffused in y | 27 +static int diffusion_y ( + const domain_t * domain, + const double diffusivity, + const double * restrict uy, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const laplacian_t * lapy = &laplacians.lapy; + BEGIN + src[cnt] += diffusivity * ( +#if NDIMS == 2 + + (*lapy).l * UY(i , j-1) + + (*lapy).c * UY(i , j ) + + (*lapy).u * UY(i , j+1) +#else + + (*lapy).l * UY(i , j-1, k ) + + (*lapy).c * UY(i , j , k ) + + (*lapy).u * UY(i , j+1, k ) +#endif + ); + END + return 0; +} + +#if NDIMS == 3 +// diffused in z | 19 +static int diffusion_z ( + const domain_t * domain, + const double diffusivity, + const double * restrict uy, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const laplacian_t * lapz = &laplacians.lapz; + BEGIN + src[cnt] += diffusivity * ( + + (*lapz).l * UY(i , j , k-1) + + (*lapz).c * UY(i , j , k ) + + (*lapz).u * UY(i , j , k+1) + ); + END + return 0; +} +#endif + +// compute right-hand-side terms, which are added to buffers | 36 +int compute_rhs_uy( + const domain_t * domain, + fluid_t * fluid +) { + if (!laplacians.is_initialised) { + if (0 != init_laplacians(domain)) { + return 1; + } + } + const double * restrict ux = fluid->ux.data; + const double * restrict uy = fluid->uy.data; +#if NDIMS == 3 + const double * restrict uz = fluid->uz.data; +#endif + const double * restrict p = fluid-> p.data; + // buffer for explicit terms + double * restrict srca = fluid->srcuy[rk_a].data; + // buffer for implicit terms + double * restrict srcg = fluid->srcuy[rk_g].data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); + // advective contributions, always explicit + advection_x(domain, uy, ux, srca); + advection_y(domain, uy, srca); +#if NDIMS == 3 + advection_z(domain, uy, uz, srca); +#endif + // pressure-gradient contribution, always implicit + pressure(domain, p, srcg); + // diffusive contributions, can be explicit or implicit + diffusion_x(domain, diffusivity, uy, param_m_implicit_x ? srcg : srca); + diffusion_y(domain, diffusivity, uy, param_m_implicit_y ? srcg : srca); +#if NDIMS == 3 + diffusion_z(domain, diffusivity, uy, param_m_implicit_z ? srcg : srca); +#endif + return 0; +} + +// update y velocity field +int update_uy( + const domain_t * domain, + const size_t rkstep, + const double dt, + fluid_t * fluid +) { + static linear_system_t linear_system = { + .is_initialised = false, + }; + if (!linear_system.is_initialised) { + // if not initialised yet, prepare linear solver + // for implicit diffusive term treatment + const bool implicit[NDIMS] = { + param_m_implicit_x, + param_m_implicit_y, +#if NDIMS == 3 + param_m_implicit_z, +#endif + }; + const size_t glsizes[NDIMS] = { + domain->glsizes[0], + domain->glsizes[1], +#if NDIMS == 3 + domain->glsizes[2], +#endif + }; + if (0 != linear_system_init(domain->info, implicit, glsizes, &linear_system)) { + return 1; + } + } + // compute increments | 25 + { + const double coef_a = rkcoefs[rkstep][rk_a]; + const double coef_b = rkcoefs[rkstep][rk_b]; + const double coef_g = rkcoefs[rkstep][rk_g]; + const double * restrict srcuya = fluid->srcuy[rk_a].data; + const double * restrict srcuyb = fluid->srcuy[rk_b].data; + const double * restrict srcuyg = fluid->srcuy[rk_g].data; + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + double * restrict duy = linear_system.x1pncl; +#if NDIMS == 2 + const size_t nitems = isize * jsize; +#else + const size_t nitems = isize * jsize * ksize; +#endif + for (size_t n = 0; n < nitems; n++) { + duy[n] = + + coef_a * dt * srcuya[n] + + coef_b * dt * srcuyb[n] + + coef_g * dt * srcuyg[n]; + } + } + // solve linear systems if necessary + { + const double prefactor = + 0.5 * rkcoefs[rkstep][rk_g] * dt * fluid_compute_momentum_diffusivity(fluid); + // solve linear systems in x | 18 + if (param_m_implicit_x) { + tdm_info_t * tdm_info = linear_system.tdm_x; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapx = laplacians.lapx; + for (int i = 0; i < size; i++) { + tdm_l[i] = - prefactor * lapx[i].l; + tdm_c[i] = 1. - prefactor * lapx[i].c; + tdm_u[i] = - prefactor * lapx[i].u; + } + tdm.solve(tdm_info, linear_system.x1pncl); + } + // solve linear systems in y | 28 + if (param_m_implicit_y) { + sdecomp.transpose.execute( + linear_system.transposer_x1_to_y1, + linear_system.x1pncl, + linear_system.y1pncl + ); + tdm_info_t * tdm_info = linear_system.tdm_y; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapy = &laplacians.lapy; + for (int j = 0; j < size; j++) { + tdm_l[j] = - prefactor * (*lapy).l; + tdm_c[j] = 1. - prefactor * (*lapy).c; + tdm_u[j] = - prefactor * (*lapy).u; + } + tdm.solve(tdm_info, linear_system.y1pncl); + sdecomp.transpose.execute( + linear_system.transposer_y1_to_x1, + linear_system.y1pncl, + linear_system.x1pncl + ); + } +#if NDIMS == 3 + // solve linear systems in z | 28 + if (param_m_implicit_z) { + sdecomp.transpose.execute( + linear_system.transposer_x1_to_z2, + linear_system.x1pncl, + linear_system.z2pncl + ); + tdm_info_t * tdm_info = linear_system.tdm_z; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapz = &laplacians.lapz; + for (int k = 0; k < size; k++) { + tdm_l[k] = - prefactor * (*lapz).l; + tdm_c[k] = 1. - prefactor * (*lapz).c; + tdm_u[k] = - prefactor * (*lapz).u; + } + tdm.solve(tdm_info, linear_system.z2pncl); + sdecomp.transpose.execute( + linear_system.transposer_z2_to_x1, + linear_system.z2pncl, + linear_system.x1pncl + ); + } +#endif + } + // update velocity field | 19 + { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict duy = linear_system.x1pncl; + double * restrict uy = fluid->uy.data; + BEGIN +#if NDIMS == 2 + UY(i, j) += duy[cnt]; +#else + UY(i, j, k) += duy[cnt]; +#endif + END + if (0 != fluid_update_boundaries_uy(domain, &fluid->uy)) { + return 1; + } + } + return 0; +} + diff --git a/src/fluid/predict/uz.c b/src/fluid/predict/uz.c new file mode 100644 index 00000000..42ac9c9a --- /dev/null +++ b/src/fluid/predict/uz.c @@ -0,0 +1,431 @@ +#if NDIMS == 3 +#include "param.h" +#include "memory.h" +#include "runge_kutta.h" +#include "linear_system.h" +#include "tdm.h" +#include "domain.h" +#include "fluid.h" +#include "fluid_solver.h" +#include "internal.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" +#include "array_macros/fluid/ux.h" +#include "array_macros/fluid/uy.h" +#include "array_macros/fluid/uz.h" +#include "array_macros/fluid/p.h" + +static laplacians_t laplacians = { + .is_initialised = false, +}; + +// map [1 : isize] in code to [0 : isize - 1] in memory +#define LAPX(I) lapx[(I) - 1] + +static int init_laplacians ( + const domain_t * domain +) { + // vector laplacian in x | 15 + { + const int isize = domain->glsizes[0]; + const double * hxxf = domain->hxxf; + const double * jdxf = domain->jdxf; + const double * jdxc = domain->jdxc; + laplacians.lapx = memory_calloc(isize, sizeof(laplacian_t)); + for (int i = 1; i <= isize; i++) { + const double l = 1. / JDXC(i ) * JDXF(i ) / HXXF(i ) / HXXF(i ); + const double u = 1. / JDXC(i ) * JDXF(i+1) / HXXF(i+1) / HXXF(i+1); + const double c = - l - u; + laplacians.LAPX(i).l = l; + laplacians.LAPX(i).c = c; + laplacians.LAPX(i).u = u; + } + } + // vector laplacian in y | 9 + { + const double hy = domain->hy; + const double l = 1. / hy / hy; + const double u = 1. / hy / hy; + const double c = - l - u; + laplacians.lapy.l = l; + laplacians.lapy.c = c; + laplacians.lapy.u = u; + } + // vector laplacian in z | 9 + { + const double hz = domain->hz; + const double l = 1. / hz / hz; + const double u = 1. / hz / hz; + const double c = - l - u; + laplacians.lapz.l = l; + laplacians.lapz.c = c; + laplacians.lapz.u = u; + } + laplacians.is_initialised = true; + return 0; +} + +#define BEGIN \ + for (int cnt = 0, k = 1; k <= ksize; k++) { \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++, cnt++) { +#define END \ + } \ + } \ + } + +// advected in x | 33 +static int advection_x ( + const domain_t * domain, + const double * restrict uz, + const double * restrict ux, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double * restrict hxxf = domain->hxxf; + const double * restrict jdxf = domain->jdxf; + const double * restrict jdxc = domain->jdxc; + BEGIN + const double hx_xm = HXXF(i ); + const double hx_xp = HXXF(i+1); + const double jd_xm = JDXF(i ); + const double jd_x0 = JDXC(i ); + const double jd_xp = JDXF(i+1); + const double ux_xm = + 0.5 * jd_xm / hx_xm * UX(i , j , k-1) + + 0.5 * jd_xm / hx_xm * UX(i , j , k ); + const double ux_xp = + 0.5 * jd_xp / hx_xp * UX(i+1, j , k-1) + + 0.5 * jd_xp / hx_xp * UX(i+1, j , k ); + const double l = - 0.5 * ux_xm; + const double u = + 0.5 * ux_xp; + const double c = - l - u; + src[cnt] -= 1. / jd_x0 * ( + + l * UZ(i-1, j , k ) + + c * UZ(i , j , k ) + + u * UZ(i+1, j , k ) + ); + END + return 0; +} + +// advected in y | 28 +static int advection_y ( + const domain_t * domain, + const double * restrict uz, + const double * restrict uy, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double hy = domain->hy; + const double * restrict jdxc = domain->jdxc; + BEGIN + const double jd = JDXC(i ); + const double uy_ym = + 0.5 * jd / hy * UY(i , j , k-1) + + 0.5 * jd / hy * UY(i , j , k ); + const double uy_yp = + 0.5 * jd / hy * UY(i , j+1, k-1) + + 0.5 * jd / hy * UY(i , j+1, k ); + const double l = - 0.5 * uy_ym; + const double u = + 0.5 * uy_yp; + const double c = - l - u; + src[cnt] -= 1. / jd * ( + + l * UZ(i , j-1, k ) + + c * UZ(i , j , k ) + + u * UZ(i , j+1, k ) + ); + END + return 0; +} + +// advected in z | 27 +static int advection_z ( + const domain_t * domain, + const double * restrict uz, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double hz = domain->hz; + const double * restrict jdxc = domain->jdxc; + BEGIN + const double jd = JDXC(i ); + const double uz_zm = + 0.5 * jd / hz * UZ(i , j , k-1) + + 0.5 * jd / hz * UZ(i , j , k ); + const double uz_zp = + 0.5 * jd / hz * UZ(i , j , k ) + + 0.5 * jd / hz * UZ(i , j , k+1); + const double l = - 0.5 * uz_zm; + const double u = + 0.5 * uz_zp; + const double c = - l - u; + src[cnt] -= 1. / jd * ( + + l * UZ(i , j , k-1) + + c * UZ(i , j , k ) + + u * UZ(i , j , k+1) + ); + END + return 0; +} + +// pressure gradient effect | 17 +static int pressure ( + const domain_t * domain, + const double * restrict p, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double hz = domain->hz; + BEGIN + src[cnt] -= 1. / hz * ( + - P(i , j , k-1) + + P(i , j , k ) + ); + END + return 0; +} + +// diffused in x | 19 +static int diffusion_x ( + const domain_t * domain, + const double diffusivity, + const double * restrict uz, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const laplacian_t * lapx = laplacians.lapx; + BEGIN + src[cnt] += diffusivity * ( + + LAPX(i).l * UZ(i-1, j , k ) + + LAPX(i).c * UZ(i , j , k ) + + LAPX(i).u * UZ(i+1, j , k ) + ); + END + return 0; +} + +// diffused in y | 19 +static int diffusion_y ( + const domain_t * domain, + const double diffusivity, + const double * restrict uz, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const laplacian_t * lapy = &laplacians.lapy; + BEGIN + src[cnt] += diffusivity * ( + + (*lapy).l * UZ(i , j-1, k ) + + (*lapy).c * UZ(i , j , k ) + + (*lapy).u * UZ(i , j+1, k ) + ); + END + return 0; +} + +// diffused in z | 19 +static int diffusion_z ( + const domain_t * domain, + const double diffusivity, + const double * restrict uz, + double * restrict src +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const laplacian_t * lapz = &laplacians.lapz; + BEGIN + src[cnt] += diffusivity * ( + + (*lapz).l * UZ(i , j , k-1) + + (*lapz).c * UZ(i , j , k ) + + (*lapz).u * UZ(i , j , k+1) + ); + END + return 0; +} + +// compute right-hand-side terms, which are added to buffers | 30 +int compute_rhs_uz ( + const domain_t * domain, + fluid_t * fluid +) { + if (!laplacians.is_initialised) { + if (0 != init_laplacians(domain)) { + return 1; + } + } + const double * restrict ux = fluid->ux.data; + const double * restrict uy = fluid->uy.data; + const double * restrict uz = fluid->uz.data; + const double * restrict p = fluid-> p.data; + // buffer for explicit terms + double * restrict srca = fluid->srcuz[rk_a].data; + // buffer for implicit terms + double * restrict srcg = fluid->srcuz[rk_g].data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); + // advective contributions, always explicit + advection_x(domain, uz, ux, srca); + advection_y(domain, uz, uy, srca); + advection_z(domain, uz, srca); + // pressure-gradient contribution, always implicit + pressure(domain, p, srcg); + // diffusive contributions, can be explicit or implicit + diffusion_x(domain, diffusivity, uz, param_m_implicit_x ? srcg : srca); + diffusion_y(domain, diffusivity, uz, param_m_implicit_y ? srcg : srca); + diffusion_z(domain, diffusivity, uz, param_m_implicit_z ? srcg : srca); + return 0; +} + +// update z velocity field +int update_uz ( + const domain_t * domain, + const size_t rkstep, + const double dt, + fluid_t * fluid +) { + static linear_system_t linear_system = { + .is_initialised = false, + }; + if (!linear_system.is_initialised) { + // if not initialised yet, prepare linear solver + // for implicit diffusive term treatment + const bool implicit[NDIMS] = { + param_m_implicit_x, + param_m_implicit_y, + param_m_implicit_z, + }; + const size_t glsizes[NDIMS] = { + domain->glsizes[0], + domain->glsizes[1], + domain->glsizes[2], + }; + if (0 != linear_system_init(domain->info, implicit, glsizes, &linear_system)) { + return 1; + } + } + // compute increments | 19 + { + const double coef_a = rkcoefs[rkstep][rk_a]; + const double coef_b = rkcoefs[rkstep][rk_b]; + const double coef_g = rkcoefs[rkstep][rk_g]; + const double * restrict srcuza = fluid->srcuz[rk_a].data; + const double * restrict srcuzb = fluid->srcuz[rk_b].data; + const double * restrict srcuzg = fluid->srcuz[rk_g].data; + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + double * restrict duz = linear_system.x1pncl; + const size_t nitems = isize * jsize * ksize; + for (size_t n = 0; n < nitems; n++) { + duz[n] = + + coef_a * dt * srcuza[n] + + coef_b * dt * srcuzb[n] + + coef_g * dt * srcuzg[n]; + } + } + // solve linear systems if necessary + { + const double prefactor = + 0.5 * rkcoefs[rkstep][rk_g] * dt * fluid_compute_momentum_diffusivity(fluid); + // solve linear systems in x | 18 + if (param_m_implicit_x) { + tdm_info_t * tdm_info = linear_system.tdm_x; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapx = laplacians.lapx; + for (int i = 0; i < size; i++) { + tdm_l[i] = - prefactor * lapx[i].l; + tdm_c[i] = 1. - prefactor * lapx[i].c; + tdm_u[i] = - prefactor * lapx[i].u; + } + tdm.solve(tdm_info, linear_system.x1pncl); + } + // solve linear systems in y | 28 + if (param_m_implicit_y) { + sdecomp.transpose.execute( + linear_system.transposer_x1_to_y1, + linear_system.x1pncl, + linear_system.y1pncl + ); + tdm_info_t * tdm_info = linear_system.tdm_y; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapy = &laplacians.lapy; + for (int j = 0; j < size; j++) { + tdm_l[j] = - prefactor * (*lapy).l; + tdm_c[j] = 1. - prefactor * (*lapy).c; + tdm_u[j] = - prefactor * (*lapy).u; + } + tdm.solve(tdm_info, linear_system.y1pncl); + sdecomp.transpose.execute( + linear_system.transposer_y1_to_x1, + linear_system.y1pncl, + linear_system.x1pncl + ); + } + // solve linear systems in z | 28 + if (param_m_implicit_z) { + sdecomp.transpose.execute( + linear_system.transposer_x1_to_z2, + linear_system.x1pncl, + linear_system.z2pncl + ); + tdm_info_t * tdm_info = linear_system.tdm_z; + int size = 0; + double * restrict tdm_l = NULL; + double * restrict tdm_c = NULL; + double * restrict tdm_u = NULL; + tdm.get_size(tdm_info, &size); + tdm.get_l(tdm_info, &tdm_l); + tdm.get_c(tdm_info, &tdm_c); + tdm.get_u(tdm_info, &tdm_u); + const laplacian_t * lapz = &laplacians.lapz; + for (int k = 0; k < size; k++) { + tdm_l[k] = - prefactor * (*lapz).l; + tdm_c[k] = 1. - prefactor * (*lapz).c; + tdm_u[k] = - prefactor * (*lapz).u; + } + tdm.solve(tdm_info, linear_system.z2pncl); + sdecomp.transpose.execute( + linear_system.transposer_z2_to_x1, + linear_system.z2pncl, + linear_system.x1pncl + ); + } + } + // update velocity field | 13 + { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double * restrict duz = linear_system.x1pncl; + double * restrict uz = fluid->uz.data; + BEGIN + UZ(i, j, k) += duz[cnt]; + END + if (0 != fluid_update_boundaries_uz(domain, &fluid->uz)) { + return 1; + } + } + return 0; +} +#endif diff --git a/src/fluid/save.c b/src/fluid/save.c index 0b0aa9b9..b320d727 100644 --- a/src/fluid/save.c +++ b/src/fluid/save.c @@ -15,8 +15,8 @@ int fluid_save( int myrank = root; sdecomp.get_comm_rank(domain->info, &myrank); if(root == myrank){ - fileio.w_serial(dirname, "m_dif", 0, NULL, fileio.npy_double, sizeof(double), &fluid->m_dif); - fileio.w_serial(dirname, "t_dif", 0, NULL, fileio.npy_double, sizeof(double), &fluid->t_dif); + fileio.w_serial(dirname, "Ra", 0, NULL, fileio.npy_double, sizeof(double), &fluid->Ra); + fileio.w_serial(dirname, "Pr", 0, NULL, fileio.npy_double, sizeof(double), &fluid->Pr); } // collective array.dump(domain, dirname, "ux", fileio.npy_double, &fluid->ux); diff --git a/src/fluid/update_pressure.c b/src/fluid/update_pressure.c index 63c945d5..f1dc74c4 100644 --- a/src/fluid/update_pressure.c +++ b/src/fluid/update_pressure.c @@ -1,36 +1,37 @@ #include "param.h" #include "runge_kutta.h" -#include "memory.h" #include "domain.h" #include "fluid.h" #include "fluid_solver.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" #include "array_macros/fluid/p.h" #include "array_macros/fluid/psi.h" #if NDIMS == 2 #define BEGIN \ - for(int j = 1; j <= jsize; j++){ \ - for(int i = 1; i <= isize; i++){ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++) { #define END \ } \ } #else #define BEGIN \ - for(int k = 1; k <= ksize; k++){ \ - for(int j = 1; j <= jsize; j++){ \ - for(int i = 1; i <= isize; i++){ + for (int k = 1; k <= ksize; k++) { \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++) { #define END \ } \ } \ } #endif -static inline int add_explicit( +// explicit contribution | 20 +static int explicit_contribution ( const domain_t * domain, const fluid_t * fluid -){ +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; #if NDIMS == 3 @@ -38,154 +39,154 @@ static inline int add_explicit( #endif const double * restrict psi = fluid->psi.data; double * restrict p = fluid->p.data; -#if NDIMS == 2 BEGIN - // explicit contribution | 1 +#if NDIMS == 2 P(i, j) += PSI(i, j); - END #else - BEGIN - // explicit contribution | 1 P(i, j, k) += PSI(i, j, k); - END #endif + END return 0; } -static inline int add_implicit_x( +// x implicit contribution | 43 +static int implicit_x_contribution ( const domain_t * domain, const double prefactor, fluid_t * fluid -){ +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; #if NDIMS == 3 const int ksize = domain->mysizes[2]; #endif - const double * restrict dxf = domain->dxf; - const double * restrict dxc = domain->dxc; + const double * restrict hxxf = domain->hxxf; + const double * restrict jdxf = domain->jdxf; + const double * restrict jdxc = domain->jdxc; const double * restrict psi = fluid->psi.data; double * restrict p = fluid->p.data; -#if NDIMS == 2 BEGIN - // x implicit contribution | 6 - const double dpsidx_xm = (- PSI(i-1, j ) + PSI(i , j )) / DXC(i ); - const double dpsidx_xp = (- PSI(i , j ) + PSI(i+1, j )) / DXC(i+1); - P(i, j) -= prefactor / DXF(i ) * ( - - dpsidx_xm - + dpsidx_xp - ); - END + const double hx_xm = HXXF(i ); + const double hx_xp = HXXF(i+1); + const double jd_xm = JDXF(i ); + const double jd_x0 = JDXC(i ); + const double jd_xp = JDXF(i+1); + const double l = 1. / jd_x0 * jd_xm / hx_xm / hx_xm; + const double u = 1. / jd_x0 * jd_xp / hx_xp / hx_xp; + const double c = - l - u; +#if NDIMS == 2 + const double psi_xm = PSI(i-1, j ); + const double psi_x0 = PSI(i , j ); + const double psi_xp = PSI(i+1, j ); + double * pre = &P(i, j); #else - BEGIN - // x implicit contribution | 6 - const double dpsidx_xm = (- PSI(i-1, j , k ) + PSI(i , j , k )) / DXC(i ); - const double dpsidx_xp = (- PSI(i , j , k ) + PSI(i+1, j , k )) / DXC(i+1); - P(i, j, k) -= prefactor / DXF(i ) * ( - - dpsidx_xm - + dpsidx_xp + const double psi_xm = PSI(i-1, j , k ); + const double psi_x0 = PSI(i , j , k ); + const double psi_xp = PSI(i+1, j , k ); + double * pre = &P(i, j, k); +#endif + *pre -= prefactor * ( + + l * psi_xm + + c * psi_x0 + + u * psi_xp ); END -#endif return 0; } -static inline int add_implicit_y( +// y implicit contribution | 36 +static int implicit_y_contribution ( const domain_t * domain, const double prefactor, fluid_t * fluid -){ +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; #if NDIMS == 3 const int ksize = domain->mysizes[2]; #endif - const double dy = domain->dy; + const double hy = domain->hy; const double * restrict psi = fluid->psi.data; double * restrict p = fluid->p.data; -#if NDIMS == 2 BEGIN - // y implicit contribution | 6 - const double dpsidy_ym = (- PSI(i , j-1) + PSI(i , j )) / dy; - const double dpsidy_yp = (- PSI(i , j ) + PSI(i , j+1)) / dy; - P(i, j) -= prefactor / dy * ( - - dpsidy_ym - + dpsidy_yp - ); - END + const double l = 1. / hy / hy; + const double u = 1. / hy / hy; + const double c = - l - u; +#if NDIMS == 2 + const double psi_ym = PSI(i , j-1); + const double psi_y0 = PSI(i , j ); + const double psi_yp = PSI(i , j+1); + double * pre = &P(i, j); #else - BEGIN - // y implicit contribution | 6 - const double dpsidy_ym = (- PSI(i , j-1, k ) + PSI(i , j , k )) / dy; - const double dpsidy_yp = (- PSI(i , j , k ) + PSI(i , j+1, k )) / dy; - P(i, j, k) -= prefactor / dy * ( - - dpsidy_ym - + dpsidy_yp + const double psi_ym = PSI(i , j-1, k ); + const double psi_y0 = PSI(i , j , k ); + const double psi_yp = PSI(i , j+1, k ); + double * pre = &P(i, j, k); +#endif + *pre -= prefactor * ( + + l * psi_ym + + c * psi_y0 + + u * psi_yp ); END -#endif return 0; } #if NDIMS == 3 -static inline int add_implicit_z( +// z implicit contribution | 26 +static int implicit_z_contribution ( const domain_t * domain, const double prefactor, fluid_t * fluid -){ +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; const int ksize = domain->mysizes[2]; - const double dz = domain->dz; + const double hz = domain->hz; const double * restrict psi = fluid->psi.data; double * restrict p = fluid->p.data; BEGIN - // z implicit contribution | 6 - const double dpsidz_zm = (- PSI(i , j , k-1) + PSI(i , j , k )) / dz; - const double dpsidz_zp = (- PSI(i , j , k ) + PSI(i , j , k+1)) / dz; - P(i, j, k) -= prefactor / dz * ( - - dpsidz_zm - + dpsidz_zp + const double l = 1. / hz / hz; + const double u = 1. / hz / hz; + const double c = - l - u; + const double psi_zm = PSI(i , j , k-1); + const double psi_z0 = PSI(i , j , k ); + const double psi_zp = PSI(i , j , k+1); + P(i, j, k) -= prefactor * ( + + l * psi_zm + + c * psi_z0 + + u * psi_zp ); END return 0; } #endif -/** - * @brief update pressure using scalar potential psi - * @param[in] domain : information related to MPI domain decomposition - * @param[in] rkstep : Runge-Kutta step - * @param[in] dt : time step size - * @param[in,out] fluid : scalar potential (in), pressure (out) - * @return : error code - */ -int fluid_update_pressure( +// update pressure field using scalar potential | 28 +int fluid_update_pressure ( const domain_t * domain, const size_t rkstep, const double dt, fluid_t * fluid -){ +) { // explicit contribution, always present - add_explicit(domain, fluid); - // gamma dt diffusivity / 2 | 2 + explicit_contribution(domain, fluid); const double prefactor = - 0.5 * rkcoefs[rkstep][rk_g] * dt * fluid->m_dif; - // additional corrections if diffusive terms - // in the direction is treated implicitly - if(param_m_implicit_x){ - add_implicit_x(domain, prefactor, fluid); + 0.5 * rkcoefs[rkstep][rk_g] * dt * fluid_compute_momentum_diffusivity(fluid); + // additional terms if diffusive terms in the direction is treated implicitly + if (param_m_implicit_x) { + implicit_x_contribution(domain, prefactor, fluid); } - if(param_m_implicit_y){ - add_implicit_y(domain, prefactor, fluid); + if (param_m_implicit_y) { + implicit_y_contribution(domain, prefactor, fluid); } #if NDIMS == 3 - if(param_m_implicit_z){ - add_implicit_z(domain, prefactor, fluid); + if (param_m_implicit_z) { + implicit_z_contribution(domain, prefactor, fluid); } #endif - // impose boundary conditions and communicate halo cells | 3 - if(0 != fluid_update_boundaries_p(domain, &fluid->p)){ + // impose boundary conditions and communicate halo cells + if (0 != fluid_update_boundaries_p(domain, &fluid->p)) { return 1; } return 0; diff --git a/src/integrate.c b/src/integrate.c index c3a3f5b9..b7779402 100644 --- a/src/integrate.c +++ b/src/integrate.c @@ -18,16 +18,7 @@ int integrate( // Runge-Kutta iterations // max iteration, should be three for(size_t rkstep = 0; rkstep < RKSTEPMAX; rkstep++){ - // predict flow field | 14 - // compute right-hand-side terms of RK scheme - if(0 != fluid_compute_rhs(domain, fluid)){ - return 1; - } - // couple external factors, by default buoyancy force - if(0 != fluid_couple_external_force(domain, fluid)){ - return 1; - } - // update flow field + // predict flow field | 5 // NOTE: while the temperature is fully updated here, // the velocity field is still non-solenoidal if(0 != fluid_predict_field(domain, rkstep, *dt, fluid)){ diff --git a/src/logging/README.rst b/src/logging/README.rst deleted file mode 100644 index 80d66560..00000000 --- a/src/logging/README.rst +++ /dev/null @@ -1,26 +0,0 @@ -######## -logging/ -######## - -Functions to monitor the running simulation. - -* divergence.c - - Compute and output maximum local divergence of the flow field. - -* energy.c - - Compute and output the total squared velocity (in each direction) and the total squared temperature of the flow field. - -* internal.h - - Private functions which are only used by this directory is declared. - -* main.c - - Call other logging functions. - -* momentum.c - - Compute and output the net momentum in each direction. - diff --git a/src/logging/dissipated_squared_temperature.c b/src/logging/dissipated_squared_temperature.c new file mode 100644 index 00000000..a5a5d37d --- /dev/null +++ b/src/logging/dissipated_squared_temperature.c @@ -0,0 +1,161 @@ +#include +#include "domain.h" +#include "fluid.h" +#include "fluid_solver.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" +#include "array_macros/fluid/t.h" +#include "internal.h" + +// dtdx component | 41 +static int get_dtdx ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict hxxf = domain->hxxf; + const double * restrict jdxf = domain->jdxf; + const double * restrict t = fluid->t.data; + const double diffusivity = fluid_compute_temperature_diffusivity(fluid); +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + const double hx = HXXF(i); + const double jd = JDXF(i); + const double dt = + - T(i-1, j ) + + T(i , j ); + *quantity += diffusivity * jd * pow(1. / hx * dt, 2.); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + const double hx = HXXF(i); + const double jd = JDXF(i); + const double dt = + - T(i-1, j , k ) + + T(i , j , k ); + *quantity += diffusivity * jd * pow(1. / hx * dt, 2.); + } + } + } +#endif + return 0; +} + +// dtdy component | 39 +static int get_dtdy ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double hy = domain->hy; + const double * restrict jdxc = domain->jdxc; + const double * restrict t = fluid->t.data; + const double diffusivity = fluid_compute_temperature_diffusivity(fluid); +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double jd = JDXC(i); + const double dt = + - T(i , j-1) + + T(i , j ); + *quantity += diffusivity * jd * pow(1. / hy * dt, 2.); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double jd = JDXC(i); + const double dt = + - T(i , j-1, k ) + + T(i , j , k ); + *quantity += diffusivity * jd * pow(1. / hy * dt, 2.); + } + } + } +#endif + return 0; +} + +#if NDIMS == 3 +// dtdz component | 25 +static int get_dtdz ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double hz = domain->hz; + const double * restrict jdxc = domain->jdxc; + const double * restrict t = fluid->t.data; + const double diffusivity = fluid_compute_temperature_diffusivity(fluid); + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double jd = JDXC(i); + const double dt = + - T(i , j , k-1) + + T(i , j , k ); + *quantity += diffusivity * jd * pow(1. / hz * dt, 2.); + } + } + } + return 0; +} +#endif + +/** + * @brief compute dissipation of squared temperature + * @param[in] fname : file name to which the log is written + * @param[in] domain : information related to MPI domain decomposition + * @param[in] time : current simulation time + * @param[in] fluid : diffusivity and temperature field + * @return : error code + */ +int logging_check_dissipated_squared_temperature ( + const char fname[], + const domain_t * domain, + const double time, + const fluid_t * fluid +) { + const int root = 0; + int myrank = root; + MPI_Comm comm_cart = MPI_COMM_NULL; + sdecomp.get_comm_rank(domain->info, &myrank); + sdecomp.get_comm_cart(domain->info, &comm_cart); + double quantity = 0.; + get_dtdx(domain, fluid, &quantity); + get_dtdy(domain, fluid, &quantity); +#if NDIMS == 3 + get_dtdz(domain, fluid, &quantity); +#endif + const void * sendbuf = root == myrank ? MPI_IN_PLACE : &quantity; + void * recvbuf = &quantity; + MPI_Reduce(sendbuf, recvbuf, 1, MPI_DOUBLE, MPI_SUM, root, comm_cart); + logging_internal_output( + fname, + domain, + time, + 1, + &quantity + ); + return 0; +} + diff --git a/src/logging/dissipated_squared_velocity.c b/src/logging/dissipated_squared_velocity.c new file mode 100644 index 00000000..be5c09f2 --- /dev/null +++ b/src/logging/dissipated_squared_velocity.c @@ -0,0 +1,377 @@ +#include +#include "domain.h" +#include "fluid.h" +#include "fluid_solver.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/hxxc.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" +#include "array_macros/fluid/ux.h" +#include "array_macros/fluid/uy.h" +#if NDIMS == 3 +#include "array_macros/fluid/uz.h" +#endif +#include "internal.h" + +// duxdx component | 42 +static int get_duxdx ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict hxxc = domain->hxxc; + const double * restrict jdxc = domain->jdxc; + const double * restrict ux = fluid->ux.data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double hx = HXXC(i); + const double jd = JDXC(i); + const double dux = + - UX(i , j ) + + UX(i+1, j ); + *quantity += diffusivity * jd * pow(1. / hx * dux, 2.); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double hx = HXXC(i); + const double jd = JDXC(i); + const double dux = + - UX(i , j , k ) + + UX(i+1, j , k ); + *quantity += diffusivity * jd * pow(1. / hx * dux, 2.); + } + } + } +#endif + return 0; +} + +// duxdy component | 40 +static int get_duxdy ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double hy = domain->hy; + const double * restrict jdxf = domain->jdxf; + const double * restrict ux = fluid->ux.data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + const double jd = JDXF(i); + const double dux = + - UX(i , j-1) + + UX(i , j ); + *quantity += diffusivity * jd * pow(1. / hy * dux, 2.); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + const double jd = JDXF(i); + const double dux = + - UX(i , j-1, k ) + + UX(i , j , k ); + *quantity += diffusivity * jd * pow(1. / hy * dux, 2.); + } + } + } +#endif + return 0; +} + +#if NDIMS == 3 +// duxdz component | 25 +static int get_duxdz ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double hz = domain->hz; + const double * restrict jdxf = domain->jdxf; + const double * restrict ux = fluid->ux.data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + const double jd = JDXF(i); + const double dux = + - UX(i , j , k-1) + + UX(i , j , k ); + *quantity += diffusivity * jd * pow(1. / hz * dux, 2.); + } + } + } + return 0; +} +#endif + +// duydx component | 42 +static int get_duydx ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict hxxf = domain->hxxf; + const double * restrict jdxf = domain->jdxf; + const double * restrict uy = fluid->uy.data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + const double hx = HXXF(i); + const double jd = JDXF(i); + const double duy = + - UY(i-1, j ) + + UY(i , j ); + *quantity += diffusivity * jd * pow(1. / hx * duy, 2.); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + const double hx = HXXF(i); + const double jd = JDXF(i); + const double duy = + - UY(i-1, j , k ) + + UY(i , j , k ); + *quantity += diffusivity * jd * pow(1. / hx * duy, 2.); + } + } + } +#endif + return 0; +} + +// duydy component | 40 +static int get_duydy ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double hy = domain->hy; + const double * restrict jdxc = domain->jdxc; + const double * restrict uy = fluid->uy.data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double jd = JDXC(i); + const double duy = + - UY(i , j ) + + UY(i , j+1); + *quantity += diffusivity * jd * pow(1. / hy * duy, 2.); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double jd = JDXC(i); + const double duy = + - UY(i , j , k ) + + UY(i , j+1, k ); + *quantity += diffusivity * jd * pow(1. / hy * duy, 2.); + } + } + } +#endif + return 0; +} + +#if NDIMS == 3 +// duydz component | 25 +static int get_duydz ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double hz = domain->hz; + const double * restrict jdxc = domain->jdxc; + const double * restrict uy = fluid->uy.data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double jd = JDXC(i); + const double duy = + - UY(i , j , k-1) + + UY(i , j , k ); + *quantity += diffusivity * jd * pow(1. / hz * duy, 2.); + } + } + } + return 0; +} +#endif + +#if NDIMS == 3 +// duzdx component | 26 +static int get_duzdx ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double * restrict hxxf = domain->hxxf; + const double * restrict jdxf = domain->jdxf; + const double * restrict uz = fluid->uz.data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + const double hx = HXXF(i); + const double jd = JDXF(i); + const double duz = + - UZ(i-1, j , k ) + + UZ(i , j , k ); + *quantity += diffusivity * jd * pow(1. / hx * duz, 2.); + } + } + } + return 0; +} +#endif + +#if NDIMS == 3 +// duzdy component | 25 +static int get_duzdy ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double hy = domain->hy; + const double * restrict jdxc = domain->jdxc; + const double * restrict uz = fluid->uz.data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double jd = JDXC(i); + const double duz = + - UZ(i , j-1, k ) + + UZ(i , j , k ); + *quantity += diffusivity * jd * pow(1. / hy * duz, 2.); + } + } + } + return 0; +} +#endif + +#if NDIMS == 3 +// duzdz component | 25 +static int get_duzdz ( + const domain_t * domain, + const fluid_t * fluid, + double * quantity +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; + const int ksize = domain->mysizes[2]; + const double hz = domain->hz; + const double * restrict jdxc = domain->jdxc; + const double * restrict uz = fluid->uz.data; + const double diffusivity = fluid_compute_momentum_diffusivity(fluid); + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + const double jd = JDXC(i); + const double duz = + - UZ(i , j , k ) + + UZ(i , j , k+1); + *quantity += diffusivity * jd * pow(1. / hz * duz, 2.); + } + } + } + return 0; +} +#endif + +/** + * @brief compute dissipation of squared velocity + * @param[in] fname : file name to which the log is written + * @param[in] domain : information related to MPI domain decomposition + * @param[in] time : current simulation time + * @param[in] fluid : diffusivity and velocity field + * @return : error code + */ +int logging_check_dissipated_squared_velocity ( + const char fname[], + const domain_t * domain, + const double time, + const fluid_t * fluid +) { + const int root = 0; + int myrank = root; + MPI_Comm comm_cart = MPI_COMM_NULL; + sdecomp.get_comm_rank(domain->info, &myrank); + sdecomp.get_comm_cart(domain->info, &comm_cart); + double quantity = 0.; + get_duxdx(domain, fluid, &quantity); + get_duxdy(domain, fluid, &quantity); +#if NDIMS == 3 + get_duxdz(domain, fluid, &quantity); +#endif + get_duydx(domain, fluid, &quantity); + get_duydy(domain, fluid, &quantity); +#if NDIMS == 3 + get_duydz(domain, fluid, &quantity); +#endif +#if NDIMS == 3 + get_duzdx(domain, fluid, &quantity); + get_duzdy(domain, fluid, &quantity); + get_duzdz(domain, fluid, &quantity); +#endif + const void * sendbuf = root == myrank ? MPI_IN_PLACE : &quantity; + void * recvbuf = &quantity; + MPI_Reduce(sendbuf, recvbuf, 1, MPI_DOUBLE, MPI_SUM, root, comm_cart); + logging_internal_output( + fname, + domain, + time, + 1, + &quantity + ); + return 0; +} + diff --git a/src/logging/divergence.c b/src/logging/divergence.c deleted file mode 100644 index f8378226..00000000 --- a/src/logging/divergence.c +++ /dev/null @@ -1,102 +0,0 @@ -#include -#include -#include "domain.h" -#include "fluid.h" -#include "fileio.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/fluid/ux.h" -#include "array_macros/fluid/uy.h" -#if NDIMS == 3 -#include "array_macros/fluid/uz.h" -#endif -#include "internal.h" - -/** - * @brief check divergence and write the maximum value - * @param[in] fname : file name to which the log is written - * @param[in] domain : domain information - * @param[in] time : current simulation time - * @param[in] fluid : velocity - * @return : error code - */ -int logging_check_divergence( - const char fname[], - const domain_t * domain, - const double time, - const fluid_t * fluid -){ - const int root = 0; - int myrank = root; - MPI_Comm comm_cart = MPI_COMM_NULL; - sdecomp.get_comm_rank(domain->info, &myrank); - sdecomp.get_comm_cart(domain->info, &comm_cart); - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict ux = fluid->ux.data; - const double * restrict uy = fluid->uy.data; -#if NDIMS == 3 - const double * restrict uz = fluid->uz.data; -#endif - double divmax = 0.; -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // compute local divergence | 8 - const double dx = DXF(i ); - const double ux_xm = UX(i , j ); - const double ux_xp = UX(i+1, j ); - const double uy_ym = UY(i , j ); - const double uy_yp = UY(i , j+1); - const double div = - +(ux_xp - ux_xm) / dx - +(uy_yp - uy_ym) / dy; - // check maximum | 1 - divmax = fmax(divmax, fabs(div)); - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // compute local divergence | 11 - const double dx = DXF(i ); - const double ux_xm = UX(i , j , k ); - const double ux_xp = UX(i+1, j , k ); - const double uy_ym = UY(i , j , k ); - const double uy_yp = UY(i , j+1, k ); - const double uz_zm = UZ(i , j , k ); - const double uz_zp = UZ(i , j , k+1); - const double div = - +(ux_xp - ux_xm) / dx - +(uy_yp - uy_ym) / dy - +(uz_zp - uz_zm) / dz; - // check maximum | 1 - divmax = fmax(divmax, fabs(div)); - } - } - } -#endif - // collect information among all processes - const void * sendbuf = root == myrank ? MPI_IN_PLACE : &divmax; - void * recvbuf = &divmax; - MPI_Reduce(sendbuf, recvbuf, 1, MPI_DOUBLE, MPI_MAX, root, comm_cart); - // result is written to a file from the main process - if(root == myrank){ - FILE * fp = fileio.fopen(fname, "a"); - if(NULL == fp){ - return 0; - } - fprintf(fp, "%8.2f % .1e\n", time, divmax); - fileio.fclose(fp); - } - return 0; -} - diff --git a/src/logging/energy.c b/src/logging/energy.c deleted file mode 100644 index ea5f081d..00000000 --- a/src/logging/energy.c +++ /dev/null @@ -1,142 +0,0 @@ -#include -#include -#include "domain.h" -#include "fluid.h" -#include "fileio.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/ux.h" -#include "array_macros/fluid/uy.h" -#if NDIMS == 3 -#include "array_macros/fluid/uz.h" -#endif -#include "array_macros/fluid/t.h" -#include "internal.h" - -/** - * @brief compute total kinetic and thermal energies - * @param[in] fname : file name to which the log is written - * @param[in] domain : information related to MPI domain decomposition - * @param[in] time : current simulation time - * @param[in] fluid : velocity and temperature - * @return : error code - */ -int logging_check_energy( - const char fname[], - const domain_t * domain, - const double time, - const fluid_t * fluid -){ - const int root = 0; - int myrank = root; - MPI_Comm comm_cart = MPI_COMM_NULL; - sdecomp.get_comm_rank(domain->info, &myrank); - sdecomp.get_comm_cart(domain->info, &comm_cart); - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict ux = fluid->ux.data; - const double * restrict uy = fluid->uy.data; -#if NDIMS == 3 - const double * restrict uz = fluid->uz.data; -#endif - const double * restrict t = fluid->t.data; - // velocity in each dimension and plus thermal energy - double quantities[NDIMS + 1] = {0.}; - // compute quadratic quantity in x direction | 19 -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 2; i <= isize; i++){ - const double dx = DXC(i ); - const double cellsize = dx * dy; - quantities[0] += 0.5 * pow(UX(i, j), 2.) * cellsize; - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 2; i <= isize; i++){ - const double dx = DXC(i ); - const double cellsize = dx * dy * dz; - quantities[0] += 0.5 * pow(UX(i, j, k), 2.) * cellsize; - } - } - } -#endif - // compute quadratic quantity in y direction | 19 -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - const double dx = DXF(i ); - const double cellsize = dx * dy; - quantities[1] += 0.5 * pow(UY(i, j), 2.) * cellsize; - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - const double dx = DXF(i ); - const double cellsize = dx * dy * dz; - quantities[1] += 0.5 * pow(UY(i, j, k), 2.) * cellsize; - } - } - } -#endif -#if NDIMS == 3 - // compute quadratic quantity in z direction | 9 - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - const double dx = DXF(i ); - const double cellsize = dx * dy * dz; - quantities[2] += 0.5 * pow(UZ(i, j, k), 2.) * cellsize; - } - } - } -#endif - // compute thermal energy | 19 -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - const double dx = DXF(i ); - const double cellsize = dx * dy; - quantities[NDIMS] += 0.5 * pow(T(i, j), 2.) * cellsize; - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - const double dx = DXF(i ); - const double cellsize = dx * dy * dz; - quantities[NDIMS] += 0.5 * pow(T(i, j, k), 2.) * cellsize; - } - } - } -#endif - const void * sendbuf = root == myrank ? MPI_IN_PLACE : quantities; - void * recvbuf = quantities; - MPI_Reduce(sendbuf, recvbuf, NDIMS + 1, MPI_DOUBLE, MPI_SUM, root, comm_cart); - if(root == myrank){ - FILE * fp = fileio.fopen(fname, "a"); - if(NULL == fp){ - return 0; - } - fprintf(fp, "%8.2f ", time); - for(int n = 0; n < NDIMS + 1; n++){ - fprintf(fp, "% 18.15e%c", quantities[n], NDIMS == n ? '\n' : ' '); - } - fileio.fclose(fp); - } - return 0; -} - diff --git a/src/logging/heat_transfer.c b/src/logging/heat_transfer.c new file mode 100644 index 00000000..750f069e --- /dev/null +++ b/src/logging/heat_transfer.c @@ -0,0 +1,82 @@ +#include "domain.h" +#include "fluid.h" +#include "fluid_solver.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/fluid/t.h" +#include "internal.h" + +#if NDIMS == 2 +#define BEGIN \ + for (int j = 1; j <= jsize; j++) { +#define END \ + } +#else +#define BEGIN \ + for (int k = 1; k <= ksize; k++) { \ + for (int j = 1; j <= jsize; j++) { +#define END \ + } \ + } +#endif + +/** + * @brief compute net heat transfer on the walls + * @param[in] fname : file name to which the log is written + * @param[in] domain : information related to MPI domain decomposition + * @param[in] time : current simulation time + * @param[in] fluid : diffusivity and temperature field + * @return : error code + */ +int logging_check_heat_transfer ( + const char fname[], + const domain_t * domain, + const double time, + const fluid_t * fluid +) { + const int root = 0; + int myrank = root; + MPI_Comm comm_cart = MPI_COMM_NULL; + sdecomp.get_comm_rank(domain->info, &myrank); + sdecomp.get_comm_cart(domain->info, &comm_cart); + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict hxxf = domain->hxxf; + const double * restrict jdxf = domain->jdxf; + const double * restrict t = fluid->t.data; + // compute heat transfer on the walls | 22 + const double diffusivity = fluid_compute_temperature_diffusivity(fluid); + // on the bottom and top walls + double energies[2] = {0., 0.}; + BEGIN + const double hx_xm = HXXF( 1); + const double hx_xp = HXXF(isize + 1); + const double jd_xm = JDXF( 1); + const double jd_xp = JDXF(isize + 1); +#if NDIMS == 2 + const double dt_xm = - T( 0, j) + T( 1, j); + const double dt_xp = - T(isize, j) + T(isize + 1, j); +#else + const double dt_xm = - T( 0, j, k) + T( 1, j, k); + const double dt_xp = - T(isize, j, k) + T(isize + 1, j, k); +#endif + energies[0] -= diffusivity * jd_xm / hx_xm / hx_xm * dt_xm; + energies[1] -= diffusivity * jd_xp / hx_xp / hx_xp * dt_xp; + END + const size_t nitems = sizeof(energies) / sizeof(energies[0]); + const void * sendbuf = root == myrank ? MPI_IN_PLACE : energies; + void * recvbuf = energies; + MPI_Reduce(sendbuf, recvbuf, nitems, MPI_DOUBLE, MPI_SUM, root, comm_cart); + logging_internal_output( + fname, + domain, + time, + nitems, + energies + ); + return 0; +} + diff --git a/src/logging/injected_squared_velocity.c b/src/logging/injected_squared_velocity.c new file mode 100644 index 00000000..f445879b --- /dev/null +++ b/src/logging/injected_squared_velocity.c @@ -0,0 +1,82 @@ +#include "domain.h" +#include "fluid.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/fluid/ux.h" +#include "array_macros/fluid/t.h" +#include "internal.h" + +#if NDIMS == 2 +#define BEGIN \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 2; i <= isize; i++) { +#define END \ + } \ + } +#else +#define BEGIN \ + for (int k = 1; k <= ksize; k++) { \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 2; i <= isize; i++) { +#define END \ + } \ + } \ + } +#endif + +/** + * @brief compute injected squared velocity + * @param[in] fname : file name to which the log is written + * @param[in] domain : information related to MPI domain decomposition + * @param[in] time : current simulation time + * @param[in] fluid : velocity and temperature field + * @return : error code + */ +int logging_check_injected_squared_velocity ( + const char fname[], + const domain_t * domain, + const double time, + const fluid_t * fluid +) { + const int root = 0; + int myrank = root; + MPI_Comm comm_cart = MPI_COMM_NULL; + sdecomp.get_comm_rank(domain->info, &myrank); + sdecomp.get_comm_cart(domain->info, &comm_cart); + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict jdxf = domain->jdxf; + const double * restrict ux = fluid->ux.data; + const double * restrict t = fluid-> t.data; + // compute injected squared velocity | 19 + double quantity = 0.; + BEGIN + const double jd_x0 = JDXF(i); +#if NDIMS == 2 + const double ux_x0 = UX(i, j); + const double t_x0 = + + 0.5 * T(i-1, j ) + + 0.5 * T(i , j ); +#else + const double ux_x0 = UX(i, j, k); + const double t_x0 = + + 0.5 * T(i-1, j , k ) + + 0.5 * T(i , j , k ); +#endif + quantity += jd_x0 * ux_x0 * t_x0; + END + const void * sendbuf = root == myrank ? MPI_IN_PLACE : &quantity; + void * recvbuf = &quantity; + MPI_Reduce(sendbuf, recvbuf, 1, MPI_DOUBLE, MPI_SUM, root, comm_cart); + logging_internal_output( + fname, + domain, + time, + 1, + &quantity + ); + return 0; +} + diff --git a/src/logging/internal.h b/src/logging/internal.h index b9d6c0f8..1e760022 100644 --- a/src/logging/internal.h +++ b/src/logging/internal.h @@ -1,28 +1,57 @@ #if !defined(LOGGING_INTERNAL_H) #define LOGGING_INTERNAL_H -extern int logging_check_divergence( +extern int logging_internal_output ( + const char fname[], + const domain_t * domain, + const double time, + const size_t nitems, + const double * items +); + +extern int logging_check_max_divergence ( + const char fname[], + const domain_t * domain, + const double time, + const fluid_t * fluid +); + +extern int logging_check_total_momentum ( + const char fname[], + const domain_t * domain, + const double time, + const fluid_t * fluid +); + +extern int logging_check_total_energy ( + const char fname[], + const domain_t * domain, + const double time, + const fluid_t * fluid +); + +extern int logging_check_heat_transfer ( const char fname[], const domain_t * domain, const double time, const fluid_t * fluid ); -extern int logging_check_momentum( +extern int logging_check_injected_squared_velocity ( const char fname[], const domain_t * domain, const double time, const fluid_t * fluid ); -extern int logging_check_energy( +int logging_check_dissipated_squared_velocity ( const char fname[], const domain_t * domain, const double time, const fluid_t * fluid ); -extern int logging_check_nusselt( +int logging_check_dissipated_squared_temperature ( const char fname[], const domain_t * domain, const double time, diff --git a/src/logging/main.c b/src/logging/main.c index f9b90470..1fd0bc50 100644 --- a/src/logging/main.c +++ b/src/logging/main.c @@ -17,11 +17,11 @@ static double g_next = 0.; * @param[in] domain : MPI communicator * @param[in] time : current time (hereafter in free-fall time units) */ -static int init( +static int init ( const domain_t * domain, const double time -){ - if(0 != config.get_double("log_rate", &g_rate)){ +) { + if (0 != config.get_double("log_rate", &g_rate)) { return 1; } g_next = g_rate * ceil( @@ -30,7 +30,7 @@ static int init( const int root = 0; int myrank = root; sdecomp.get_comm_rank(domain->info, &myrank); - if(root == myrank){ + if (root == myrank) { printf("LOGGING\n"); printf("\tnext: % .3e\n", g_next); printf("\trate: % .3e\n", g_rate); @@ -48,20 +48,20 @@ static int init( * @param[in] dt : time step size * @param[in] wtime : current wall time */ -static void show_progress( +static void show_progress ( const char fname[], const domain_t * domain, const double time, const size_t step, const double dt, const double wtime -){ +) { const int root = 0; int myrank = root; sdecomp.get_comm_rank(domain->info, &myrank); - if(root == myrank){ + if (root == myrank) { FILE * fp = fileio.fopen(fname, "a"); - if(NULL != fp){ + if (NULL != fp) { // show progress to standard output and file | 8 // output to stdout and file #define MPRINT(...) { \ @@ -75,6 +75,30 @@ static void show_progress( } } +int logging_internal_output ( + const char fname[], + const domain_t * domain, + const double time, + const size_t nitems, + const double * items +) { + const int root = 0; + int myrank = root; + sdecomp.get_comm_rank(domain->info, &myrank); + if (root == myrank) { + FILE * fp = fileio.fopen(fname, "a"); + if (NULL == fp) { + return 1; + } + fprintf(fp, "%18.15e ", time); + for (size_t n = 0; n < nitems; n++) { + fprintf(fp, "% 18.15e%c", items[n], nitems - 1 == n ? '\n' : ' '); + } + fileio.fclose(fp); + } + return 0; +} + /** * @brief output log files to be monitored during simulation * @param[in] domain : information related to MPI domain decomposition @@ -84,19 +108,22 @@ static void show_progress( * @param[in] wtime : current wall time * @param[in] fluid : velocity and temperature */ -static void check_and_output( +static void check_and_output ( const domain_t * domain, const size_t step, const double time, const double dt, const double wtime, const fluid_t * fluid -){ - show_progress ("output/log/progress.dat", domain, time, step, dt, wtime); - logging_check_divergence("output/log/divergence.dat", domain, time, fluid); - logging_check_momentum ("output/log/momentum.dat", domain, time, fluid); - logging_check_energy ("output/log/energy.dat", domain, time, fluid); - logging_check_nusselt ("output/log/nusselt.dat", domain, time, fluid); +) { + show_progress ("output/log/progress.dat", domain, time, step, dt, wtime); + logging_check_max_divergence ("output/log/max_divergence.dat", domain, time, fluid); + logging_check_total_momentum ("output/log/total_momentum.dat", domain, time, fluid); + logging_check_total_energy ("output/log/total_energy.dat", domain, time, fluid); + logging_check_heat_transfer ("output/log/heat_transfer.dat", domain, time, fluid); + logging_check_injected_squared_velocity ("output/log/injected_squared_velocity.dat", domain, time, fluid); + logging_check_dissipated_squared_velocity ("output/log/dissipated_squared_velocity.dat", domain, time, fluid); + logging_check_dissipated_squared_temperature("output/log/dissipated_squared_temperature.dat", domain, time, fluid); g_next += g_rate; } @@ -104,9 +131,9 @@ static void check_and_output( * @brief getter of a member: g_next * @return : g_next */ -static double get_next_time( +static double get_next_time ( void -){ +) { return g_next; } diff --git a/src/logging/max_divergence.c b/src/logging/max_divergence.c new file mode 100644 index 00000000..9582b5ae --- /dev/null +++ b/src/logging/max_divergence.c @@ -0,0 +1,112 @@ +#include +#include "domain.h" +#include "fluid.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" +#include "array_macros/fluid/ux.h" +#include "array_macros/fluid/uy.h" +#if NDIMS == 3 +#include "array_macros/fluid/uz.h" +#endif +#include "internal.h" + +#if NDIMS == 2 +#define BEGIN \ + for (int cnt = 0, j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++, cnt++) { +#define END \ + } \ + } +#else +#define BEGIN \ + for (int cnt = 0, k = 1; k <= ksize; k++) { \ + for (int j = 1; j <= jsize; j++) { \ + for (int i = 1; i <= isize; i++, cnt++) { +#define END \ + } \ + } \ + } +#endif + +/** + * @brief check maximum divergence and write it to a file + * @param[in] fname : file name to which the log is written + * @param[in] domain : domain information + * @param[in] time : current simulation time + * @param[in] fluid : velocity + * @return : error code + */ +int logging_check_max_divergence ( + const char fname[], + const domain_t * domain, + const double time, + const fluid_t * fluid +) { + const int root = 0; + int myrank = root; + MPI_Comm comm_cart = MPI_COMM_NULL; + sdecomp.get_comm_rank(domain->info, &myrank); + sdecomp.get_comm_cart(domain->info, &comm_cart); + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict hxxf = domain->hxxf; + const double hy = domain->hy; +#if NDIMS == 3 + const double hz = domain->hz; +#endif + const double * restrict jdxf = domain->jdxf; + const double * restrict jdxc = domain->jdxc; + const double * restrict ux = fluid->ux.data; + const double * restrict uy = fluid->uy.data; +#if NDIMS == 3 + const double * restrict uz = fluid->uz.data; +#endif + // check max local divergence | 30 + double divmax = 0.; + BEGIN + const double hx_xm = HXXF(i ); + const double hx_xp = HXXF(i+1); + const double jd_xm = JDXF(i ); + const double jd_x0 = JDXC(i ); + const double jd_xp = JDXF(i+1); +#if NDIMS == 2 + const double ux_xm = UX(i , j ); + const double ux_xp = UX(i+1, j ); + const double uy_ym = UY(i , j ); + const double uy_yp = UY(i , j+1); +#else + const double ux_xm = UX(i , j , k ); + const double ux_xp = UX(i+1, j , k ); + const double uy_ym = UY(i , j , k ); + const double uy_yp = UY(i , j+1, k ); + const double uz_zm = UZ(i , j , k ); + const double uz_zp = UZ(i , j , k+1); +#endif + const double div = 1. / jd_x0 * ( + - jd_xm / hx_xm * ux_xm + jd_xp / hx_xp * ux_xp + - jd_x0 / hy * uy_ym + jd_x0 / hy * uy_yp +#if NDIMS == 3 + - jd_x0 / hz * uz_zm + jd_x0 / hz * uz_zp +#endif + ); + // check maximum + divmax = fmax(divmax, fabs(div)); + END + // collect information among all processes + const void * sendbuf = root == myrank ? MPI_IN_PLACE : &divmax; + void * recvbuf = &divmax; + MPI_Reduce(sendbuf, recvbuf, 1, MPI_DOUBLE, MPI_MAX, root, comm_cart); + logging_internal_output( + fname, + domain, + time, + 1, + &divmax + ); + return 0; +} + diff --git a/src/logging/momentum.c b/src/logging/momentum.c deleted file mode 100644 index 9f202ac1..00000000 --- a/src/logging/momentum.c +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include "domain.h" -#include "fluid.h" -#include "fileio.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/ux.h" -#include "array_macros/fluid/uy.h" -#if NDIMS == 3 -#include "array_macros/fluid/uz.h" -#endif -#include "internal.h" - -/** - * @brief compute total momenta - * @param[in] fname : file name to which the log is written - * @param[in] domain : information about domain decomposition and size - * @param[in] time : current simulation time - * @param[in] fluid : velocity - * @return : error code - */ -int logging_check_momentum( - const char fname[], - const domain_t * domain, - const double time, - const fluid_t * fluid -){ - const int root = 0; - int myrank = root; - MPI_Comm comm_cart = MPI_COMM_NULL; - sdecomp.get_comm_rank(domain->info, &myrank); - sdecomp.get_comm_cart(domain->info, &comm_cart); - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict ux = fluid->ux.data; - const double * restrict uy = fluid->uy.data; -#if NDIMS == 3 - const double * restrict uz = fluid->uz.data; -#endif - double moms[NDIMS] = {0.}; - // compute total x-momentum | 19 -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 2; i <= isize; i++){ - const double dx = DXC(i ); - const double cellsize = dx * dy; - moms[0] += UX(i, j) * cellsize; - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 2; i <= isize; i++){ - const double dx = DXC(i ); - const double cellsize = dx * dy * dz; - moms[0] += UX(i, j, k) * cellsize; - } - } - } -#endif - // compute total y-momentum | 19 -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - const double dx = DXF(i ); - const double cellsize = dx * dy; - moms[1] += UY(i, j) * cellsize; - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - const double dx = DXF(i ); - const double cellsize = dx * dy * dz; - moms[1] += UY(i, j, k) * cellsize; - } - } - } -#endif -#if NDIMS == 3 - // compute total z-momentum | 9 - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - const double dx = DXF(i ); - const double cellsize = dx * dy * dz; - moms[2] += UZ(i, j, k) * cellsize; - } - } - } -#endif - const void * sendbuf = root == myrank ? MPI_IN_PLACE : moms; - void * recvbuf = moms; - MPI_Reduce(sendbuf, recvbuf, NDIMS, MPI_DOUBLE, MPI_SUM, root, comm_cart); - if(root == myrank){ - FILE * fp = fileio.fopen(fname, "a"); - if(NULL == fp){ - return 0; - } - fprintf(fp, "%8.2f ", time); - for(int n = 0; n < NDIMS; n++){ - fprintf(fp, "% 18.15e%c", moms[n], NDIMS - 1 == n ? '\n' : ' '); - } - fileio.fclose(fp); - } - return 0; -} - diff --git a/src/logging/nusselt/README.rst b/src/logging/nusselt/README.rst deleted file mode 100644 index ba55860b..00000000 --- a/src/logging/nusselt/README.rst +++ /dev/null @@ -1,6 +0,0 @@ -######## -nusselt/ -######## - -Functions to monitor the instantaneous Nusselt number computed differently. - diff --git a/src/logging/nusselt/heat_flux.c b/src/logging/nusselt/heat_flux.c deleted file mode 100644 index 880f2ce8..00000000 --- a/src/logging/nusselt/heat_flux.c +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include "domain.h" -#include "fluid.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/t.h" -#include "internal.h" - -/** - * @brief compute Nusselt number based on the heat flux on the walls - * @param[in] domain : information related to MPI domain decomposition - * @param[in] fluid : temperature and diffusivity - * @return : Nusselt number - */ -double logging_internal_compute_nu_heat_flux( - const domain_t * domain, - const fluid_t * fluid -){ - MPI_Comm comm_cart = MPI_COMM_NULL; - sdecomp.get_comm_cart(domain->info, &comm_cart); - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict t = fluid->t.data; - const double diffusivity = fluid->t_dif; - // heat flux on the walls | 33 - // reference heat flux - const double ref = logging_internal_compute_reference_heat_flux(domain, fluid); - // integral on the two walls - double retval = 0.; -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - const double ds = dy; - const double dx_xm = DXC( 1); - const double dx_xp = DXC(isize+1); - const double dt_xm = + T( 1, j) - T( 0, j); - const double dt_xp = + T(isize+1, j) - T(isize, j); - const double dtdx_xm = dt_xm / dx_xm; - const double dtdx_xp = dt_xp / dx_xp; - // average two walls - retval -= 0.5 * diffusivity * (dtdx_xm + dtdx_xp) * ds; - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - const double ds = dy * dz; - const double dx_xm = DXC( 1); - const double dx_xp = DXC(isize+1); - const double dt_xm = + T( 1, j, k) - T( 0, j, k); - const double dt_xp = + T(isize+1, j, k) - T(isize, j, k); - const double dtdx_xm = dt_xm / dx_xm; - const double dtdx_xp = dt_xp / dx_xp; - // average two walls - retval -= 0.5 * diffusivity * (dtdx_xm + dtdx_xp) * ds; - } - } -#endif - const int root = 0; - int myrank = root; - sdecomp.get_comm_rank(domain->info, &myrank); - const void * sendbuf = root == myrank ? MPI_IN_PLACE : &retval; - void * recvbuf = &retval; - MPI_Reduce(sendbuf, recvbuf, 1, MPI_DOUBLE, MPI_SUM, root, comm_cart); - return retval / ref; -} - diff --git a/src/logging/nusselt/internal.h b/src/logging/nusselt/internal.h deleted file mode 100644 index 7d7181d5..00000000 --- a/src/logging/nusselt/internal.h +++ /dev/null @@ -1,29 +0,0 @@ -#if !defined(LOGGING_NUSSELT_INTERNAL_H) -#define LOGGING_NUSSELT_INTERNAL_H - -extern double logging_internal_compute_reference_heat_flux( - const domain_t * domain, - const fluid_t * fluid -); - -extern double logging_internal_compute_nu_heat_flux( - const domain_t * domain, - const fluid_t * fluid -); - -extern double logging_internal_compute_nu_kinetic_energy_injection( - const domain_t * domain, - const fluid_t * fluid -); - -extern double logging_internal_compute_nu_kinetic_energy_dissipation( - const domain_t * domain, - const fluid_t * fluid -); - -extern double logging_internal_compute_nu_thermal_energy_dissipation( - const domain_t * domain, - const fluid_t * fluid -); - -#endif // LOGGING_NUSSELT_INTERNAL_H diff --git a/src/logging/nusselt/kinetic_energy_dissipation.c b/src/logging/nusselt/kinetic_energy_dissipation.c deleted file mode 100644 index 95f39322..00000000 --- a/src/logging/nusselt/kinetic_energy_dissipation.c +++ /dev/null @@ -1,484 +0,0 @@ -#include -#include -#include "domain.h" -#include "fluid.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/ux.h" -#include "array_macros/fluid/uy.h" -#if NDIMS == 3 -#include "array_macros/fluid/uz.h" -#endif -#include "internal.h" - -static double get_ux_x_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict ux = fluid->ux.data; - const double diffusivity = fluid->m_dif; - double dissipation = 0.; -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize + 1; i++){ - // ux-x contribution | 13 - const double cellsize = DXC(i ) * dy; - // negative direction - if(1 != i){ - const double duxdx = 1. / DXF(i-1) * (UX(i , j ) - UX(i-1, j )); - const double w = DXF(i-1) / DXC(i ); - dissipation += diffusivity * 0.5 * w * pow(duxdx, 2.) * cellsize; - } - // positive direction - if(isize + 1 != i){ - const double duxdx = 1. / DXF(i ) * (UX(i+1, j ) - UX(i , j )); - const double w = DXF(i ) / DXC(i ); - dissipation += diffusivity * 0.5 * w * pow(duxdx, 2.) * cellsize; - } - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize + 1; i++){ - // ux-x contribution | 13 - const double cellsize = DXC(i ) * dy * dz; - // negative direction - if(1 != i){ - const double duxdx = 1. / DXF(i-1) * (UX(i , j , k ) - UX(i-1, j , k )); - const double w = DXF(i-1) / DXC(i ); - dissipation += diffusivity * 0.5 * w * pow(duxdx, 2.) * cellsize; - } - // positive direction - if(isize + 1 != i){ - const double duxdx = 1. / DXF(i ) * (UX(i+1, j , k ) - UX(i , j , k )); - const double w = DXF(i ) / DXC(i ); - dissipation += diffusivity * 0.5 * w * pow(duxdx, 2.) * cellsize; - } - } - } - } -#endif - return dissipation; -} - -static double get_ux_y_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict ux = fluid->ux.data; - const double diffusivity = fluid->m_dif; - double dissipation = 0.; -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize + 1; i++){ - // ux-y contribution | 11 - const double cellsize = DXC(i ) * dy; - // negative direction - { - const double duxdy = 1. / dy * (UX(i , j ) - UX(i , j-1)); - dissipation += diffusivity * 0.5 * pow(duxdy, 2.) * cellsize; - } - // positive direction - { - const double duxdy = 1. / dy * (UX(i , j+1) - UX(i , j )); - dissipation += diffusivity * 0.5 * pow(duxdy, 2.) * cellsize; - } - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize + 1; i++){ - // ux-y contribution | 11 - const double cellsize = DXC(i ) * dy * dz; - // negative direction - { - const double duxdy = 1. / dy * (UX(i , j , k ) - UX(i , j-1, k )); - dissipation += diffusivity * 0.5 * pow(duxdy, 2.) * cellsize; - } - // positive direction - { - const double duxdy = 1. / dy * (UX(i , j+1, k ) - UX(i , j , k )); - dissipation += diffusivity * 0.5 * pow(duxdy, 2.) * cellsize; - } - } - } - } -#endif - return dissipation; -} - -#if NDIMS == 3 -static double get_ux_z_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; - const double dz = domain->dz; - const double * restrict ux = fluid->ux.data; - const double diffusivity = fluid->m_dif; - double dissipation = 0.; - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize + 1; i++){ - // ux-z contribution | 11 - const double cellsize = DXC(i ) * dy * dz; - // negative direction - { - const double duxdz = 1. / dz * (UX(i , j , k ) - UX(i , j , k-1)); - dissipation += diffusivity * 0.5 * pow(duxdz, 2.) * cellsize; - } - // positive direction - { - const double duxdz = 1. / dz * (UX(i , j , k+1) - UX(i , j , k )); - dissipation += diffusivity * 0.5 * pow(duxdz, 2.) * cellsize; - } - } - } - } - return dissipation; -} -#endif - -static double get_uy_x_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict uy = fluid->uy.data; - const double diffusivity = fluid->m_dif; - double dissipation = 0.; -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // uy-x contribution | 15 - const double cellsize = DXF(i ) * dy; - // negative direction - { - const double duydx = 1. / DXC(i ) * (UY(i , j ) - UY(i-1, j )); - const double w0 = DXC(i ) / DXF(i ); - const double w1 = 1 == i ? 2. : 1.; - dissipation += diffusivity * 0.5 * w0 * w1 * pow(duydx, 2.) * cellsize; - } - // positive direction - { - const double duydx = 1. / DXC(i+1) * (UY(i+1, j ) - UY(i , j )); - const double w0 = DXC(i+1) / DXF(i ); - const double w1 = isize == i ? 2. : 1.; - dissipation += diffusivity * 0.5 * w0 * w1 * pow(duydx, 2.) * cellsize; - } - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // uy-x contribution | 15 - const double cellsize = DXF(i ) * dy * dz; - // negative direction - { - const double duydx = 1. / DXC(i ) * (UY(i , j , k ) - UY(i-1, j , k )); - const double w0 = DXC(i ) / DXF(i ); - const double w1 = 1 == i ? 2. : 1.; - dissipation += diffusivity * 0.5 * w0 * w1 * pow(duydx, 2.) * cellsize; - } - // positive direction - { - const double duydx = 1. / DXC(i+1) * (UY(i+1, j , k ) - UY(i , j , k )); - const double w0 = DXC(i+1) / DXF(i ); - const double w1 = isize == i ? 2. : 1.; - dissipation += diffusivity * 0.5 * w0 * w1 * pow(duydx, 2.) * cellsize; - } - } - } - } -#endif - return dissipation; -} - -static double get_uy_y_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict uy = fluid->uy.data; - const double diffusivity = fluid->m_dif; - double dissipation = 0.; -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // uy-y contribution | 11 - const double cellsize = DXF(i ) * dy; - // negative direction - { - const double duydy = 1. / dy * (UY(i , j ) - UY(i , j-1)); - dissipation += diffusivity * 0.5 * pow(duydy, 2.) * cellsize; - } - // positive direction - { - const double duydy = 1. / dy * (UY(i , j+1) - UY(i , j )); - dissipation += diffusivity * 0.5 * pow(duydy, 2.) * cellsize; - } - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // uy-y contribution | 11 - const double cellsize = DXF(i ) * dy * dz; - // negative direction - { - const double duydy = 1. / dy * (UY(i , j , k ) - UY(i , j-1, k )); - dissipation += diffusivity * 0.5 * pow(duydy, 2.) * cellsize; - } - // positive direction - { - const double duydy = 1. / dy * (UY(i , j+1, k ) - UY(i , j , k )); - dissipation += diffusivity * 0.5 * pow(duydy, 2.) * cellsize; - } - } - } - } -#endif - return dissipation; -} - -#if NDIMS == 3 -static double get_uy_z_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double * restrict dxf = domain->dxf; - const double dy = domain->dy; - const double dz = domain->dz; - const double * restrict uy = fluid->uy.data; - const double diffusivity = fluid->m_dif; - double dissipation = 0.; - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // uy-z contribution | 11 - const double cellsize = DXF(i ) * dy * dz; - // negative direction - { - const double duydz = 1. / dz * (UY(i , j , k ) - UY(i , j , k-1)); - dissipation += diffusivity * 0.5 * pow(duydz, 2.) * cellsize; - } - // positive direction - { - const double duydz = 1. / dz * (UY(i , j , k+1) - UY(i , j , k )); - dissipation += diffusivity * 0.5 * pow(duydz, 2.) * cellsize; - } - } - } - } - return dissipation; -} -#endif - -#if NDIMS == 3 -static double get_uz_x_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double * restrict dxf = domain->dxf; - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; - const double dz = domain->dz; - const double * restrict uz = fluid->uz.data; - const double diffusivity = fluid->m_dif; - double dissipation = 0.; - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // uz-x contribution | 15 - const double cellsize = DXF(i ) * dy * dz; - // negative direction - { - const double duzdx = 1. / DXC(i ) * (UZ(i , j , k ) - UZ(i-1, j , k )); - const double w0 = DXC(i ) / DXF(i ); - const double w1 = 1 == i ? 2. : 1.; - dissipation += diffusivity * 0.5 * w0 * w1 * pow(duzdx, 2.) * cellsize; - } - // positive direction - { - const double duzdx = 1. / DXC(i+1) * (UZ(i+1, j , k ) - UZ(i , j , k )); - const double w0 = DXC(i+1) / DXF(i ); - const double w1 = isize == i ? 2. : 1.; - dissipation += diffusivity * 0.5 * w0 * w1 * pow(duzdx, 2.) * cellsize; - } - } - } - } - return dissipation; -} -#endif - -#if NDIMS == 3 -static double get_uz_y_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double * restrict dxf = domain->dxf; - const double dy = domain->dy; - const double dz = domain->dz; - const double * restrict uz = fluid->uz.data; - const double diffusivity = fluid->m_dif; - double dissipation = 0.; - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // uz-y contribution | 11 - const double cellsize = DXF(i ) * dy * dz; - // negative direction - { - const double duzdy = 1. / dy * (UZ(i , j , k ) - UZ(i , j-1, k )); - dissipation += diffusivity * 0.5 * pow(duzdy, 2.) * cellsize; - } - // positive direction - { - const double duzdy = 1. / dy * (UZ(i , j+1, k ) - UZ(i , j , k )); - dissipation += diffusivity * 0.5 * pow(duzdy, 2.) * cellsize; - } - } - } - } - return dissipation; -} -#endif - -#if NDIMS == 3 -static double get_uz_z_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double * restrict dxf = domain->dxf; - const double dy = domain->dy; - const double dz = domain->dz; - const double * restrict uz = fluid->uz.data; - const double diffusivity = fluid->m_dif; - double dissipation = 0.; - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // uz-z contribution | 11 - const double cellsize = DXF(i ) * dy * dz; - // negative direction - { - const double duzdz = 1. / dz * (UZ(i , j , k ) - UZ(i , j , k-1)); - dissipation += diffusivity * 0.5 * pow(duzdz, 2.) * cellsize; - } - // positive direction - { - const double duzdz = 1. / dz * (UZ(i , j , k+1) - UZ(i , j , k )); - dissipation += diffusivity * 0.5 * pow(duzdz, 2.) * cellsize; - } - } - } - } - return dissipation; -} -#endif - -/** - * @brief compute Nusselt number based on kinetic dissipation - * @param[in] domain : information related to MPI domain decomposition - * @param[in] fluid : flow field - * @return : Nusselt number - */ -double logging_internal_compute_nu_kinetic_energy_dissipation( - const domain_t * domain, - const fluid_t * fluid -){ - MPI_Comm comm_cart = MPI_COMM_NULL; - sdecomp.get_comm_cart(domain->info, &comm_cart); - // compute kinetic energy dissipation | 24 - // reference heat flux - const double ref = logging_internal_compute_reference_heat_flux(domain, fluid); - double retval = 0.; - // ux contribution - retval += get_ux_x_contribution(domain, fluid); - retval += get_ux_y_contribution(domain, fluid); -#if NDIMS == 3 - retval += get_ux_z_contribution(domain, fluid); -#endif - // uy contribution - retval += get_uy_x_contribution(domain, fluid); - retval += get_uy_y_contribution(domain, fluid); -#if NDIMS == 3 - retval += get_uy_z_contribution(domain, fluid); -#endif -#if NDIMS == 3 - // uz contribution - retval += get_uz_x_contribution(domain, fluid); - retval += get_uz_y_contribution(domain, fluid); - retval += get_uz_z_contribution(domain, fluid); -#endif - const int root = 0; - int myrank = root; - sdecomp.get_comm_rank(domain->info, &myrank); - const void * sendbuf = root == myrank ? MPI_IN_PLACE : &retval; - void * recvbuf = &retval; - MPI_Reduce(sendbuf, recvbuf, 1, MPI_DOUBLE, MPI_SUM, root, comm_cart); - retval /= ref; - return retval + 1.; -} - diff --git a/src/logging/nusselt/kinetic_energy_injection.c b/src/logging/nusselt/kinetic_energy_injection.c deleted file mode 100644 index 3d458e85..00000000 --- a/src/logging/nusselt/kinetic_energy_injection.c +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include "domain.h" -#include "fluid.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/ux.h" -#include "array_macros/fluid/t.h" -#include "internal.h" - -/** - * @brief compute Nusselt number based on the total energy injection - * @param[in] domain : information related to MPI domain decomposition - * @param[in] fluid : wall-normal velocity (ux) and temperature - * @return : Nusselt number - */ -double logging_internal_compute_nu_kinetic_energy_injection( - const domain_t * domain, - const fluid_t * fluid -){ - MPI_Comm comm_cart = MPI_COMM_NULL; - sdecomp.get_comm_cart(domain->info, &comm_cart); - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict ux = fluid->ux.data; - const double * restrict t = fluid->t .data; - // energy injection | 35 - // reference heat flux - const double ref = logging_internal_compute_reference_heat_flux(domain, fluid); - // integral in the whole domain - double retval = 0.; -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 2; i <= isize; i++){ - const double dx = DXC(i ); - const double vel = UX(i, j); - const double t_ = + 0.5 * T(i-1, j) + 0.5 * T(i, j); - const double integrand = vel * t_; - const double cellsize = dx * dy; - retval += integrand * cellsize; - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 2; i <= isize; i++){ - const double dx = DXC(i ); - const double vel = UX(i, j, k); - const double t_ = + 0.5 * T(i-1, j, k) + 0.5 * T(i, j, k); - const double integrand = vel * t_; - const double cellsize = dx * dy * dz; - retval += integrand * cellsize; - } - } - } -#endif - const int root = 0; - int myrank = root; - sdecomp.get_comm_rank(domain->info, &myrank); - const void * sendbuf = root == myrank ? MPI_IN_PLACE : &retval; - void * recvbuf = &retval; - MPI_Reduce(sendbuf, recvbuf, 1, MPI_DOUBLE, MPI_SUM, root, comm_cart); - // normalise by the reference value - retval /= ref; - // add laminar contribution - retval += 1.; - return retval; -} - diff --git a/src/logging/nusselt/main.c b/src/logging/nusselt/main.c deleted file mode 100644 index 2ce2f846..00000000 --- a/src/logging/nusselt/main.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include "domain.h" -#include "fluid.h" -#include "fileio.h" -#include "internal.h" - -/** - * @brief compute Nusselt number based on various definitions - * @param[in] fname : file name to which the log is written - * @param[in] domain : information related to MPI domain decomposition - * @param[in] time : current simulation time - * @param[in] fluid : velocity, temperature, and their diffusivities - * @return : error code - */ -int logging_check_nusselt( - const char fname[], - const domain_t * domain, - const double time, - const fluid_t * fluid -){ - const int root = 0; - int myrank = root; - MPI_Comm comm_cart = MPI_COMM_NULL; - sdecomp.get_comm_rank(domain->info, &myrank); - sdecomp.get_comm_cart(domain->info, &comm_cart); - // compute Nusselt in several ways - const double results[] = { - // compute heat flux on the walls - logging_internal_compute_nu_heat_flux(domain, fluid), - // compute kinetic energy injection - logging_internal_compute_nu_kinetic_energy_injection(domain, fluid), - // compute kinetic energy dissipation - logging_internal_compute_nu_kinetic_energy_dissipation(domain, fluid), - // comoute thermal energy dissipation - logging_internal_compute_nu_thermal_energy_dissipation(domain, fluid), - }; - if(root == myrank){ - FILE * fp = fileio.fopen(fname, "a"); - if(NULL == fp){ - return 0; - } - fprintf(fp, "%8.2f ", time); - const int ntypes = sizeof(results) / sizeof(results[0]); - for(int n = 0; n < ntypes; n++){ - fprintf(fp, "% 18.15e%c", results[n], ntypes - 1 == n ? '\n' : ' '); - } - fileio.fclose(fp); - } - return 0; -} - diff --git a/src/logging/nusselt/reference.c b/src/logging/nusselt/reference.c deleted file mode 100644 index 291bba44..00000000 --- a/src/logging/nusselt/reference.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "domain.h" -#include "fluid.h" - -double logging_internal_compute_reference_heat_flux( - const domain_t * domain, - const fluid_t * fluid -){ - // compute laminar heat flux | 10 - const double ly = domain->lengths[1]; -#if NDIMS == 3 - const double lz = domain->lengths[2]; -#endif - const double diffusivity = fluid->t_dif; -#if NDIMS == 2 - return diffusivity * ly; -#else - return diffusivity * ly * lz; -#endif -} - diff --git a/src/logging/nusselt/thermal_energy_dissipation.c b/src/logging/nusselt/thermal_energy_dissipation.c deleted file mode 100644 index ff6fd2a3..00000000 --- a/src/logging/nusselt/thermal_energy_dissipation.c +++ /dev/null @@ -1,199 +0,0 @@ -#include -#include -#include "domain.h" -#include "fluid.h" -#include "array_macros/domain/dxf.h" -#include "array_macros/domain/dxc.h" -#include "array_macros/fluid/t.h" -#include "internal.h" - -static double get_x_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; - const double * restrict dxc = domain->dxc; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict t = fluid->t.data; - const double diffusivity = fluid->t_dif; - double dissipation = 0.; -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // x contribution | 15 - const double cellsize = DXF(i ) * dy; - // negative direction - { - const double dtdx = 1. / DXC(i ) * (T(i , j ) - T(i-1, j )); - const double w0 = DXC(i ) / DXF(i ); - const double w1 = 1 == i ? 2. : 1.; - dissipation += diffusivity * 0.5 * w0 * w1 * pow(dtdx, 2.) * cellsize; - } - // positive direction - { - const double dtdx = 1. / DXC(i+1) * (T(i+1, j ) - T(i , j )); - const double w0 = DXC(i+1) / DXF(i ); - const double w1 = isize == i ? 2. : 1.; - dissipation += diffusivity * 0.5 * w0 * w1 * pow(dtdx, 2.) * cellsize; - } - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // x contribution | 15 - const double cellsize = DXF(i ) * dy * dz; - // negative direction - { - const double dtdx = 1. / DXC(i ) * (T(i , j , k ) - T(i-1, j , k )); - const double w0 = DXC(i ) / DXF(i ); - const double w1 = 1 == i ? 2. : 1.; - dissipation += diffusivity * 0.5 * w0 * w1 * pow(dtdx, 2.) * cellsize; - } - // positive direction - { - const double dtdx = 1. / DXC(i+1) * (T(i+1, j , k ) - T(i , j , k )); - const double w0 = DXC(i+1) / DXF(i ); - const double w1 = isize == i ? 2. : 1.; - dissipation += diffusivity * 0.5 * w0 * w1 * pow(dtdx, 2.) * cellsize; - } - } - } - } -#endif - return dissipation; -} - -static double get_y_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict dxf = domain->dxf; - const double dy = domain->dy; -#if NDIMS == 3 - const double dz = domain->dz; -#endif - const double * restrict t = fluid->t.data; - const double diffusivity = fluid->t_dif; - double dissipation = 0.; -#if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // y contribution | 11 - const double cellsize = DXF(i ) * dy; - // negative direction - { - const double dtdy = 1. / dy * (T(i , j ) - T(i , j-1)); - dissipation += diffusivity * 0.5 * pow(dtdy, 2.) * cellsize; - } - // positive direction - { - const double dtdy = 1. / dy * (T(i , j+1) - T(i , j )); - dissipation += diffusivity * 0.5 * pow(dtdy, 2.) * cellsize; - } - } - } -#else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // y contribution | 11 - const double cellsize = DXF(i ) * dy * dz; - // negative direction - { - const double dtdy = 1. / dy * (T(i , j , k ) - T(i , j-1, k )); - dissipation += diffusivity * 0.5 * pow(dtdy, 2.) * cellsize; - } - // positive direction - { - const double dtdy = 1. / dy * (T(i , j+1, k ) - T(i , j , k )); - dissipation += diffusivity * 0.5 * pow(dtdy, 2.) * cellsize; - } - } - } - } -#endif - return dissipation; -} - -#if NDIMS == 3 -static double get_z_contribution( - const domain_t * domain, - const fluid_t * fluid -){ - const int isize = domain->mysizes[0]; - const int jsize = domain->mysizes[1]; - const int ksize = domain->mysizes[2]; - const double * restrict dxf = domain->dxf; - const double dy = domain->dy; - const double dz = domain->dz; - const double * restrict t = fluid->t.data; - const double diffusivity = fluid->t_dif; - double dissipation = 0.; - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize; i++){ - // z contribution | 11 - const double cellsize = DXF(i ) * dy * dz; - // negative direction - { - const double dtdz = 1. / dz * (T(i , j , k ) - T(i , j , k-1)); - dissipation += diffusivity * 0.5 * pow(dtdz, 2.) * cellsize; - } - // positive direction - { - const double dtdz = 1. / dz * (T(i , j , k+1) - T(i , j , k )); - dissipation += diffusivity * 0.5 * pow(dtdz, 2.) * cellsize; - } - } - } - } - return dissipation; -} -#endif - -/** - * @brief compute Nusselt number based on thermal dissipation - * @param[in] domain : information related to MPI domain decomposition - * @param[in] fluid : temperature and its diffusivity - * @return : Nusselt number - */ -double logging_internal_compute_nu_thermal_energy_dissipation( - const domain_t * domain, - const fluid_t * fluid -){ - const int root = 0; - int myrank = root; - MPI_Comm comm_cart = MPI_COMM_NULL; - sdecomp.get_comm_rank(domain->info, &myrank); - sdecomp.get_comm_cart(domain->info, &comm_cart); - // compute thermal energy dissipation | 11 - // reference heat flux - const double ref = logging_internal_compute_reference_heat_flux(domain, fluid); - double retval = 0.; - retval += get_x_contribution(domain, fluid); - retval += get_y_contribution(domain, fluid); -#if NDIMS == 3 - retval += get_z_contribution(domain, fluid); -#endif - const void * sendbuf = root == myrank ? MPI_IN_PLACE : &retval; - void * recvbuf = &retval; - MPI_Reduce(sendbuf, recvbuf, 1, MPI_DOUBLE, MPI_SUM, root, comm_cart); - retval /= ref; - return retval; -} - diff --git a/src/logging/total_energy.c b/src/logging/total_energy.c new file mode 100644 index 00000000..089c85b0 --- /dev/null +++ b/src/logging/total_energy.c @@ -0,0 +1,120 @@ +#include +#include "domain.h" +#include "fluid.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" +#include "array_macros/fluid/ux.h" +#include "array_macros/fluid/uy.h" +#if NDIMS == 3 +#include "array_macros/fluid/uz.h" +#endif +#include "array_macros/fluid/t.h" +#include "internal.h" + +/** + * @brief compute total quadratic quantities + * @param[in] fname : file name to which the log is written + * @param[in] domain : information related to MPI domain decomposition + * @param[in] time : current simulation time + * @param[in] fluid : velocity + * @return : error code + */ +int logging_check_total_energy( + const char fname[], + const domain_t * domain, + const double time, + const fluid_t * fluid +) { + const int root = 0; + int myrank = root; + MPI_Comm comm_cart = MPI_COMM_NULL; + sdecomp.get_comm_rank(domain->info, &myrank); + sdecomp.get_comm_cart(domain->info, &comm_cart); + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict jdxf = domain->jdxf; + const double * restrict jdxc = domain->jdxc; + const double * restrict ux = fluid->ux.data; + const double * restrict uy = fluid->uy.data; +#if NDIMS == 3 + const double * restrict uz = fluid->uz.data; +#endif + const double * restrict t = fluid->t.data; + // velocity for each dimension and scalar + double quantities[NDIMS + 1] = {0.}; + // compute quadratic quantity in x direction +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 2; i <= isize; i++) { + quantities[0] += JDXF(i ) * 0.5 * pow(UX(i, j), 2.); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 2; i <= isize; i++) { + quantities[0] += JDXF(i ) * 0.5 * pow(UX(i, j, k), 2.); + } + } + } +#endif + // compute quadratic quantity in y direction +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + quantities[1] += JDXC(i ) * 0.5 * pow(UY(i, j), 2.); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + quantities[1] += JDXC(i ) * 0.5 * pow(UY(i, j, k), 2.); + } + } + } +#endif +#if NDIMS == 3 + // compute quadratic quantity in z direction + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + quantities[2] += JDXC(i ) * 0.5 * pow(UZ(i, j, k), 2.); + } + } + } +#endif + // compute quadratic quantity of scalar +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + quantities[NDIMS] += JDXC(i ) * 0.5 * pow(T(i, j), 2.); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + quantities[NDIMS] += JDXC(i ) * 0.5 * pow(T(i, j, k), 2.); + } + } + } +#endif + // output information + const size_t nitems = sizeof(quantities) / sizeof(quantities[0]); + const void * sendbuf = root == myrank ? MPI_IN_PLACE : quantities; + void * recvbuf = quantities; + MPI_Reduce(sendbuf, recvbuf, nitems, MPI_DOUBLE, MPI_SUM, root, comm_cart); + logging_internal_output( + fname, + domain, + time, + nitems, + quantities + ); + return 0; +} + diff --git a/src/logging/total_momentum.c b/src/logging/total_momentum.c new file mode 100644 index 00000000..06a81e66 --- /dev/null +++ b/src/logging/total_momentum.c @@ -0,0 +1,99 @@ +#include "domain.h" +#include "fluid.h" +#include "array_macros/domain/jdxf.h" +#include "array_macros/domain/jdxc.h" +#include "array_macros/fluid/ux.h" +#include "array_macros/fluid/uy.h" +#if NDIMS == 3 +#include "array_macros/fluid/uz.h" +#endif +#include "internal.h" + +/** + * @brief compute total momenta + * @param[in] fname : file name to which the log is written + * @param[in] domain : information about domain decomposition and size + * @param[in] time : current simulation time + * @param[in] fluid : velocity + * @return : error code + */ +int logging_check_total_momentum ( + const char fname[], + const domain_t * domain, + const double time, + const fluid_t * fluid +) { + const int root = 0; + int myrank = root; + MPI_Comm comm_cart = MPI_COMM_NULL; + sdecomp.get_comm_rank(domain->info, &myrank); + sdecomp.get_comm_cart(domain->info, &comm_cart); + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict jdxf = domain->jdxf; + const double * restrict jdxc = domain->jdxc; + const double * restrict ux = fluid->ux.data; + const double * restrict uy = fluid->uy.data; +#if NDIMS == 3 + const double * restrict uz = fluid->uz.data; +#endif + double momenta[NDIMS] = {0.}; + // compute total x-momentum | 15 +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 2; i <= isize; i++) { + momenta[0] += JDXF(i) * UX(i, j); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 2; i <= isize; i++) { + momenta[0] += JDXF(i) * UX(i, j, k); + } + } + } +#endif + // compute total y-momentum | 15 +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + momenta[1] += JDXC(i) * UY(i, j); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + momenta[1] += JDXC(i) * UY(i, j, k); + } + } + } +#endif +#if NDIMS == 3 + // compute total z-momentum | 7 + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize; i++) { + momenta[2] += JDXC(i) * UZ(i, j, k); + } + } + } +#endif + const size_t nitems = sizeof(momenta) / sizeof(momenta[0]); + const void * sendbuf = root == myrank ? MPI_IN_PLACE : momenta; + void * recvbuf = momenta; + MPI_Reduce(sendbuf, recvbuf, nitems, MPI_DOUBLE, MPI_SUM, root, comm_cart); + logging_internal_output( + fname, + domain, + time, + nitems, + momenta + ); + return 0; +} + diff --git a/src/main.c b/src/main.c index fc32abcc..35717208 100644 --- a/src/main.c +++ b/src/main.c @@ -12,21 +12,6 @@ #include "config.h" #include "fileio.h" -static int save_entrypoint( - const domain_t * domain, - const size_t step, - const double time, - const fluid_t * fluid -){ - char * dirname = NULL; - save.prepare(domain, step, &dirname); - fileio.w_serial(dirname, "step", 0, NULL, fileio.npy_size_t, sizeof(size_t), &step); - fileio.w_serial(dirname, "time", 0, NULL, fileio.npy_double, sizeof(double), &time); - domain_save(dirname, domain); - fluid_save(dirname, domain, fluid); - return 0; -} - /** * @brief main function * @param[in] argc : number of arguments (expect 2) @@ -34,10 +19,10 @@ static int save_entrypoint( * where a set of initial condition is contained * @return : error code */ -int main( +int main ( int argc, char * argv[] -){ +) { // launch MPI, start timer | 5 MPI_Init(NULL, NULL); const int root = 0; @@ -45,64 +30,64 @@ int main( MPI_Comm_rank(MPI_COMM_WORLD, &myrank); const double tic = timer(); // find name of directory where IC is stored | 7 - if(2 != argc){ - if(root == myrank){ + if (2 != argc) { + if (root == myrank) { printf("directory name should be given as input\n"); } goto abort; } const char * dirname_ic = argv[1]; // initialise fileio object - if(0 != fileio.init()){ + if (0 != fileio.init()) { goto abort; } // initialise time step and time units | 8 size_t step = 0; - if(0 != fileio.r_serial(dirname_ic, "step", 0, NULL, fileio.npy_size_t, sizeof(size_t), &step)){ + if (0 != fileio.r_serial(dirname_ic, "step", 0, NULL, fileio.npy_size_t, sizeof(size_t), &step)) { goto abort; } double time = 0.; - if(0 != fileio.r_serial(dirname_ic, "time", 0, NULL, fileio.npy_double, sizeof(double), &time)){ + if (0 != fileio.r_serial(dirname_ic, "time", 0, NULL, fileio.npy_double, sizeof(double), &time)) { goto abort; } // initialise structures | 8 domain_t domain = {0}; - if(0 != domain_init(dirname_ic, &domain)){ + if (0 != domain_init(dirname_ic, &domain)) { goto abort; } fluid_t fluid = {0}; - if(0 != fluid_init(dirname_ic, &domain, &fluid)){ + if (0 != fluid_init(dirname_ic, &domain, &fluid)) { goto abort; } // initialise auxiliary objects | 9 - if(0 != logging.init(&domain, time)){ + if (0 != logging.init(&domain, time)) { goto abort; } - if(0 != save.init(&domain, time)){ + if (0 != save.init(&domain, time)) { goto abort; } - if(0 != statistics.init(&domain, time)){ + if (0 != statistics.init(&domain, time)) { goto abort; } // check termination conditions double timemax = 0.; - if(0 != config.get_double("timemax", &timemax)){ + if (0 != config.get_double("timemax", &timemax)) { goto abort; } double wtimemax = 0.; - if(0 != config.get_double("wtimemax", &wtimemax)){ + if (0 != config.get_double("wtimemax", &wtimemax)) { goto abort; } // report - if(root == myrank){ + if (root == myrank) { printf("step: %zu, time: % .7e\n", step, time); printf("timemax: % .7e, wtimemax: % .7e\n", timemax, wtimemax); } // main loop - for(;;){ + for (;;) { // proceed for one step double dt = 0.; - if(0 != integrate(&domain, &fluid, &dt)){ + if (0 != integrate(&domain, &fluid, &dt)) { goto abort; } // update step and simulation / wall time | 3 @@ -111,29 +96,29 @@ int main( const double toc = timer(); // terminate if one of the following conditions is met | 8 // the simulation is finished - if(timemax < time){ + if (timemax < time) { break; } // wall time limit is reached - if(wtimemax < toc - tic){ + if (wtimemax < toc - tic) { break; } // compute and output log regularly | 3 - if(logging.get_next_time() < time){ + if (logging.get_next_time() < time) { logging.check_and_output(&domain, step, time, dt, toc - tic, &fluid); } // save flow fields regularly | 3 - if(save.get_next_time() < time){ - save_entrypoint(&domain, step, time, &fluid); + if (save.get_next_time() < time) { + save.output(&domain, step, time, &fluid); } // collect statistics regularly | 3 - if(statistics.get_next_time() < time){ + if (statistics.get_next_time() < time) { statistics.collect(&domain, &fluid); } } // finalisation // save final flow fields | 1 - save_entrypoint(&domain, step, time, &fluid); + save.output(&domain, step, time, &fluid); // save collected statistics | 1 statistics.output(&domain, step); // finalise MPI | 2 diff --git a/src/save.c b/src/save.c index 69a4bd10..e1c539bc 100644 --- a/src/save.c +++ b/src/save.c @@ -3,9 +3,9 @@ #include #include #include "sdecomp.h" -#include "param.h" #include "memory.h" #include "domain.h" +#include "fluid.h" #include "save.h" #include "fileio.h" #include "config.h" @@ -27,16 +27,16 @@ static double g_next = 0.; * @param[in] domain : MPI communicator * @param[in] time : current time (hereafter in free-fall time units) */ -static int init( +static int init ( const domain_t * domain, const double time -){ +) { // fetch timings - if(0 != config.get_double("save_rate", &g_rate)){ + if (0 != config.get_double("save_rate", &g_rate)) { return 1; } double after = 0.; - if(0 != config.get_double("save_after", &after)){ + if (0 != config.get_double("save_after", &after)) { return 1; } // schedule next event @@ -52,7 +52,7 @@ static int init( const int root = 0; int myrank = root; sdecomp.get_comm_rank(domain->info, &myrank); - if(root == myrank){ + if (root == myrank) { FILE * stream = stdout; fprintf(stream, "SAVE\n"); fprintf(stream, "\tdest: %s\n", g_dirname_prefix); @@ -64,37 +64,45 @@ static int init( } /** - * @brief prepare place to output flow fields - * @param[in] domain : information related to MPI domain decomposition - * @param[in] step : time step - * @param[out] dirname : name of created directory + * @brief output an instantaneous flow field + * @param[in] domain : information related to MPI domain decomposition + * @param[in] step : time step + * @param[in] time : current simulation time + * @param[in] fluid : flow field */ -static int prepare( +static int output ( const domain_t * domain, - const int step, - char ** dirname -){ + const size_t step, + const double time, + const fluid_t * fluid +) { // set directory name snprintf( g_dirname, g_dirname_nchars + 1, - "%s%0*d", + "%s%0*zu", g_dirname_prefix, g_dirname_ndigits, step ); - *dirname = g_dirname; // get communicator to identify the main process const int root = 0; int myrank = root; sdecomp.get_comm_rank(domain->info, &myrank); // create directory - if(root == myrank){ + if (root == myrank) { // although it may fail, anyway continue, which is designed to be safe - fileio.mkdir(*dirname); + fileio.mkdir(g_dirname); } // wait for the main process to complete making directory MPI_Barrier(MPI_COMM_WORLD); + // save quantities + if (root == myrank) { + fileio.w_serial(g_dirname, "step", 0, NULL, fileio.npy_size_t, sizeof(size_t), &step); + fileio.w_serial(g_dirname, "time", 0, NULL, fileio.npy_double, sizeof(double), &time); + } + domain_save(g_dirname, domain); + fluid_save(g_dirname, domain, fluid); // schedule next saving event g_next += g_rate; return 0; @@ -104,15 +112,15 @@ static int prepare( * @brief getter of a member: g_next * @return : g_next */ -static double get_next_time( +static double get_next_time ( void -){ +) { return g_next; } const save_t save = { .init = init, - .prepare = prepare, + .output = output, .get_next_time = get_next_time, }; diff --git a/src/statistics.c b/src/statistics.c index ac2e9321..a0b39e8d 100644 --- a/src/statistics.c +++ b/src/statistics.c @@ -3,12 +3,15 @@ #include #include #include "sdecomp.h" -#include "param.h" #include "memory.h" #include "domain.h" +#include "fluid.h" +#include "fluid_solver.h" #include "statistics.h" #include "fileio.h" #include "config.h" +#include "array_macros/domain/hxxf.h" +#include "array_macros/domain/jdxf.h" #include "array_macros/fluid/ux.h" #include "array_macros/fluid/uy.h" #if NDIMS == 3 @@ -25,11 +28,8 @@ #endif #include "array_macros/statistics/t1.h" #include "array_macros/statistics/t2.h" -#include "array_macros/statistics/uxt.h" - -// compress 3D statistical field to 1D (true) -// or save it as it is (false) -static const bool g_reduction = true; +#include "array_macros/statistics/adv.h" +#include "array_macros/statistics/dif.h" // parameters to specify directory name static const char g_dirname_prefix[] = {"output/stat/step"}; @@ -55,11 +55,8 @@ static array_t g_uz2 = {0}; #endif static array_t g_t1 = {0}; static array_t g_t2 = {0}; -static array_t g_uxt = {0}; - -// diffusivities, store here for convenience -static double g_m_dif = 0.; -static double g_t_dif = 0.; +static array_t g_adv = {0}; +static array_t g_dif = {0}; /** * @brief constructor - initialise and allocate internal buffers, schedule collection @@ -67,16 +64,16 @@ static double g_t_dif = 0.; * @param[in] time : current time (hereafter in free-fall time units) * @return : error code */ -static int init( +static int init ( const domain_t * domain, const double time -){ +) { // fetch timings - if(0 != config.get_double("stat_rate", &g_rate)){ + if (0 != config.get_double("stat_rate", &g_rate)) { return 1; } double after = 0.; - if(0 != config.get_double("stat_after", &after)){ + if (0 != config.get_double("stat_after", &after)) { return 1; } g_next = g_rate * ceil( @@ -88,22 +85,23 @@ static int init( + g_dirname_ndigits; g_dirname = memory_calloc(g_dirname_nchars + 2, sizeof(char)); // prepare arrays - if(0 != array.prepare(domain, UX1_NADDS, sizeof(double), &g_ux1)) return 1; - if(0 != array.prepare(domain, UX2_NADDS, sizeof(double), &g_ux2)) return 1; - if(0 != array.prepare(domain, UY1_NADDS, sizeof(double), &g_uy1)) return 1; - if(0 != array.prepare(domain, UY2_NADDS, sizeof(double), &g_uy2)) return 1; + if (0 != array.create(domain, UX1_NADDS, sizeof(double), &g_ux1)) return 1; + if (0 != array.create(domain, UX2_NADDS, sizeof(double), &g_ux2)) return 1; + if (0 != array.create(domain, UY1_NADDS, sizeof(double), &g_uy1)) return 1; + if (0 != array.create(domain, UY2_NADDS, sizeof(double), &g_uy2)) return 1; #if NDIMS == 3 - if(0 != array.prepare(domain, UZ1_NADDS, sizeof(double), &g_uz1)) return 1; - if(0 != array.prepare(domain, UZ2_NADDS, sizeof(double), &g_uz2)) return 1; + if (0 != array.create(domain, UZ1_NADDS, sizeof(double), &g_uz1)) return 1; + if (0 != array.create(domain, UZ2_NADDS, sizeof(double), &g_uz2)) return 1; #endif - if(0 != array.prepare(domain, T1_NADDS, sizeof(double), &g_t1 )) return 1; - if(0 != array.prepare(domain, T2_NADDS, sizeof(double), &g_t2 )) return 1; - if(0 != array.prepare(domain, UXT_NADDS, sizeof(double), &g_uxt)) return 1; + if (0 != array.create(domain, T1_NADDS, sizeof(double), &g_t1 )) return 1; + if (0 != array.create(domain, T2_NADDS, sizeof(double), &g_t2 )) return 1; + if (0 != array.create(domain, ADV_NADDS, sizeof(double), &g_adv)) return 1; + if (0 != array.create(domain, DIF_NADDS, sizeof(double), &g_dif)) return 1; // report const int root = 0; int myrank = root; sdecomp.get_comm_rank(domain->info, &myrank); - if(root == myrank){ + if (root == myrank) { FILE * stream = stdout; fprintf(stream, "STATISTICS\n"); fprintf(stream, "\tdest: %s\n", g_dirname_prefix); @@ -114,25 +112,16 @@ static int init( return 0; } -/** - * @brief getter of a member: g_next - * @return : g_next - */ -static double get_next_time( +static double get_next_time ( void -){ +) { return g_next; } -/** - * @brief compute ux^1 and ux^2 and add results to the arrays - * @param[in] domain : information related to MPI domain decomposition - * @param[in] ux : x velocity - */ -static void collect_mean_ux( +static void collect_mean_ux ( const domain_t * domain, const double * restrict ux -){ +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; #if NDIMS == 3 @@ -141,16 +130,16 @@ static void collect_mean_ux( double * restrict ux1 = g_ux1.data; double * restrict ux2 = g_ux2.data; #if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize + 1; i++){ + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { UX1(i, j) += pow(UX(i, j), 1.); UX2(i, j) += pow(UX(i, j), 2.); } } #else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize + 1; i++){ + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { UX1(i, j, k) += pow(UX(i, j, k), 1.); UX2(i, j, k) += pow(UX(i, j, k), 2.); } @@ -159,15 +148,10 @@ static void collect_mean_ux( #endif } -/** - * @brief compute uy^1 and uy^2 and add results to the arrays - * @param[in] domain : information related to MPI domain decomposition - * @param[in] uy : y velocity - */ -static void collect_mean_uy( +static void collect_mean_uy ( const domain_t * domain, const double * restrict uy -){ +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; #if NDIMS == 3 @@ -176,16 +160,16 @@ static void collect_mean_uy( double * restrict uy1 = g_uy1.data; double * restrict uy2 = g_uy2.data; #if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 0; i <= isize + 1; i++){ + for (int j = 1; j <= jsize; j++) { + for (int i = 0; i <= isize + 1; i++) { UY1(i, j) += pow(UY(i, j), 1.); UY2(i, j) += pow(UY(i, j), 2.); } } #else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 0; i <= isize + 1; i++){ + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 0; i <= isize + 1; i++) { UY1(i, j, k) += pow(UY(i, j, k), 1.); UY2(i, j, k) += pow(UY(i, j, k), 2.); } @@ -195,23 +179,18 @@ static void collect_mean_uy( } #if NDIMS == 3 -/** - * @brief compute uz^1 and uz^2 and add results to the arrays - * @param[in] domain : information related to MPI domain decomposition - * @param[in] uz : z velocity - */ -static void collect_mean_uz( +static void collect_mean_uz ( const domain_t * domain, const double * restrict uz -){ +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; const int ksize = domain->mysizes[2]; double * restrict uz1 = g_uz1.data; double * restrict uz2 = g_uz2.data; - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 0; i <= isize + 1; i++){ + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 0; i <= isize + 1; i++) { UZ1(i, j, k) += pow(UZ(i, j, k), 1.); UZ2(i, j, k) += pow(UZ(i, j, k), 2.); } @@ -220,15 +199,10 @@ static void collect_mean_uz( } #endif -/** - * @brief compute T^1 and T^2 and add results to the arrays - * @param[in] domain : information related to MPI domain decomposition - * @param[in] t : temperature - */ -static void collect_mean_t( +static void collect_mean_t ( const domain_t * domain, const double * restrict t -){ +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; #if NDIMS == 3 @@ -237,16 +211,16 @@ static void collect_mean_t( double * restrict t1 = g_t1.data; double * restrict t2 = g_t2.data; #if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 0; i <= isize + 1; i++){ + for (int j = 1; j <= jsize; j++) { + for (int i = 0; i <= isize + 1; i++) { T1(i, j) += pow(T(i, j), 1.); T2(i, j) += pow(T(i, j), 2.); } } #else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 0; i <= isize + 1; i++){ + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 0; i <= isize + 1; i++) { T1(i, j, k) += pow(T(i, j, k), 1.); T2(i, j, k) += pow(T(i, j, k), 2.); } @@ -255,40 +229,64 @@ static void collect_mean_t( #endif } -/** - * @brief compute ux T and add results to the arrays - * @param[in] domain : information related to MPI domain decomposition - * @param[in] ux : x velocity - * @param[in] t : temperature - */ -static void collect_uxt( +static void collect_adv ( const domain_t * domain, const double * restrict ux, const double * restrict t -){ +) { + const int isize = domain->mysizes[0]; + const int jsize = domain->mysizes[1]; +#if NDIMS == 3 + const int ksize = domain->mysizes[2]; +#endif + const double * restrict hxxf = domain->hxxf; + const double * restrict jdxf = domain->jdxf; + double * restrict adv = g_adv.data; +#if NDIMS == 2 + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + ADV(i, j) += JDXF(i) / HXXF(i) * UX(i, j) * (0.5 * T(i-1, j ) + 0.5 * T(i , j )); + } + } +#else + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + ADV(i, j, k) += JDXF(i) / HXXF(i) * UX(i, j, k) * (0.5 * T(i-1, j , k ) + 0.5 * T(i , j , k )); + } + } + } +#endif +} + +static void collect_dif ( + const domain_t * domain, + const double diffusivity, + const double * restrict t +) { const int isize = domain->mysizes[0]; const int jsize = domain->mysizes[1]; #if NDIMS == 3 const int ksize = domain->mysizes[2]; #endif - double * restrict uxt = g_uxt.data; + const double * restrict hxxf = domain->hxxf; + const double * restrict jdxf = domain->jdxf; + double * restrict dif = g_dif.data; #if NDIMS == 2 - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize + 1; i++){ - const double t_ = - + 0.5 * T(i-1, j ) - + 0.5 * T(i , j ); - UXT(i, j) += UX(i, j) * t_; + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + const double t_xm = T(i-1, j ); + const double t_xp = T(i , j ); + DIF(i, j) -= diffusivity * JDXF(i) / HXXF(i) / HXXF(i) * (t_xp - t_xm); } } #else - for(int k = 1; k <= ksize; k++){ - for(int j = 1; j <= jsize; j++){ - for(int i = 1; i <= isize + 1; i++){ - const double t_ = - + 0.5 * T(i-1, j , k ) - + 0.5 * T(i , j , k ); - UXT(i, j, k) += UX(i, j, k) * t_; + for (int k = 1; k <= ksize; k++) { + for (int j = 1; j <= jsize; j++) { + for (int i = 1; i <= isize + 1; i++) { + const double t_xm = T(i-1, j , k ); + const double t_xp = T(i , j , k ); + DIF(i, j, k) -= diffusivity * JDXF(i) / HXXF(i) / HXXF(i) * (t_xp - t_xm); } } } @@ -301,10 +299,10 @@ static void collect_uxt( * @param[in] fluid : flow field * @return : error code */ -static int collect( +static int collect ( const domain_t * domain, const fluid_t * fluid -){ +) { // collect temporally-averaged quantities collect_mean_ux(domain, fluid->ux.data); collect_mean_uy(domain, fluid->uy.data); @@ -312,10 +310,8 @@ static int collect( collect_mean_uz(domain, fluid->uz.data); #endif collect_mean_t(domain, fluid->t.data); - collect_uxt(domain, fluid->ux.data, fluid->t.data); - // assign diffusivities - g_m_dif = fluid->m_dif; - g_t_dif = fluid->t_dif; + collect_adv(domain, fluid->ux.data, fluid->t.data); + collect_dif(domain, fluid_compute_temperature_diffusivity(fluid), fluid->t.data); // increment number of samples g_num += 1; // schedule next event @@ -323,78 +319,6 @@ static int collect( return 0; } -/** - * @brief reduce N-dimensional (xy, xyz) array to 1D (x) vector - * @param[in] domain : information related to MPI domain decomposition - * @param[in] dirname : name of directory - * @param[in] dsetname : name of dataset - * @param[in] array : N-dimensional array to be reduced - */ -static int reduce_and_write( - const domain_t * domain, - const char dirname[], - const char dsetname[], - const array_t * array -){ - // NOTE: assuming no halo cells in y - if(0 != array->nadds[1][0] || 0 != array->nadds[1][1]){ - printf("stat array seems to have irregular shape in y\n"); - return 1; - } -#if NDIMS == 3 - // NOTE: assuming no halo cells in z - if(0 != array->nadds[2][0] || 0 != array->nadds[2][1]){ - printf("stat array seems to have irregular shape in z\n"); - return 1; - } -#endif - const int isize = domain->mysizes[0] - + array->nadds[0][0] - + array->nadds[0][1]; - const int jsize = domain->mysizes[1]; -#if NDIMS == 3 - const int ksize = domain->mysizes[2]; -#endif - const double * restrict data = array->data; - double * restrict vec = memory_calloc(isize, sizeof(double)); -#if NDIMS == 2 - for(int j = 0; j < jsize; j++){ - for(int i = 0; i < isize; i++){ - vec[i] += data[j * isize + i]; - } - } -#else - for(int k = 0; k < ksize; k++){ - for(int j = 0; j < jsize; j++){ - for(int i = 0; i < isize; i++){ - vec[i] += data[k * jsize * isize + j * isize + i]; - } - } - } -#endif - const int root = 0; - int myrank = root; - sdecomp.get_comm_rank(domain->info, &myrank); - MPI_Comm comm_cart = MPI_COMM_NULL; - sdecomp.get_comm_cart(domain->info, &comm_cart); - const void * sendbuf = root == myrank ? MPI_IN_PLACE : vec; - void * recvbuf = vec; - MPI_Reduce(sendbuf, recvbuf, isize, MPI_DOUBLE, MPI_SUM, root, comm_cart); - if(root == myrank){ - fileio.w_serial( - dirname, - dsetname, - 1, - (size_t [1]){isize}, - fileio.npy_double, - sizeof(double), - vec - ); - } - memory_free(vec); - return 0; -} - /** * @brief save structures which contains collected statistical data * @param[in] domain : information related to MPI domain decomposition @@ -404,10 +328,10 @@ static int reduce_and_write( static int output( const domain_t * domain, const size_t step -){ +) { // when no statistics are collected (g_num is 0), // no reason to save, so abort - if(0 == g_num){ + if (0 == g_num) { return 0; } // set directory name @@ -424,13 +348,11 @@ static int output( int myrank = root; sdecomp.get_comm_rank(domain->info, &myrank); // create directory and save scalars from main process - if(root == myrank){ + if (root == myrank) { // although it may fail, anyway continue, which is designed to be safe fileio.mkdir(g_dirname); // save scalars fileio.w_serial(g_dirname, "num", 0, NULL, fileio.npy_size_t, sizeof(size_t), &g_num); - fileio.w_serial(g_dirname, "m_dif", 0, NULL, fileio.npy_double, sizeof(double), &g_m_dif); - fileio.w_serial(g_dirname, "t_dif", 0, NULL, fileio.npy_double, sizeof(double), &g_t_dif); } // wait for the main process to complete making directory MPI_Barrier(MPI_COMM_WORLD); @@ -453,22 +375,13 @@ static int output( #endif {.name = "t1", .array = &g_t1 }, {.name = "t2", .array = &g_t2 }, - {.name = "uxt", .array = &g_uxt}, + {.name = "adv", .array = &g_adv}, + {.name = "dif", .array = &g_dif}, }; - // for each variable, call corresponding saver - const size_t nvars = sizeof(variables) / sizeof(variable_t); - if(g_reduction){ - // reduce 3D array to 1D vector - for(size_t n = 0; n < nvars; n++){ - const variable_t * v = variables + n; - reduce_and_write(domain, g_dirname, v->name, v->array); - } - }else{ - // dump 3D field (as it is) - for(size_t n = 0; n < nvars; n++){ - const variable_t * v = variables + n; - array.dump(domain, g_dirname, v->name, fileio.npy_double, v->array); - } + // dump each statistical variable to a file + for (size_t n = 0; n < sizeof(variables) / sizeof(variables[0]); n++) { + const variable_t * v = variables + n; + array.dump(domain, g_dirname, v->name, fileio.npy_double, v->array); } return 0; } diff --git a/src/tdm.c b/src/tdm.c index ef66454e..0d9a2b03 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -26,7 +26,7 @@ /* divide the first row by center-diagonal term | 2 */ \ v[0] = u[0] / c[0]; \ q[0] = q[0] / c[0]; \ - /* forward substitution | 7 */ \ + /* forward sweep | 7 */ \ for(int i = 1; i < n - 1; i++){ \ /* assume positive-definite system */ \ /* to skip zero-division checks */ \ diff --git a/tools/define_arrays.py b/tools/define_arrays.py index 52b19832..a9428571 100644 --- a/tools/define_arrays.py +++ b/tools/define_arrays.py @@ -127,7 +127,7 @@ def gen_1d(dname, vname, bounds): output(dname, vname, text) -def gen_nd(dname, vname, bounds): +def gen_nd(dname, vname, only_3d, bounds): # prepare macros for N-dimensional array lbound_0 = get_lbound(bounds[0][0]) ubound_0 = get_ubound(bounds[0][1], "isize") @@ -154,7 +154,7 @@ def gen_nd(dname, vname, bounds): ) text = str() # 2D array (not for z-related things) - if "z" not in vname: + if not only_3d: text += ( f"#if NDIMS == 2\n" f"// [{lbound_0} : {ubound_0}]," @@ -192,39 +192,45 @@ def gen_nd(dname, vname, bounds): def domain(root): dname = f"{root}/domain" os.system(f"rm {dname}/*.h") - gen_1d(dname, "xf", (+0, +1)) - gen_1d(dname, "xc", (+1, +1)) - gen_1d(dname, "dxf", (+0, +0)) - gen_1d(dname, "dxc", (+0, +1)) + # wall-normal cell-face (xf) and center (xc) positions + gen_1d(dname, "xf", (+0, +1)) + gen_1d(dname, "xc", (+1, +1)) + # wall-normal scale factors at wall-normal faces and centers + gen_1d(dname, "hxxf", (+0, +1)) + gen_1d(dname, "hxxc", (+0, +0)) + # Jacobian determinants at wall-normal faces and centers + gen_1d(dname, "jdxf", (+0, +1)) + gen_1d(dname, "jdxc", (+0, +0)) def fluid(root): dname = f"{root}/fluid" os.system(f"rm {dname}/*.h") - gen_nd(dname, "ux", ((+0, +1), (+1, +1), (+1, +1))) - gen_nd(dname, "uy", ((+1, +1), (+1, +1), (+1, +1))) - gen_nd(dname, "uz", ((+1, +1), (+1, +1), (+1, +1))) - gen_nd(dname, "p", ((+1, +1), (+1, +1), (+1, +1))) - gen_nd(dname, "t", ((+1, +1), (+1, +1), (+1, +1))) - gen_nd(dname, "psi", ((+1, +1), (+1, +1), (+1, +1))) - gen_nd(dname, "srcux", ((-1, +0), (+0, +0), (+0, +0))) - gen_nd(dname, "srcuy", ((+0, +0), (+0, +0), (+0, +0))) - gen_nd(dname, "srcuz", ((+0, +0), (+0, +0), (+0, +0))) - gen_nd(dname, "srct", ((+0, +0), (+0, +0), (+0, +0))) + gen_nd(dname, "ux", False, ((+0, +1), (+1, +1), (+1, +1))) + gen_nd(dname, "uy", False, ((+1, +1), (+1, +1), (+1, +1))) + gen_nd(dname, "uz", True, ((+1, +1), (+1, +1), (+1, +1))) + gen_nd(dname, "p", False, ((+1, +1), (+1, +1), (+1, +1))) + gen_nd(dname, "t", False, ((+1, +1), (+1, +1), (+1, +1))) + gen_nd(dname, "psi", False, ((+1, +1), (+1, +1), (+1, +1))) + gen_nd(dname, "srcux", False, ((-1, +0), (+0, +0), (+0, +0))) + gen_nd(dname, "srcuy", False, ((+0, +0), (+0, +0), (+0, +0))) + gen_nd(dname, "srcuz", True, ((+0, +0), (+0, +0), (+0, +0))) + gen_nd(dname, "srct", False, ((+0, +0), (+0, +0), (+0, +0))) def statistics(root): dname = f"{root}/statistics" os.system(f"rm {dname}/*.h") - gen_nd(dname, "ux1", ((+0, +1), (+0, +0), (+0, +0))) - gen_nd(dname, "ux2", ((+0, +1), (+0, +0), (+0, +0))) - gen_nd(dname, "uy1", ((+1, +1), (+0, +0), (+0, +0))) - gen_nd(dname, "uy2", ((+1, +1), (+0, +0), (+0, +0))) - gen_nd(dname, "uz1", ((+1, +1), (+0, +0), (+0, +0))) - gen_nd(dname, "uz2", ((+1, +1), (+0, +0), (+0, +0))) - gen_nd(dname, "t1", ((+1, +1), (+0, +0), (+0, +0))) - gen_nd(dname, "t2", ((+1, +1), (+0, +0), (+0, +0))) - gen_nd(dname, "uxt", ((+0, +1), (+0, +0), (+0, +0))) + gen_nd(dname, "ux1", False, ((+0, +1), (+0, +0), (+0, +0))) + gen_nd(dname, "ux2", False, ((+0, +1), (+0, +0), (+0, +0))) + gen_nd(dname, "uy1", False, ((+1, +1), (+0, +0), (+0, +0))) + gen_nd(dname, "uy2", False, ((+1, +1), (+0, +0), (+0, +0))) + gen_nd(dname, "uz1", True, ((+1, +1), (+0, +0), (+0, +0))) + gen_nd(dname, "uz2", True, ((+1, +1), (+0, +0), (+0, +0))) + gen_nd(dname, "t1", False, ((+1, +1), (+0, +0), (+0, +0))) + gen_nd(dname, "t2", False, ((+1, +1), (+0, +0), (+0, +0))) + gen_nd(dname, "adv", False, ((+0, +1), (+0, +0), (+0, +0))) + gen_nd(dname, "dif", False, ((+0, +1), (+0, +0), (+0, +0))) if __name__ == "__main__": @@ -235,3 +241,4 @@ def statistics(root): fluid(root) # arrays to store temporally-averaged statistics statistics(root) +