├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── images ├── evrard_di.gif ├── khi_di.gif ├── khi_st.gif ├── shocktube_dens.gif └── shocktube_gsph.gif ├── include ├── CMakeLists.txt ├── bhtree.hpp ├── defines.hpp ├── disph │ ├── CMakeLists.txt │ ├── d_fluid_force.hpp │ └── d_pre_interaction.hpp ├── exception.hpp ├── exhaustive_search.hpp ├── fluid_force.hpp ├── gravity_force.hpp ├── gsph │ ├── CMakeLists.txt │ ├── g_fluid_force.hpp │ └── g_pre_interaction.hpp ├── kernel │ ├── CMakeLists.txt │ ├── cubic_spline.hpp │ ├── kernel_function.hpp │ └── wendland_kernel.hpp ├── logger.hpp ├── module.hpp ├── openmp.hpp ├── output.hpp ├── parameters.hpp ├── particle.hpp ├── periodic.hpp ├── pre_interaction.hpp ├── simulation.hpp ├── solver.hpp ├── timestep.hpp └── vector_type.hpp ├── sample ├── evrard │ └── evrard.json ├── gresho_chan_vortex │ └── gresho_chan_vortex.json ├── hydrostatic │ └── hydrostatic.json ├── khi │ └── khi.json ├── pairing_instability │ └── pairing_instability.json └── shock_tube │ └── shock_tube.json ├── sph.sln ├── sph.vcxproj ├── sph.vcxproj.filters ├── src ├── CMakeLists.txt ├── bhtree.cpp ├── disph │ ├── CMakeLists.txt │ ├── d_fluid_force.cpp │ └── d_pre_interaction.cpp ├── exhaustive_search.cpp ├── fluid_force.cpp ├── gravity_force.cpp ├── gsph │ ├── CMakeLists.txt │ ├── g_fluid_force.cpp │ └── g_pre_interaction.cpp ├── logger.cpp ├── main.cpp ├── output.cpp ├── pre_interaction.cpp ├── sample │ ├── CMakeLists.txt │ ├── evrard.cpp │ ├── gresho_chan_vortex.cpp │ ├── hydrostatic.cpp │ ├── khi.cpp │ ├── pairing_instability.cpp │ └── shock_tube.cpp ├── simulation.cpp ├── solver.cpp └── timestep.cpp └── test └── kernel_test ├── kernel_test.cpp ├── kernel_test.vcxproj └── kernel_test.vcxproj.filters /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | obj/ 3 | x64/ 4 | sph\.exe\.stackdump 5 | .vs/ 6 | *.vcxproj.user 7 | 8 | parameters\.json 9 | sample/shock_tube/results/ 10 | 11 | sample/gresho_chan_vortex/results/ 12 | 13 | sample/hydrostatic/results/ 14 | 15 | sample/khi/results/ 16 | 17 | sample/evrard/results/ 18 | 19 | sample/pairing_instability/results/ 20 | 21 | build/ 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(sphcode CXX) 3 | 4 | find_package(OpenMP REQUIRED) 5 | find_package(Boost REQUIRED) 6 | 7 | add_executable(sph) 8 | target_compile_features(sph PUBLIC cxx_std_14) 9 | target_compile_options(sph 10 | PUBLIC 11 | -Wall 12 | -Wno-sign-compare 13 | -Wno-maybe-uninitialized 14 | -funroll-loops 15 | -ffast-math 16 | ) 17 | target_include_directories(sph PUBLIC include) 18 | target_link_libraries(sph 19 | PUBLIC 20 | OpenMP::OpenMP_CXX 21 | Boost::boost 22 | ) 23 | 24 | add_subdirectory(include) 25 | add_subdirectory(src) 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 mitchiinaga 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | 3 | # C++11 4 | CXXFLAGS = -std=gnu++14 5 | 6 | # Optimization 7 | CXXFLAGS += -O3 -funroll-loops -ffast-math 8 | 9 | # OpenMP 10 | CXXFLAGS += -fopenmp 11 | 12 | # use for debug 13 | CXXFLAGS += -Wall 14 | CXXFLAGS += -Wno-sign-compare 15 | CXXFLAGS += -Wno-maybe-uninitialized 16 | #CXXFLAGS += -g =D_DEBUG 17 | 18 | SRCDIR = src 19 | BUILDDIR = obj 20 | INC = -Iinclude 21 | 22 | SRCEXT = cpp 23 | DEPEXT = d 24 | OBJEXT = o 25 | 26 | TARGET = sph 27 | 28 | # Makefileの参考: https://minus9d.hatenablog.com/entry/2017/10/20/222901 29 | # DO NOT EDIT BELOW THIS LINE 30 | #------------------------------------------------------- 31 | 32 | sources = $(shell find $(SRCDIR) -type f -name *.$(SRCEXT)) 33 | objects = $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(subst $(SRCEXT),$(OBJEXT),$(sources))) 34 | dependencies = $(subst .$(OBJEXT),.$(DEPEXT),$(objects)) 35 | 36 | # Defauilt Make 37 | all: $(TARGET) 38 | 39 | # Remake 40 | remake: clean all 41 | 42 | # ディレクトリ生成 43 | directories: 44 | @mkdir -p $(BUILDDIR) 45 | 46 | # 中間生成物のためのディレクトリを削除 47 | clean: 48 | @$(RM) -rf $(BUILDDIR)/* $(TARGET) 49 | 50 | # 自動抽出した.dファイルを読み込む 51 | -include $(dependencies) 52 | 53 | # オブジェクトファイルをリンクしてバイナリを生成 54 | $(TARGET): $(objects) 55 | $(CXX) -o $(TARGET) $(CXXFLAGS) $^ $(FLAGS) 56 | 57 | # ソースファイルのコンパイルしてオブジェクトファイルを生成 58 | # また、ソースファイルの依存関係を自動抽出して.dファイルに保存 59 | $(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) 60 | @mkdir -p $(dir $@) 61 | $(CXX) $(CXXFLAGS) $(INC) -c -o $@ $< 62 | @$(CXX) $(CXXFLAGS) $(INC) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT) 63 | @cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp 64 | @sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT) 65 | @sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT) 66 | @rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp 67 | 68 | # Non-File Targets 69 | .PHONY: all remake clean 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SPHCODE 2 | Smoothed Particle Hydrodynamics (SPH)法のサンプルコードです。圧縮性流体専用です。 3 | 4 | ## コンパイル 5 | 次元を `include/defines.hpp` の `DIM` に設定してからコンパイルします。 6 | 7 | ### Visual Studio 2017 8 | `sph.sln` を開いてコンパイルします。環境変数 `BOOST_INC_PATH` にBoostのパスを設定しておいてください。 9 | * 例: `BOOST_INC_PATH=C:\boost\boost_1_67_0\include\boost-1_67` 10 | 11 | ### Makefile 12 | Linux環境ではMakefileを使ってコンパイルできます。あまりちゃんと動作確認してません。 13 | GCCバージョン7.4.0でコンパイルチェックしています。 14 | 15 | [Makefileの書き方に関する備忘録 その4 - minus9d's diary](https://minus9d.hatenablog.com/entry/2017/10/20/222901) を参考にしています。 16 | 17 | ### CMake 18 | 次のコマンドでビルドします。 19 | ```Shell 20 | mkdir build 21 | cd build 22 | cmake .. 23 | make 24 | ``` 25 | 26 | ## 実行 27 | ### サンプル実行 28 | 次のコマンドを実行します。 29 | ```Shell 30 | ./sph 31 | ``` 32 | #### \ 33 | サンプルの名前を指定します。以下のサンプルを用意しています。 34 | 35 | |サンプル名|DIM|説明| 36 | |:---|:---|:---| 37 | |shock_tube|1|衝撃波管問題 (e.g. Hernquist & Katz 1989)| 38 | |pairing_instability|2|粒子の初期配置をグリッド状から少しだけずらしています。カーネル関数の設定によっては粒子同士がくっついてしまいます。| 39 | |gresho_chan_vortex|2|Gresho-Chan vortex (Gresho & Chan 1990)。圧力勾配力と遠心力が釣り合うような初期条件です。| 40 | |hydrostatic|2|静水圧 (Saitoh & Makino 2013)。圧力は全領域で一定ですが密度差があり、高密度領域を低密度領域が囲うような粒子配置となっています。| 41 | |khi|2|Kelvin-Helmholtz 不安定性 (Springel 2010)| 42 | |evrard|3|Evrard collapse (Evrard 1988)。自己重力入りのテスト計算です。| 43 | 44 | #### \ 45 | OpenMPのスレッド数を指定します。省略した場合は使用可能な最大スレッド数 (`omp_get_max_threads()` の戻り値)となります。 46 | 47 | ### 任意設定で実行 48 | `src/solver.cpp` の `Solver::make_initial_condition()` に初期条件を実装し、コンパイルしてから、次のコマンドを実行します。 49 | ```Shell 50 | ./sph 51 | ``` 52 | #### \ 53 | パラメータを入力したjsonファイルを指定します。 54 | 55 | ## 計算例 56 | ### 衝撃波管問題 57 | Godunov SPH法を使用 58 | 59 | * 密度の時間発展 60 | 61 | ![shocktube_gsph.gif](https://raw.githubusercontent.com/mitchiinaga/sphcode/master/images/shocktube_gsph.gif) 62 | 63 | * 厳密解との比較 64 | 65 | ![shocktube_dens.gif](https://raw.githubusercontent.com/mitchiinaga/sphcode/master/images/shocktube_dens.gif) 66 | 67 | ### Kelvin-Helmholtz不安定性 68 | * Standard SPH 69 | 70 | ![khi_st.gif](https://raw.githubusercontent.com/mitchiinaga/sphcode/master/images/khi_st.gif) 71 | 72 | * Density Independent SPH 73 | 74 | ![khi_di.gif](https://raw.githubusercontent.com/mitchiinaga/sphcode/master/images/khi_di.gif) 75 | 76 | ### Evrard collapse 77 | Density Independent SPH法 + Balsara switch + 時間依存人工粘性 78 | 79 | ![evrard_di.gif](https://raw.githubusercontent.com/mitchiinaga/sphcode/master/images/evrard_di.gif) 80 | 81 | ## 実装 82 | ### SPH方程式 83 | #### Standard SPH (density-energy formulation; Springel & Hernquist 2002, Monaghan 2002) 84 | 最小作用の原理から導出したSPH法の方程式です。smoothing length の空間微分 (grad-h term)を考慮した方程式系になっています。 85 | 86 | #### Density Independent SPH (pressure-energy formulation; Saitoh & Makino 2013; Hopkins 2013) 87 | 状態方程式からSPH粒子の体積を求めることによって、密度に陽に依存しない方程式にします。接触不連続面を正しく扱えるようになります。 88 | 89 | #### Godunov SPH (Inutsuka 2002; Cha & Whitworth 2003; Murante et al. 2011) 90 | Riemann solverを使って粒子間相互作用を計算することで、計算を安定化させる数値拡散が自動的に入るようになります。 91 | 92 | ### カーネル関数 93 | #### Cubic spline (e.g. Monaghan 1992) 94 | 昔からよく使われているオーソドックスなカーネル関数です。 95 | 96 | #### Wendland C4 (Wendland 1995) 97 | Cubic splineカーネルより高次のカーネル関数です。影響半径内の粒子数が大きいときに粒子同士がくっついてしまう不安定性 (pairing instability; Dehnen & Aly 2012) を防ぐことができます。 98 | 99 | ### 人工粘性 100 | #### signal velocity formulation (Monaghan 1997) 101 | Riemann solverによる数値拡散から類推して導出された人工粘性です。 102 | 103 | #### Balsara switch (Balsara 1995) 104 | 速度の回転が発散より大きい領域で人工粘性係数を小さくすることで、シアー領域に余分な粘性が効かないようにします。 105 | 106 | #### 時間依存人工粘性係数 (Rosswog et al. 2000) 107 | SPH粒子それぞれの人工粘性係数を時間変化させます。圧縮領域では人工粘性係数を大きく、それ以外では小さくするようにします。 108 | 109 | ### その他 110 | #### 人工熱伝導 (Price 2008; Wadsley et al. 2008) 111 | エネルギー方程式に人工的な熱伝導項を入れることで、接触不連続面での非物理的な圧力ジャンプを抑制できます。 112 | 113 | #### 自己重力 (Hernquist & Katz 1989) 114 | カーネル関数とsmoothing lengthによって重力ソフトニングを行います。 115 | 116 | #### Tree (Hernquist & Katz 1989) 117 | 近傍粒子探索に木構造を使うことで、計算量のオーダーをO(N^2)からO(N logN)に減らすことができます。 118 | 119 | ## パラメータ 120 | デフォルト値が空欄のパラメータは、必ず指定する必要があります。 121 | 122 | |パラメータ|型|説明|デフォルト値| 123 | |:---|:---|:---|:---| 124 | |outputDirectory|string|結果ファイルの出力先ディレクトリ|| 125 | |startTime|real|計算開始時刻|0| 126 | |endTime|real|計算終了時刻|| 127 | |outputTime|real|粒子データの出力間隔を時間で指定|(endTime - startTime) / 100| 128 | |energyTime|real|エネルギーの出力間隔を時間で指定|outputTime| 129 | |SPHType|string|SPH方程式の指定。"ssph", "disph", "gsph"のいずれか|"ssph"| 130 | |cflSound|real|音速による時間刻み制限を決めるパラメータ|0.3| 131 | |cflForce|real|粒子に働く力による時間刻み制限を決めるパラメータ|0.125| 132 | |avAlpha|real|初期人工粘性係数|1| 133 | |useBalsaraSwitch|bool|Balsara switchを有効にする|true| 134 | |useTimeDependentAV|bool|時間依存人工粘性を有効にする|false| 135 | |alphaMax|real|時間依存人工粘性の係数の上限値|2.0| 136 | |alphaMin|real|時間依存人工粘性の係数の下限値|0.1| 137 | |epsilonAV|real|時間依存人工粘性の係数の減衰タイムスケールを決めるパラメータ|0.2| 138 | |useArtificialConductivity|bool|人工熱伝導を使用する|false| 139 | |alphaAC|real|人工熱伝導係数|1.0| 140 | |maxTreeLevel|int|ツリーの最大レベル|20| 141 | |leafParticleNumber|int|ツリーの葉ノードに入る粒子数の最大値|1| 142 | |neighborNumber|int|近傍粒子数|32| 143 | |gamma|real|比熱比|| 144 | |kernel|string|カーネル関数。"cubic_spline"または"wendland"|"cubic_spline"| 145 | |iterativeSmoothingLength|bool|smoothing lengthをNewton法で求める|true| 146 | |periodic|bool|周期境界を使用する|false| 147 | |rangeMax|real array|周期境界使用時の座標の上限|| 148 | |rangeMin|real array|周期境界使用時の座標の下限|| 149 | |useGravity|bool|重力計算を有効にする|false| 150 | |G|real|重力定数|1| 151 | |theta|real|ツリー法で使用する見込み角|0.5| 152 | |use2ndOrderGSPH|bool|Godunov SPH法でMUSCL補間を使用する|true| 153 | 154 | ## 参考文献 155 | * Balsara, D. S. (1995). von Neumann stability analysis of smoothed particle hydrodynamics--suggestions for optimal algorithms. Journal of Computational Physics, 121(2), 357–372. https://doi.org/10.1016/S0021-9991(95)90221-X 156 | * Cha, S. H., & Whitworth, A. P. (2003). Implementations and tests of Godunov-type particle hydrodynamics. Monthly Notices of the Royal Astronomical Society, 340(1), 73–90. https://doi.org/10.1046/j.1365-8711.2003.06266.x 157 | * Dehnen, W., & Aly, H. (2012). Improving convergence in smoothed particle hydrodynamics simulations without pairing instability. Monthly Notices of the Royal Astronomical Society, 425(2), 1068–1082. https://doi.org/10.1111/j.1365-2966.2012.21439.x 158 | * Evrard, A. E. (1988). Beyond N-body: 3D cosmological gas dynamics. Monthly Notices of the Royal Astronomical Society, 235(3), 911–934. https://doi.org/10.1093/mnras/235.3.911 159 | * Gresho, P. M., & Chan, S. T. (1990). On the theory of semi-implicit projection methods for viscous incompressible flow and its implementation via a finite element method that also introduces a nearly consistent mass matrix. Part 2: Implementation. International Journal for Numerical Methods in Fluids, 11(5), 621–659. https://doi.org/10.1002/fld.1650110510 160 | * Hernquist, L., & Katz, N. (1989). TREESPH - A unification of SPH with the hierarchical tree method. The Astrophysical Journal Supplement Series, 70, 419. https://doi.org/10.1086/191344 161 | * Hopkins, P. F. (2013). A general class of Lagrangian smoothed particle hydrodynamics methods and implications for fluid mixing problems. Monthly Notices of the Royal Astronomical Society, 428(4), 2840–2856. https://doi.org/10.1093/mnras/sts210 162 | * Inutsuka, S. (2002). Reformulation of Smoothed Particle Hydrodynamics with Riemann Solver. Journal of Computational Physics, 179, 238. https://doi.org/10.1006/jcph.2002.7053 163 | * Murante, G., Borgani, S., Brunino, R., & Cha, S.-H. (2011). Hydrodynamic simulations with the Godunov smoothed particle hydrodynamics. Monthly Notices of the Royal Astronomical Society, 417(1), 136–153. https://doi.org/10.1111/j.1365-2966.2011.19021.x 164 | * Monaghan, J. J. (1992). Smoothed Particle Hydrodynamics. Annual Review of Astronomy and Astrophysics, 30(1), 543–574. https://doi.org/10.1146/annurev.aa.30.090192.002551 165 | * Monaghan, J. J. (1997). SPH and Riemann Solvers. Journal of Computational Physics, 136(2), 298–307. https://doi.org/10.1006/jcph.1997.5732 166 | * Monaghan, J. J. (2002). SPH compressible turbulence. Monthly Notices of the Royal Astronomical Society, 335(3), 843–852. https://doi.org/10.1046/j.1365-8711.2002.05678.x 167 | * Price, D. J. (2008). Modelling discontinuities and Kelvin–Helmholtz instabilities in SPH. Journal of Computational Physics, 227(24), 10040–10057. https://doi.org/10.1016/j.jcp.2008.08.011 168 | * Rosswog, S., Davies, M. B., Thielemann, F.-K., & Piran, T. (2000). Merging neutron stars: asymmetric systems. Astronomy and Astrophysics, 360, 171–184. Retrieved from http://adsabs.harvard.edu/abs/2000A&A...360..171R%5Cnpapers2://publication/uuid/39C9D6F4-C091-499D-8F66-867A98C4DD32 169 | * Saitoh, T. R., & Makino, J. (2013). A DENSITY-INDEPENDENT FORMULATION OF SMOOTHED PARTICLE HYDRODYNAMICS. The Astrophysical Journal, 768(1), 44. https://doi.org/10.1088/0004-637X/768/1/44 170 | * Springel, V. (2010). E pur si muove: Galilean-invariant cosmological hydrodynamical simulations on a moving mesh. Monthly Notices of the Royal Astronomical Society, 401(2), 791–851. https://doi.org/10.1111/j.1365-2966.2009.15715.x 171 | * Springel, V., & Hernquist, L. (2002). Cosmological smoothed particle hydrodynamics simulations: the entropy equation. Monthly Notices of the Royal Astronomical Society, 333(3), 649–664. https://doi.org/10.1046/j.1365-8711.2002.05445.x 172 | * Wadsley, J. W., Veeravalli, G., & Couchman, H. M. P. (2008). On the treatment of entropy mixing in numerical cosmology. Monthly Notices of the Royal Astronomical Society, 387(1), 427–438. https://doi.org/10.1111/j.1365-2966.2008.13260.x 173 | * Wendland, H. (1995). Piecewise polynomial, positive definite and compactly supported radial functions of minimal degree. Advances in Computational Mathematics, 4(1), 389–396. https://doi.org/10.1007/BF02123482 174 | -------------------------------------------------------------------------------- /images/evrard_di.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchiinaga/sphcode/41e74dad04a1af9364f48e20fd6ba14a71443214/images/evrard_di.gif -------------------------------------------------------------------------------- /images/khi_di.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchiinaga/sphcode/41e74dad04a1af9364f48e20fd6ba14a71443214/images/khi_di.gif -------------------------------------------------------------------------------- /images/khi_st.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchiinaga/sphcode/41e74dad04a1af9364f48e20fd6ba14a71443214/images/khi_st.gif -------------------------------------------------------------------------------- /images/shocktube_dens.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchiinaga/sphcode/41e74dad04a1af9364f48e20fd6ba14a71443214/images/shocktube_dens.gif -------------------------------------------------------------------------------- /images/shocktube_gsph.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchiinaga/sphcode/41e74dad04a1af9364f48e20fd6ba14a71443214/images/shocktube_gsph.gif -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(sph 2 | PUBLIC 3 | bhtree.hpp 4 | defines.hpp 5 | exception.hpp 6 | exhaustive_search.hpp 7 | fluid_force.hpp 8 | gravity_force.hpp 9 | logger.hpp 10 | module.hpp 11 | openmp.hpp 12 | output.hpp 13 | parameters.hpp 14 | particle.hpp 15 | periodic.hpp 16 | pre_interaction.hpp 17 | simulation.hpp 18 | solver.hpp 19 | timestep.hpp 20 | vector_type.hpp 21 | ) 22 | 23 | add_subdirectory(disph) 24 | add_subdirectory(gsph) 25 | add_subdirectory(kernel) 26 | -------------------------------------------------------------------------------- /include/bhtree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "vector_type.hpp" 8 | #include "particle.hpp" 9 | 10 | namespace sph 11 | { 12 | struct SPHParameters; 13 | class Periodic; 14 | 15 | constexpr int NCHILD = DIM == 1 ? 2 : DIM == 2 ? 4 : 8; 16 | 17 | class BHTree 18 | { 19 | class BHNode 20 | { 21 | public: 22 | SPHParticle * first; 23 | real mass; 24 | int num; 25 | BHNode * childs[NCHILD]; 26 | vec_t center; 27 | vec_t m_center; // mass center 28 | real edge; 29 | int level; 30 | real kernel_size; 31 | bool is_leaf; 32 | 33 | void clear() { 34 | first = nullptr; 35 | mass = 0.0; 36 | num = 0; 37 | std::fill(childs, childs + NCHILD, nullptr); 38 | center = 0.0; 39 | m_center = 0.0; 40 | edge = 0.0; 41 | level = 0; 42 | kernel_size = 0.0; 43 | is_leaf = false; 44 | } 45 | 46 | void root_clear() { 47 | first = nullptr; 48 | mass = 0.0; 49 | num = 0; 50 | m_center = 0.0; 51 | kernel_size = 0.0; 52 | is_leaf = false; 53 | } 54 | 55 | void create_tree(BHNode * & nodes, int & remaind, const int max_level, const int leaf_particle_num); 56 | void assign(SPHParticle * particle, BHNode * & nodes, int & remaind); 57 | real set_kernel(); 58 | void neighbor_search(const SPHParticle & p_i, std::vector & neighbor_list, int & n_neighbor, const bool is_ij, const Periodic * periodic); 59 | void calc_force(SPHParticle & p_i, const real theta2, const real g_constant, const Periodic * periodic); 60 | }; 61 | 62 | int m_max_level; 63 | int m_leaf_particle_num; 64 | bool m_is_periodic; 65 | vec_t m_range_max; 66 | vec_t m_range_min; 67 | std::shared_ptr m_periodic; 68 | BHNode m_root; 69 | std::shared_ptr m_nodes; 70 | int m_node_size; 71 | 72 | real m_g_constant; 73 | real m_theta; 74 | real m_theta2; 75 | public: 76 | void initialize(std::shared_ptr param); 77 | void resize(const int particle_num, const int tree_size = 5); 78 | void make(std::vector & particles, const int particle_num); 79 | void set_kernel(); 80 | int neighbor_search(const SPHParticle & p_i, std::vector & neighbor_list, const std::vector & particles, const bool is_ij = false); 81 | void tree_force(SPHParticle & p_i); 82 | }; 83 | 84 | } -------------------------------------------------------------------------------- /include/defines.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define DIM 1 6 | typedef double real; 7 | 8 | #ifndef M_PI 9 | #define M_PI 3.1415926535897932384626433832795028841971693993751 10 | #endif 11 | 12 | inline real inner_product(const real (&v1)[DIM], const real (&v2)[DIM]) 13 | { 14 | #if DIM == 1 15 | return v1[0] * v2[0]; 16 | #elif DIM == 2 17 | return v1[0] * v2[0] + v1[1] * v2[1]; 18 | #elif DIM == 3 19 | return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; 20 | #endif 21 | } 22 | 23 | inline real sqr(real x) { return x * x; } 24 | inline real pow3(real x) { return x * x * x; } 25 | inline real pow4(real x) { return x * x * x * x; } 26 | inline real pow5(real x) { return x * x * x * x * x; } 27 | inline real pow6(real x) { return x * x * x * x * x * x; } 28 | 29 | constexpr int neighbor_list_size = 20; 30 | 31 | // for debug 32 | //#define EXHAUSTIVE_SEARCH 33 | -------------------------------------------------------------------------------- /include/disph/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(sph 2 | PUBLIC 3 | d_fluid_force.hpp 4 | d_pre_interaction.hpp 5 | ) 6 | -------------------------------------------------------------------------------- /include/disph/d_fluid_force.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fluid_force.hpp" 4 | 5 | namespace sph 6 | { 7 | namespace disph 8 | { 9 | 10 | class FluidForce : public sph::FluidForce { 11 | real m_gamma; 12 | public: 13 | void initialize(std::shared_ptr param) override; 14 | void calculation(std::shared_ptr sim) override; 15 | }; 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /include/disph/d_pre_interaction.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pre_interaction.hpp" 4 | 5 | namespace sph 6 | { 7 | namespace disph 8 | { 9 | 10 | class PreInteraction : public sph::PreInteraction { 11 | real newton_raphson ( 12 | const SPHParticle & p_i, 13 | const std::vector & particles, 14 | const std::vector & neighbor_list, 15 | const int n_neighbor, 16 | const Periodic * periodic, 17 | const KernelFunction * kernel 18 | ) override; 19 | 20 | public: 21 | void calculation(std::shared_ptr sim) override; 22 | }; 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /include/exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "logger.hpp" 8 | 9 | #define THROW_ERROR(...)\ 10 | do {\ 11 | std::ostringstream os;\ 12 | sph::make_error_message(os, __VA_ARGS__);\ 13 | throw sph::SPHException(os.str(), __FILE__, __func__, __LINE__);\ 14 | } while(0) 15 | 16 | namespace sph 17 | { 18 | 19 | inline void make_error_message(std::ostringstream & os) {} 20 | 21 | template 22 | inline void make_error_message(std::ostringstream & os, Head && head, Tail &&... tail) 23 | { 24 | os << head; 25 | make_error_message(os, std::move(tail)...); 26 | } 27 | 28 | class SPHException : public std::exception { 29 | std::string m_message; 30 | const char * m_file; 31 | const char * m_func; 32 | int m_line; 33 | public: 34 | SPHException(const std::string & message) : m_message(message) {}; 35 | SPHException(const std::string & message, const char * file, const char * func, const int line) : 36 | m_message(message), m_file(file), m_func(func), m_line(line) {}; 37 | 38 | const char * get_file_name() const 39 | { 40 | return m_file; 41 | } 42 | 43 | const char * get_func_name() const 44 | { 45 | return m_func; 46 | } 47 | 48 | int get_line_number() const 49 | { 50 | return m_line; 51 | } 52 | const char * what() const throw() 53 | { 54 | return m_message.c_str(); 55 | } 56 | }; 57 | 58 | template 59 | inline void exception_handler(F && func) 60 | { 61 | try { 62 | func(); 63 | } 64 | catch(sph::SPHException & e) { 65 | if(sph::Logger::is_open()) { 66 | WRITE_LOG << "catch exception."; 67 | WRITE_LOG << e.what(); 68 | WRITE_LOG << "file: " << e.get_file_name(); 69 | WRITE_LOG << "func: " << e.get_func_name(); 70 | WRITE_LOG << "line: " << e.get_line_number(); 71 | } else { 72 | std::cerr << "catch exception." << std::endl; 73 | std::cerr << e.what() << std::endl; 74 | std::cerr << "file: " << e.get_file_name() << std::endl; 75 | std::cerr << "func: " << e.get_func_name() << std::endl; 76 | std::cerr << "line: " << e.get_line_number() << std::endl; 77 | } 78 | std::exit(EXIT_FAILURE); 79 | } 80 | catch(std::exception & e) { 81 | if(sph::Logger::is_open()) { 82 | WRITE_LOG << e.what(); 83 | } else { 84 | std::cerr << e.what() << std::endl; 85 | } 86 | std::exit(EXIT_FAILURE); 87 | } 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /include/exhaustive_search.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "defines.hpp" 6 | 7 | namespace sph 8 | { 9 | 10 | struct SPHParticle; 11 | class Periodic; 12 | 13 | int exhaustive_search( 14 | SPHParticle & p_i, 15 | const real kernel_size, 16 | const std::vector & particles, 17 | const int num, 18 | std::vector & neighbor_list, 19 | const int list_size, 20 | Periodic const * periodic, 21 | const bool is_ij); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /include/fluid_force.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "module.hpp" 4 | #include "vector_type.hpp" 5 | 6 | namespace sph 7 | { 8 | class SPHParticle; 9 | class Periodic; 10 | 11 | class FluidForce : public Module { 12 | protected: 13 | int m_neighbor_number; 14 | bool m_use_ac; 15 | real m_alpha_ac; 16 | bool m_use_gravity; 17 | 18 | real artificial_viscosity(const SPHParticle & p_i, const SPHParticle & p_j, const vec_t & r_ij); 19 | real artificial_conductivity(const SPHParticle & p_i, const SPHParticle & p_j, const vec_t & r_ij, const vec_t & dw_ij); 20 | 21 | public: 22 | virtual void initialize(std::shared_ptr param) override; 23 | virtual void calculation(std::shared_ptr sim) override; 24 | }; 25 | } -------------------------------------------------------------------------------- /include/gravity_force.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "module.hpp" 4 | #include "vector_type.hpp" 5 | 6 | namespace sph 7 | { 8 | class SPHParticle; 9 | 10 | class GravityForce : public Module { 11 | bool m_is_valid; 12 | real m_constant; 13 | 14 | public: 15 | void initialize(std::shared_ptr param) override; 16 | void calculation(std::shared_ptr sim) override; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /include/gsph/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(sph 2 | PUBLIC 3 | g_fluid_force.hpp 4 | g_pre_interaction.hpp 5 | ) 6 | -------------------------------------------------------------------------------- /include/gsph/g_fluid_force.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "fluid_force.hpp" 5 | 6 | namespace sph 7 | { 8 | namespace gsph 9 | { 10 | 11 | class FluidForce : public sph::FluidForce { 12 | bool m_is_2nd_order; 13 | real m_gamma; 14 | 15 | // (velocity, density, pressure, sound speed) 16 | std::function m_solver; 17 | 18 | void hll_solver(); 19 | public: 20 | void initialize(std::shared_ptr param) override; 21 | void calculation(std::shared_ptr sim) override; 22 | }; 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /include/gsph/g_pre_interaction.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pre_interaction.hpp" 4 | 5 | namespace sph 6 | { 7 | namespace gsph 8 | { 9 | 10 | class PreInteraction : public sph::PreInteraction { 11 | bool m_is_2nd_order; 12 | public: 13 | void initialize(std::shared_ptr param) override; 14 | void calculation(std::shared_ptr sim) override; 15 | }; 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /include/kernel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(sph 2 | PUBLIC 3 | cubic_spline.hpp 4 | kernel_function.hpp 5 | wendland_kernel.hpp 6 | ) 7 | -------------------------------------------------------------------------------- /include/kernel/cubic_spline.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "defines.hpp" 6 | #include "kernel_function.hpp" 7 | 8 | // cubic spline kernel 9 | namespace sph 10 | { 11 | namespace Spline 12 | { 13 | #if DIM == 1 14 | constexpr real sigma_cubic = 2.0 / 3.0; 15 | #elif DIM == 2 16 | constexpr real sigma_cubic = 10.0 / (7.0 * M_PI); 17 | #else 18 | constexpr real sigma_cubic = 1.0 / M_PI; 19 | #endif 20 | 21 | class Cubic : public KernelFunction { 22 | public: 23 | Cubic() 24 | { 25 | } 26 | 27 | real w(const real r, const real h) const 28 | { 29 | const real h_ = h * 0.5; 30 | const real q = r / h_; 31 | return sigma_cubic / powh(h_) * (0.25 * pow3(0.5 * (2.0 - q + std::abs(2.0 - q))) - pow3(0.5 * (1.0 - q + std::abs(1.0 - q)))); 32 | } 33 | 34 | vec_t dw(const vec_t &rij, const real r, const real h) const 35 | { 36 | if(r == 0.0) { 37 | return vec_t(0); 38 | } 39 | const real h_ = h * 0.5; 40 | const real q = r / h_; 41 | const real c = -sigma_cubic / (powh(h_) * h_ * r) * (0.75 * sqr(0.5 * (2.0 - q + std::abs(2.0 - q))) - 3.0 * sqr(0.5 * (1.0 - q + std::abs(1.0 - q)))); 42 | return rij * c; 43 | } 44 | 45 | real dhw(const real r, const real h) const 46 | { 47 | const real h_ = h * 0.5; 48 | const real q = r / h_; 49 | return 0.5 * sigma_cubic / (powh(h_) * h_) * (sqr((std::abs(2.0 - q) + 2.0 - q) * 0.5) * ((3. + DIM) * 0.25 * q - 0.5 * DIM) 50 | + sqr((std::abs(1.0 - q) + 1.0 - q) * 0.5) * ((-3.0 - DIM) * q + DIM)); 51 | } 52 | }; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /include/kernel/kernel_function.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vector_type.hpp" 4 | 5 | namespace sph 6 | { 7 | 8 | inline real powh(const real h) { 9 | #if DIM == 1 10 | return h; 11 | #elif DIM == 2 12 | return h * h; 13 | #elif DIM == 3 14 | return h * h * h; 15 | #endif 16 | } 17 | 18 | class KernelFunction { 19 | public: 20 | virtual real w(const real r, const real h) const = 0; // W(r,h) 21 | virtual vec_t dw(const vec_t &rij, const real r, const real h) const = 0; // grad W(r,h) 22 | virtual real dhw(const real r, const real h) const = 0; // dW(r,h)/dh 23 | }; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /include/kernel/wendland_kernel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "defines.hpp" 7 | #include "kernel_function.hpp" 8 | 9 | // Wendland (1995), Dehnen & Aly (2012) 10 | namespace sph 11 | { 12 | namespace Wendland { 13 | 14 | // Wendland C4 Kernel 15 | #if DIM == 1 16 | constexpr real sigma_c4 = 0.0; 17 | #elif DIM == 2 18 | constexpr real sigma_c4 = 9.0 / M_PI; 19 | #else 20 | constexpr real sigma_c4 = 495. / (32 * M_PI); 21 | #endif 22 | 23 | class C4Kernel : public KernelFunction { 24 | public: 25 | C4Kernel() 26 | { 27 | assert(DIM != 1); 28 | } 29 | 30 | real w(const real r, const real h) const 31 | { 32 | const real q = r / h; 33 | return sigma_c4 / powh(h) * pow6(0.5 * (1.0 - q + std::abs(1.0 - q))) * (1.0 + 6.0 * q + 35.0 / 3.0 * q * q); 34 | } 35 | 36 | vec_t dw(const vec_t &rij, const real r, const real h) const 37 | { 38 | const real q = r / h; 39 | const real c = -56.0 / 3.0 * sigma_c4 / (powh(h) * sqr(h)) * pow5(0.5 * (1.0 - q + std::abs(1.0 - q))) * (1.0 + 5.0 * q); 40 | return rij * c; 41 | } 42 | 43 | real dhw(const real r, const real h) const 44 | { 45 | const double q = r / h; 46 | return -sigma_c4 / (powh(h) * h * 3.0) * pow5(0.5 * (1.0 - q + std::abs(1.0 - q))) 47 | * (3.0 * DIM + 15.0 * DIM * q + (-56.0 + 17.0 * DIM) * q * q - 35.0 * (8.0 + DIM) * pow3(q)); 48 | } 49 | }; 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /include/logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace sph 9 | { 10 | 11 | class Logger { 12 | static std::string dir_name; 13 | static std::ofstream log_io; 14 | static bool open_flag; 15 | 16 | std::ostringstream m_msg; 17 | bool m_log_only; 18 | public: 19 | Logger(bool log_only = false) : m_log_only(log_only) {} 20 | ~Logger() { 21 | log_io << m_msg.str() << std::endl; 22 | if(!m_log_only) { 23 | std::cout << m_msg.str() << std::endl; 24 | } 25 | } 26 | 27 | static void open(const std::string & output_dir); 28 | static void open(const char * output_dir); 29 | static std::string get_dir_name() { return dir_name; } 30 | static bool is_open() { return open_flag; } 31 | 32 | template 33 | Logger & operator<<(const T & msg) { 34 | m_msg << msg; 35 | return *this; 36 | } 37 | }; 38 | 39 | #define WRITE_LOG sph::Logger() 40 | #define WRITE_LOG_ONLY sph::Logger(true) 41 | 42 | } -------------------------------------------------------------------------------- /include/module.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace sph 6 | { 7 | struct SPHParameters; 8 | class Simulation; 9 | 10 | class Module { 11 | public: 12 | virtual void initialize(std::shared_ptr param) = 0; 13 | virtual void calculation(std::shared_ptr sim) = 0; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /include/openmp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "defines.hpp" 6 | 7 | #ifdef _OPENMP 8 | #include 9 | #endif 10 | 11 | class omp_real { 12 | int m_threads; 13 | std::unique_ptr m_values; 14 | 15 | public: 16 | omp_real(real const v = 0.0) 17 | { 18 | #ifdef _OPENMP 19 | m_threads = omp_get_max_threads(); 20 | #else 21 | m_threads = 1; 22 | #endif 23 | m_values = std::make_unique(m_threads); 24 | 25 | for(int i = 0; i < m_threads; ++i) { 26 | m_values[i] = v; 27 | } 28 | } 29 | 30 | real & get() 31 | { 32 | #ifdef _OPENMP 33 | return m_values[omp_get_thread_num()]; 34 | #else 35 | return m_values[0]; 36 | #endif 37 | } 38 | 39 | real min() 40 | { 41 | real v = m_values[0]; 42 | for(int i = 1; i < m_threads; ++i) { 43 | if(v > m_values[i]) { 44 | v = m_values[i]; 45 | } 46 | } 47 | return v; 48 | } 49 | 50 | real max() 51 | { 52 | real v = m_values[0]; 53 | for(int i = 1; i < m_threads; ++i) { 54 | if(v < m_values[i]) { 55 | v = m_values[i]; 56 | } 57 | } 58 | return v; 59 | } 60 | 61 | real sum() 62 | { 63 | real v = 0; 64 | for(int i = 0; i < m_threads; ++i) { 65 | v += m_values[i]; 66 | } 67 | return v; 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /include/output.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "defines.hpp" 7 | 8 | namespace sph 9 | { 10 | class SPHParticle; 11 | class Simulation; 12 | 13 | class Output { 14 | int m_count; 15 | std::ofstream m_out_energy; 16 | public: 17 | Output(int count = 0); 18 | ~Output(); 19 | void output_particle(std::shared_ptr sim); 20 | void output_energy(std::shared_ptr sim); 21 | }; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /include/parameters.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "defines.hpp" 4 | 5 | namespace sph 6 | { 7 | 8 | enum struct SPHType { 9 | SSPH, 10 | DISPH, 11 | GSPH, 12 | }; 13 | 14 | enum struct KernelType { 15 | CUBIC_SPLINE, 16 | WENDLAND, 17 | UNKNOWN, 18 | }; 19 | 20 | struct SPHParameters { 21 | 22 | struct Time { 23 | real start; 24 | real end; 25 | real output; 26 | real energy; 27 | } time; 28 | 29 | SPHType type; 30 | 31 | struct CFL { 32 | real sound; 33 | real force; 34 | } cfl; 35 | 36 | struct ArtificialViscosity { 37 | real alpha; 38 | bool use_balsara_switch; 39 | bool use_time_dependent_av; 40 | real alpha_max; 41 | real alpha_min; 42 | real epsilon; // tau = h / (epsilon * c) 43 | } av; 44 | 45 | struct ArtificialConductivity { 46 | real alpha; 47 | bool is_valid; 48 | } ac; 49 | 50 | struct Tree { 51 | int max_level; 52 | int leaf_particle_num; 53 | } tree; 54 | 55 | struct Physics { 56 | int neighbor_number; 57 | real gamma; 58 | } physics; 59 | 60 | KernelType kernel; 61 | 62 | bool iterative_sml; 63 | 64 | struct Periodic { 65 | bool is_valid; 66 | real range_max[DIM]; 67 | real range_min[DIM]; 68 | } periodic; 69 | 70 | struct Gravity { 71 | bool is_valid; 72 | real constant; 73 | real theta; 74 | } gravity; 75 | 76 | struct GSPH { 77 | bool is_2nd_order; 78 | } gsph; 79 | }; 80 | 81 | } -------------------------------------------------------------------------------- /include/particle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vector_type.hpp" 4 | 5 | namespace sph 6 | { 7 | 8 | class SPHParticle { 9 | public: 10 | vec_t pos; // position 11 | vec_t vel; // velocity 12 | vec_t vel_p; // velocity at t + dt/2 13 | vec_t acc; // acceleration 14 | real mass; // mass 15 | real dens; // mass density 16 | real pres; // pressure 17 | real ene; // internal energy 18 | real ene_p; // internal energy at t + dt/2 19 | real dene; // du/dt 20 | real sml; // smoothing length 21 | real sound; // sound speed 22 | 23 | real balsara; // balsara switch 24 | real alpha; // AV coefficient 25 | 26 | real gradh; // grad-h term 27 | 28 | real phi = 0.0; // potential 29 | 30 | int id; 31 | int neighbor; 32 | SPHParticle *next = nullptr; 33 | }; 34 | 35 | } -------------------------------------------------------------------------------- /include/periodic.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "vector_type.hpp" 7 | #include "parameters.hpp" 8 | 9 | namespace sph 10 | { 11 | 12 | class Periodic { 13 | bool m_is_valid; 14 | vec_t m_max; 15 | vec_t m_min; 16 | vec_t m_range; 17 | 18 | public: 19 | Periodic() : m_is_valid(false) {} 20 | void initialize(std::shared_ptr param) 21 | { 22 | if(param->periodic.is_valid) { 23 | m_is_valid = true; 24 | for(int i = 0; i < DIM; ++i) { 25 | m_max[i] = param->periodic.range_max[i]; 26 | m_min[i] = param->periodic.range_min[i]; 27 | } 28 | m_range = m_max - m_min; 29 | } else { 30 | m_is_valid = false; 31 | } 32 | } 33 | 34 | vec_t calc_r_ij(const vec_t & r_i, const vec_t & r_j) const 35 | { 36 | if(m_is_valid) { 37 | const vec_t r_ij1 = r_i - r_j; 38 | const vec_t r_ij2 = r_ij1 + m_range; 39 | const vec_t r_ij3 = r_ij1 - m_range; 40 | vec_t r_ij; 41 | 42 | #define R_IJ(a, b, c)\ 43 | if(std::abs(r_ij##a[i]) <= std::abs(r_ij##b[i]) && std::abs(r_ij##a[i]) <= std::abs(r_ij##c[i])) {\ 44 | r_ij[i] = r_ij##a[i];\ 45 | continue;\ 46 | } 47 | 48 | for(int i = 0; i < DIM; ++i) { 49 | R_IJ(1, 2, 3); 50 | R_IJ(2, 3, 1); 51 | R_IJ(3, 1, 2); 52 | assert(false); 53 | } 54 | 55 | return r_ij; 56 | } else { 57 | return r_i - r_j; 58 | } 59 | } 60 | 61 | void apply(vec_t & r) const 62 | { 63 | if(m_is_valid) { 64 | for(int i = 0; i < DIM; ++i) { 65 | if(r[i] < m_min[i]) { 66 | r[i] += m_range[i]; 67 | } else if(r[i] > m_max[i]) { 68 | r[i] -= m_range[i]; 69 | } 70 | } 71 | } 72 | } 73 | }; 74 | 75 | } -------------------------------------------------------------------------------- /include/pre_interaction.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "module.hpp" 6 | #include "particle.hpp" 7 | 8 | namespace sph 9 | { 10 | class Periodic; 11 | class KernelFunction; 12 | 13 | class PreInteraction : public Module { 14 | protected: 15 | bool m_use_balsara_switch; 16 | bool m_use_time_dependent_av; 17 | real m_alpha_max; 18 | real m_alpha_min; 19 | real m_epsilon; // tau = h / (epsilon * c) 20 | real m_gamma; 21 | int m_neighbor_number; 22 | real m_kernel_ratio; 23 | bool m_iteration; 24 | bool m_first; 25 | 26 | virtual real newton_raphson( 27 | const SPHParticle & p_i, 28 | const std::vector & particles, 29 | const std::vector & neighbor_list, 30 | const int n_neighbor, 31 | const Periodic * periodic, 32 | const KernelFunction * kernel 33 | ); 34 | void initial_smoothing(std::shared_ptr sim); 35 | 36 | public: 37 | virtual void initialize(std::shared_ptr param) override; 38 | virtual void calculation(std::shared_ptr sim) override; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /include/simulation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "particle.hpp" 8 | 9 | namespace sph 10 | { 11 | 12 | struct SPHParameters; 13 | class KernelFunction; 14 | class Periodic; 15 | class BHTree; 16 | 17 | #define ADD_MEMBER(type, name)\ 18 | public:\ 19 | void set_##name(type v) { m_##name = v; }\ 20 | type & get_##name() { return m_##name; }\ 21 | private:\ 22 | type m_##name 23 | 24 | class Simulation { 25 | ADD_MEMBER(std::vector, particles); 26 | ADD_MEMBER(int, particle_num); 27 | ADD_MEMBER(real, time); 28 | ADD_MEMBER(real, dt); 29 | ADD_MEMBER(real, h_per_v_sig); 30 | ADD_MEMBER(std::shared_ptr, kernel); 31 | ADD_MEMBER(std::shared_ptr, periodic); 32 | ADD_MEMBER(std::shared_ptr, tree); 33 | 34 | std::unordered_map> additional_scalar_array; 35 | std::unordered_map> additional_vector_array; 36 | 37 | public: 38 | Simulation(std::shared_ptr param); 39 | void update_time(); 40 | void make_tree(); 41 | void add_scalar_array(const std::vector & names); 42 | void add_vector_array(const std::vector & names); 43 | std::vector & get_scalar_array(const std::string & name); 44 | std::vector & get_vector_array(const std::string & name); 45 | }; 46 | 47 | #undef ADD_MEMBER 48 | 49 | } -------------------------------------------------------------------------------- /include/solver.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "defines.hpp" 11 | 12 | namespace sph 13 | { 14 | 15 | struct SPHParameters; 16 | class Simulation; 17 | class Output; 18 | 19 | class Module; 20 | 21 | enum struct Sample { 22 | ShockTube, 23 | GreshoChanVortex, 24 | PairingInstability, 25 | HydroStatic, 26 | KHI, 27 | Evrard, 28 | DoNotUse, 29 | }; 30 | 31 | class Solver { 32 | std::shared_ptr m_param; 33 | std::shared_ptr m_output; 34 | std::string m_output_dir; 35 | std::shared_ptr m_sim; 36 | 37 | // modules 38 | std::shared_ptr m_timestep; 39 | std::shared_ptr m_pre; 40 | std::shared_ptr m_fforce; 41 | std::shared_ptr m_gforce; 42 | 43 | void read_parameterfile(const char * filename); 44 | void make_initial_condition(); 45 | void initialize(); 46 | void predict(); 47 | void correct(); 48 | void integrate(); 49 | 50 | // for sample 51 | Sample m_sample; 52 | std::unordered_map m_sample_parameters; 53 | 54 | void make_shock_tube(); 55 | void make_gresho_chan_vortex(); 56 | void make_pairing_instability(); 57 | void make_hydrostatic(); 58 | void make_khi(); 59 | void make_evrard(); 60 | 61 | public: 62 | Solver(int argc, char * argv[]); 63 | void run(); 64 | }; 65 | 66 | } -------------------------------------------------------------------------------- /include/timestep.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "module.hpp" 4 | 5 | namespace sph 6 | { 7 | 8 | class TimeStep : public Module { 9 | real c_sound; // dt_s = c_sound * h / c 10 | real c_force; // dt_f = c_force * sqrt(h / a) 11 | public: 12 | void initialize(std::shared_ptr param) override; 13 | void calculation(std::shared_ptr sim) override; 14 | }; 15 | } -------------------------------------------------------------------------------- /include/vector_type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "defines.hpp" 6 | 7 | class vec_t { 8 | real vec[DIM]; 9 | public: 10 | // Constructor 11 | #if DIM == 1 12 | vec_t(const real x = 0) { 13 | vec[0] = x; 14 | } 15 | #elif DIM == 2 16 | vec_t(const real x = 0, const real y = 0) { 17 | vec[0] = x; 18 | vec[1] = y; 19 | } 20 | #elif DIM == 3 21 | vec_t(const real x = 0, const real y = 0, const real z = 0) { 22 | vec[0] = x; 23 | vec[1] = y; 24 | vec[2] = z; 25 | } 26 | #endif 27 | 28 | vec_t(const vec_t & a) { 29 | for(int i = 0; i < DIM; ++i) vec[i] = a[i]; 30 | } 31 | 32 | vec_t(const real (&a)[DIM]) { 33 | for(int i = 0; i < DIM; ++i) vec[i] = a[i]; 34 | } 35 | 36 | // Operator 37 | real & operator[](const int i) { return vec[i]; } 38 | const real & operator[](const int i) const { return vec[i]; } 39 | 40 | vec_t & operator=(const vec_t &a) { 41 | for(int i = 0; i < DIM; ++i) vec[i] = a[i]; 42 | return *this; 43 | } 44 | 45 | vec_t & operator=(const real (&a)[DIM]) { 46 | for(int i = 0; i < DIM; ++i) vec[i] = a[i]; 47 | return *this; 48 | } 49 | 50 | vec_t & operator=(const real a) { 51 | for(int i = 0; i < DIM; ++i) vec[i] = a; 52 | return *this; 53 | } 54 | 55 | const vec_t & operator+() const { return *this; } 56 | 57 | const vec_t operator-() const { 58 | #if DIM == 1 59 | return vec_t(-vec[0]); 60 | #elif DIM == 2 61 | return vec_t(-vec[0], -vec[1]); 62 | #elif DIM == 3 63 | return vec_t(-vec[0], -vec[1], -vec[2]); 64 | #endif 65 | } 66 | 67 | // += 68 | vec_t & operator+=(const vec_t &a) { 69 | for(int i = 0; i < DIM; ++i) vec[i] += a[i]; 70 | return *this; 71 | } 72 | 73 | vec_t & operator+=(const real (&a)[DIM]) { 74 | for(int i = 0; i < DIM; ++i) vec[i] += a[i]; 75 | return *this; 76 | } 77 | 78 | vec_t & operator+=(const real a) { 79 | for(int i = 0; i < DIM; ++i) vec[i] += a; 80 | return *this; 81 | } 82 | 83 | // -= 84 | vec_t & operator-=(const vec_t &a) { 85 | for(int i = 0; i < DIM; ++i) vec[i] -= a[i]; 86 | return *this; 87 | } 88 | 89 | vec_t & operator-=(const real (&a)[DIM]) { 90 | for(int i = 0; i < DIM; ++i) vec[i] -= a[i]; 91 | return *this; 92 | } 93 | 94 | vec_t & operator-=(const real a) { 95 | for(int i = 0; i < DIM; ++i) vec[i] -= a; 96 | return *this; 97 | } 98 | 99 | vec_t & operator*=(const real a) { 100 | for(int i = 0; i < DIM; ++i) vec[i] *= a; 101 | return *this; 102 | } 103 | 104 | vec_t & operator/=(const real a) { 105 | for(int i = 0; i < DIM; ++i) vec[i] /= a; 106 | return *this; 107 | } 108 | 109 | // + 110 | vec_t operator+(const vec_t &a) const { 111 | #if DIM == 1 112 | return vec_t(vec[0] + a[0]); 113 | #elif DIM == 2 114 | return vec_t(vec[0] + a[0], vec[1] + a[1]); 115 | #elif DIM == 3 116 | return vec_t(vec[0] + a[0], vec[1] + a[1], vec[2] + a[2]); 117 | #endif 118 | } 119 | 120 | vec_t operator+(const real (&a)[DIM]) const { 121 | #if DIM == 1 122 | return vec_t(vec[0] + a[0]); 123 | #elif DIM == 2 124 | return vec_t(vec[0] + a[0], vec[1] + a[1]); 125 | #elif DIM == 3 126 | return vec_t(vec[0] + a[0], vec[1] + a[1], vec[2] + a[2]); 127 | #endif 128 | } 129 | 130 | vec_t operator+(const real a) const { 131 | #if DIM == 1 132 | return vec_t(vec[0] + a); 133 | #elif DIM == 2 134 | return vec_t(vec[0] + a, vec[1] + a); 135 | #elif DIM == 3 136 | return vec_t(vec[0] + a, vec[1] + a, vec[2] + a); 137 | #endif 138 | } 139 | 140 | // - 141 | vec_t operator-(const vec_t &a) const { 142 | #if DIM == 1 143 | return vec_t(vec[0] - a[0]); 144 | #elif DIM == 2 145 | return vec_t(vec[0] - a[0], vec[1] - a[1]); 146 | #elif DIM == 3 147 | return vec_t(vec[0] - a[0], vec[1] - a[1], vec[2] - a[2]); 148 | #endif 149 | } 150 | 151 | vec_t operator-(const real (&a)[DIM]) const { 152 | #if DIM == 1 153 | return vec_t(vec[0] - a[0]); 154 | #elif DIM == 2 155 | return vec_t(vec[0] - a[0], vec[1] - a[1]); 156 | #elif DIM == 3 157 | return vec_t(vec[0] - a[0], vec[1] - a[1], vec[2] - a[2]); 158 | #endif 159 | } 160 | 161 | vec_t operator-(const real a) const { 162 | #if DIM == 1 163 | return vec_t(vec[0] - a); 164 | #elif DIM == 2 165 | return vec_t(vec[0] - a, vec[1] - a); 166 | #elif DIM == 3 167 | return vec_t(vec[0] - a, vec[1] - a, vec[2] - a); 168 | #endif 169 | } 170 | 171 | vec_t operator*(const real a) const { 172 | #if DIM == 1 173 | return vec_t(vec[0] * a); 174 | #elif DIM == 2 175 | return vec_t(vec[0] * a, vec[1] * a); 176 | #elif DIM == 3 177 | return vec_t(vec[0] * a, vec[1] * a, vec[2] * a); 178 | #endif 179 | } 180 | 181 | vec_t operator/(const real a) const { 182 | #if DIM == 1 183 | return vec_t(vec[0] / a); 184 | #elif DIM == 2 185 | return vec_t(vec[0] / a, vec[1] / a); 186 | #elif DIM == 3 187 | return vec_t(vec[0] / a, vec[1] / a, vec[2] / a); 188 | #endif 189 | } 190 | 191 | const real *get_array() const { return vec; } 192 | }; 193 | 194 | inline real inner_product(const vec_t &a, const vec_t &b) 195 | { 196 | #if DIM == 1 197 | return a[0] * b[0]; 198 | #elif DIM == 2 199 | return a[0] * b[0] + a[1] * b[1]; 200 | #elif DIM == 3 201 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; 202 | #endif 203 | } 204 | 205 | inline real inner_product(const vec_t &a, const real (&b)[DIM]) 206 | { 207 | #if DIM == 1 208 | return a[0] * b[0]; 209 | #elif DIM == 2 210 | return a[0] * b[0] + a[1] * b[1]; 211 | #elif DIM == 3 212 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; 213 | #endif 214 | } 215 | 216 | inline real inner_product(const real (&a)[DIM], const vec_t &b) 217 | { 218 | #if DIM == 1 219 | return a[0] * b[0]; 220 | #elif DIM == 2 221 | return a[0] * b[0] + a[1] * b[1]; 222 | #elif DIM == 3 223 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; 224 | #endif 225 | } 226 | 227 | inline real abs2(const vec_t &a) 228 | { 229 | return inner_product(a, a); 230 | } 231 | 232 | namespace std { 233 | inline real abs(const vec_t &a) 234 | { 235 | return std::sqrt(inner_product(a, a)); 236 | } 237 | } 238 | 239 | #if DIM == 2 240 | inline real vector_product(const vec_t &a, const vec_t &b) 241 | { 242 | return a[0] * b[1] - a[1] * b[0]; 243 | } 244 | 245 | inline real vector_product(const vec_t &a, const real (&b)[DIM]) 246 | { 247 | return a[0] * b[1] - a[1] * b[0]; 248 | } 249 | 250 | inline real vector_product(const real (&a)[DIM], const vec_t &b) 251 | { 252 | return a[0] * b[1] - a[1] * b[0]; 253 | } 254 | #elif DIM == 3 255 | inline vec_t vector_product(const vec_t &a, const vec_t &b) 256 | { 257 | return vec_t(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]); 258 | } 259 | 260 | inline vec_t vector_product(const vec_t &a, const real (&b)[DIM]) 261 | { 262 | return vec_t(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]); 263 | } 264 | 265 | inline vec_t vector_product(const real (&a)[DIM], const vec_t &b) 266 | { 267 | return vec_t(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]); 268 | } 269 | #endif 270 | 271 | inline real distance(const vec_t &a, const vec_t &b) 272 | { 273 | return std::abs(a - b); 274 | } 275 | -------------------------------------------------------------------------------- /sample/evrard/evrard.json: -------------------------------------------------------------------------------- 1 | { 2 | "outputDirectory" : "sample/evrard/results", 3 | "endTime" : 3.0, 4 | "avAlpha" : 1.0, 5 | "neighborNumber" : 32, 6 | "useBalsaraSwitch" : true, 7 | "useTimeDependentAV" : true, 8 | "useArtificialConductivity" : false, 9 | "leafParticleNumber" : 32, 10 | "gamma" : 1.66666666666666666666666666666666667, 11 | "kernel" : "wendland", 12 | "N" : 30, 13 | "periodic" : false, 14 | "useGravity" : true, 15 | "SPHType" : "disph" 16 | } 17 | -------------------------------------------------------------------------------- /sample/gresho_chan_vortex/gresho_chan_vortex.json: -------------------------------------------------------------------------------- 1 | { 2 | "outputDirectory" : "sample/gresho_chan_vortex/results", 3 | "endTime" : 1.0, 4 | "avAlpha" : 1.0, 5 | "neighborNumber" : 32, 6 | "useBalsaraSwitch" : true, 7 | "leafParticleNumber" : 32, 8 | "gamma" : 1.66666666666666666666666666666666667, 9 | "kernel" : "wendland", 10 | "N" : 64, 11 | "periodic" : true, 12 | "rangeMax" : [0.5, 0.5], 13 | "rangeMin" : [-0.5,-0.5], 14 | "SPHType" : "ssph" 15 | } 16 | -------------------------------------------------------------------------------- /sample/hydrostatic/hydrostatic.json: -------------------------------------------------------------------------------- 1 | { 2 | "outputDirectory" : "sample/hydrostatic/results", 3 | "endTime" : 8.0, 4 | "avAlpha" : 1.0, 5 | "neighborNumber" : 32, 6 | "useBalsaraSwitch" : true, 7 | "leafParticleNumber" : 16, 8 | "gamma" : 1.66666666666666666666666666666666667, 9 | "kernel" : "wendland", 10 | "N" : 32, 11 | "periodic" : true, 12 | "rangeMax" : [0.5, 0.5], 13 | "rangeMin" : [-0.5,-0.5], 14 | "SPHType" : "disph" 15 | } 16 | -------------------------------------------------------------------------------- /sample/khi/khi.json: -------------------------------------------------------------------------------- 1 | { 2 | "outputDirectory" : "sample/khi/results", 3 | "endTime" : 3.0, 4 | "outputTime" : 0.1, 5 | "avAlpha" : 1.0, 6 | "neighborNumber" : 32, 7 | "useBalsaraSwitch" : true, 8 | "useTimeDependentAV" : true, 9 | "useArtificialConductivity" : false, 10 | "leafParticleNumber" : 16, 11 | "gamma" : 1.66666666666666666666666666666666667, 12 | "kernel" : "wendland", 13 | "N" : 256, 14 | "periodic" : true, 15 | "rangeMax" : [1.0, 1.0], 16 | "rangeMin" : [0.0, 0.0], 17 | "SPHType" : "ssph" 18 | } 19 | -------------------------------------------------------------------------------- /sample/pairing_instability/pairing_instability.json: -------------------------------------------------------------------------------- 1 | { 2 | "outputDirectory" : "sample/pairing_instability/results", 3 | "endTime" : 1.0, 4 | "avAlpha" : 1.0, 5 | "neighborNumber" : 32, 6 | "leafParticleNumber" : 32, 7 | "gamma" : 1.66666666666666666666666666666666667, 8 | "kernel" : "cubic_spline", 9 | "N" : 64, 10 | "periodic" : true, 11 | "rangeMax" : [0.5, 0.5], 12 | "rangeMin" : [-0.5,-0.5] 13 | } 14 | -------------------------------------------------------------------------------- /sample/shock_tube/shock_tube.json: -------------------------------------------------------------------------------- 1 | { 2 | "outputDirectory" : "sample/shock_tube/results", 3 | "endTime" : 0.2, 4 | "avAlpha" : 1.0, 5 | "neighborNumber" : 4, 6 | "gamma" : 1.4, 7 | "kernel" : "cubic_spline", 8 | "N" : 50, 9 | "periodic" : true, 10 | "iterativeSmoothingLength" : true, 11 | "rangeMax" : [1.5], 12 | "rangeMin" : [-0.5], 13 | "useTimeDependentAV" : false, 14 | "SPHType" : "ssph" 15 | } 16 | -------------------------------------------------------------------------------- /sph.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2043 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sph", "sph.vcxproj", "{D8A4361E-1E86-4086-A131-57C6A441AD9B}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kernel_test", "test\kernel_test\kernel_test.vcxproj", "{2E88A7F5-A4A3-42EC-AB6A-4FABAE06FD25}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {D8A4361E-1E86-4086-A131-57C6A441AD9B}.Debug|x64.ActiveCfg = Debug|x64 19 | {D8A4361E-1E86-4086-A131-57C6A441AD9B}.Debug|x64.Build.0 = Debug|x64 20 | {D8A4361E-1E86-4086-A131-57C6A441AD9B}.Debug|x86.ActiveCfg = Debug|x64 21 | {D8A4361E-1E86-4086-A131-57C6A441AD9B}.Debug|x86.Build.0 = Debug|x64 22 | {D8A4361E-1E86-4086-A131-57C6A441AD9B}.Release|x64.ActiveCfg = Release|x64 23 | {D8A4361E-1E86-4086-A131-57C6A441AD9B}.Release|x64.Build.0 = Release|x64 24 | {D8A4361E-1E86-4086-A131-57C6A441AD9B}.Release|x86.ActiveCfg = Release|Win32 25 | {D8A4361E-1E86-4086-A131-57C6A441AD9B}.Release|x86.Build.0 = Release|Win32 26 | {2E88A7F5-A4A3-42EC-AB6A-4FABAE06FD25}.Debug|x64.ActiveCfg = Debug|x64 27 | {2E88A7F5-A4A3-42EC-AB6A-4FABAE06FD25}.Debug|x64.Build.0 = Debug|x64 28 | {2E88A7F5-A4A3-42EC-AB6A-4FABAE06FD25}.Debug|x86.ActiveCfg = Debug|Win32 29 | {2E88A7F5-A4A3-42EC-AB6A-4FABAE06FD25}.Debug|x86.Build.0 = Debug|Win32 30 | {2E88A7F5-A4A3-42EC-AB6A-4FABAE06FD25}.Release|x64.ActiveCfg = Release|x64 31 | {2E88A7F5-A4A3-42EC-AB6A-4FABAE06FD25}.Release|x64.Build.0 = Release|x64 32 | {2E88A7F5-A4A3-42EC-AB6A-4FABAE06FD25}.Release|x86.ActiveCfg = Release|Win32 33 | {2E88A7F5-A4A3-42EC-AB6A-4FABAE06FD25}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {68235FA8-2215-4616-94A0-BE2688F53446} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /sph.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {D8A4361E-1E86-4086-A131-57C6A441AD9B} 24 | Win32Proj 25 | 10.0.16299.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | 33 | 34 | Application 35 | false 36 | v141 37 | 38 | 39 | Application 40 | true 41 | v141 42 | 43 | 44 | Application 45 | false 46 | v141 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | true 68 | 69 | 70 | true 71 | 72 | 73 | 74 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 75 | MultiThreadedDebugDLL 76 | Level3 77 | ProgramDatabase 78 | Disabled 79 | 80 | 81 | MachineX86 82 | true 83 | Console 84 | 85 | 86 | 87 | 88 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 89 | MultiThreadedDLL 90 | Level3 91 | ProgramDatabase 92 | 93 | 94 | MachineX86 95 | true 96 | Console 97 | true 98 | true 99 | 100 | 101 | 102 | 103 | %(AdditionalIncludeDirectories);include;$(BOOST_INC_PATH) 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | 107 | 108 | Console 109 | 110 | 111 | 112 | 113 | %(AdditionalIncludeDirectories);include;$(BOOST_INC_PATH) 114 | true 115 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 116 | 117 | 118 | Console 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /sph.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav 15 | 16 | 17 | {889bba9e-e8ba-48ca-a4ce-fd77da855a6f} 18 | 19 | 20 | {3f5c3c31-1757-4dce-8305-b86596934f62} 21 | 22 | 23 | {30858a98-da3e-4047-86ff-f5859ddce2c1} 24 | 25 | 26 | {fd625825-0f71-4891-a1d0-6cda4470a850} 27 | 28 | 29 | {747d701f-f9b3-45c7-bfe1-d8f8385217a7} 30 | 31 | 32 | {3bb55d42-fa86-425e-9417-b6d795e7178f} 33 | 34 | 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files 53 | 54 | 55 | Source Files 56 | 57 | 58 | Source Files 59 | 60 | 61 | Source Files 62 | 63 | 64 | Source Files 65 | 66 | 67 | Source Files 68 | 69 | 70 | Source Files\sample 71 | 72 | 73 | Source Files\sample 74 | 75 | 76 | Source Files\sample 77 | 78 | 79 | Source Files\sample 80 | 81 | 82 | Source Files\sample 83 | 84 | 85 | Source Files\sample 86 | 87 | 88 | Source Files\disph 89 | 90 | 91 | Source Files\disph 92 | 93 | 94 | Source Files\gsph 95 | 96 | 97 | Source Files\gsph 98 | 99 | 100 | 101 | 102 | Header Files 103 | 104 | 105 | Header Files 106 | 107 | 108 | Header Files 109 | 110 | 111 | Header Files 112 | 113 | 114 | Header Files 115 | 116 | 117 | Header Files 118 | 119 | 120 | Header Files 121 | 122 | 123 | Header Files 124 | 125 | 126 | Header Files 127 | 128 | 129 | Header Files 130 | 131 | 132 | Header Files 133 | 134 | 135 | Header Files 136 | 137 | 138 | Header Files 139 | 140 | 141 | Header Files\kernel 142 | 143 | 144 | Header Files\kernel 145 | 146 | 147 | Header Files\kernel 148 | 149 | 150 | Header Files 151 | 152 | 153 | Header Files 154 | 155 | 156 | Header Files 157 | 158 | 159 | Header Files 160 | 161 | 162 | Header Files 163 | 164 | 165 | Header Files\disph 166 | 167 | 168 | Header Files\disph 169 | 170 | 171 | Header Files\gsph 172 | 173 | 174 | Header Files\gsph 175 | 176 | 177 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(sph 2 | PRIVATE 3 | main.cpp 4 | bhtree.cpp 5 | exhaustive_search.cpp 6 | fluid_force.cpp 7 | gravity_force.cpp 8 | logger.cpp 9 | output.cpp 10 | pre_interaction.cpp 11 | simulation.cpp 12 | solver.cpp 13 | timestep.cpp 14 | ) 15 | 16 | add_subdirectory(disph) 17 | add_subdirectory(gsph) 18 | add_subdirectory(sample) -------------------------------------------------------------------------------- /src/bhtree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "parameters.hpp" 4 | #include "bhtree.hpp" 5 | #include "openmp.hpp" 6 | #include "exception.hpp" 7 | #include "periodic.hpp" 8 | 9 | namespace sph 10 | { 11 | 12 | void BHTree::initialize(std::shared_ptr param) 13 | { 14 | m_max_level = param->tree.max_level; 15 | m_leaf_particle_num = param->tree.leaf_particle_num; 16 | m_root.clear(); 17 | m_root.level = 1; 18 | m_is_periodic = param->periodic.is_valid; 19 | if(m_is_periodic) { 20 | m_range_max = param->periodic.range_max; 21 | m_range_min = param->periodic.range_min; 22 | m_root.center = (m_range_max + m_range_min) * 0.5; 23 | auto range = m_range_max - m_range_min; 24 | real l = 0.0; 25 | for(int i = 0; i < DIM; ++i) { 26 | if(l < range[i]) { 27 | l = range[i]; 28 | } 29 | } 30 | m_root.edge = l; 31 | } 32 | m_periodic = std::make_shared(); 33 | m_periodic->initialize(param); 34 | 35 | if(param->gravity.is_valid) { 36 | m_g_constant = param->gravity.constant; 37 | m_theta = param->gravity.theta; 38 | m_theta2 = m_theta * m_theta; 39 | } 40 | } 41 | 42 | void BHTree::resize(const int particle_num, const int tree_size) 43 | { 44 | assert(m_nodes.get() == nullptr); 45 | 46 | m_node_size = particle_num * tree_size; 47 | m_nodes = std::shared_ptr(new BHNode[m_node_size], std::default_delete()); 48 | 49 | #pragma omp parallel for 50 | for(int i = 0; i < m_node_size; ++i) { 51 | m_nodes.get()[i].clear(); 52 | } 53 | } 54 | 55 | void BHTree::make(std::vector & particles, const int particle_num) 56 | { 57 | m_root.root_clear(); 58 | 59 | if(!m_is_periodic) { 60 | omp_real r_min[DIM]; 61 | omp_real r_max[DIM]; 62 | for(int i = 0; i < DIM; ++i) { 63 | r_min[i].get() = std::numeric_limits::max(); 64 | r_max[i].get() = std::numeric_limits::lowest(); 65 | } 66 | 67 | #pragma omp parallel for 68 | for(int i = 0; i < particle_num; ++i) { 69 | auto & r_i = particles[i].pos; 70 | for(int j = 0; j < DIM; ++j) { 71 | if(r_min[j].get() > r_i[j]) { 72 | r_min[j].get() = r_i[j]; 73 | } 74 | if(r_max[j].get() < r_i[j]) { 75 | r_max[j].get() = r_i[j]; 76 | } 77 | } 78 | } 79 | 80 | vec_t range_min, range_max; 81 | for(int i = 0; i < DIM; ++i) { 82 | range_min[i] = r_min[i].min(); 83 | range_max[i] = r_max[i].max(); 84 | } 85 | 86 | m_root.center = (range_max + range_min) * 0.5; 87 | auto range = range_max - range_min; 88 | real l = 0.0; 89 | for(int i = 0; i < DIM; ++i) { 90 | if(l < range[i]) { 91 | l = range[i]; 92 | } 93 | } 94 | m_root.edge = l; 95 | } 96 | 97 | #pragma omp parallel for 98 | for(int i = 0; i < particle_num - 1; ++i) { 99 | particles[i].next = &particles[i + 1]; 100 | } 101 | particles[particle_num - 1].next = nullptr; 102 | m_root.first = &particles[0]; 103 | 104 | int remaind = m_node_size; 105 | auto * p = m_nodes.get(); 106 | m_root.create_tree(p, remaind, m_max_level, m_leaf_particle_num); 107 | } 108 | 109 | void BHTree::set_kernel() 110 | { 111 | m_root.set_kernel(); 112 | } 113 | 114 | int BHTree::neighbor_search(const SPHParticle & p_i, std::vector & neighbor_list, const std::vector & particles, const bool is_ij) 115 | { 116 | int n_neighbor = 0; 117 | m_root.neighbor_search(p_i, neighbor_list, n_neighbor, is_ij, m_periodic.get()); 118 | 119 | const auto & pos_i = p_i.pos; 120 | std::sort(neighbor_list.begin(), neighbor_list.begin() + n_neighbor, [&](const int a, const int b) { 121 | const vec_t r_ia = m_periodic->calc_r_ij(pos_i, particles[a].pos); 122 | const vec_t r_ib = m_periodic->calc_r_ij(pos_i, particles[b].pos); 123 | return abs2(r_ia) < abs2(r_ib); 124 | }); 125 | return n_neighbor; 126 | } 127 | 128 | void BHTree::tree_force(SPHParticle & p_i) 129 | { 130 | p_i.phi = 0.0; 131 | m_root.calc_force(p_i, m_theta2, m_g_constant, m_periodic.get()); 132 | } 133 | 134 | // --------------------------------------------------------------------------------------------------------------- // 135 | 136 | void BHTree::BHNode::create_tree(BHNode * & nodes, int & remaind, const int max_level, const int leaf_particle_num) 137 | { 138 | std::fill(childs, childs + NCHILD, nullptr); 139 | 140 | auto * pp = first; 141 | do { 142 | auto * pnext = pp->next; 143 | assign(pp, nodes, remaind); 144 | pp = pnext; 145 | } while(pp != nullptr); 146 | 147 | int num_child = 0; 148 | for(int i = 0; i < NCHILD; ++i) { 149 | auto * child = childs[i]; 150 | if(child) { 151 | ++num_child; 152 | child->m_center /= child->mass; 153 | 154 | if(child->num > leaf_particle_num && level < max_level) { 155 | child->create_tree(nodes, remaind, max_level, leaf_particle_num); 156 | } else { 157 | child->is_leaf = true; 158 | } 159 | } 160 | } 161 | } 162 | 163 | void BHTree::BHNode::assign(SPHParticle * particle, BHNode * & nodes, int & remaind) 164 | { 165 | auto & p_i = *particle; 166 | const auto & pos = p_i.pos; 167 | 168 | int index = 0; 169 | int mask = 1; 170 | for(int i = 0; i < DIM; ++i) { 171 | if(pos[i] > center[i]) { 172 | index |= mask; 173 | } 174 | mask <<= 1; 175 | } 176 | 177 | auto * child = childs[index]; 178 | if(!child) { 179 | if(remaind < 0) { 180 | THROW_ERROR("There is no free node."); 181 | } 182 | childs[index] = nodes; 183 | child = childs[index]; 184 | ++nodes; 185 | --remaind; 186 | child->clear(); 187 | child->level = level + 1; 188 | child->edge = edge * 0.5; 189 | 190 | int a = 1; 191 | real b = 2.0; 192 | for(int i = 0; i < DIM; ++i) { 193 | child->center[i] = center[i] + ((index & a) * b - 1.0) * edge * 0.25; 194 | a <<= 1; 195 | b *= 0.5; 196 | } 197 | } 198 | 199 | child->num++; 200 | child->mass += p_i.mass; 201 | child->m_center += pos * p_i.mass; 202 | p_i.next = child->first; 203 | child->first = particle; 204 | } 205 | 206 | real BHTree::BHNode::set_kernel() 207 | { 208 | real kernel = 0.0; 209 | if(is_leaf) { 210 | auto * p = first; 211 | while(p) { 212 | const real h = p->sml; 213 | if(h > kernel) { 214 | kernel = h; 215 | } 216 | p = p->next; 217 | } 218 | } else { 219 | for(int i = 0; i < NCHILD; ++i) { 220 | auto * child = childs[i]; 221 | if(child) { 222 | const real h = child->set_kernel(); 223 | if(h > kernel) { 224 | kernel = h; 225 | } 226 | } 227 | } 228 | } 229 | 230 | kernel_size = kernel; 231 | return kernel_size; 232 | } 233 | 234 | void BHTree::BHNode::neighbor_search(const SPHParticle & p_i, std::vector & neighbor_list, int & n_neighbor, const bool is_ij, const Periodic * periodic) 235 | { 236 | const vec_t & r_i = p_i.pos; 237 | const real h = is_ij ? std::max(p_i.sml, kernel_size) : p_i.sml; 238 | const real h2 = h * h; 239 | const real l2 = sqr(edge * 0.5 + h); 240 | const vec_t d = periodic->calc_r_ij(r_i, center); 241 | real dx2_max = sqr(d[0]); 242 | for(int i = 1; i < DIM; ++i) { 243 | const real dx2 = sqr(d[i]); 244 | if(dx2 > dx2_max) { 245 | dx2_max = dx2; 246 | } 247 | } 248 | 249 | if(dx2_max <= l2) { 250 | if(is_leaf) { 251 | auto * p = first; 252 | while(p) { 253 | const vec_t & r_j = p->pos; 254 | const vec_t r_ij = periodic->calc_r_ij(r_i, r_j); 255 | const real r2 = abs2(r_ij); 256 | if(r2 < h2) { 257 | neighbor_list[n_neighbor] = p->id; 258 | ++n_neighbor; 259 | } 260 | p = p->next; 261 | } 262 | } else { 263 | for(int i = 0; i < NCHILD; ++i) { 264 | if(childs[i]) { 265 | childs[i]->neighbor_search(p_i, neighbor_list, n_neighbor, is_ij, periodic); 266 | } 267 | } 268 | } 269 | } 270 | } 271 | 272 | // Hernquist & Katz (1989) 273 | inline real f(const real r, const real h) 274 | { 275 | const real e = h * 0.5; 276 | const real u = r / e; 277 | 278 | if(u < 1.0) { 279 | return (-0.5 * u * u * (1.0 / 3.0 - 3.0 / 20 * u * u + u * u * u / 20) + 1.4) / e; 280 | } else if(u < 2.0) { 281 | return -1.0 / (15 * r) + (-u * u * (4.0 / 3.0 - u + 0.3 * u * u - u * u * u / 30) + 1.6) / e; 282 | } else { 283 | return 1 / r; 284 | } 285 | } 286 | 287 | inline real g(const real r, const real h) 288 | { 289 | const real e = h * 0.5; 290 | const real u = r / e; 291 | 292 | if(u < 1.0) { 293 | return (4.0 / 3.0 - 1.2 * u * u + 0.5 * u * u * u) / (e * e * e); 294 | } else if(u < 2.0) { 295 | return (-1.0 / 15 + 8.0 / 3 * u * u * u - 3 * u * u * u * u + 1.2 * u * u * u * u * u - u * u * u * u * u * u / 6.0) / (r * r * r); 296 | } else { 297 | return 1 / (r * r * r); 298 | } 299 | } 300 | 301 | void BHTree::BHNode::calc_force(SPHParticle & p_i, const real theta2, const real g_constant, const Periodic * periodic) 302 | { 303 | const vec_t & r_i = p_i.pos; 304 | const real l2 = edge * edge; 305 | const vec_t d = periodic->calc_r_ij(r_i, m_center); 306 | const real d2 = abs2(d); 307 | 308 | if(l2 > theta2 * d2) { 309 | if(is_leaf) { 310 | auto * p = first; 311 | while(p) { 312 | const vec_t & r_j = p->pos; 313 | const vec_t r_ij = periodic->calc_r_ij(r_i, r_j); 314 | const real r = std::abs(r_ij); 315 | p_i.phi -= g_constant * p->mass * (f(r, p_i.sml) + f(r, p->sml)) * 0.5; 316 | p_i.acc -= r_ij * (g_constant * p->mass * (g(r, p_i.sml) + g(r, p->sml)) * 0.5); 317 | p = p->next; 318 | } 319 | } else { 320 | for(int i = 0; i < NCHILD; ++i) { 321 | if(childs[i]) { 322 | childs[i]->calc_force(p_i, theta2, g_constant, periodic); 323 | } 324 | } 325 | } 326 | } else { 327 | const real r_inv = 1.0 / std::sqrt(d2); 328 | p_i.phi -= g_constant * mass * r_inv; 329 | p_i.acc -= d * (g_constant * mass * pow3(r_inv)); 330 | } 331 | } 332 | 333 | } -------------------------------------------------------------------------------- /src/disph/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(sph 2 | PRIVATE 3 | d_fluid_force.cpp 4 | d_pre_interaction.cpp 5 | ) -------------------------------------------------------------------------------- /src/disph/d_fluid_force.cpp: -------------------------------------------------------------------------------- 1 | #include "defines.hpp" 2 | #include "disph/d_fluid_force.hpp" 3 | #include "particle.hpp" 4 | #include "periodic.hpp" 5 | #include "simulation.hpp" 6 | #include "bhtree.hpp" 7 | #include "kernel/kernel_function.hpp" 8 | 9 | #ifdef EXHAUSTIVE_SEARCH 10 | #include "exhaustive_search.hpp" 11 | #endif 12 | 13 | namespace sph 14 | { 15 | namespace disph 16 | { 17 | 18 | void FluidForce::initialize(std::shared_ptr param) 19 | { 20 | sph::FluidForce::initialize(param); 21 | m_gamma = param->physics.gamma; 22 | } 23 | 24 | // Hopkins (2013) 25 | // pressure-energy formulation 26 | void FluidForce::calculation(std::shared_ptr sim) 27 | { 28 | auto & particles = sim->get_particles(); 29 | auto * periodic = sim->get_periodic().get(); 30 | const int num = sim->get_particle_num(); 31 | auto * kernel = sim->get_kernel().get(); 32 | auto * tree = sim->get_tree().get(); 33 | 34 | #pragma omp parallel for 35 | for(int i = 0; i < num; ++i) { 36 | auto & p_i = particles[i]; 37 | std::vector neighbor_list(m_neighbor_number * neighbor_list_size); 38 | 39 | // neighbor search 40 | #ifdef EXHAUSTIVE_SEARCH 41 | int const n_neighbor = exhaustive_search(p_i, p_i.sml, particles, num, neighbor_list, m_neighbor_number * neighbor_list_size, periodic, true); 42 | #else 43 | int const n_neighbor = tree->neighbor_search(p_i, neighbor_list, particles, true); 44 | #endif 45 | 46 | // fluid force 47 | const vec_t & r_i = p_i.pos; 48 | const vec_t & v_i = p_i.vel; 49 | const real gamma2_u_i = sqr(m_gamma - 1.0) * p_i.ene; 50 | const real gamma2_u_per_pres_i = gamma2_u_i / p_i.pres; 51 | const real m_u_inv = 1.0 / (p_i.mass * p_i.ene); 52 | const real h_i = p_i.sml; 53 | const real gradh_i = p_i.gradh; 54 | 55 | vec_t acc(0.0); 56 | real dene = 0.0; 57 | 58 | for(int n = 0; n < n_neighbor; ++n) { 59 | int const j = neighbor_list[n]; 60 | auto & p_j = particles[j]; 61 | const vec_t r_ij = periodic->calc_r_ij(r_i, p_j.pos); 62 | const real r = std::abs(r_ij); 63 | 64 | if(r >= std::max(h_i, p_j.sml) || r == 0.0) { 65 | continue; 66 | } 67 | 68 | const vec_t dw_i = kernel->dw(r_ij, r, h_i); 69 | const vec_t dw_j = kernel->dw(r_ij, r, p_j.sml); 70 | const vec_t dw_ij = (dw_i + dw_j) * 0.5; 71 | const vec_t v_ij = v_i - p_j.vel; 72 | const real f_ij = 1.0 - gradh_i / (p_j.mass * p_j.ene); 73 | const real f_ji = 1.0 - p_j.gradh * m_u_inv; 74 | const real u_per_pres_j = p_j.ene / p_j.pres; 75 | 76 | const real pi_ij = artificial_viscosity(p_i, p_j, r_ij); 77 | const real dene_ac = m_use_ac ? artificial_conductivity(p_i, p_j, r_ij, dw_ij) : 0.0; 78 | 79 | acc -= dw_i * (p_j.mass * (gamma2_u_per_pres_i * p_j.ene * f_ij + 0.5 * pi_ij)) + dw_j * (p_j.mass * (gamma2_u_i * u_per_pres_j * f_ji + 0.5 * pi_ij)); 80 | dene += p_j.mass * gamma2_u_per_pres_i * p_j.ene * f_ij * inner_product(v_ij, dw_i) + 0.5 * p_j.mass * pi_ij * inner_product(v_ij, dw_ij) + dene_ac; 81 | } 82 | 83 | p_i.acc = acc; 84 | p_i.dene = dene; 85 | } 86 | } 87 | 88 | } 89 | } -------------------------------------------------------------------------------- /src/disph/d_pre_interaction.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "disph/d_pre_interaction.hpp" 4 | #include "parameters.hpp" 5 | #include "simulation.hpp" 6 | #include "periodic.hpp" 7 | #include "openmp.hpp" 8 | #include "kernel/kernel_function.hpp" 9 | #include "exception.hpp" 10 | #include "bhtree.hpp" 11 | 12 | #ifdef EXHAUSTIVE_SEARCH 13 | #include "exhaustive_search.hpp" 14 | #endif 15 | 16 | namespace sph 17 | { 18 | namespace disph 19 | { 20 | 21 | void PreInteraction::calculation(std::shared_ptr sim) 22 | { 23 | if(m_first) { 24 | initial_smoothing(sim); 25 | m_first = false; 26 | } 27 | 28 | auto & particles = sim->get_particles(); 29 | auto * periodic = sim->get_periodic().get(); 30 | const int num = sim->get_particle_num(); 31 | auto * kernel = sim->get_kernel().get(); 32 | const real dt = sim->get_dt(); 33 | auto * tree = sim->get_tree().get(); 34 | 35 | omp_real h_per_v_sig(std::numeric_limits::max()); 36 | 37 | #pragma omp parallel for 38 | for(int i = 0; i < num; ++i) { 39 | auto & p_i = particles[i]; 40 | std::vector neighbor_list(m_neighbor_number * neighbor_list_size); 41 | 42 | // guess smoothing length 43 | constexpr real A = DIM == 1 ? 2.0 : 44 | DIM == 2 ? M_PI : 45 | 4.0 * M_PI / 3.0; 46 | p_i.sml = std::pow(m_neighbor_number * p_i.mass / (p_i.dens * A), 1.0 / DIM) * m_kernel_ratio; 47 | 48 | // neighbor search 49 | #ifdef EXHAUSTIVE_SEARCH 50 | const int n_neighbor_tmp = exhaustive_search(p_i, p_i.sml, particles, num, neighbor_list, m_neighbor_number * neighbor_list_size, periodic, false); 51 | #else 52 | const int n_neighbor_tmp = tree->neighbor_search(p_i, neighbor_list, particles, false); 53 | #endif 54 | // smoothing length 55 | if(m_iteration) { 56 | p_i.sml = newton_raphson(p_i, particles, neighbor_list, n_neighbor_tmp, periodic, kernel); 57 | } 58 | 59 | // density etc. 60 | real dens_i = 0.0; 61 | real pres_i = 0.0; 62 | real dh_pres_i = 0.0; 63 | real n_i = 0.0; 64 | real dh_n_i = 0.0; 65 | real v_sig_max = p_i.sound * 2.0; 66 | const vec_t & pos_i = p_i.pos; 67 | int n_neighbor = 0; 68 | for(int n = 0; n < n_neighbor_tmp; ++n) { 69 | int const j = neighbor_list[n]; 70 | auto & p_j = particles[j]; 71 | const vec_t r_ij = periodic->calc_r_ij(pos_i, p_j.pos); 72 | const real r = std::abs(r_ij); 73 | 74 | if(r >= p_i.sml) { 75 | break; 76 | } 77 | 78 | ++n_neighbor; 79 | const real w_ij = kernel->w(r, p_i.sml); 80 | const real dhw_ij = kernel->dhw(r, p_i.sml); 81 | dens_i += p_j.mass * w_ij; 82 | n_i += w_ij; 83 | pres_i += p_j.mass * p_j.ene * w_ij; 84 | dh_pres_i += p_j.mass * p_j.ene * dhw_ij; 85 | dh_n_i += dhw_ij; 86 | 87 | if(i != j) { 88 | const real v_sig = p_i.sound + p_j.sound - 3.0 * inner_product(r_ij, p_i.vel - p_j.vel) / r; 89 | if(v_sig > v_sig_max) { 90 | v_sig_max = v_sig; 91 | } 92 | } 93 | } 94 | 95 | p_i.dens = dens_i; 96 | p_i.pres = (m_gamma - 1.0) * pres_i; 97 | // f_ij = 1 - p_i.gradh / (p_j.mass * p_j.ene) 98 | p_i.gradh = p_i.sml / (DIM * n_i) * dh_pres_i / (1.0 + p_i.sml / (DIM * n_i) * dh_n_i); 99 | p_i.neighbor = n_neighbor; 100 | 101 | const real h_per_v_sig_i = p_i.sml / v_sig_max; 102 | if(h_per_v_sig.get() > h_per_v_sig_i) { 103 | h_per_v_sig.get() = h_per_v_sig_i; 104 | } 105 | 106 | // Artificial viscosity 107 | if(m_use_balsara_switch && DIM != 1) { 108 | #if DIM != 1 109 | // balsara switch 110 | real div_v = 0.0; 111 | #if DIM == 2 112 | real rot_v = 0.0; 113 | #else 114 | vec_t rot_v = 0.0; 115 | #endif 116 | for(int n = 0; n < n_neighbor; ++n) { 117 | int const j = neighbor_list[n]; 118 | auto & p_j = particles[j]; 119 | const vec_t r_ij = periodic->calc_r_ij(pos_i, p_j.pos); 120 | const real r = std::abs(r_ij); 121 | const vec_t dw = kernel->dw(r_ij, r, p_i.sml); 122 | const vec_t v_ij = p_i.vel - p_j.vel; 123 | div_v -= p_j.mass * p_j.ene * inner_product(v_ij, dw); 124 | rot_v += vector_product(v_ij, dw) * (p_j.mass * p_j.ene); 125 | } 126 | const real p_inv = (m_gamma - 1.0) / p_i.pres; 127 | div_v *= p_inv; 128 | rot_v *= p_inv; 129 | p_i.balsara = std::abs(div_v) / (std::abs(div_v) + std::abs(rot_v) + 1e-4 * p_i.sound / p_i.sml); 130 | 131 | // time dependent alpha 132 | if(m_use_time_dependent_av) { 133 | const real tau_inv = m_epsilon * p_i.sound / p_i.sml; 134 | const real dalpha = (-(p_i.alpha - m_alpha_min) * tau_inv + std::max(-div_v, (real)0.0) * (m_alpha_max - p_i.alpha)) * dt; 135 | p_i.alpha += dalpha; 136 | } 137 | #endif 138 | } else if(m_use_time_dependent_av) { 139 | real div_v = 0.0; 140 | for(int n = 0; n < n_neighbor; ++n) { 141 | int const j = neighbor_list[n]; 142 | auto & p_j = particles[j]; 143 | const vec_t r_ij = periodic->calc_r_ij(pos_i, p_j.pos); 144 | const real r = std::abs(r_ij); 145 | const vec_t dw = kernel->dw(r_ij, r, p_i.sml); 146 | const vec_t v_ij = p_i.vel - p_j.vel; 147 | div_v -= p_j.mass * p_j.ene * inner_product(v_ij, dw); 148 | } 149 | const real p_inv = (m_gamma - 1.0) / p_i.pres; 150 | div_v *= p_inv; 151 | const real tau_inv = m_epsilon * p_i.sound / p_i.sml; 152 | const real s_i = std::max(-div_v, (real)0.0); 153 | p_i.alpha = (p_i.alpha + dt * tau_inv * m_alpha_min + s_i * dt * m_alpha_max) / (1.0 + dt * tau_inv + s_i * dt); 154 | } 155 | } 156 | 157 | sim->set_h_per_v_sig(h_per_v_sig.min()); 158 | 159 | #ifndef EXHAUSTIVE_SEARCH 160 | tree->set_kernel(); 161 | #endif 162 | } 163 | 164 | inline real powh_(const real h) { 165 | #if DIM == 1 166 | return 1; 167 | #elif DIM == 2 168 | return h; 169 | #elif DIM == 3 170 | return h * h; 171 | #endif 172 | } 173 | 174 | real PreInteraction::newton_raphson( 175 | const SPHParticle & p_i, 176 | const std::vector & particles, 177 | const std::vector & neighbor_list, 178 | const int n_neighbor, 179 | const Periodic * periodic, 180 | const KernelFunction * kernel 181 | ) 182 | { 183 | real h_i = p_i.sml / m_kernel_ratio; 184 | constexpr real A = DIM == 1 ? 2.0 : 185 | DIM == 2 ? M_PI : 186 | 4.0 * M_PI / 3.0; 187 | const real b = m_neighbor_number / A; 188 | 189 | // f = n h^d - b 190 | // f' = dn/dh h^d + d n h^{d-1} 191 | 192 | constexpr real epsilon = 1e-4; 193 | constexpr int max_iter = 10; 194 | const auto & r_i = p_i.pos; 195 | for(int i = 0; i < max_iter; ++i) { 196 | const real h_b = h_i; 197 | 198 | real dens = 0.0; 199 | real ddens = 0.0; 200 | for(int n = 0; n < n_neighbor; ++n) { 201 | int const j = neighbor_list[n]; 202 | auto & p_j = particles[j]; 203 | const vec_t r_ij = periodic->calc_r_ij(r_i, p_j.pos); 204 | const real r = std::abs(r_ij); 205 | 206 | if(r >= h_i) { 207 | break; 208 | } 209 | 210 | dens += kernel->w(r, h_i); 211 | ddens += kernel->dhw(r, h_i); 212 | } 213 | 214 | const real f = dens * powh(h_i) - b; 215 | const real df = ddens * powh(h_i) + DIM * dens * powh_(h_i); 216 | 217 | h_i -= f / df; 218 | 219 | if(std::abs(h_i - h_b) < (h_i + h_b) * epsilon) { 220 | return h_i; 221 | } 222 | } 223 | 224 | #pragma omp critical 225 | { 226 | WRITE_LOG << "Particle id " << p_i.id << " is not convergence"; 227 | } 228 | 229 | return p_i.sml / m_kernel_ratio; 230 | } 231 | 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/exhaustive_search.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "vector_type.hpp" 4 | #include "particle.hpp" 5 | #include "periodic.hpp" 6 | #include "exhaustive_search.hpp" 7 | 8 | namespace sph { 9 | 10 | // 全探索 (デバッグ用) 11 | int exhaustive_search( 12 | SPHParticle & p_i, 13 | const real kernel_size, 14 | const std::vector & particles, 15 | const int num, 16 | std::vector & neighbor_list, 17 | const int list_size, 18 | Periodic const * periodic, 19 | const bool is_ij) 20 | { 21 | const real kernel_size_i2 = kernel_size * kernel_size; 22 | const vec_t & pos_i = p_i.pos; 23 | int count = 0; 24 | for(int j = 0; j < num; ++j) { 25 | const auto & p_j = particles[j]; 26 | const vec_t r_ij = periodic->calc_r_ij(pos_i, p_j.pos); 27 | const real r2 = abs2(r_ij); 28 | const real kernel_size2 = is_ij ? std::max(kernel_size_i2, p_j.sml * p_j.sml) : kernel_size_i2; 29 | if(r2 < kernel_size2) { 30 | neighbor_list[count] = j; 31 | ++count; 32 | } 33 | } 34 | 35 | std::sort(neighbor_list.begin(), neighbor_list.begin() + count, [&](const int a, const int b) { 36 | const vec_t r_ia = periodic->calc_r_ij(pos_i, particles[a].pos); 37 | const vec_t r_ib = periodic->calc_r_ij(pos_i, particles[b].pos); 38 | return abs2(r_ia) < abs2(r_ib); 39 | }); 40 | 41 | return count; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/fluid_force.cpp: -------------------------------------------------------------------------------- 1 | #include "defines.hpp" 2 | #include "fluid_force.hpp" 3 | #include "particle.hpp" 4 | #include "periodic.hpp" 5 | #include "simulation.hpp" 6 | #include "bhtree.hpp" 7 | #include "kernel/kernel_function.hpp" 8 | 9 | #ifdef EXHAUSTIVE_SEARCH 10 | #include "exhaustive_search.hpp" 11 | #endif 12 | 13 | namespace sph 14 | { 15 | 16 | void FluidForce::initialize(std::shared_ptr param) 17 | { 18 | m_neighbor_number = param->physics.neighbor_number; 19 | m_use_ac = param->ac.is_valid; 20 | if(m_use_ac) { 21 | m_alpha_ac = param->ac.alpha; 22 | m_use_gravity = param->gravity.is_valid; 23 | } 24 | } 25 | 26 | void FluidForce::calculation(std::shared_ptr sim) 27 | { 28 | auto & particles = sim->get_particles(); 29 | auto * periodic = sim->get_periodic().get(); 30 | const int num = sim->get_particle_num(); 31 | auto * kernel = sim->get_kernel().get(); 32 | auto * tree = sim->get_tree().get(); 33 | 34 | #pragma omp parallel for 35 | for(int i = 0; i < num; ++i) { 36 | auto & p_i = particles[i]; 37 | std::vector neighbor_list(m_neighbor_number * neighbor_list_size); 38 | 39 | // neighbor search 40 | #ifdef EXHAUSTIVE_SEARCH 41 | int const n_neighbor = exhaustive_search(p_i, p_i.sml, particles, num, neighbor_list, m_neighbor_number * neighbor_list_size, periodic, true); 42 | #else 43 | int const n_neighbor = tree->neighbor_search(p_i, neighbor_list, particles, true); 44 | #endif 45 | 46 | // fluid force 47 | const vec_t & r_i = p_i.pos; 48 | const vec_t & v_i = p_i.vel; 49 | const real p_per_rho2_i = p_i.pres / sqr(p_i.dens); 50 | const real h_i = p_i.sml; 51 | const real gradh_i = p_i.gradh; 52 | 53 | vec_t acc(0.0); 54 | real dene = 0.0; 55 | 56 | for(int n = 0; n < n_neighbor; ++n) { 57 | int const j = neighbor_list[n]; 58 | auto & p_j = particles[j]; 59 | const vec_t r_ij = periodic->calc_r_ij(r_i, p_j.pos); 60 | const real r = std::abs(r_ij); 61 | 62 | if(r >= std::max(h_i, p_j.sml) || r == 0.0) { 63 | continue; 64 | } 65 | 66 | const vec_t dw_i = kernel->dw(r_ij, r, h_i); 67 | const vec_t dw_j = kernel->dw(r_ij, r, p_j.sml); 68 | const vec_t dw_ij = (dw_i + dw_j) * 0.5; 69 | const vec_t v_ij = v_i - p_j.vel; 70 | 71 | const real pi_ij = artificial_viscosity(p_i, p_j, r_ij); 72 | const real dene_ac = m_use_ac ? artificial_conductivity(p_i, p_j, r_ij, dw_ij) : 0.0; 73 | 74 | #if 0 75 | acc -= dw_ij * (p_j.mass * (p_per_rho2_i + p_j.pres / sqr(p_j.dens) + pi_ij)); 76 | dene += p_j.mass * (p_per_rho2_i + 0.5 * pi_ij) * inner_product(v_ij, dw_ij); 77 | #else 78 | acc -= dw_i * (p_j.mass * (p_per_rho2_i * gradh_i + 0.5 * pi_ij)) + dw_j * (p_j.mass * (p_j.pres / sqr(p_j.dens) * p_j.gradh + 0.5 * pi_ij)); 79 | dene += p_j.mass * p_per_rho2_i * gradh_i * inner_product(v_ij, dw_i) + 0.5 * p_j.mass * pi_ij * inner_product(v_ij, dw_ij) + dene_ac; 80 | #endif 81 | } 82 | 83 | p_i.acc = acc; 84 | p_i.dene = dene; 85 | } 86 | } 87 | 88 | // Monaghan (1997) 89 | real FluidForce::artificial_viscosity(const SPHParticle & p_i, const SPHParticle & p_j, const vec_t & r_ij) 90 | { 91 | const auto v_ij = p_i.vel - p_j.vel; 92 | const real vr = inner_product(v_ij, r_ij); 93 | 94 | if(vr < 0) { 95 | const real alpha = 0.5 * (p_i.alpha + p_j.alpha); 96 | const real balsara = 0.5 * (p_i.balsara + p_j.balsara); 97 | const real w_ij = vr / std::abs(r_ij); 98 | const real v_sig = p_i.sound + p_j.sound - 3.0 * w_ij; 99 | const real rho_ij_inv = 2.0 / (p_i.dens + p_j.dens); 100 | 101 | const real pi_ij = -0.5 * balsara * alpha * v_sig * w_ij * rho_ij_inv; 102 | return pi_ij; 103 | } else { 104 | return 0; 105 | } 106 | } 107 | 108 | real FluidForce::artificial_conductivity(const SPHParticle & p_i, const SPHParticle & p_j, const vec_t & r_ij, const vec_t & dw_ij) 109 | { 110 | // Wadsley et al. (2008) or Price (2008) 111 | const real v_sig = m_use_gravity ? 112 | std::abs(inner_product(p_i.vel - p_j.vel, r_ij) / std::abs(r_ij)) : 113 | std::sqrt(2.0 * std::abs(p_i.pres - p_j.pres) / (p_i.dens + p_j.dens)); 114 | 115 | return m_alpha_ac * p_j.mass * v_sig * (p_i.ene - p_j.ene) * inner_product(dw_ij, r_ij) / std::abs(r_ij); 116 | } 117 | 118 | } -------------------------------------------------------------------------------- /src/gravity_force.cpp: -------------------------------------------------------------------------------- 1 | #include "defines.hpp" 2 | #include "gravity_force.hpp" 3 | #include "particle.hpp" 4 | #include "periodic.hpp" 5 | #include "simulation.hpp" 6 | #include "bhtree.hpp" 7 | 8 | #ifdef EXHAUSTIVE_SEARCH 9 | #include "exhaustive_search.hpp" 10 | #endif 11 | 12 | namespace sph 13 | { 14 | 15 | // Hernquist & Katz (1989) 16 | inline real f(const real r, const real h) 17 | { 18 | const real e = h * 0.5; 19 | const real u = r / e; 20 | 21 | if(u < 1.0) { 22 | return (-0.5 * u * u * (1.0 / 3.0 - 3.0 / 20 * u * u + u * u * u / 20) + 1.4) / e; 23 | } else if(u < 2.0) { 24 | return -1.0 / (15 * r) + (-u * u * (4.0 / 3.0 - u + 0.3 * u * u - u * u * u / 30) + 1.6) / e; 25 | } else { 26 | return 1 / r; 27 | } 28 | } 29 | 30 | inline real g(const real r, const real h) 31 | { 32 | const real e = h * 0.5; 33 | const real u = r / e; 34 | 35 | if(u < 1.0) { 36 | return (4.0 / 3.0 - 1.2 * u * u + 0.5 * u * u * u) / (e * e * e); 37 | } else if(u < 2.0) { 38 | return (-1.0 / 15 + 8.0 / 3 * u * u * u - 3 * u * u * u * u + 1.2 * u * u * u * u * u - u * u * u * u * u * u / 6.0) / (r * r * r); 39 | } else { 40 | return 1 / (r * r * r); 41 | } 42 | } 43 | 44 | void GravityForce::initialize(std::shared_ptr param) 45 | { 46 | m_is_valid = param->gravity.is_valid; 47 | if(m_is_valid) { 48 | m_constant = param->gravity.constant; 49 | } 50 | } 51 | 52 | void GravityForce::calculation(std::shared_ptr sim) 53 | { 54 | if(!m_is_valid) { 55 | return; 56 | } 57 | 58 | auto & particles = sim->get_particles(); 59 | const int num = sim->get_particle_num(); 60 | #ifdef EXHAUSTIVE_SEARCH 61 | auto * periodic = sim->get_periodic().get(); 62 | #else 63 | auto * tree = sim->get_tree().get(); 64 | #endif 65 | 66 | #pragma omp parallel for 67 | for(int i = 0; i < num; ++i) { 68 | auto & p_i = particles[i]; 69 | 70 | #ifdef EXHAUSTIVE_SEARCH 71 | real phi = 0.0; 72 | vec_t force(0.0); 73 | const vec_t & r_i = p_i.pos; 74 | 75 | for(int j = 0; j < num; ++j) { 76 | const auto & p_j = particles[j]; 77 | const vec_t r_ij = periodic->calc_r_ij(r_i, p_j.pos); 78 | const real r = std::abs(r_ij); 79 | phi -= m_constant * p_j.mass * (f(r, p_i.sml) + f(r, p_j.sml)) * 0.5; 80 | force -= r_ij * (m_constant * p_j.mass * (g(r, p_i.sml) + g(r, p_j.sml)) * 0.5); 81 | } 82 | 83 | p_i.acc += force; 84 | p_i.phi = phi; 85 | #else 86 | tree->tree_force(p_i); 87 | #endif 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/gsph/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(sph 2 | PRIVATE 3 | g_fluid_force.cpp 4 | g_pre_interaction.cpp 5 | ) -------------------------------------------------------------------------------- /src/gsph/g_fluid_force.cpp: -------------------------------------------------------------------------------- 1 | #include "defines.hpp" 2 | #include "particle.hpp" 3 | #include "periodic.hpp" 4 | #include "simulation.hpp" 5 | #include "bhtree.hpp" 6 | #include "kernel/kernel_function.hpp" 7 | #include "gsph/g_fluid_force.hpp" 8 | 9 | #ifdef EXHAUSTIVE_SEARCH 10 | #include "exhaustive_search.hpp" 11 | #endif 12 | 13 | namespace sph 14 | { 15 | namespace gsph 16 | { 17 | 18 | void FluidForce::initialize(std::shared_ptr param) 19 | { 20 | sph::FluidForce::initialize(param); 21 | m_is_2nd_order = param->gsph.is_2nd_order; 22 | m_gamma = param->physics.gamma; 23 | 24 | hll_solver(); 25 | } 26 | 27 | // van Leer (1979) limiter 28 | inline real limiter(const real dq1, const real dq2) 29 | { 30 | const real dq1dq2 = dq1 * dq2; 31 | if(dq1dq2 <= 0) { 32 | return 0.0; 33 | } else { 34 | return 2.0 * dq1dq2 / (dq1 + dq2); 35 | } 36 | } 37 | 38 | // Cha & Whitworth (2003) 39 | void FluidForce::calculation(std::shared_ptr sim) 40 | { 41 | auto & particles = sim->get_particles(); 42 | auto * periodic = sim->get_periodic().get(); 43 | const int num = sim->get_particle_num(); 44 | auto * kernel = sim->get_kernel().get(); 45 | auto * tree = sim->get_tree().get(); 46 | const real dt = sim->get_dt(); 47 | 48 | // for MUSCL 49 | auto & grad_d = sim->get_vector_array("grad_density"); 50 | auto & grad_p = sim->get_vector_array("grad_pressure"); 51 | vec_t * grad_v[DIM] = { 52 | sim->get_vector_array("grad_velocity_0").data(), 53 | #if DIM == 2 54 | sim->get_vector_array("grad_velocity_1").data(), 55 | #elif DIM == 3 56 | sim->get_vector_array("grad_velocity_1").data(), 57 | sim->get_vector_array("grad_velocity_2").data(), 58 | #endif 59 | }; 60 | 61 | #pragma omp parallel for 62 | for(int i = 0; i < num; ++i) { 63 | auto & p_i = particles[i]; 64 | std::vector neighbor_list(m_neighbor_number * neighbor_list_size); 65 | 66 | // neighbor search 67 | #ifdef EXHAUSTIVE_SEARCH 68 | int const n_neighbor = exhaustive_search(p_i, p_i.sml, particles, num, neighbor_list, m_neighbor_number * neighbor_list_size, periodic, true); 69 | #else 70 | int const n_neighbor = tree->neighbor_search(p_i, neighbor_list, particles, true); 71 | #endif 72 | 73 | // fluid force 74 | const vec_t & r_i = p_i.pos; 75 | const vec_t & v_i = p_i.vel; 76 | const real h_i = p_i.sml; 77 | const real rho2_inv_i = 1.0 / sqr(p_i.dens); 78 | 79 | vec_t acc(0.0); 80 | real dene = 0.0; 81 | 82 | for(int n = 0; n < n_neighbor; ++n) { 83 | int const j = neighbor_list[n]; 84 | auto & p_j = particles[j]; 85 | const vec_t r_ij = periodic->calc_r_ij(r_i, p_j.pos); 86 | const real r = std::abs(r_ij); 87 | 88 | if(r >= std::max(h_i, p_j.sml) || r == 0.0) { 89 | continue; 90 | } 91 | 92 | const real r_inv = 1.0 / r; 93 | const vec_t e_ij = r_ij * r_inv; 94 | const real ve_i = inner_product(v_i, e_ij); 95 | const real ve_j = inner_product(p_j.vel, e_ij); 96 | real vstar, pstar; 97 | 98 | if(m_is_2nd_order) { 99 | // Murante et al. (2011) 100 | 101 | real right[4], left[4]; 102 | const real delta_i = 0.5 * (1.0 - p_i.sound * dt * r_inv); 103 | const real delta_j = 0.5 * (1.0 - p_j.sound * dt * r_inv); 104 | 105 | // velocity 106 | const real dv_ij = ve_i - ve_j; 107 | vec_t dv_i, dv_j; 108 | for(int k = 0; k < DIM; ++k) { 109 | dv_i[k] = inner_product(grad_v[k][i], e_ij); 110 | dv_j[k] = inner_product(grad_v[k][j], e_ij); 111 | } 112 | const real dve_i = inner_product(dv_i, e_ij) * r; 113 | const real dve_j = inner_product(dv_j, e_ij) * r; 114 | right[0] = ve_i - limiter(dv_ij, dve_i) * delta_i; 115 | left[0] = ve_j + limiter(dv_ij, dve_j) * delta_j; 116 | 117 | // density 118 | const real dd_ij = p_i.dens - p_j.dens; 119 | const real dd_i = inner_product(grad_d[i], e_ij) * r; 120 | const real dd_j = inner_product(grad_d[j], e_ij) * r; 121 | right[1] = p_i.dens - limiter(dd_ij, dd_i) * delta_i; 122 | left[1] = p_j.dens + limiter(dd_ij, dd_j) * delta_j; 123 | 124 | // pressure 125 | const real dp_ij = p_i.pres - p_j.pres; 126 | const real dp_i = inner_product(grad_p[i], e_ij) * r; 127 | const real dp_j = inner_product(grad_p[j], e_ij) * r; 128 | right[2] = p_i.pres - limiter(dp_ij, dp_i) * delta_i; 129 | left[2] = p_j.pres + limiter(dp_ij, dp_j) * delta_j; 130 | 131 | // sound speed 132 | right[3] = std::sqrt(m_gamma * right[2] / right[1]); 133 | left[3] = std::sqrt(m_gamma * left[2] / left[1]); 134 | 135 | m_solver(left, right, pstar, vstar); 136 | } else { 137 | const real right[4] = { 138 | ve_i, 139 | p_i.dens, 140 | p_i.pres, 141 | p_i.sound, 142 | }; 143 | const real left[4] = { 144 | ve_j, 145 | p_j.dens, 146 | p_j.pres, 147 | p_j.sound, 148 | }; 149 | 150 | m_solver(left, right, pstar, vstar); 151 | } 152 | 153 | const vec_t dw_i = kernel->dw(r_ij, r, h_i); 154 | const vec_t dw_j = kernel->dw(r_ij, r, p_j.sml); 155 | const vec_t v_ij = e_ij * vstar; 156 | const real rho2_inv_j = 1.0 / sqr(p_j.dens); 157 | const vec_t f = dw_i * (p_j.mass * pstar * rho2_inv_i) + dw_j * (p_j.mass * pstar * rho2_inv_j); 158 | 159 | acc -= f; 160 | dene -= inner_product(f, v_ij - v_i); 161 | } 162 | 163 | p_i.acc = acc; 164 | p_i.dene = dene; 165 | } 166 | } 167 | 168 | void FluidForce::hll_solver() 169 | { 170 | m_solver = [&](const real left[], const real right[], real & pstar, real & vstar) { 171 | const real u_l = left[0]; 172 | const real rho_l = left[1]; 173 | const real p_l = left[2]; 174 | const real c_l = left[3]; 175 | 176 | const real u_r = right[0]; 177 | const real rho_r = right[1]; 178 | const real p_r = right[2]; 179 | const real c_r = right[3]; 180 | 181 | const real roe_l = std::sqrt(rho_l); 182 | const real roe_r = std::sqrt(rho_r); 183 | const real roe_inv = 1.0 / (roe_l + roe_r); 184 | 185 | const real u_t = (roe_l * u_l + roe_r * u_r) * roe_inv; 186 | const real c_t = (roe_l * c_l + roe_r * c_r) * roe_inv; 187 | const real s_l = std::min(u_l - c_l, u_t - c_t); 188 | const real s_r = std::max(u_r + c_r, u_t + c_t); 189 | 190 | const real c1 = rho_l * (s_l - u_l); 191 | const real c2 = rho_r * (s_r - u_r); 192 | const real c3 = 1.0 / (c1 - c2); 193 | const real c4 = p_l - u_l * c1; 194 | const real c5 = p_r - u_r * c2; 195 | 196 | vstar = (c5 - c4) * c3; 197 | pstar = (c1 * c5 - c2 * c4) * c3; 198 | }; 199 | } 200 | 201 | } 202 | } -------------------------------------------------------------------------------- /src/gsph/g_pre_interaction.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "parameters.hpp" 4 | #include "gsph/g_pre_interaction.hpp" 5 | #include "simulation.hpp" 6 | #include "periodic.hpp" 7 | #include "openmp.hpp" 8 | #include "kernel/kernel_function.hpp" 9 | #include "exception.hpp" 10 | #include "bhtree.hpp" 11 | 12 | #ifdef EXHAUSTIVE_SEARCH 13 | #include "exhaustive_search.hpp" 14 | #endif 15 | 16 | namespace sph 17 | { 18 | namespace gsph 19 | { 20 | 21 | void PreInteraction::initialize(std::shared_ptr param) 22 | { 23 | sph::PreInteraction::initialize(param); 24 | m_is_2nd_order = param->gsph.is_2nd_order; 25 | } 26 | 27 | void PreInteraction::calculation(std::shared_ptr sim) 28 | { 29 | if(m_first) { 30 | initial_smoothing(sim); 31 | m_first = false; 32 | } 33 | 34 | auto & particles = sim->get_particles(); 35 | auto * periodic = sim->get_periodic().get(); 36 | const int num = sim->get_particle_num(); 37 | auto * kernel = sim->get_kernel().get(); 38 | auto * tree = sim->get_tree().get(); 39 | 40 | omp_real h_per_v_sig(std::numeric_limits::max()); 41 | 42 | // for MUSCL 43 | auto & grad_d = sim->get_vector_array("grad_density"); 44 | auto & grad_p = sim->get_vector_array("grad_pressure"); 45 | vec_t * grad_v[DIM] = { 46 | sim->get_vector_array("grad_velocity_0").data(), 47 | #if DIM == 2 48 | sim->get_vector_array("grad_velocity_1").data(), 49 | #elif DIM == 3 50 | sim->get_vector_array("grad_velocity_1").data(), 51 | sim->get_vector_array("grad_velocity_2").data(), 52 | #endif 53 | }; 54 | 55 | #pragma omp parallel for 56 | for(int i = 0; i < num; ++i) { 57 | auto & p_i = particles[i]; 58 | std::vector neighbor_list(m_neighbor_number * neighbor_list_size); 59 | 60 | // guess smoothing length 61 | constexpr real A = DIM == 1 ? 2.0 : 62 | DIM == 2 ? M_PI : 63 | 4.0 * M_PI / 3.0; 64 | p_i.sml = std::pow(m_neighbor_number * p_i.mass / (p_i.dens * A), 1.0 / DIM) * m_kernel_ratio; 65 | 66 | // neighbor search 67 | #ifdef EXHAUSTIVE_SEARCH 68 | const int n_neighbor_tmp = exhaustive_search(p_i, p_i.sml, particles, num, neighbor_list, m_neighbor_number * neighbor_list_size, periodic, false); 69 | #else 70 | const int n_neighbor_tmp = tree->neighbor_search(p_i, neighbor_list, particles, false); 71 | #endif 72 | // smoothing length 73 | if(m_iteration) { 74 | p_i.sml = newton_raphson(p_i, particles, neighbor_list, n_neighbor_tmp, periodic, kernel); 75 | } 76 | 77 | // density etc. 78 | real dens_i = 0.0; 79 | real v_sig_max = p_i.sound * 2.0; 80 | const vec_t & pos_i = p_i.pos; 81 | int n_neighbor = 0; 82 | for(int n = 0; n < n_neighbor_tmp; ++n) { 83 | int const j = neighbor_list[n]; 84 | auto & p_j = particles[j]; 85 | const vec_t r_ij = periodic->calc_r_ij(pos_i, p_j.pos); 86 | const real r = std::abs(r_ij); 87 | 88 | if(r >= p_i.sml) { 89 | break; 90 | } 91 | 92 | ++n_neighbor; 93 | dens_i += p_j.mass * kernel->w(r, p_i.sml); 94 | 95 | if(i != j) { 96 | const real v_sig = p_i.sound + p_j.sound - 3.0 * inner_product(r_ij, p_i.vel - p_j.vel) / r; 97 | if(v_sig > v_sig_max) { 98 | v_sig_max = v_sig; 99 | } 100 | } 101 | } 102 | 103 | p_i.dens = dens_i; 104 | p_i.pres = (m_gamma - 1.0) * dens_i * p_i.ene; 105 | p_i.neighbor = n_neighbor; 106 | 107 | const real h_per_v_sig_i = p_i.sml / v_sig_max; 108 | if(h_per_v_sig.get() > h_per_v_sig_i) { 109 | h_per_v_sig.get() = h_per_v_sig_i; 110 | } 111 | 112 | // MUSCL法のための勾配計算 113 | if(!m_is_2nd_order) { 114 | continue; 115 | } 116 | 117 | vec_t dd, du; // dP = (gamma - 1) * (rho * du + drho * u) 118 | vec_t dv[DIM]; 119 | for(int n = 0; n < n_neighbor; ++n) { 120 | int const j = neighbor_list[n]; 121 | auto & p_j = particles[j]; 122 | const vec_t r_ij = periodic->calc_r_ij(pos_i, p_j.pos); 123 | const real r = std::abs(r_ij); 124 | const vec_t dw_ij = kernel->dw(r_ij, r, p_i.sml); 125 | dd += dw_ij * p_j.mass; 126 | du += dw_ij * (p_j.mass * (p_j.ene - p_i.ene)); 127 | for(int k = 0; k < DIM; ++k) { 128 | dv[k] += dw_ij * (p_j.mass * (p_j.vel[k] - p_i.vel[k])); 129 | } 130 | } 131 | grad_d[i] = dd; 132 | grad_p[i] = (dd * p_i.ene + du) * (m_gamma - 1.0); 133 | const real rho_inv = 1.0 / p_i.dens; 134 | for(int k = 0; k < DIM; ++k) { 135 | grad_v[k][i] = dv[k] * rho_inv; 136 | } 137 | } 138 | 139 | sim->set_h_per_v_sig(h_per_v_sig.min()); 140 | 141 | #ifndef EXHAUSTIVE_SEARCH 142 | tree->set_kernel(); 143 | #endif 144 | } 145 | 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/logger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "logger.hpp" 6 | #include "defines.hpp" 7 | #include "exception.hpp" 8 | 9 | #ifdef _WIN32 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | namespace sph 16 | { 17 | std::string Logger::dir_name; 18 | std::ofstream Logger::log_io; 19 | bool Logger::open_flag = false; 20 | 21 | void Logger::open(const std::string & output_dir) 22 | { 23 | open(output_dir.c_str()); 24 | } 25 | 26 | void Logger::open(const char * output_dir) 27 | { 28 | struct stat st; 29 | bool is_mkdir = false; 30 | if(stat(output_dir, &st)) { 31 | #ifdef _WIN32 32 | if(_mkdir(output_dir) == -1) { 33 | #else 34 | if(mkdir(output_dir, 0775) == -1) { 35 | #endif 36 | THROW_ERROR("cannot open directory"); 37 | } 38 | is_mkdir = true; 39 | } 40 | 41 | std::time_t now = std::time(nullptr); 42 | std::tm * pnow = std::localtime(&now); 43 | std::string logfile = output_dir + (boost::format("/%04d%02d%02d%02d%02d%02d.log") % (pnow->tm_year + 1900) % (pnow->tm_mon + 1) % pnow->tm_mday % pnow->tm_hour % pnow->tm_min % pnow->tm_sec).str(); 44 | log_io.open(logfile); 45 | if(is_mkdir) { 46 | log_io << "mkdir " << output_dir << std::endl; 47 | } 48 | dir_name = output_dir; 49 | open_flag = true; 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "solver.hpp" 4 | #include "exception.hpp" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | std::ios_base::sync_with_stdio(false); 9 | sph::exception_handler([&]() { 10 | sph::Solver solver(argc, argv); 11 | solver.run(); 12 | }); 13 | return 0; 14 | } -------------------------------------------------------------------------------- /src/output.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "output.hpp" 6 | #include "logger.hpp" 7 | #include "defines.hpp" 8 | #include "particle.hpp" 9 | #include "simulation.hpp" 10 | 11 | namespace sph 12 | { 13 | 14 | inline void output(const SPHParticle & p, std::ofstream &out, const char sep = ' ') 15 | { 16 | #define OUTPUT_SCALAR(name) do { out << p.name << sep; } while(0) 17 | #define OUTPUT_VECTOR(name) do { for(int i = 0; i < DIM; ++i) out << p.name[i] << sep; } while(0) 18 | 19 | OUTPUT_VECTOR(pos); 20 | OUTPUT_VECTOR(vel); 21 | OUTPUT_VECTOR(acc); 22 | OUTPUT_SCALAR(mass); 23 | OUTPUT_SCALAR(dens); 24 | OUTPUT_SCALAR(pres); 25 | OUTPUT_SCALAR(ene); 26 | OUTPUT_SCALAR(sml); 27 | OUTPUT_SCALAR(id); 28 | OUTPUT_SCALAR(neighbor); 29 | OUTPUT_SCALAR(alpha); 30 | OUTPUT_SCALAR(gradh); 31 | out << std::endl; 32 | } 33 | 34 | Output::Output(int count) 35 | { 36 | m_count = count; 37 | const std::string dir_name = Logger::get_dir_name(); 38 | const std::string file_name = dir_name + "/energy.dat"; 39 | m_out_energy.open(file_name); 40 | m_out_energy << "# time kinetic thermal potential total\n"; 41 | } 42 | 43 | Output::~Output() 44 | { 45 | m_out_energy.close(); 46 | } 47 | 48 | void Output::output_particle(std::shared_ptr sim) 49 | { 50 | const auto & particles = sim->get_particles(); 51 | const int num = sim->get_particle_num(); 52 | const real time = sim->get_time(); 53 | 54 | const std::string dir_name = Logger::get_dir_name(); 55 | const std::string file_name = dir_name + (boost::format("/%05d.dat") % m_count).str(); 56 | std::ofstream out(file_name); 57 | out << "# " << time << std::endl; 58 | 59 | for(int i = 0; i < num; ++i) { 60 | output(particles[i], out); 61 | } 62 | WRITE_LOG << "write " << file_name; 63 | ++m_count; 64 | } 65 | 66 | void Output::output_energy(std::shared_ptr sim) 67 | { 68 | const auto & particles = sim->get_particles(); 69 | const int num = sim->get_particle_num(); 70 | const real time = sim->get_time(); 71 | 72 | real kinetic = 0.0; 73 | real thermal = 0.0; 74 | real potential = 0.0; 75 | 76 | #pragma omp parallel for reduction(+: kinetic, thermal, potential) 77 | for(int i = 0; i < num; ++i) { 78 | const auto & p_i = particles[i]; 79 | kinetic += 0.5 * p_i.mass * abs2(p_i.vel); 80 | thermal += p_i.mass * p_i.ene; 81 | potential += 0.5 * p_i.mass * p_i.phi; 82 | } 83 | 84 | const real e_k = kinetic; 85 | const real e_t = thermal; 86 | const real e_p = potential; 87 | const real total = e_k + e_t + e_p; 88 | 89 | m_out_energy << time << " " << e_k << " " << e_t << " " << e_p << " " << total << std::endl; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/pre_interaction.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "parameters.hpp" 4 | #include "pre_interaction.hpp" 5 | #include "simulation.hpp" 6 | #include "periodic.hpp" 7 | #include "openmp.hpp" 8 | #include "kernel/kernel_function.hpp" 9 | #include "exception.hpp" 10 | #include "bhtree.hpp" 11 | 12 | #ifdef EXHAUSTIVE_SEARCH 13 | #include "exhaustive_search.hpp" 14 | #endif 15 | 16 | namespace sph 17 | { 18 | 19 | void PreInteraction::initialize(std::shared_ptr param) 20 | { 21 | m_use_time_dependent_av = param->av.use_time_dependent_av; 22 | if(m_use_time_dependent_av) { 23 | m_alpha_max = param->av.alpha_max; 24 | m_alpha_min = param->av.alpha_min; 25 | m_epsilon = param->av.epsilon; 26 | } 27 | m_use_balsara_switch = param->av.use_balsara_switch; 28 | m_gamma = param->physics.gamma; 29 | m_neighbor_number = param->physics.neighbor_number; 30 | m_iteration = param->iterative_sml; 31 | if(m_iteration) { 32 | m_kernel_ratio = 1.2; 33 | } else { 34 | m_kernel_ratio = 1.0; 35 | } 36 | m_first = true; 37 | } 38 | 39 | void PreInteraction::calculation(std::shared_ptr sim) 40 | { 41 | if(m_first) { 42 | initial_smoothing(sim); 43 | m_first = false; 44 | } 45 | 46 | auto & particles = sim->get_particles(); 47 | auto * periodic = sim->get_periodic().get(); 48 | const int num = sim->get_particle_num(); 49 | auto * kernel = sim->get_kernel().get(); 50 | const real dt = sim->get_dt(); 51 | auto * tree = sim->get_tree().get(); 52 | 53 | omp_real h_per_v_sig(std::numeric_limits::max()); 54 | 55 | #pragma omp parallel for 56 | for(int i = 0; i < num; ++i) { 57 | auto & p_i = particles[i]; 58 | std::vector neighbor_list(m_neighbor_number * neighbor_list_size); 59 | 60 | // guess smoothing length 61 | constexpr real A = DIM == 1 ? 2.0 : 62 | DIM == 2 ? M_PI : 63 | 4.0 * M_PI / 3.0; 64 | p_i.sml = std::pow(m_neighbor_number * p_i.mass / (p_i.dens * A), 1.0 / DIM) * m_kernel_ratio; 65 | 66 | // neighbor search 67 | #ifdef EXHAUSTIVE_SEARCH 68 | const int n_neighbor_tmp = exhaustive_search(p_i, p_i.sml, particles, num, neighbor_list, m_neighbor_number * neighbor_list_size, periodic, false); 69 | #else 70 | const int n_neighbor_tmp = tree->neighbor_search(p_i, neighbor_list, particles, false); 71 | #endif 72 | // smoothing length 73 | if(m_iteration) { 74 | p_i.sml = newton_raphson(p_i, particles, neighbor_list, n_neighbor_tmp, periodic, kernel); 75 | } 76 | 77 | // density etc. 78 | real dens_i = 0.0; 79 | real dh_dens_i = 0.0; 80 | real v_sig_max = p_i.sound * 2.0; 81 | const vec_t & pos_i = p_i.pos; 82 | int n_neighbor = 0; 83 | for(int n = 0; n < n_neighbor_tmp; ++n) { 84 | int const j = neighbor_list[n]; 85 | auto & p_j = particles[j]; 86 | const vec_t r_ij = periodic->calc_r_ij(pos_i, p_j.pos); 87 | const real r = std::abs(r_ij); 88 | 89 | if(r >= p_i.sml) { 90 | break; 91 | } 92 | 93 | ++n_neighbor; 94 | dens_i += p_j.mass * kernel->w(r, p_i.sml); 95 | dh_dens_i += p_j.mass * kernel->dhw(r, p_i.sml); 96 | 97 | if(i != j) { 98 | const real v_sig = p_i.sound + p_j.sound - 3.0 * inner_product(r_ij, p_i.vel - p_j.vel) / r; 99 | if(v_sig > v_sig_max) { 100 | v_sig_max = v_sig; 101 | } 102 | } 103 | } 104 | 105 | p_i.dens = dens_i; 106 | p_i.pres = (m_gamma - 1.0) * dens_i * p_i.ene; 107 | p_i.gradh = 1.0 / (1.0 + p_i.sml / (DIM * dens_i) * dh_dens_i); 108 | p_i.neighbor = n_neighbor; 109 | 110 | const real h_per_v_sig_i = p_i.sml / v_sig_max; 111 | if(h_per_v_sig.get() > h_per_v_sig_i) { 112 | h_per_v_sig.get() = h_per_v_sig_i; 113 | } 114 | 115 | // Artificial viscosity 116 | if(m_use_balsara_switch && DIM != 1) { 117 | #if DIM != 1 118 | // balsara switch 119 | real div_v = 0.0; 120 | #if DIM == 2 121 | real rot_v = 0.0; 122 | #else 123 | vec_t rot_v = 0.0; 124 | #endif 125 | for(int n = 0; n < n_neighbor; ++n) { 126 | int const j = neighbor_list[n]; 127 | auto & p_j = particles[j]; 128 | const vec_t r_ij = periodic->calc_r_ij(pos_i, p_j.pos); 129 | const real r = std::abs(r_ij); 130 | const vec_t dw = kernel->dw(r_ij, r, p_i.sml); 131 | const vec_t v_ij = p_i.vel - p_j.vel; 132 | div_v -= p_j.mass * inner_product(v_ij, dw); 133 | rot_v += vector_product(v_ij, dw) * p_j.mass; 134 | } 135 | div_v /= p_i.dens; 136 | rot_v /= p_i.dens; 137 | p_i.balsara = std::abs(div_v) / (std::abs(div_v) + std::abs(rot_v) + 1e-4 * p_i.sound / p_i.sml); 138 | 139 | // time dependent alpha 140 | if(m_use_time_dependent_av) { 141 | const real tau_inv = m_epsilon * p_i.sound / p_i.sml; 142 | const real dalpha = (-(p_i.alpha - m_alpha_min) * tau_inv + std::max(-div_v, (real)0.0) * (m_alpha_max - p_i.alpha)) * dt; 143 | p_i.alpha += dalpha; 144 | } 145 | #endif 146 | } else if(m_use_time_dependent_av) { 147 | real div_v = 0.0; 148 | for(int n = 0; n < n_neighbor; ++n) { 149 | int const j = neighbor_list[n]; 150 | auto & p_j = particles[j]; 151 | const vec_t r_ij = periodic->calc_r_ij(pos_i, p_j.pos); 152 | const real r = std::abs(r_ij); 153 | const vec_t dw = kernel->dw(r_ij, r, p_i.sml); 154 | const vec_t v_ij = p_i.vel - p_j.vel; 155 | div_v -= p_j.mass * inner_product(v_ij, dw); 156 | } 157 | div_v /= p_i.dens; 158 | const real tau_inv = m_epsilon * p_i.sound / p_i.sml; 159 | const real s_i = std::max(-div_v, (real)0.0); 160 | p_i.alpha = (p_i.alpha + dt * tau_inv * m_alpha_min + s_i * dt * m_alpha_max) / (1.0 + dt * tau_inv + s_i * dt); 161 | } 162 | } 163 | 164 | sim->set_h_per_v_sig(h_per_v_sig.min()); 165 | 166 | #ifndef EXHAUSTIVE_SEARCH 167 | tree->set_kernel(); 168 | #endif 169 | } 170 | 171 | void PreInteraction::initial_smoothing(std::shared_ptr sim) 172 | { 173 | auto & particles = sim->get_particles(); 174 | auto * periodic = sim->get_periodic().get(); 175 | const int num = sim->get_particle_num(); 176 | auto * kernel = sim->get_kernel().get(); 177 | auto * tree = sim->get_tree().get(); 178 | 179 | #pragma omp parallel for 180 | for(int i = 0; i < num; ++i) { 181 | auto & p_i = particles[i]; 182 | const vec_t & pos_i = p_i.pos; 183 | std::vector neighbor_list(m_neighbor_number * neighbor_list_size); 184 | 185 | // guess smoothing length 186 | constexpr real A = DIM == 1 ? 2.0 : 187 | DIM == 2 ? M_PI : 188 | 4.0 * M_PI / 3.0; 189 | p_i.sml = std::pow(m_neighbor_number * p_i.mass / (p_i.dens * A), 1.0 / DIM); 190 | 191 | // neighbor search 192 | #ifdef EXHAUSTIVE_SEARCH 193 | int const n_neighbor = exhaustive_search(p_i, p_i.sml, particles, num, neighbor_list, m_neighbor_number * neighbor_list_size, periodic, false); 194 | #else 195 | int const n_neighbor = tree->neighbor_search(p_i, neighbor_list, particles, false); 196 | #endif 197 | 198 | // density 199 | real dens_i = 0.0; 200 | for(int n = 0; n < n_neighbor; ++n) { 201 | int const j = neighbor_list[n]; 202 | auto & p_j = particles[j]; 203 | const vec_t r_ij = periodic->calc_r_ij(pos_i, p_j.pos); 204 | const real r = std::abs(r_ij); 205 | 206 | if(r >= p_i.sml) { 207 | break; 208 | } 209 | 210 | dens_i += p_j.mass * kernel->w(r, p_i.sml); 211 | } 212 | 213 | p_i.dens = dens_i; 214 | } 215 | } 216 | 217 | inline real powh_(const real h) { 218 | #if DIM == 1 219 | return 1; 220 | #elif DIM == 2 221 | return h; 222 | #elif DIM == 3 223 | return h * h; 224 | #endif 225 | } 226 | 227 | real PreInteraction::newton_raphson( 228 | const SPHParticle & p_i, 229 | const std::vector & particles, 230 | const std::vector & neighbor_list, 231 | const int n_neighbor, 232 | const Periodic * periodic, 233 | const KernelFunction * kernel 234 | ) 235 | { 236 | real h_i = p_i.sml / m_kernel_ratio; 237 | constexpr real A = DIM == 1 ? 2.0 : 238 | DIM == 2 ? M_PI : 239 | 4.0 * M_PI / 3.0; 240 | const real b = p_i.mass * m_neighbor_number / A; 241 | 242 | // f = rho h^d - b 243 | // f' = drho/dh h^d + d rho h^{d-1} 244 | 245 | constexpr real epsilon = 1e-4; 246 | constexpr int max_iter = 10; 247 | const auto & r_i = p_i.pos; 248 | for(int i = 0; i < max_iter; ++i) { 249 | const real h_b = h_i; 250 | 251 | real dens = 0.0; 252 | real ddens = 0.0; 253 | for(int n = 0; n < n_neighbor; ++n) { 254 | int const j = neighbor_list[n]; 255 | auto & p_j = particles[j]; 256 | const vec_t r_ij = periodic->calc_r_ij(r_i, p_j.pos); 257 | const real r = std::abs(r_ij); 258 | 259 | if(r >= h_i) { 260 | break; 261 | } 262 | 263 | dens += p_j.mass * kernel->w(r, h_i); 264 | ddens += p_j.mass * kernel->dhw(r, h_i); 265 | } 266 | 267 | const real f = dens * powh(h_i) - b; 268 | const real df = ddens * powh(h_i) + DIM * dens * powh_(h_i); 269 | 270 | h_i -= f / df; 271 | 272 | if(std::abs(h_i - h_b) < (h_i + h_b) * epsilon) { 273 | return h_i; 274 | } 275 | } 276 | 277 | #pragma omp critical 278 | { 279 | WRITE_LOG << "Particle id " << p_i.id << " is not convergence"; 280 | } 281 | 282 | return p_i.sml / m_kernel_ratio; 283 | } 284 | 285 | } 286 | -------------------------------------------------------------------------------- /src/sample/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(sph 2 | PRIVATE 3 | evrard.cpp 4 | gresho_chan_vortex.cpp 5 | hydrostatic.cpp 6 | khi.cpp 7 | pairing_instability.cpp 8 | shock_tube.cpp 9 | ) -------------------------------------------------------------------------------- /src/sample/evrard.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "solver.hpp" 4 | #include "simulation.hpp" 5 | #include "particle.hpp" 6 | #include "exception.hpp" 7 | #include "parameters.hpp" 8 | 9 | namespace sph 10 | { 11 | 12 | // Evrard collapse (Evrard 1988) 13 | void Solver::make_evrard() 14 | { 15 | #if DIM != 3 16 | THROW_ERROR("DIM != 3"); 17 | #else 18 | 19 | const int N = boost::any_cast(m_sample_parameters["N"]); 20 | auto & p = m_sim->get_particles(); 21 | const real dx = 2.0 / N; 22 | 23 | for(int i = 0; i < N; ++i) { 24 | for(int j = 0; j < N; ++j) { 25 | for(int k = 0; k < N; ++k) { 26 | vec_t r = { 27 | (i + 0.5) * dx - 1.0, 28 | (j + 0.5) * dx - 1.0, 29 | (k + 0.5) * dx - 1.0 30 | }; 31 | const real r_0 = std::abs(r); 32 | if(r_0 > 1.0) { 33 | continue; 34 | } 35 | 36 | if(r_0 > 0.0) { 37 | const real r_abs = std::pow(r_0, 1.5); 38 | r *= r_abs / r_0; 39 | } 40 | 41 | SPHParticle p_i; 42 | p_i.pos = r; 43 | p.emplace_back(p_i); 44 | } 45 | } 46 | } 47 | 48 | const real mass = 1.0 / p.size(); 49 | const real gamma = m_param->physics.gamma; 50 | const real G = m_param->gravity.constant; 51 | const real u = 0.05 * G; 52 | 53 | int i = 0; 54 | for(auto & p_i : p) { 55 | p_i.vel = 0.0; 56 | p_i.vel = 0.0; 57 | p_i.mass = mass; 58 | p_i.dens = 1.0 / (2.0 * M_PI * std::abs(p_i.pos)); 59 | p_i.ene = u; 60 | p_i.pres = (gamma - 1.0) * p_i.dens * u; 61 | p_i.id = i; 62 | i++; 63 | } 64 | 65 | m_sim->set_particles(p); 66 | m_sim->set_particle_num(p.size()); 67 | #endif 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/sample/gresho_chan_vortex.cpp: -------------------------------------------------------------------------------- 1 | #include "solver.hpp" 2 | #include "simulation.hpp" 3 | #include "particle.hpp" 4 | #include "exception.hpp" 5 | #include "parameters.hpp" 6 | 7 | namespace sph 8 | { 9 | 10 | // Gresho-Chan vortex (Gresho & Chan 1990) 11 | 12 | real vortex_velocity(const real r) 13 | { 14 | if(r < 0.2) { 15 | return 5.0 * r; 16 | } else if(r < 0.4) { 17 | return 2.0 - 5.0 * r; 18 | } else { 19 | return 0.0; 20 | } 21 | } 22 | 23 | real vortex_pressure(const real r) 24 | { 25 | if(r < 0.2) { 26 | return 5.0 + 12.5 * r * r; 27 | } else if(r < 0.4) { 28 | return 9.0 + 12.5 * r * r - 20.0 * r + 4.0 * std::log(5.0 * r); 29 | } else { 30 | return 3.0 + 4.0 * std::log(2.0); 31 | } 32 | } 33 | 34 | void Solver::make_gresho_chan_vortex() 35 | { 36 | #if DIM != 2 37 | THROW_ERROR("DIM != 2"); 38 | #else 39 | const int N = boost::any_cast(m_sample_parameters["N"]); 40 | const real dx = 1.0 / N; 41 | 42 | const int num = N * N; 43 | std::vector p(num); 44 | 45 | real x = -0.5 + dx * 0.5; 46 | real y = -0.5 + dx * 0.5; 47 | const real mass = 1.0 / num; 48 | const real gamma = m_param->physics.gamma; 49 | 50 | for(int i = 0; i < num; ++i) { 51 | auto & p_i = p[i]; 52 | 53 | p_i.pos[0] = x; 54 | p_i.pos[1] = y; 55 | const real r = std::abs(p_i.pos); 56 | const real vel = vortex_velocity(r); 57 | vec_t dir(-y, x); 58 | dir /= r; 59 | p_i.vel = dir * vel; 60 | p_i.dens = 1.0; 61 | p_i.pres = vortex_pressure(r); 62 | p_i.mass = mass; 63 | p_i.ene = p_i.pres / ((gamma - 1.0) * p_i.dens); 64 | p_i.id = i; 65 | 66 | x += dx; 67 | if(x > 0.5) { 68 | x = -0.5 + dx * 0.5; 69 | y += dx; 70 | } 71 | } 72 | 73 | m_sim->set_particles(p); 74 | m_sim->set_particle_num(p.size()); 75 | #endif 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/sample/hydrostatic.cpp: -------------------------------------------------------------------------------- 1 | #include "solver.hpp" 2 | #include "simulation.hpp" 3 | #include "particle.hpp" 4 | #include "exception.hpp" 5 | #include "parameters.hpp" 6 | 7 | namespace sph 8 | { 9 | 10 | // Hydrostatic Equilibrium test (Saitoh & Makino 2013) 11 | 12 | void Solver::make_hydrostatic() 13 | { 14 | #if DIM != 2 15 | THROW_ERROR("DIM != 2"); 16 | #else 17 | 18 | const int N = boost::any_cast(m_sample_parameters["N"]); 19 | const real dx1 = 0.5 / N; 20 | const real dx2 = dx1 * 2.0; 21 | const real mass = 1.0 / (N * N); 22 | const real gamma = m_param->physics.gamma; 23 | int i = 0; 24 | 25 | std::vector p; 26 | 27 | // dens region 28 | real x = -0.25 + dx1 * 0.5; 29 | real y = -0.25 + dx1 * 0.5; 30 | while(y < 0.25) { 31 | SPHParticle p_i; 32 | p_i.pos[0] = x; 33 | p_i.pos[1] = y; 34 | p_i.mass = mass; 35 | p_i.dens = 4.0; 36 | p_i.pres = 2.5; 37 | p_i.ene = p_i.pres / ((gamma - 1.0) * p_i.dens); 38 | p_i.id = i; 39 | p.push_back(p_i); 40 | 41 | x += dx1; 42 | if(x > 0.25) { 43 | x = -0.25 + dx1 * 0.5; 44 | y += dx1; 45 | } 46 | ++i; 47 | } 48 | 49 | // ambient 50 | x = -0.5 + dx2 * 0.5; 51 | y = -0.5 + dx2 * 0.5; 52 | while(y < 0.5) { 53 | SPHParticle p_i; 54 | p_i.pos[0] = x; 55 | p_i.pos[1] = y; 56 | p_i.mass = mass; 57 | p_i.dens = 1.0; 58 | p_i.pres = 2.5; 59 | p_i.ene = p_i.pres / ((gamma - 1.0) * p_i.dens); 60 | p_i.id = i; 61 | p.push_back(p_i); 62 | 63 | do { 64 | x += dx2; 65 | if(x > 0.5) { 66 | x = -0.5 + dx2 * 0.5; 67 | y += dx2; 68 | } 69 | } while(x > -0.25 && x < 0.25 && y > -0.25 && y < 0.25); 70 | ++i; 71 | } 72 | 73 | m_sim->set_particles(p); 74 | m_sim->set_particle_num(p.size()); 75 | #endif 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/sample/khi.cpp: -------------------------------------------------------------------------------- 1 | #include "solver.hpp" 2 | #include "simulation.hpp" 3 | #include "particle.hpp" 4 | #include "exception.hpp" 5 | #include "parameters.hpp" 6 | 7 | namespace sph 8 | { 9 | 10 | // Kelvin-Helmholtz instability (Springel 2010) 11 | 12 | void Solver::make_khi() 13 | { 14 | #if DIM != 2 15 | THROW_ERROR("DIM != 2"); 16 | #else 17 | 18 | const int N = boost::any_cast(m_sample_parameters["N"]); 19 | const int num = N * N * 3 / 4; 20 | const real dx = 1.0 / N; 21 | const real mass = 1.5 / num; 22 | const real gamma = m_param->physics.gamma; 23 | 24 | std::vector p(num); 25 | 26 | // dens region 27 | real x = dx * 0.5; 28 | real y = dx * 0.5; 29 | 30 | int region = 1; 31 | bool odd = true; 32 | 33 | auto vy = [](const real x, const real y) { 34 | constexpr real sigma2_inv = 2 / (0.05 * 0.05); 35 | return 0.1 * std::sin(4.0 * M_PI * x) * (std::exp(-sqr(y - 0.25) * 0.5 * sigma2_inv) + std::exp(-sqr(y - 0.75) * 0.5 * sigma2_inv)); 36 | }; 37 | 38 | for(int i = 0; i < num; ++i) { 39 | auto & p_i = p[i]; 40 | p_i.pos[0] = x; 41 | p_i.pos[1] = y; 42 | p_i.vel[0] = region == 1 ? -0.5 : 0.5; 43 | p_i.vel[1] = vy(x, y); 44 | p_i.mass = mass; 45 | p_i.dens = static_cast(region); 46 | p_i.pres = 2.5; 47 | p_i.ene = p_i.pres / ((gamma - 1.0) * p_i.dens); 48 | p_i.id = i; 49 | 50 | x += region == 1 ? 2.0 * dx : dx; 51 | 52 | if(x > 1.0) { 53 | y += dx; 54 | 55 | if(y > 0.25 && y < 0.75) { 56 | region = 2; 57 | } else { 58 | region = 1; 59 | } 60 | 61 | if(region == 1) { 62 | if(odd) { 63 | odd = false; 64 | x = dx * 1.5; 65 | } else { 66 | odd = true; 67 | x = dx * 0.5; 68 | } 69 | } else { 70 | x = dx * 0.5; 71 | } 72 | } 73 | } 74 | 75 | m_sim->set_particles(p); 76 | m_sim->set_particle_num(p.size()); 77 | #endif 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/sample/pairing_instability.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "solver.hpp" 4 | #include "simulation.hpp" 5 | #include "particle.hpp" 6 | #include "exception.hpp" 7 | #include "parameters.hpp" 8 | 9 | namespace sph 10 | { 11 | 12 | // Pairing instability test 13 | 14 | void Solver::make_pairing_instability() 15 | { 16 | #if DIM != 2 17 | THROW_ERROR("DIM != 2"); 18 | #else 19 | const int N = boost::any_cast(m_sample_parameters["N"]); 20 | const real dx = 1.0 / N; 21 | 22 | const int num = N * N; 23 | std::vector p(num); 24 | 25 | real x = -0.5 + dx * 0.5; 26 | real y = -0.5 + dx * 0.5; 27 | const real mass = 1.0 / num; 28 | const real gamma = m_param->physics.gamma; 29 | 30 | std::mt19937 engine(1); 31 | std::uniform_real_distribution dist(-dx * 0.05, dx * 0.05); 32 | 33 | for(int i = 0; i < num; ++i) { 34 | auto & p_i = p[i]; 35 | 36 | p_i.pos[0] = x + dist(engine); 37 | p_i.pos[1] = y + dist(engine); 38 | p_i.vel = 0.0; 39 | p_i.dens = 1.0; 40 | p_i.pres = 1.0; 41 | p_i.mass = mass; 42 | p_i.ene = p_i.pres / ((gamma - 1.0) * p_i.dens); 43 | p_i.id = i; 44 | 45 | x += dx; 46 | if(x > 0.5) { 47 | x = -0.5 + dx * 0.5; 48 | y += dx; 49 | } 50 | } 51 | 52 | m_sim->set_particles(p); 53 | m_sim->set_particle_num(p.size()); 54 | #endif 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/sample/shock_tube.cpp: -------------------------------------------------------------------------------- 1 | #include "solver.hpp" 2 | #include "simulation.hpp" 3 | #include "particle.hpp" 4 | #include "exception.hpp" 5 | #include "parameters.hpp" 6 | 7 | namespace sph 8 | { 9 | 10 | // shock tube test (e.g. Hernquist & Katz 1989) 11 | 12 | void Solver::make_shock_tube() 13 | { 14 | #if DIM != 1 15 | THROW_ERROR("DIM != 1"); 16 | #else 17 | 18 | const int N = boost::any_cast(m_sample_parameters["N"]); 19 | const real dx_r = 0.5 / N; 20 | const real dx_l = dx_r * 0.25; 21 | 22 | const int num = N * 10; 23 | std::vector p(num); 24 | 25 | real x = -0.5 + dx_l * 0.5; 26 | real dx = dx_l; 27 | real dens = 1.0; 28 | real pres = 1.0; 29 | const real mass = 0.5 / N * 0.25; 30 | const real gamma = m_param->physics.gamma; 31 | bool left = true; 32 | 33 | for(int i = 0; i < num; ++i) { 34 | auto & p_i = p[i]; 35 | p_i.pos[0] = x; 36 | p_i.vel[0] = 0.0; 37 | p_i.dens = dens; 38 | p_i.pres = pres; 39 | p_i.mass = mass; 40 | p_i.ene = p_i.pres / ((gamma - 1.0) * p_i.dens); 41 | p_i.id = i; 42 | 43 | x += dx; 44 | if(x > 0.5 && left) { 45 | x = 0.5 + dx_r * 0.5; 46 | dx = dx_r; 47 | dens = 0.25; 48 | pres = 0.1795; 49 | left = false; 50 | } 51 | } 52 | 53 | m_sim->set_particles(p); 54 | m_sim->set_particle_num(p.size()); 55 | #endif 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/simulation.cpp: -------------------------------------------------------------------------------- 1 | #include "parameters.hpp" 2 | #include "simulation.hpp" 3 | #include "exception.hpp" 4 | #include "periodic.hpp" 5 | #include "bhtree.hpp" 6 | #include "kernel/cubic_spline.hpp" 7 | #include "kernel/wendland_kernel.hpp" 8 | 9 | namespace sph 10 | { 11 | 12 | Simulation::Simulation(std::shared_ptr param) 13 | { 14 | if(param->kernel == KernelType::CUBIC_SPLINE) { 15 | m_kernel = std::make_shared(); 16 | } else if(param->kernel == KernelType::WENDLAND) { 17 | m_kernel = std::make_shared(); 18 | } else { 19 | THROW_ERROR("kernel is unknown."); 20 | } 21 | 22 | m_periodic = std::make_shared(); 23 | m_periodic->initialize(param); 24 | 25 | m_tree = std::make_shared(); 26 | m_tree->initialize(param); 27 | 28 | m_time = param->time.start; 29 | m_dt = 0.0; 30 | } 31 | 32 | void Simulation::update_time() 33 | { 34 | m_time += m_dt; 35 | } 36 | 37 | void Simulation::make_tree() 38 | { 39 | m_tree->make(m_particles, m_particle_num); 40 | } 41 | 42 | void Simulation::add_scalar_array(const std::vector & names) 43 | { 44 | const int num = m_particle_num; 45 | for(const auto & name : names) { 46 | additional_scalar_array[name].resize(num); 47 | } 48 | } 49 | 50 | void Simulation::add_vector_array(const std::vector & names) 51 | { 52 | const int num = m_particle_num; 53 | for(const auto & name : names) { 54 | additional_vector_array[name].resize(num); 55 | } 56 | } 57 | 58 | std::vector & Simulation::get_scalar_array(const std::string & name) 59 | { 60 | auto it = additional_scalar_array.find(name); 61 | if(it != additional_scalar_array.end()) { 62 | return it->second; 63 | } else { 64 | THROW_ERROR("additional_scalar_array does not have ", name); 65 | } 66 | } 67 | 68 | std::vector & Simulation::get_vector_array(const std::string & name) 69 | { 70 | auto it = additional_vector_array.find(name); 71 | if(it != additional_vector_array.end()) { 72 | return it->second; 73 | } else { 74 | THROW_ERROR("additional_vector_array does not have ", name); 75 | } 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /src/solver.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "solver.hpp" 7 | #include "parameters.hpp" 8 | #include "particle.hpp" 9 | #include "logger.hpp" 10 | #include "exception.hpp" 11 | #include "output.hpp" 12 | #include "simulation.hpp" 13 | #include "periodic.hpp" 14 | #include "bhtree.hpp" 15 | 16 | // modules 17 | #include "timestep.hpp" 18 | #include "pre_interaction.hpp" 19 | #include "fluid_force.hpp" 20 | #include "gravity_force.hpp" 21 | #include "disph/d_pre_interaction.hpp" 22 | #include "disph/d_fluid_force.hpp" 23 | #include "gsph/g_pre_interaction.hpp" 24 | #include "gsph/g_fluid_force.hpp" 25 | 26 | #ifdef _OPENMP 27 | #include 28 | #endif 29 | 30 | namespace sph 31 | { 32 | 33 | Solver::Solver(int argc, char * argv[]) 34 | { 35 | std::cout << "--------------SPH simulation-------------\n\n"; 36 | if(argc == 1) { 37 | std::cerr << "how to use\n" << std::endl; 38 | std::cerr << "sph " << std::endl; 39 | std::exit(EXIT_FAILURE); 40 | } else { 41 | read_parameterfile(argv[1]); 42 | } 43 | 44 | Logger::open(m_output_dir); 45 | 46 | #ifdef _OPENMP 47 | WRITE_LOG << "Open MP is valid."; 48 | int num_threads; 49 | if(argc == 3) { 50 | num_threads = std::atoi(argv[2]); 51 | omp_set_num_threads(num_threads); 52 | } else { 53 | num_threads = omp_get_max_threads(); 54 | } 55 | WRITE_LOG << "the number of threads = " << num_threads << "\n"; 56 | #else 57 | WRITE_LOG << "OpenMP is invalid.\n"; 58 | #endif 59 | WRITE_LOG << "parameters"; 60 | 61 | WRITE_LOG << "output directory = " << m_output_dir; 62 | 63 | WRITE_LOG << "time"; 64 | WRITE_LOG << "* start time = " << m_param->time.start; 65 | WRITE_LOG << "* end time = " << m_param->time.end; 66 | WRITE_LOG << "* output time = " << m_param->time.output; 67 | WRITE_LOG << "* enerty output time = " << m_param->time.energy; 68 | 69 | switch(m_param->type) { 70 | case SPHType::SSPH: 71 | WRITE_LOG << "SPH type: Standard SPH"; 72 | break; 73 | case SPHType::DISPH: 74 | WRITE_LOG << "SPH type: Density Independent SPH"; 75 | break; 76 | case SPHType::GSPH: 77 | if(m_param->gsph.is_2nd_order) { 78 | WRITE_LOG << "SPH type: Godunov SPH (2nd order)"; 79 | } else { 80 | WRITE_LOG << "SPH type: Godunov SPH (1st order)"; 81 | } 82 | break; 83 | } 84 | 85 | WRITE_LOG << "CFL condition"; 86 | WRITE_LOG << "* sound speed = " << m_param->cfl.sound; 87 | WRITE_LOG << "* force = " << m_param->cfl.force; 88 | 89 | WRITE_LOG << "Artificial Viscosity"; 90 | WRITE_LOG << "* alpha = " << m_param->av.alpha; 91 | if(m_param->av.use_balsara_switch) { 92 | WRITE_LOG << "* use Balsara switch"; 93 | } 94 | if(m_param->av.use_time_dependent_av) { 95 | WRITE_LOG << "* use time dependent AV"; 96 | WRITE_LOG << "* alpha max = " << m_param->av.alpha_max; 97 | WRITE_LOG << "* alpha min = " << m_param->av.alpha_min; 98 | WRITE_LOG << "* epsilon = " << m_param->av.epsilon; 99 | } 100 | 101 | if(m_param->ac.is_valid) { 102 | WRITE_LOG << "Artificial Conductivity"; 103 | WRITE_LOG << "* alpha = " << m_param->ac.alpha; 104 | } 105 | 106 | WRITE_LOG << "Tree"; 107 | WRITE_LOG << "* max tree level = " << m_param->tree.max_level; 108 | WRITE_LOG << "* leaf particle number = " << m_param->tree.leaf_particle_num; 109 | 110 | WRITE_LOG << "Physics"; 111 | WRITE_LOG << "* Neighbor number = " << m_param->physics.neighbor_number; 112 | WRITE_LOG << "* gamma = " << m_param->physics.gamma; 113 | 114 | WRITE_LOG << "Kernel"; 115 | if(m_param->kernel == KernelType::CUBIC_SPLINE) { 116 | WRITE_LOG << "* Cubic Spline"; 117 | } else if(m_param->kernel == KernelType::WENDLAND) { 118 | WRITE_LOG << "* Wendland"; 119 | } else { 120 | THROW_ERROR("kernel is unknown."); 121 | } 122 | 123 | if(m_param->iterative_sml) { 124 | WRITE_LOG << "Iterative calculation for smoothing length is valid."; 125 | } 126 | 127 | if(m_param->periodic.is_valid) { 128 | WRITE_LOG << "Periodic boundary condition is valid."; 129 | } 130 | 131 | if(m_param->gravity.is_valid) { 132 | WRITE_LOG << "Gravity is valid."; 133 | WRITE_LOG << "G = " << m_param->gravity.constant; 134 | WRITE_LOG << "theta = " << m_param->gravity.theta; 135 | } 136 | 137 | switch(m_sample) { 138 | #define WRITE_SAMPLE(a, b) case a: WRITE_LOG << "Sample: " b " test"; break 139 | WRITE_SAMPLE(Sample::ShockTube, "shock tube"); 140 | WRITE_SAMPLE(Sample::GreshoChanVortex, "Gresho-Chan vortex"); 141 | WRITE_SAMPLE(Sample::PairingInstability, "Pairing Instability"); 142 | WRITE_SAMPLE(Sample::HydroStatic, "Hydro static"); 143 | WRITE_SAMPLE(Sample::KHI, "Kelvin-Helmholtz Instability"); 144 | WRITE_SAMPLE(Sample::Evrard, "Evrard collapse"); 145 | #undef WRITE_SAMPLE 146 | default: 147 | break; 148 | } 149 | 150 | WRITE_LOG; 151 | 152 | m_output = std::make_shared(); 153 | } 154 | 155 | void Solver::read_parameterfile(const char * filename) 156 | { 157 | namespace pt = boost::property_tree; 158 | 159 | m_param = std::make_shared(); 160 | 161 | pt::ptree input; 162 | 163 | std::string name_str = filename; 164 | if(name_str == "shock_tube") { 165 | pt::read_json("sample/shock_tube/shock_tube.json", input); 166 | m_sample = Sample::ShockTube; 167 | m_sample_parameters["N"] = input.get("N", 100); 168 | } else if(name_str == "gresho_chan_vortex") { 169 | pt::read_json("sample/gresho_chan_vortex/gresho_chan_vortex.json", input); 170 | m_sample = Sample::GreshoChanVortex; 171 | m_sample_parameters["N"] = input.get("N", 64); 172 | } else if(name_str == "pairing_instability") { 173 | pt::read_json("sample/pairing_instability/pairing_instability.json", input); 174 | m_sample = Sample::PairingInstability; 175 | m_sample_parameters["N"] = input.get("N", 64); 176 | } else if(name_str == "hydrostatic") { 177 | pt::read_json("sample/hydrostatic/hydrostatic.json", input); 178 | m_sample = Sample::HydroStatic; 179 | m_sample_parameters["N"] = input.get("N", 32); 180 | } else if(name_str == "khi") { 181 | pt::read_json("sample/khi/khi.json", input); 182 | m_sample = Sample::KHI; 183 | m_sample_parameters["N"] = input.get("N", 128); 184 | } else if(name_str == "evrard") { 185 | pt::read_json("sample/evrard/evrard.json", input); 186 | m_sample = Sample::Evrard; 187 | m_sample_parameters["N"] = input.get("N", 20); 188 | } else { 189 | pt::read_json(filename, input); 190 | m_sample = Sample::DoNotUse; 191 | } 192 | 193 | m_output_dir = input.get("outputDirectory"); 194 | 195 | // time 196 | m_param->time.start = input.get("startTime", real(0)); 197 | m_param->time.end = input.get("endTime"); 198 | if(m_param->time.end < m_param->time.start) { 199 | THROW_ERROR("endTime < startTime"); 200 | } 201 | m_param->time.output = input.get("outputTime", (m_param->time.end - m_param->time.start) / 100); 202 | m_param->time.energy = input.get("energyTime", m_param->time.output); 203 | 204 | // type 205 | std::string sph_type = input.get("SPHType", "ssph"); 206 | if(sph_type == "ssph") { 207 | m_param->type = SPHType::SSPH; 208 | } else if(sph_type == "disph") { 209 | m_param->type = SPHType::DISPH; 210 | } else if(sph_type == "gsph") { 211 | m_param->type = SPHType::GSPH; 212 | } else { 213 | THROW_ERROR("Unknown SPH type"); 214 | } 215 | 216 | // CFL 217 | m_param->cfl.sound = input.get("cflSound", 0.3); 218 | m_param->cfl.force = input.get("cflForce", 0.125); 219 | 220 | // Artificial Viscosity 221 | m_param->av.alpha = input.get("avAlpha", 1.0); 222 | m_param->av.use_balsara_switch = input.get("useBalsaraSwitch", true); 223 | m_param->av.use_time_dependent_av = input.get("useTimeDependentAV", false); 224 | if(m_param->av.use_time_dependent_av) { 225 | m_param->av.alpha_max = input.get("alphaMax", 2.0); 226 | m_param->av.alpha_min = input.get("alphaMin", 0.1); 227 | if(m_param->av.alpha_max < m_param->av.alpha_min) { 228 | THROW_ERROR("alphaMax < alphaMin"); 229 | } 230 | m_param->av.epsilon = input.get("epsilonAV", 0.2); 231 | } 232 | 233 | // Artificial Conductivity 234 | m_param->ac.is_valid = input.get("useArtificialConductivity", false); 235 | if(m_param->ac.is_valid) { 236 | m_param->ac.alpha = input.get("alphaAC", 1.0); 237 | } 238 | 239 | // Tree 240 | m_param->tree.max_level = input.get("maxTreeLevel", 20); 241 | m_param->tree.leaf_particle_num = input.get("leafParticleNumber", 1); 242 | 243 | // Physics 244 | m_param->physics.neighbor_number = input.get("neighborNumber", 32); 245 | m_param->physics.gamma = input.get("gamma"); 246 | 247 | // Kernel 248 | std::string kernel_name = input.get("kernel", "cubic_spline"); 249 | if(kernel_name == "cubic_spline") { 250 | m_param->kernel = KernelType::CUBIC_SPLINE; 251 | } else if(kernel_name == "wendland") { 252 | m_param->kernel = KernelType::WENDLAND; 253 | } else { 254 | THROW_ERROR("kernel is unknown."); 255 | } 256 | 257 | // smoothing length 258 | m_param->iterative_sml = input.get("iterativeSmoothingLength", true); 259 | 260 | // periodic 261 | m_param->periodic.is_valid = input.get("periodic", false); 262 | if(m_param->periodic.is_valid) { 263 | { 264 | auto & range_max = input.get_child("rangeMax"); 265 | if(range_max.size() != DIM) { 266 | THROW_ERROR("rangeMax != DIM"); 267 | } 268 | int i = 0; 269 | for(auto & v : range_max) { 270 | m_param->periodic.range_max[i] = std::stod(v.second.data()); 271 | ++i; 272 | } 273 | } 274 | 275 | { 276 | auto & range_min = input.get_child("rangeMin"); 277 | if(range_min.size() != DIM) { 278 | THROW_ERROR("rangeMax != DIM"); 279 | } 280 | int i = 0; 281 | for(auto & v : range_min) { 282 | m_param->periodic.range_min[i] = std::stod(v.second.data()); 283 | ++i; 284 | } 285 | } 286 | } 287 | 288 | // gravity 289 | m_param->gravity.is_valid = input.get("useGravity", false); 290 | if(m_param->gravity.is_valid) { 291 | m_param->gravity.constant = input.get("G", 1.0); 292 | m_param->gravity.theta = input.get("theta", 0.5); 293 | } 294 | 295 | // GSPH 296 | if(m_param->type == SPHType::GSPH) { 297 | m_param->gsph.is_2nd_order = input.get("use2ndOrderGSPH", true); 298 | } 299 | } 300 | 301 | void Solver::run() 302 | { 303 | initialize(); 304 | assert(m_sim->get_particles().size() == m_sim->get_particle_num()); 305 | 306 | const real t_end = m_param->time.end; 307 | real t_out = m_param->time.output; 308 | real t_ene = m_param->time.energy; 309 | 310 | m_output->output_particle(m_sim); 311 | m_output->output_energy(m_sim); 312 | 313 | const auto start = std::chrono::system_clock::now(); 314 | auto t_cout_i = start; 315 | int loop = 0; 316 | 317 | real t = m_sim->get_time(); 318 | while(t < t_end) { 319 | integrate(); 320 | const real dt = m_sim->get_dt(); 321 | const int num = m_sim->get_particle_num(); 322 | ++loop; 323 | 324 | m_sim->update_time(); 325 | t = m_sim->get_time(); 326 | 327 | // 1秒毎に画面出力 328 | const auto t_cout_f = std::chrono::system_clock::now(); 329 | const real t_cout_s = std::chrono::duration_cast(t_cout_f - t_cout_i).count(); 330 | if(t_cout_s >= 1.0) { 331 | WRITE_LOG << "loop: " << loop << ", time: " << t << ", dt: " << dt << ", num: " << num; 332 | t_cout_i = std::chrono::system_clock::now(); 333 | } else { 334 | WRITE_LOG_ONLY << "loop: " << loop << ", time: " << t << ", dt: " << dt << ", num: " << num; 335 | } 336 | 337 | if(t > t_out) { 338 | m_output->output_particle(m_sim); 339 | t_out += m_param->time.output; 340 | } 341 | 342 | if(t > t_ene) { 343 | m_output->output_energy(m_sim); 344 | t_ene += m_param->time.energy; 345 | } 346 | } 347 | const auto end = std::chrono::system_clock::now(); 348 | const real calctime = std::chrono::duration_cast(end - start).count(); 349 | WRITE_LOG << "\ncalculation is finished"; 350 | WRITE_LOG << "calclation time: " << calctime << " ms"; 351 | } 352 | 353 | void Solver::initialize() 354 | { 355 | m_sim = std::make_shared(m_param); 356 | 357 | make_initial_condition(); 358 | 359 | m_timestep = std::make_shared(); 360 | if(m_param->type == SPHType::SSPH) { 361 | m_pre = std::make_shared(); 362 | m_fforce = std::make_shared(); 363 | } else if(m_param->type == SPHType::DISPH) { 364 | m_pre = std::make_shared(); 365 | m_fforce = std::make_shared(); 366 | } else if(m_param->type == SPHType::GSPH) { 367 | m_pre = std::make_shared(); 368 | m_fforce = std::make_shared(); 369 | } 370 | m_gforce = std::make_shared(); 371 | 372 | // GSPH 373 | if(m_param->type == SPHType::GSPH) { 374 | std::vector names; 375 | names.push_back("grad_density"); 376 | names.push_back("grad_pressure"); 377 | names.push_back("grad_velocity_0"); 378 | #if DIM == 2 379 | names.push_back("grad_velocity_1"); 380 | #elif DIM == 3 381 | names.push_back("grad_velocity_1"); 382 | names.push_back("grad_velocity_2"); 383 | #endif 384 | m_sim->add_vector_array(names); 385 | } 386 | 387 | m_timestep->initialize(m_param); 388 | m_pre->initialize(m_param); 389 | m_fforce->initialize(m_param); 390 | m_gforce->initialize(m_param); 391 | 392 | auto & p = m_sim->get_particles(); 393 | const int num = m_sim->get_particle_num(); 394 | const real gamma = m_param->physics.gamma; 395 | const real c_sound = gamma * (gamma - 1.0); 396 | 397 | assert(p.size() == num); 398 | const real alpha = m_param->av.alpha; 399 | #pragma omp parallel for 400 | for(int i = 0; i < num; ++i) { 401 | p[i].alpha = alpha; 402 | p[i].balsara = 1.0; 403 | p[i].sound = std::sqrt(c_sound * p[i].ene); 404 | } 405 | 406 | #ifndef EXHAUSTIVE_SEARCH 407 | auto tree = m_sim->get_tree(); 408 | tree->resize(num); 409 | tree->make(p, num); 410 | #endif 411 | 412 | m_pre->calculation(m_sim); 413 | m_fforce->calculation(m_sim); 414 | m_gforce->calculation(m_sim); 415 | } 416 | 417 | void Solver::integrate() 418 | { 419 | m_timestep->calculation(m_sim); 420 | 421 | predict(); 422 | #ifndef EXHAUSTIVE_SEARCH 423 | m_sim->make_tree(); 424 | #endif 425 | m_pre->calculation(m_sim); 426 | m_fforce->calculation(m_sim); 427 | m_gforce->calculation(m_sim); 428 | correct(); 429 | } 430 | 431 | void Solver::predict() 432 | { 433 | auto & p = m_sim->get_particles(); 434 | const int num = m_sim->get_particle_num(); 435 | auto * periodic = m_sim->get_periodic().get(); 436 | const real dt = m_sim->get_dt(); 437 | const real gamma = m_param->physics.gamma; 438 | const real c_sound = gamma * (gamma - 1.0); 439 | 440 | assert(p.size() == num); 441 | 442 | #pragma omp parallel for 443 | for(int i = 0; i < num; ++i) { 444 | // k -> k+1/2 445 | p[i].vel_p = p[i].vel + p[i].acc * (0.5 * dt); 446 | p[i].ene_p = p[i].ene + p[i].dene * (0.5 * dt); 447 | 448 | // k -> k+1 449 | p[i].pos += p[i].vel_p * dt; 450 | p[i].vel += p[i].acc * dt; 451 | p[i].ene += p[i].dene * dt; 452 | p[i].sound = std::sqrt(c_sound * p[i].ene); 453 | 454 | periodic->apply(p[i].pos); 455 | } 456 | } 457 | 458 | void Solver::correct() 459 | { 460 | auto & p = m_sim->get_particles(); 461 | const int num = m_sim->get_particle_num(); 462 | const real dt = m_sim->get_dt(); 463 | const real gamma = m_param->physics.gamma; 464 | const real c_sound = gamma * (gamma - 1.0); 465 | 466 | assert(p.size() == num); 467 | 468 | #pragma omp parallel for 469 | for(int i = 0; i < num; ++i) { 470 | p[i].vel = p[i].vel_p + p[i].acc * (0.5 * dt); 471 | p[i].ene = p[i].ene_p + p[i].dene * (0.5 * dt); 472 | p[i].sound = std::sqrt(c_sound * p[i].ene); 473 | } 474 | } 475 | 476 | void Solver::make_initial_condition() 477 | { 478 | switch(m_sample) { 479 | #define MAKE_SAMPLE(a, b) case a: make_##b(); break 480 | MAKE_SAMPLE(Sample::ShockTube, shock_tube); 481 | MAKE_SAMPLE(Sample::GreshoChanVortex, gresho_chan_vortex); 482 | MAKE_SAMPLE(Sample::PairingInstability, pairing_instability); 483 | MAKE_SAMPLE(Sample::HydroStatic, hydrostatic); 484 | MAKE_SAMPLE(Sample::KHI, khi); 485 | MAKE_SAMPLE(Sample::Evrard, evrard); 486 | case Sample::DoNotUse: 487 | 488 | // サンプルを使わない場合はここを実装 489 | 490 | break; 491 | default: 492 | THROW_ERROR("unknown sample type."); 493 | #undef MAKE_SAMPLE 494 | } 495 | } 496 | 497 | } -------------------------------------------------------------------------------- /src/timestep.cpp: -------------------------------------------------------------------------------- 1 | #include "parameters.hpp" 2 | #include "timestep.hpp" 3 | #include "particle.hpp" 4 | #include "simulation.hpp" 5 | #include "openmp.hpp" 6 | 7 | #include 8 | 9 | namespace sph 10 | { 11 | 12 | void TimeStep::initialize(std::shared_ptr param) 13 | { 14 | c_sound = param->cfl.sound; 15 | c_force = param->cfl.force; 16 | } 17 | 18 | void TimeStep::calculation(std::shared_ptr sim) 19 | { 20 | auto & particles = sim->get_particles(); 21 | const int num = sim->get_particle_num(); 22 | 23 | omp_real dt_min(std::numeric_limits::max()); 24 | #pragma omp parallel for 25 | for(int i = 0; i < num; ++i) { 26 | const real acc_abs = std::abs(particles[i].acc); 27 | if(acc_abs > 0.0) { 28 | const real dt_force_i = c_force * std::sqrt(particles[i].sml / acc_abs); 29 | if(dt_force_i < dt_min.get()) { 30 | dt_min.get() = dt_force_i; 31 | } 32 | } 33 | } 34 | 35 | const real dt_sound_i = c_sound * sim->get_h_per_v_sig(); 36 | 37 | sim->set_dt(std::min(dt_sound_i, dt_min.min())); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /test/kernel_test/kernel_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "kernel/cubic_spline.hpp" 4 | #include "kernel/wendland_kernel.hpp" 5 | 6 | // 数値微分と比較する 7 | 8 | int main() 9 | { 10 | std::ios_base::sync_with_stdio(false); 11 | 12 | constexpr int n_max = 10000; 13 | 14 | // spline 15 | { 16 | std::cout << "cubic spline" << std::endl; 17 | sph::Spline::Cubic cs; 18 | 19 | std::cout << "compare dw(x)/dx to (w(x + dx/2) - w(x - dx/2)) / dx" << std::endl; 20 | for(int n = 100; n < n_max; n *= 2) { 21 | const real dx = 1.0 / n; 22 | real error = 0.0; 23 | for(real x = dx * 0.5; x < 1.0; x += dx) { 24 | const vec_t r(x); 25 | auto dw1 = cs.dw(r, x, 1.0); 26 | auto dw2 = (cs.w(x + dx * 0.5, 1.0) - cs.w(x - dx * 0.5, 1.0)) / dx; 27 | error += std::abs(dw1[0] - dw2); 28 | } 29 | error /= n; 30 | std::cout << "error (n = " << n << "): " << error << std::endl; 31 | } 32 | 33 | std::cout << "compare dw(h)/dh to (w(h + dh/2) - w(h - dh/2)) / dh" << std::endl; 34 | for(int n = 100; n < n_max; n *= 2) { 35 | const real dx = 1.0 / n; 36 | real error = 0.0; 37 | for(real x = dx * 0.5; x < 1.0; x += dx) { 38 | const vec_t r(x); 39 | auto dw1 = cs.dhw(x, 1.0); 40 | auto dw2 = (cs.w(x, 1.0 + dx * 0.5) - cs.w(x, 1.0 - dx * 0.5)) / dx; 41 | error += std::abs(dw1 - dw2); 42 | } 43 | error /= n; 44 | std::cout << "error (n = " << n << "): " << error << std::endl; 45 | } 46 | } 47 | 48 | std::cout << std::endl; 49 | 50 | // wendland 51 | { 52 | std::cout << "Wendland C4" << std::endl; 53 | sph::Wendland::C4Kernel wl; 54 | 55 | std::cout << "compare dw(x)/dx to (w(x + dx/2) - w(x - dx/2)) / dx" << std::endl; 56 | for(int n = 100; n < n_max; n *= 2) { 57 | const real dx = 1.0 / n; 58 | real error = 0.0; 59 | for(real x = dx * 0.5; x < 1.0; x += dx) { 60 | const vec_t r(x); 61 | auto dw1 = wl.dw(r, x, 1.0); 62 | auto dw2 = (wl.w(x + dx * 0.5, 1.0) - wl.w(x - dx * 0.5, 1.0)) / dx; 63 | error += std::abs(dw1[0] - dw2); 64 | } 65 | error /= n; 66 | std::cout << "error (n = " << n << "): " << error << std::endl; 67 | } 68 | 69 | std::cout << "compare dw(h)/dh to (w(h + dh/2) - w(h - dh/2)) / dh" << std::endl; 70 | for(int n = 100; n < n_max; n *= 2) { 71 | const real dx = 1.0 / n; 72 | real error = 0.0; 73 | for(real x = dx * 0.5; x < 1.0; x += dx) { 74 | const vec_t r(x); 75 | auto dw1 = wl.dhw(x, 1.0); 76 | auto dw2 = (wl.w(x, 1.0 + dx * 0.5) - wl.w(x, 1.0 - dx * 0.5)) / dx; 77 | error += std::abs(dw1 - dw2); 78 | } 79 | error /= n; 80 | std::cout << "error (n = " << n << "): " << error << std::endl; 81 | } 82 | } 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /test/kernel_test/kernel_test.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {2E88A7F5-A4A3-42EC-AB6A-4FABAE06FD25} 24 | Win32Proj 25 | kerneltest 26 | 10.0.16299.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | false 75 | 76 | 77 | true 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | NotUsing 88 | Level3 89 | MaxSpeed 90 | true 91 | true 92 | true 93 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | true 95 | %(AdditionalIncludeDirectories);..\..\include;$(BOOST_INC_PATH) 96 | 97 | 98 | Console 99 | true 100 | true 101 | true 102 | 103 | 104 | 105 | 106 | Use 107 | Level3 108 | Disabled 109 | true 110 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 111 | true 112 | 113 | 114 | Console 115 | true 116 | 117 | 118 | 119 | 120 | NotUsing 121 | Level3 122 | Disabled 123 | true 124 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 125 | true 126 | %(AdditionalIncludeDirectories);..\..\include;$(BOOST_INC_PATH) 127 | 128 | 129 | Console 130 | true 131 | 132 | 133 | 134 | 135 | Use 136 | Level3 137 | MaxSpeed 138 | true 139 | true 140 | true 141 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 142 | true 143 | 144 | 145 | Console 146 | true 147 | true 148 | true 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /test/kernel_test/kernel_test.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | ソース ファイル 20 | 21 | 22 | --------------------------------------------------------------------------------