diff --git a/examples/helio_swifter_comparison/swiftest_vs_swifter.ipynb b/examples/helio_swifter_comparison/swiftest_vs_swifter.ipynb index 7f0b1d4b9..9a4c22cb1 100644 --- a/examples/helio_swifter_comparison/swiftest_vs_swifter.ipynb +++ b/examples/helio_swifter_comparison/swiftest_vs_swifter.ipynb @@ -43,9 +43,9 @@ "output_type": "stream", "text": [ "Reading Swiftest file param.swiftest.in\n", - "Reading in time 1.001e+00\n", + "Reading in time 1.000e+00\n", "Creating Dataset\n", - "Successfully converted 1463 output frames.\n", + "Successfully converted 1462 output frames.\n", "Swiftest simulation data stored as xarray DataSet .ds\n" ] } diff --git a/examples/rmvs_swifter_comparison/1pl_1tp_encounter/swiftest_vs_swifter.ipynb b/examples/rmvs_swifter_comparison/1pl_1tp_encounter/swiftest_vs_swifter.ipynb index d9be0df4d..29dcf43aa 100644 --- a/examples/rmvs_swifter_comparison/1pl_1tp_encounter/swiftest_vs_swifter.ipynb +++ b/examples/rmvs_swifter_comparison/1pl_1tp_encounter/swiftest_vs_swifter.ipynb @@ -81,8 +81,8 @@ { "data": { "text/plain": [ - "[,\n", - " ]" + "[,\n", + " ]" ] }, "execution_count": 6, diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/cb.swiftest.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/cb.swiftest.in index 4c5d87040..d0ae0ed15 100644 Binary files a/examples/symba_swifter_comparison/1pl_1pl_encounter/cb.swiftest.in and b/examples/symba_swifter_comparison/1pl_1pl_encounter/cb.swiftest.in differ diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/init_cond.py b/examples/symba_swifter_comparison/1pl_1pl_encounter/init_cond.py index 7600320c2..ece9101e0 100755 --- a/examples/symba_swifter_comparison/1pl_1pl_encounter/init_cond.py +++ b/examples/symba_swifter_comparison/1pl_1pl_encounter/init_cond.py @@ -131,8 +131,7 @@ plfile = FortranFile(swiftest_pl, 'w') plfile.write_record(npl) - -plfile.write_record(np.array([plid1, plid2])) +plfile.write_record(np.array([plid1, plid2], dtype=np.int32)) plfile.write_record(np.vstack([p_pl1[0],p_pl2[0]])) plfile.write_record(np.vstack([p_pl1[1],p_pl2[1]])) plfile.write_record(np.vstack([p_pl1[2],p_pl2[2]])) @@ -173,6 +172,7 @@ print(f'ENC_OUT {swiftest_enc}') print(f'EXTRA_FORCE no') print(f'BIG_DISCARD no') +print(f'DISCARD_OUT discard.swiftest.out') print(f'ROTATION no') print(f'GR no') print(f'MU2KG {MU2KG}') diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/param.swiftest.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/param.swiftest.in index 1866557b2..d44f4df0e 100644 --- a/examples/symba_swifter_comparison/1pl_1pl_encounter/param.swiftest.in +++ b/examples/symba_swifter_comparison/1pl_1pl_encounter/param.swiftest.in @@ -5,7 +5,7 @@ DT 0.0006844626967830253 CB_IN cb.swiftest.in PL_IN pl.swiftest.in TP_IN tp.swiftest.in -IN_TYPE ASCII +IN_TYPE REAL8 ISTEP_OUT 1 ISTEP_DUMP 1 BIN_OUT bin.swiftest.dat @@ -22,6 +22,7 @@ CHK_QMIN_RANGE 0.004650467260962157 1000.0 ENC_OUT enc.swiftest.dat EXTRA_FORCE no BIG_DISCARD no +DISCARD_OUT discard.swiftest.out ROTATION no GR no MU2KG 1.988409870698051e+30 diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swifter.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swifter.in index 0eb21018b..9f0548fc1 100644 --- a/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swifter.in +++ b/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swifter.in @@ -1,5 +1,5 @@ 3 ! Planet input file generated using init_cond.py -1 39.47692640889762629 +1 39.476926408897625196 0.0 0.0 0.0 0.0 0.0 0.0 2 0.00012002693582795244940133 0.010044724833237892 diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swiftest.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swiftest.in index 19c6d6e3a..1bda0535d 100644 Binary files a/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swiftest.in and b/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swiftest.in differ diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/swiftest_vs_swifter.ipynb b/examples/symba_swifter_comparison/1pl_1pl_encounter/swiftest_vs_swifter.ipynb index 34c978f58..3a80eebd1 100644 --- a/examples/symba_swifter_comparison/1pl_1pl_encounter/swiftest_vs_swifter.ipynb +++ b/examples/symba_swifter_comparison/1pl_1pl_encounter/swiftest_vs_swifter.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -35,7 +35,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -57,7 +57,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -66,7 +66,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -75,23 +75,23 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[,\n", - " ]" + "[,\n", + " ]" ] }, - "execution_count": 6, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -108,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -466,74 +466,1150 @@ " fill: currentColor;\n", "}\n", "
<xarray.DataArray 'vx' (time (y): 221)>\n",
-       "array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,\n",
-       "        0.,  0., nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,\n",
-       "       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan])\n",
+       "array([0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "...\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
+       "       0.        , 0.        , 0.02101973, 0.02102004, 0.02102057,\n",
+       "       0.0210213 , 0.02102224, 0.02102336, 0.02102465, 0.02102612,\n",
+       "       0.02102774, 0.02102951, 0.02103142, 0.02103346, 0.02103561,\n",
+       "       0.02103787, 0.02104022, 0.02104267, 0.02104519, 0.02104778,\n",
+       "       0.02105043, 0.02105312, 0.02105586, 0.02105862, 0.0210614 ,\n",
+       "       0.02106419])\n",
        "Coordinates:\n",
-       "    id        float64 100.0\n",
-       "  * time (y)  (time (y)) float64 0.0 0.0006845 0.001369 ... 0.1492 0.1499 0.1506
" + " id float64 2.0\n", + " * time (y) (time (y)) float64 0.0 0.0006845 0.001369 ... 0.1492 0.1499 0.1506" ], "text/plain": [ "\n", - "array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", - " nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan])\n", + "array([0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + "...\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0. , 0. , 0. ,\n", + " 0. , 0. , 0.02101973, 0.02102004, 0.02102057,\n", + " 0.0210213 , 0.02102224, 0.02102336, 0.02102465, 0.02102612,\n", + " 0.02102774, 0.02102951, 0.02103142, 0.02103346, 0.02103561,\n", + " 0.02103787, 0.02104022, 0.02104267, 0.02104519, 0.02104778,\n", + " 0.02105043, 0.02105312, 0.02105586, 0.02105862, 0.0210614 ,\n", + " 0.02106419])\n", "Coordinates:\n", - " id float64 100.0\n", + " id float64 2.0\n", " * time (y) (time (y)) float64 0.0 0.0006845 0.001369 ... 0.1492 0.1499 0.1506" ] }, - "execution_count": 7, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "swiftdiff['vx'].sel(id=100)" + "swiftdiff['vx'].sel(id=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'vx' (time: 221)>\n",
+       "array([ 0.        , -0.02730963, -0.05461883, -0.08192718, -0.10923426,\n",
+       "       -0.13653965, -0.16384292, -0.19114364, -0.21844141, -0.24573578,\n",
+       "       -0.27302634, -0.30031266, -0.32759433, -0.35487091, -0.38214199,\n",
+       "       -0.40940715, -0.43666596, -0.463918  , -0.49116285, -0.51840009,\n",
+       "       -0.5456293 , -0.57285005, -0.60006193, -0.62726452, -0.6544574 ,\n",
+       "       -0.68164014, -0.70881234, -0.73597358, -0.76312342, -0.79026147,\n",
+       "       -0.8173873 , -0.8445005 , -0.87160064, -0.89868733, -0.92576014,\n",
+       "       -0.95281866, -0.97986247, -1.00689117, -1.03390434, -1.06090158,\n",
+       "       -1.08788246, -1.11484659, -1.14179356, -1.16872296, -1.19563437,\n",
+       "       -1.22252741, -1.24940165, -1.27625671, -1.30309216, -1.32990762,\n",
+       "       -1.35670269, -1.38347696, -1.41023003, -1.43696151, -1.463671  ,\n",
+       "       -1.4903581 , -1.51702243, -1.54366359, -1.57028119, -1.59687484,\n",
+       "       -1.62344416, -1.64998874, -1.67650822, -1.70300221, -1.72947032,\n",
+       "       -1.75591217, -1.78232739, -1.8087156 , -1.83507643, -1.8614095 ,\n",
+       "       -1.88771444, -1.91399088, -1.94023846, -1.96645681, -1.99264557,\n",
+       "       -2.01880437, -2.04493287, -2.0710307 , -2.09709752, -2.12313297,\n",
+       "       -2.1491367 , -2.17510838, -2.20104766, -2.2269542 , -2.25282767,\n",
+       "       -2.27866774, -2.30447407, -2.33024634, -2.35598424, -2.38168744,\n",
+       "       -2.40735563, -2.43298851, -2.45858576, -2.48414708, -2.50967219,\n",
+       "       -2.53516079, -2.56061259, -2.58602731, -2.61140468, -2.63674443,\n",
+       "...\n",
+       "       -3.28166908, -3.30592115, -3.33013189, -3.3543014 , -3.37842979,\n",
+       "       -3.40251722, -3.42656386, -3.45056991, -3.47453562, -3.49846126,\n",
+       "       -3.52234715, -3.54619364, -3.57000113, -3.59377005, -3.61750092,\n",
+       "       -3.64119426, -3.66485068, -3.68847086, -3.71205553, -3.73560548,\n",
+       "       -3.75912161, -3.78260489, -3.80605637, -3.82947723, -3.85286873,\n",
+       "       -3.87623226, -3.89956936, -3.92288168, -3.94617105, -3.96943947,\n",
+       "       -3.99268912, -4.01592242, -4.03914199, -4.06235073, -4.08555183,\n",
+       "       -4.10874879, -4.13194551, -4.15514625, -4.17835577, -4.20157933,\n",
+       "       -4.2248228 , -4.24809272, -4.2713964 , -4.29474206, -4.31813893,\n",
+       "       -4.34159746, -4.36512951, -4.38874856, -4.41247007, -4.43631182,\n",
+       "       -4.46029439, -4.48444172, -4.50878191, -4.53334814, -4.55817998,\n",
+       "       -4.58332498, -4.60884098, -4.63479915, -4.66128825, -4.68842081,\n",
+       "       -4.71634199, -4.7452432 , -4.77538326, -4.80712299, -4.84098468,\n",
+       "       -4.87776098, -4.91873117, -4.96614064, -5.02443531, -5.10428159,\n",
+       "       -5.24263186, -5.9750488 ,         nan,         nan,         nan,\n",
+       "               nan,         nan,         nan,         nan,         nan,\n",
+       "               nan,         nan,         nan,         nan,         nan,\n",
+       "               nan,         nan,         nan,         nan,         nan,\n",
+       "               nan,         nan,         nan,         nan,         nan,\n",
+       "               nan])\n",
+       "Coordinates:\n",
+       "    id       float64 100.0\n",
+       "  * time     (time) float64 0.0 0.0006845 0.001369 ... 0.1492 0.1499 0.1506
" + ], + "text/plain": [ + "\n", + "array([ 0. , -0.02730963, -0.05461883, -0.08192718, -0.10923426,\n", + " -0.13653965, -0.16384292, -0.19114364, -0.21844141, -0.24573578,\n", + " -0.27302634, -0.30031266, -0.32759433, -0.35487091, -0.38214199,\n", + " -0.40940715, -0.43666596, -0.463918 , -0.49116285, -0.51840009,\n", + " -0.5456293 , -0.57285005, -0.60006193, -0.62726452, -0.6544574 ,\n", + " -0.68164014, -0.70881234, -0.73597358, -0.76312342, -0.79026147,\n", + " -0.8173873 , -0.8445005 , -0.87160064, -0.89868733, -0.92576014,\n", + " -0.95281866, -0.97986247, -1.00689117, -1.03390434, -1.06090158,\n", + " -1.08788246, -1.11484659, -1.14179356, -1.16872296, -1.19563437,\n", + " -1.22252741, -1.24940165, -1.27625671, -1.30309216, -1.32990762,\n", + " -1.35670269, -1.38347696, -1.41023003, -1.43696151, -1.463671 ,\n", + " -1.4903581 , -1.51702243, -1.54366359, -1.57028119, -1.59687484,\n", + " -1.62344416, -1.64998874, -1.67650822, -1.70300221, -1.72947032,\n", + " -1.75591217, -1.78232739, -1.8087156 , -1.83507643, -1.8614095 ,\n", + " -1.88771444, -1.91399088, -1.94023846, -1.96645681, -1.99264557,\n", + " -2.01880437, -2.04493287, -2.0710307 , -2.09709752, -2.12313297,\n", + " -2.1491367 , -2.17510838, -2.20104766, -2.2269542 , -2.25282767,\n", + " -2.27866774, -2.30447407, -2.33024634, -2.35598424, -2.38168744,\n", + " -2.40735563, -2.43298851, -2.45858576, -2.48414708, -2.50967219,\n", + " -2.53516079, -2.56061259, -2.58602731, -2.61140468, -2.63674443,\n", + "...\n", + " -3.28166908, -3.30592115, -3.33013189, -3.3543014 , -3.37842979,\n", + " -3.40251722, -3.42656386, -3.45056991, -3.47453562, -3.49846126,\n", + " -3.52234715, -3.54619364, -3.57000113, -3.59377005, -3.61750092,\n", + " -3.64119426, -3.66485068, -3.68847086, -3.71205553, -3.73560548,\n", + " -3.75912161, -3.78260489, -3.80605637, -3.82947723, -3.85286873,\n", + " -3.87623226, -3.89956936, -3.92288168, -3.94617105, -3.96943947,\n", + " -3.99268912, -4.01592242, -4.03914199, -4.06235073, -4.08555183,\n", + " -4.10874879, -4.13194551, -4.15514625, -4.17835577, -4.20157933,\n", + " -4.2248228 , -4.24809272, -4.2713964 , -4.29474206, -4.31813893,\n", + " -4.34159746, -4.36512951, -4.38874856, -4.41247007, -4.43631182,\n", + " -4.46029439, -4.48444172, -4.50878191, -4.53334814, -4.55817998,\n", + " -4.58332498, -4.60884098, -4.63479915, -4.66128825, -4.68842081,\n", + " -4.71634199, -4.7452432 , -4.77538326, -4.80712299, -4.84098468,\n", + " -4.87776098, -4.91873117, -4.96614064, -5.02443531, -5.10428159,\n", + " -5.24263186, -5.9750488 , nan, nan, nan,\n", + " nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan,\n", + " nan])\n", + "Coordinates:\n", + " id float64 100.0\n", + " * time (time) float64 0.0 0.0006845 0.001369 ... 0.1492 0.1499 0.1506" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "swiftestsim.ds.sel(id=100)['vx']" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'vx' (time: 221)>\n",
+       "array([ 0.        , -0.02730963, -0.05461883, -0.08192718, -0.10923426,\n",
+       "       -0.13653965, -0.16384292, -0.19114364, -0.21844141, -0.24573578,\n",
+       "       -0.27302634, -0.30031266, -0.32759433, -0.35487091, -0.38214199,\n",
+       "       -0.40940715, -0.43666596, -0.463918  , -0.49116285, -0.51840009,\n",
+       "       -0.5456293 , -0.57285005, -0.60006193, -0.62726452, -0.6544574 ,\n",
+       "       -0.68164014, -0.70881234, -0.73597358, -0.76312342, -0.79026147,\n",
+       "       -0.8173873 , -0.8445005 , -0.87160064, -0.89868733, -0.92576014,\n",
+       "       -0.95281866, -0.97986247, -1.00689117, -1.03390434, -1.06090158,\n",
+       "       -1.08788246, -1.11484659, -1.14179356, -1.16872296, -1.19563437,\n",
+       "       -1.22252741, -1.24940165, -1.27625671, -1.30309216, -1.32990762,\n",
+       "       -1.35670269, -1.38347696, -1.41023003, -1.43696151, -1.463671  ,\n",
+       "       -1.4903581 , -1.51702243, -1.54366359, -1.57028119, -1.59687484,\n",
+       "       -1.62344416, -1.64998874, -1.67650822, -1.70300221, -1.72947032,\n",
+       "       -1.75591217, -1.78232739, -1.8087156 , -1.83507643, -1.8614095 ,\n",
+       "       -1.88771444, -1.91399088, -1.94023846, -1.96645681, -1.99264557,\n",
+       "       -2.01880437, -2.04493287, -2.0710307 , -2.09709752, -2.12313297,\n",
+       "       -2.1491367 , -2.17510838, -2.20104766, -2.2269542 , -2.25282767,\n",
+       "       -2.27866774, -2.30447407, -2.33024634, -2.35598424, -2.38168744,\n",
+       "       -2.40735563, -2.43298851, -2.45858576, -2.48414708, -2.50967219,\n",
+       "       -2.53516079, -2.56061259, -2.58602731, -2.61140468, -2.63674443,\n",
+       "...\n",
+       "       -3.28166908, -3.30592115, -3.33013189, -3.3543014 , -3.37842979,\n",
+       "       -3.40251722, -3.42656386, -3.45056991, -3.47453562, -3.49846126,\n",
+       "       -3.52234715, -3.54619364, -3.57000113, -3.59377005, -3.61750092,\n",
+       "       -3.64119426, -3.66485068, -3.68847086, -3.71205553, -3.73560548,\n",
+       "       -3.75912161, -3.78260489, -3.80605637, -3.82947723, -3.85286873,\n",
+       "       -3.87623226, -3.89956936, -3.92288168, -3.94617105, -3.96943947,\n",
+       "       -3.99268912, -4.01592242, -4.03914199, -4.06235073, -4.08555183,\n",
+       "       -4.10874879, -4.13194551, -4.15514625, -4.17835577, -4.20157933,\n",
+       "       -4.2248228 , -4.24809272, -4.2713964 , -4.29474206, -4.31813893,\n",
+       "       -4.34159746, -4.36512951, -4.38874856, -4.41247007, -4.43631182,\n",
+       "       -4.46029439, -4.48444172, -4.50878191, -4.53334814, -4.55817998,\n",
+       "       -4.58332498, -4.60884098, -4.63479915, -4.66128825, -4.68842081,\n",
+       "       -4.71634199, -4.7452432 , -4.77538326, -4.80712299, -4.84098468,\n",
+       "       -4.87776098, -4.91873117, -4.96614064, -5.02443531, -5.10428159,\n",
+       "       -5.24263186, -5.9750488 ,         nan,         nan,         nan,\n",
+       "               nan,         nan,         nan,         nan,         nan,\n",
+       "               nan,         nan,         nan,         nan,         nan,\n",
+       "               nan,         nan,         nan,         nan,         nan,\n",
+       "               nan,         nan,         nan,         nan,         nan,\n",
+       "               nan])\n",
+       "Coordinates:\n",
+       "    id       int64 100\n",
+       "  * time     (time) float64 0.0 0.0006845 0.001369 ... 0.1492 0.1499 0.1506
" + ], + "text/plain": [ + "\n", + "array([ 0. , -0.02730963, -0.05461883, -0.08192718, -0.10923426,\n", + " -0.13653965, -0.16384292, -0.19114364, -0.21844141, -0.24573578,\n", + " -0.27302634, -0.30031266, -0.32759433, -0.35487091, -0.38214199,\n", + " -0.40940715, -0.43666596, -0.463918 , -0.49116285, -0.51840009,\n", + " -0.5456293 , -0.57285005, -0.60006193, -0.62726452, -0.6544574 ,\n", + " -0.68164014, -0.70881234, -0.73597358, -0.76312342, -0.79026147,\n", + " -0.8173873 , -0.8445005 , -0.87160064, -0.89868733, -0.92576014,\n", + " -0.95281866, -0.97986247, -1.00689117, -1.03390434, -1.06090158,\n", + " -1.08788246, -1.11484659, -1.14179356, -1.16872296, -1.19563437,\n", + " -1.22252741, -1.24940165, -1.27625671, -1.30309216, -1.32990762,\n", + " -1.35670269, -1.38347696, -1.41023003, -1.43696151, -1.463671 ,\n", + " -1.4903581 , -1.51702243, -1.54366359, -1.57028119, -1.59687484,\n", + " -1.62344416, -1.64998874, -1.67650822, -1.70300221, -1.72947032,\n", + " -1.75591217, -1.78232739, -1.8087156 , -1.83507643, -1.8614095 ,\n", + " -1.88771444, -1.91399088, -1.94023846, -1.96645681, -1.99264557,\n", + " -2.01880437, -2.04493287, -2.0710307 , -2.09709752, -2.12313297,\n", + " -2.1491367 , -2.17510838, -2.20104766, -2.2269542 , -2.25282767,\n", + " -2.27866774, -2.30447407, -2.33024634, -2.35598424, -2.38168744,\n", + " -2.40735563, -2.43298851, -2.45858576, -2.48414708, -2.50967219,\n", + " -2.53516079, -2.56061259, -2.58602731, -2.61140468, -2.63674443,\n", + "...\n", + " -3.28166908, -3.30592115, -3.33013189, -3.3543014 , -3.37842979,\n", + " -3.40251722, -3.42656386, -3.45056991, -3.47453562, -3.49846126,\n", + " -3.52234715, -3.54619364, -3.57000113, -3.59377005, -3.61750092,\n", + " -3.64119426, -3.66485068, -3.68847086, -3.71205553, -3.73560548,\n", + " -3.75912161, -3.78260489, -3.80605637, -3.82947723, -3.85286873,\n", + " -3.87623226, -3.89956936, -3.92288168, -3.94617105, -3.96943947,\n", + " -3.99268912, -4.01592242, -4.03914199, -4.06235073, -4.08555183,\n", + " -4.10874879, -4.13194551, -4.15514625, -4.17835577, -4.20157933,\n", + " -4.2248228 , -4.24809272, -4.2713964 , -4.29474206, -4.31813893,\n", + " -4.34159746, -4.36512951, -4.38874856, -4.41247007, -4.43631182,\n", + " -4.46029439, -4.48444172, -4.50878191, -4.53334814, -4.55817998,\n", + " -4.58332498, -4.60884098, -4.63479915, -4.66128825, -4.68842081,\n", + " -4.71634199, -4.7452432 , -4.77538326, -4.80712299, -4.84098468,\n", + " -4.87776098, -4.91873117, -4.96614064, -5.02443531, -5.10428159,\n", + " -5.24263186, -5.9750488 , nan, nan, nan,\n", + " nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan,\n", + " nan])\n", + "Coordinates:\n", + " id int64 100\n", + " * time (time) float64 0.0 0.0006845 0.001369 ... 0.1492 0.1499 0.1506" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "swiftersim.ds.sel(id=100)['vx']" ] }, { diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/tp.swiftest.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/tp.swiftest.in index 573541ac9..64bf92f74 100644 Binary files a/examples/symba_swifter_comparison/1pl_1pl_encounter/tp.swiftest.in and b/examples/symba_swifter_comparison/1pl_1pl_encounter/tp.swiftest.in differ diff --git a/examples/symba_swifter_comparison/8pl_16tp_encounters/swiftest_symba_vs_swifter_symba.ipynb b/examples/symba_swifter_comparison/8pl_16tp_encounters/swiftest_symba_vs_swifter_symba.ipynb index b348d1f81..c3c42dd4f 100644 --- a/examples/symba_swifter_comparison/8pl_16tp_encounters/swiftest_symba_vs_swifter_symba.ipynb +++ b/examples/symba_swifter_comparison/8pl_16tp_encounters/swiftest_symba_vs_swifter_symba.ipynb @@ -591,8 +591,8 @@ "array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", "Coordinates:\n", " * id (id) int64 101 102 103 104 105 106 107 ... 111 112 113 114 115 116\n", - " time float64 110.0" + " time float64 110.0" ], "text/plain": [ "\n", diff --git a/examples/whm_swifter_comparison/pl.swifter.in b/examples/whm_swifter_comparison/pl.swifter.in index 141e997da..946ff123b 100644 --- a/examples/whm_swifter_comparison/pl.swifter.in +++ b/examples/whm_swifter_comparison/pl.swifter.in @@ -2,35 +2,35 @@ 0 39.476926408897625196 0.0 0.0 0.0 0.0 0.0 0.0 -1 6.5537098095653139645e-06 0.0014751238438755500459 +1 6.5537098095653139645e-06 0.0014751242768086609319 1.6306381826061645943e-05 --0.065841771551149230746 0.30388831943526661838 0.030872485461978960153 --12.104810966946379345 -1.8005812017180330847 0.9632304211885714761 -2 9.663313399581537916e-05 0.006759080797928606587 +-0.21794225400065470044 0.24570059548519398995 0.040069659678364698274 +-9.768342370075118952 -6.4098488749322373205 0.37225116289830816995 +2 9.663313399581537916e-05 0.0067590742435367571566 4.0453784346544178454e-05 --0.65269716062695148917 -0.3065765656441301057 0.033456491497379246824 -3.0899533953493179043 -6.72112303206047562 -0.2705477431358893059 -3 0.000120026935827952453094 0.010044868190633438806 +-0.60413504586259936247 -0.39527613440541492507 0.029436881824798030033 +3.992938767473374092 -6.2169034295501688922 -0.3157349287333398891 +3 0.000120026935827952453094 0.010044891628501106769 4.25875607065040958e-05 -0.58046286084934750615 -0.8332000042504307258 3.7646553415201541957e-05 -5.053802748240266633 3.568560918001247615 -0.0001869334511378976778 -4 1.2739802010675941456e-05 0.0072467082986392815006 +0.6475137988388671717 -0.78146344078682306034 3.4954277703126252982e-05 +4.7364737841481480227 3.9858178826605781494 -0.000206181980282845843 +4 1.2739802010675941456e-05 0.0072466933032545104062 2.265740805092889601e-05 --1.5891417403740180081 0.4938480736359250889 0.049330990309104823244 --1.3261523862597792352 -4.4445327547884994806 -0.060612990482397517785 -5 0.037692251088985676735 0.3552707649709459117 +-1.6060166552595489531 0.43262604649099911658 0.048461907252935247647 +-1.1388942318608360441 -4.4988235352611598648 -0.066344559364066134143 +5 0.037692251088985676735 0.3552707368190505097 0.00046732617030490929307 -4.1148395833578952363 -2.8938323061728068453 -0.080043092204059404504 -1.5541304908644199467 2.386798324664287883 -0.044683660603562371893 -6 0.011285899820091272997 0.43765596788571493287 +4.1359946230316175786 -2.8610749953481979801 -0.08065244615734604161 +1.536603427793050461 2.399023353553466048 -0.044342472584791124157 +6 0.011285899820091272997 0.4376572328164372643 0.00038925687730393611812 -6.3589256477393849565 -7.653288021415167286 -0.12000977499446359442 -1.4556566113591374531 1.2999494788820976765 -0.08051428750367411639 -7 0.0017236589478267730203 0.46957663585116591335 +6.3788284394924916754 -7.635463758938534795 -0.121111501730720202974 +1.4521392831727842248 1.3041738917825064364 -0.08044788317293871613 +7 0.0017236589478267730203 0.46959013246222981483 0.00016953449859497231466 -14.816779495279050138 13.049265812461410263 -0.14351615042000470668 --0.9586068527340353378 1.013470229424341294 0.01613039934499510156 -8 0.0020336100526728302319 0.7813355837717117843 +14.803649648126269156 13.063133279359290029 -0.14329526741228329478 +-0.9596636872292902537 1.0125665712568530355 0.016140607193432704789 +8 0.0020336100526728302319 0.78135207839715916734 0.000164587904124493665 -29.564459991843019537 -4.5824598513731222837 -0.5870359532621901577 -0.1697807691732287658 1.1426067858222827636 -0.027409347819614317105 +29.566779964594630314 -4.5668176855665958414 -0.58741108465859714904 +0.16916723445783939828 1.142713652049310879 -0.027397346380668001207 diff --git a/examples/whm_swifter_comparison/pl.swiftest.in b/examples/whm_swifter_comparison/pl.swiftest.in index a5ed4ef1c..c13f0640d 100644 --- a/examples/whm_swifter_comparison/pl.swiftest.in +++ b/examples/whm_swifter_comparison/pl.swiftest.in @@ -1,33 +1,33 @@ 8 -1 6.5537098095653139645e-06 0.0014751238438755500459 +1 6.5537098095653139645e-06 0.0014751242768086609319 1.6306381826061645943e-05 --0.065841771551149230746 0.30388831943526661838 0.030872485461978960153 --12.104810966946379345 -1.8005812017180330847 0.9632304211885714761 -2 9.663313399581537916e-05 0.006759080797928606587 +-0.21794225400065470044 0.24570059548519398995 0.040069659678364698274 +-9.768342370075118952 -6.4098488749322373205 0.37225116289830816995 +2 9.663313399581537916e-05 0.0067590742435367571566 4.0453784346544178454e-05 --0.65269716062695148917 -0.3065765656441301057 0.033456491497379246824 -3.0899533953493179043 -6.72112303206047562 -0.2705477431358893059 -3 0.000120026935827952453094 0.010044868190633438806 +-0.60413504586259936247 -0.39527613440541492507 0.029436881824798030033 +3.992938767473374092 -6.2169034295501688922 -0.3157349287333398891 +3 0.000120026935827952453094 0.010044891628501106769 4.25875607065040958e-05 -0.58046286084934750615 -0.8332000042504307258 3.7646553415201541957e-05 -5.053802748240266633 3.568560918001247615 -0.0001869334511378976778 -4 1.2739802010675941456e-05 0.0072467082986392815006 +0.6475137988388671717 -0.78146344078682306034 3.4954277703126252982e-05 +4.7364737841481480227 3.9858178826605781494 -0.000206181980282845843 +4 1.2739802010675941456e-05 0.0072466933032545104062 2.265740805092889601e-05 --1.5891417403740180081 0.4938480736359250889 0.049330990309104823244 --1.3261523862597792352 -4.4445327547884994806 -0.060612990482397517785 -5 0.037692251088985676735 0.3552707649709459117 +-1.6060166552595489531 0.43262604649099911658 0.048461907252935247647 +-1.1388942318608360441 -4.4988235352611598648 -0.066344559364066134143 +5 0.037692251088985676735 0.3552707368190505097 0.00046732617030490929307 -4.1148395833578952363 -2.8938323061728068453 -0.080043092204059404504 -1.5541304908644199467 2.386798324664287883 -0.044683660603562371893 -6 0.011285899820091272997 0.43765596788571493287 +4.1359946230316175786 -2.8610749953481979801 -0.08065244615734604161 +1.536603427793050461 2.399023353553466048 -0.044342472584791124157 +6 0.011285899820091272997 0.4376572328164372643 0.00038925687730393611812 -6.3589256477393849565 -7.653288021415167286 -0.12000977499446359442 -1.4556566113591374531 1.2999494788820976765 -0.08051428750367411639 -7 0.0017236589478267730203 0.46957663585116591335 +6.3788284394924916754 -7.635463758938534795 -0.121111501730720202974 +1.4521392831727842248 1.3041738917825064364 -0.08044788317293871613 +7 0.0017236589478267730203 0.46959013246222981483 0.00016953449859497231466 -14.816779495279050138 13.049265812461410263 -0.14351615042000470668 --0.9586068527340353378 1.013470229424341294 0.01613039934499510156 -8 0.0020336100526728302319 0.7813355837717117843 +14.803649648126269156 13.063133279359290029 -0.14329526741228329478 +-0.9596636872292902537 1.0125665712568530355 0.016140607193432704789 +8 0.0020336100526728302319 0.78135207839715916734 0.000164587904124493665 -29.564459991843019537 -4.5824598513731222837 -0.5870359532621901577 -0.1697807691732287658 1.1426067858222827636 -0.027409347819614317105 +29.566779964594630314 -4.5668176855665958414 -0.58741108465859714904 +0.16916723445783939828 1.142713652049310879 -0.027397346380668001207 diff --git a/examples/whm_swifter_comparison/swiftest_vs_swifter.ipynb b/examples/whm_swifter_comparison/swiftest_vs_swifter.ipynb index 7740f02c8..ef0a664c8 100644 --- a/examples/whm_swifter_comparison/swiftest_vs_swifter.ipynb +++ b/examples/whm_swifter_comparison/swiftest_vs_swifter.ipynb @@ -43,9 +43,9 @@ "output_type": "stream", "text": [ "Reading Swiftest file param.swiftest.in\n", - "Reading in time 1.001e+00\n", + "Reading in time 1.000e+00\n", "Creating Dataset\n", - "Successfully converted 1463 output frames.\n", + "Successfully converted 1462 output frames.\n", "Swiftest simulation data stored as xarray DataSet .ds\n" ] } @@ -107,7 +107,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -167,7 +167,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] diff --git a/examples/whm_swifter_comparison/tp.swifter.in b/examples/whm_swifter_comparison/tp.swifter.in index d4bba791e..22ca5a6ca 100644 --- a/examples/whm_swifter_comparison/tp.swifter.in +++ b/examples/whm_swifter_comparison/tp.swifter.in @@ -1,13 +1,13 @@ 4 101 -2.1778219831071528034 1.7945000787160070299 -0.344538568144980073 --2.4660672364316131263 2.6696516059587804457 0.5387135399929646282 +2.1437140623725170485 1.8307543455088179929 -0.33710883085786358393 +-2.5169991736250634084 2.6269266483088493027 0.54674712095669365287 102 -3.0442667013982411817 -0.9663926835590784803 0.40722457070173800897 -0.50161667633754136036 2.5842510880432738114 -1.8324318157740491254 +3.0507953356624089025 -0.9309107058567914761 0.38209550228666327998 +0.45214249601424874418 2.5995875558304815747 -1.8388641770977671949 103 --0.34517723265404320898 -3.1406497314215879868 0.72728042419722227496 -3.0867794854837949715 0.086392107735322389756 -0.14509697121440676101 +-0.30288545144121659103 -3.139125526168093927 0.7252151132548391166 +3.0919425994019995516 0.13633790246363267858 -0.15665049243950410883 104 --1.9619853530057589364 -0.98771442784664698067 0.2682528168870427776 -2.180176917968356245 -3.7664581464574479557 -0.15265740558307136673 +-1.9314729940131600827 -1.0389307897540689396 0.26607157142831372454 +2.2775049779995786108 -3.7157836040053666307 -0.16601542341215017115 diff --git a/examples/whm_swifter_comparison/tp.swiftest.in b/examples/whm_swifter_comparison/tp.swiftest.in index d4bba791e..22ca5a6ca 100644 --- a/examples/whm_swifter_comparison/tp.swiftest.in +++ b/examples/whm_swifter_comparison/tp.swiftest.in @@ -1,13 +1,13 @@ 4 101 -2.1778219831071528034 1.7945000787160070299 -0.344538568144980073 --2.4660672364316131263 2.6696516059587804457 0.5387135399929646282 +2.1437140623725170485 1.8307543455088179929 -0.33710883085786358393 +-2.5169991736250634084 2.6269266483088493027 0.54674712095669365287 102 -3.0442667013982411817 -0.9663926835590784803 0.40722457070173800897 -0.50161667633754136036 2.5842510880432738114 -1.8324318157740491254 +3.0507953356624089025 -0.9309107058567914761 0.38209550228666327998 +0.45214249601424874418 2.5995875558304815747 -1.8388641770977671949 103 --0.34517723265404320898 -3.1406497314215879868 0.72728042419722227496 -3.0867794854837949715 0.086392107735322389756 -0.14509697121440676101 +-0.30288545144121659103 -3.139125526168093927 0.7252151132548391166 +3.0919425994019995516 0.13633790246363267858 -0.15665049243950410883 104 --1.9619853530057589364 -0.98771442784664698067 0.2682528168870427776 -2.180176917968356245 -3.7664581464574479557 -0.15265740558307136673 +-1.9314729940131600827 -1.0389307897540689396 0.26607157142831372454 +2.2775049779995786108 -3.7157836040053666307 -0.16601542341215017115 diff --git a/python/swiftest/swiftest/io.py b/python/swiftest/swiftest/io.py index 2dd4ef7b3..5782c6444 100644 --- a/python/swiftest/swiftest/io.py +++ b/python/swiftest/swiftest/io.py @@ -5,7 +5,7 @@ import sys import tempfile -newfeaturelist = ("FRAGMENTATION", "ROTATION", "TIDES", "ENERGY", "GR", "YARKOVSKY", "YORP" ) +newfeaturelist = ("FRAGMENTATION", "ROTATION", "TIDES", "ENERGY", "GR", "YARKOVSKY", "YORP") def real2float(realstr): """ @@ -279,6 +279,7 @@ def write_labeled_param(param, param_file_name): 'CB_IN', 'BIN_OUT', 'ENC_OUT', + 'DISCARD_OUT', 'CHK_QMIN', 'CHK_RMIN', 'CHK_RMAX', @@ -889,7 +890,7 @@ def swift2swifter(swift_param, plname="", tpname="", conversion_questions={}): swifter_param['ENC_OUT'] = input("ENC_OUT: Encounter file name: [enc.dat]> ") if swifter_param['ENC_OUT'] == '': swifter_param['ENC_OUT'] = "enc.dat" - + intxt = conversion_questions.get('EXTRA_FORCE', None) if not intxt: intxt = input("EXTRA_FORCE: Use additional user-specified force routines? (y/N)> ") @@ -1228,6 +1229,13 @@ def swifter2swiftest(swifter_param, plname="", tpname="", cbname="", conversion_ swiftest_param.pop('J2', None) swiftest_param.pop('J4', None) swiftest_param.pop('RHILL_PRESENT', None) + + swiftest_param['DISCARD_OUT'] = conversion_questions.get('DISCARD_OUT', '') + if not swiftest_param['DISCARD_OUT']: + swiftest_param['DISCARD_OUT'] = input("DISCARD_OUT: Discard file name: [discard.out]> ") + if swiftest_param['DISCARD_OUT'] == '': + swiftest_param['DISCARD_OUT'] = "discard.out" + swiftest_param['! VERSION'] = "Swiftest parameter file converted from Swifter" return swiftest_param diff --git a/src/discard/discard.f90 b/src/discard/discard.f90 index 292e52c38..be377e49e 100644 --- a/src/discard/discard.f90 +++ b/src/discard/discard.f90 @@ -10,7 +10,7 @@ module subroutine discard_system(self, param) implicit none ! Arguments class(swiftest_nbody_system), intent(inout) :: self !! Swiftest system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters ! Internals logical :: lany_discards @@ -36,8 +36,9 @@ module subroutine discard_pl(self, system, param) ! Arguments class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameter + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameter + if (self%nbody == 0) return self%ldiscard(:) = .false. return @@ -55,28 +56,28 @@ module subroutine discard_tp(self, system, param) ! Arguments class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameter + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameter associate(tp => self, ntp => self%nbody, cb => system%cb, pl => system%pl, npl => system%pl%nbody) - if (ntp == 0) return + if ((ntp == 0) .or. (npl ==0)) return + if ((param%rmin >= 0.0_DP) .or. (param%rmax >= 0.0_DP) .or. & (param%rmaxu >= 0.0_DP) .or. ((param%qmin >= 0.0_DP) .and. (param%qmin_coord == "BARY"))) then - if (npl > 0) call pl%h2b(cb) - if (ntp > 0) call tp%h2b(cb) + call pl%h2b(cb) + call tp%h2b(cb) end if - if ((param%rmin >= 0.0_DP) .or. (param%rmax >= 0.0_DP) .or. (param%rmaxu >= 0.0_DP)) then - if (ntp > 0) call discard_sun_tp(tp, system, param) - end if - if (param%qmin >= 0.0_DP .and. ntp > 0) call discard_peri_tp(tp, system, param) - if (param%lclose .and. ntp > 0) call discard_pl_tp(tp, system, param) - if (any(tp%ldiscard)) call tp%spill(system%tp_discards, tp%ldiscard) + + if ((param%rmin >= 0.0_DP) .or. (param%rmax >= 0.0_DP) .or. (param%rmaxu >= 0.0_DP)) call discard_cb_tp(tp, system, param) + if (param%qmin >= 0.0_DP) call discard_peri_tp(tp, system, param) + if (param%lclose) call discard_pl_tp(tp, system, param) + if (any(tp%ldiscard)) call tp%spill(system%tp_discards, tp%ldiscard, ldestructive=.true.) end associate return end subroutine discard_tp - subroutine discard_sun_tp(tp, system, param) + subroutine discard_cb_tp(tp, system, param) !! author: David A. Minton !! !! Check to see if test particles should be discarded based on their positions relative to the Sun @@ -93,7 +94,7 @@ subroutine discard_sun_tp(tp, system, param) integer(I4B) :: i real(DP) :: energy, vb2, rb2, rh2, rmin2, rmax2, rmaxu2 - associate(ntp => tp%nbody, cb => system%cb, t => param%t, msys => system%msys) + associate(ntp => tp%nbody, cb => system%cb, t => param%t, Gmtot => system%Gmtot) rmin2 = max(param%rmin * param%rmin, cb%radius * cb%radius) rmax2 = param%rmax**2 rmaxu2 = param%rmaxu**2 @@ -113,7 +114,7 @@ subroutine discard_sun_tp(tp, system, param) else if (param%rmaxu >= 0.0_DP) then rb2 = dot_product(tp%xb(:, i), tp%xb(:, i)) vb2 = dot_product(tp%vb(:, i), tp%vb(:, i)) - energy = 0.5_DP * vb2 - msys / sqrt(rb2) + energy = 0.5_DP * vb2 - Gmtot / sqrt(rb2) if ((energy > 0.0_DP) .and. (rb2 > rmaxu2)) then tp%status(i) = DISCARDED_RMAXU write(*, *) "Particle ", tp%id(i), " is unbound and too far from barycenter at t = ", t @@ -126,7 +127,7 @@ subroutine discard_sun_tp(tp, system, param) end associate return - end subroutine discard_sun_tp + end subroutine discard_cb_tp subroutine discard_peri_tp(tp, system, param) diff --git a/src/helio/helio_coord.f90 b/src/helio/helio_coord.f90 index 0e58a3ab6..f40781810 100644 --- a/src/helio/helio_coord.f90 +++ b/src/helio/helio_coord.f90 @@ -68,14 +68,14 @@ module subroutine helio_coord_vh2vb_pl(self, cb) class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object ! Internals integer(I4B) :: i - real(DP) :: msys + real(DP) :: Gmtot if (self%nbody == 0) return associate(pl => self, npl => self%nbody) - msys = cb%Gmass + sum(pl%Gmass(1:npl)) + Gmtot = cb%Gmass + sum(pl%Gmass(1:npl)) do i = 1, NDIM - cb%vb(i) = -sum(pl%Gmass(1:npl) * pl%vh(i, 1:npl)) / msys + cb%vb(i) = -sum(pl%Gmass(1:npl) * pl%vh(i, 1:npl)) / Gmtot pl%vb(i, 1:npl) = pl%vh(i, 1:npl) + cb%vb(i) end do end associate diff --git a/src/io/io.f90 b/src/io/io.f90 index b424094eb..e159d019d 100644 --- a/src/io/io.f90 +++ b/src/io/io.f90 @@ -2,6 +2,324 @@ use swiftest contains + module subroutine io_conservation_report(self, param, lterminal) + !! author: The Purdue Swiftest Team - David A. Minton, Carlisle A. Wishard, Jennifer L.L. Pouplin, and Jacob R. Elliott + !! + !! Reports the current state of energy, mass, and angular momentum conservation in a run + implicit none + ! Arguments + class(swiftest_nbody_system), intent(inout) :: self !! Swiftest nbody system object + class(swiftest_parameters), intent(inout) :: param !! Input colleciton of user-defined parameters + logical, intent(in) :: lterminal !! Indicates whether to output information to the terminal screen + ! Internals + real(DP), dimension(NDIM) :: Ltot_now, Lorbit_now, Lspin_now + real(DP), dimension(NDIM), save :: Ltot_last, Lorbit_last, Lspin_last + real(DP), save :: ke_orbit_last, ke_spin_last, pe_last, Eorbit_last + real(DP) :: ke_orbit_now, ke_spin_now, pe_now, Eorbit_now + real(DP) :: Eorbit_error, Etotal_error, Ecoll_error + real(DP) :: Mtot_now, Merror + real(DP) :: Lmag_now, Lerror + character(len=*), parameter :: EGYFMT = '(ES23.16,10(",",ES23.16,:))' ! Format code for all simulation output + character(len=*), parameter :: EGYHEADER = '("t,Eorbit,Ecollisions,Lx,Ly,Lz,Mtot")' + integer(I4B), parameter :: EGYIU = 72 + character(len=*), parameter :: EGYTERMFMT = '(" DL/L0 = ", ES12.5 & + "; DEcollisions/|E0| = ", ES12.5, & + "; D(Eorbit+Ecollisions)/|E0| = ", ES12.5, & + "; DM/M0 = ", ES12.5)' + + associate(system => self, pl => self%pl, cb => self%cb, npl => self%pl%nbody, Ecollisions => self%Ecollisions, Lescape => self%Lescape, Mescape => self%Mescape, & + Euntracked => self%Euntracked, Eorbit_orig => param%Eorbit_orig, Mtot_orig => param%Mtot_orig, & + Ltot_orig => param%Ltot_orig(:), Lmag_orig => param%Lmag_orig, Lorbit_orig => param%Lorbit_orig(:), Lspin_orig => param%Lspin_orig(:), & + lfirst => param%lfirstenergy) + if (lfirst) then + if (param%out_stat == "OLD") then + open(unit = EGYIU, file = ENERGY_FILE, form = "formatted", status = "old", action = "write", position = "append") + else + open(unit = EGYIU, file = ENERGY_FILE, form = "formatted", status = "replace", action = "write") + write(EGYIU,EGYHEADER) + end if + end if + call system%get_energy_and_momentum(param, ke_orbit_now, ke_spin_now, pe_now, Lorbit_now, Lspin_now) + Eorbit_now = ke_orbit_now + ke_spin_now + pe_now + Ltot_now(:) = Lorbit_now(:) + Lspin_now(:) + Lescape(:) + Mtot_now = cb%mass + sum(pl%mass(1:npl)) + system%Mescape + if (lfirst) then + Eorbit_orig = Eorbit_now + Mtot_orig = Mtot_now + Lorbit_orig(:) = Lorbit_now(:) + Lspin_orig(:) = Lspin_now(:) + Ltot_orig(:) = Ltot_now(:) + Lmag_orig = norm2(Ltot_orig(:)) + lfirst = .false. + end if + + write(EGYIU,EGYFMT) param%t, Eorbit_now, Ecollisions, Ltot_now, Mtot_now + flush(EGYIU) + if (.not.lfirst .and. lterminal) then + Lmag_now = norm2(Ltot_now) + Lerror = norm2(Ltot_now - Ltot_orig) / Lmag_orig + Eorbit_error = (Eorbit_now - Eorbit_orig) / abs(Eorbit_orig) + Ecoll_error = Ecollisions / abs(Eorbit_orig) + Etotal_error = (Eorbit_now - Ecollisions - Eorbit_orig - Euntracked) / abs(Eorbit_orig) + Merror = (Mtot_now - Mtot_orig) / Mtot_orig + write(*, egytermfmt) Lerror, Ecoll_error, Etotal_error, Merror + if (Ecoll_error > 0.0_DP) then + write(*,*) 'Something has gone wrong! Collisional energy should not be positive!' + write(*,*) 'dke_orbit: ',(ke_orbit_now - ke_orbit_last) / abs(Eorbit_orig) + write(*,*) 'dke_spin : ',(ke_spin_now - ke_spin_last) / abs(Eorbit_orig) + write(*,*) 'dpe : ',(pe_now - pe_last) / abs(Eorbit_orig) + write(*,*) + end if + if (Lerror > 1e-6) then + write(*,*) 'Something has gone wrong! Angular momentum is too high!' + write(*,*) 'Lerror: ', Lerror + end if + end if + ke_orbit_last = ke_orbit_now + ke_spin_last = ke_spin_now + pe_last = pe_now + Eorbit_last = Eorbit_now + Lorbit_last(:) = Lorbit_now(:) + Lspin_last(:) = Lspin_now(:) + Ltot_last(:) = Ltot_now(:) + end associate + return + + end subroutine io_conservation_report + + + module subroutine io_dump_param(self, param_file_name) + !! author: David A. Minton + !! + !! Dump integration parameters to file + !! + !! Adapted from David E. Kaufmann's Swifter routine io_dump_param.f90 + !! Adapted from Martin Duncan's Swift routine io_dump_param.f + implicit none + ! Arguments + class(swiftest_parameters),intent(in) :: self !! Output collection of parameters + character(len=*), intent(in) :: param_file_name !! Parameter input file name (i.e. param.in) + ! Internals + integer(I4B), parameter :: LUN = 7 !! Unit number of output file + integer(I4B) :: ierr !! Error code + character(STRMAX) :: error_message !! Error message in UDIO procedure + + open(unit = LUN, file = param_file_name, status='replace', form = 'FORMATTED', iostat =ierr) + if (ierr /=0) then + write(*,*) 'Swiftest error.' + write(*,*) ' Could not open dump file: ',trim(adjustl(param_file_name)) + call util_exit(FAILURE) + end if + + !! todo: Currently this procedure does not work in user-defined derived-type input mode + !! due to compiler incompatabilities + !write(LUN,'(DT)') param + call self%writer(LUN, iotype = "none", v_list = [0], iostat = ierr, iomsg = error_message) + if (ierr /= 0) then + write(*,*) trim(adjustl(error_message)) + call util_exit(FAILURE) + end if + close(LUN) + + return + end subroutine io_dump_param + + + module subroutine io_dump_swiftest(self, param, msg) + !! author: David A. Minton + !! + !! Dump massive body data to files + !! + !! Adapted from David E. Kaufmann's Swifter routine: io_dump_pl.f90 and io_dump_tp.f90 + !! Adapted from Hal Levison's Swift routine io_dump_pl.f and io_dump_tp.f + implicit none + ! Arguments + class(swiftest_base), intent(inout) :: self !! Swiftest base object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + character(*), optional, intent(in) :: msg !! Message to display with dump operation + ! Internals + integer(I4B) :: ierr !! Error code + integer(I4B),parameter :: LUN = 7 !! Unit number for dump file + integer(I4B) :: iu = LUN + character(len=:), allocatable :: dump_file_name + + select type(self) + class is(swiftest_cb) + dump_file_name = trim(adjustl(param%incbfile)) + class is (swiftest_pl) + dump_file_name = trim(adjustl(param%inplfile)) + class is (swiftest_tp) + dump_file_name = trim(adjustl(param%intpfile)) + end select + open(unit = iu, file = dump_file_name, form = "UNFORMATTED", status = 'replace', iostat = ierr) + if (ierr /= 0) then + write(*, *) "Swiftest error:" + write(*, *) " Unable to open binary dump file " // dump_file_name + call util_exit(FAILURE) + end if + call self%write_frame(iu, param) + close(LUN) + + return + end subroutine io_dump_swiftest + + + module subroutine io_dump_system(self, param, msg) + !! author: David A. Minton + !! + !! Dumps the state of the system to files in case the simulation is interrupted. + !! As a safety mechanism, there are two dump files that are written in alternating order + !! so that if a dump file gets corrupted during writing, the user can restart from the older one. + implicit none + ! Arguments + class(swiftest_nbody_system), intent(inout) :: self !! Swiftest system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + character(*), optional, intent(in) :: msg !! Message to display with dump operation + ! Internals + class(swiftest_parameters), allocatable :: dump_param !! Local parameters variable used to parameters change input file names + !! to dump file-specific values without changing the user-defined values + integer(I4B), save :: idx = 1 !! Index of current dump file. Output flips between 2 files for extra security + !! in case the program halts during writing + character(len=:), allocatable :: param_file_name + real(DP) :: tfrac + + allocate(dump_param, source=param) + param_file_name = trim(adjustl(DUMP_PARAM_FILE(idx))) + dump_param%incbfile = trim(adjustl(DUMP_CB_FILE(idx))) + dump_param%inplfile = trim(adjustl(DUMP_PL_FILE(idx))) + dump_param%intpfile = trim(adjustl(DUMP_TP_FILE(idx))) + dump_param%out_form = XV + dump_param%out_stat = 'APPEND' + dump_param%T0 = param%t + call dump_param%dump(param_file_name) + + call self%cb%dump(dump_param) + if (self%pl%nbody > 0) call self%pl%dump(dump_param) + if (self%tp%nbody > 0) call self%tp%dump(dump_param) + + idx = idx + 1 + if (idx > NDUMPFILES) idx = 1 + + ! Print the status message (format code passed in from main driver) + tfrac = (param%t - param%t0) / (param%tstop - param%t0) + write(*,msg) param%t, tfrac, self%pl%nbody, self%tp%nbody + + return + end subroutine io_dump_system + + + module function io_get_args(integrator, param_file_name) result(ierr) + !! author: David A. Minton + !! + !! Reads in the name of the parameter file from command line arguments. + implicit none + ! Arguments + integer(I4B) :: integrator !! Symbolic code of the requested integrator + character(len=:), allocatable :: param_file_name !! Name of the input parameters file + ! Result + integer(I4B) :: ierr !! I/O error code + ! Internals + character(len=STRMAX) :: arg1, arg2 + integer :: narg,ierr_arg1, ierr_arg2 + character(len=*),parameter :: linefmt = '(A)' + + ierr = -1 ! Default is to fail + narg = command_argument_count() ! + if (narg == 2) then + call get_command_argument(1, arg1, status = ierr_arg1) + call get_command_argument(2, arg2, status = ierr_arg2) + if ((ierr_arg1 == 0) .and. (ierr_arg2 == 0)) then + ierr = 0 + call io_toupper(arg1) + select case(arg1) + case('BS') + integrator = BS + case('HELIO') + integrator = HELIO + case('RA15') + integrator = RA15 + case('TU4') + integrator = TU4 + case('WHM') + integrator = WHM + case('RMVS') + integrator = RMVS + case('SYMBA') + integrator = SYMBA + case('RINGMOONS') + integrator = RINGMOONS + case default + integrator = UNKNOWN_INTEGRATOR + write(*,*) trim(adjustl(arg1)) // ' is not a valid integrator.' + ierr = -1 + end select + param_file_name = trim(adjustl(arg2)) + end if + else + call get_command_argument(1, arg1, status = ierr_arg1) + if (ierr_arg1 == 0) then + if (arg1 == '-v' .or. arg1 == '--version') then + call util_version() + else if (arg1 == '-h' .or. arg1 == '--help') then + call util_exit(HELP) + end if + end if + end if + if (ierr /= 0) call util_exit(USAGE) + + return + end function io_get_args + + + module function io_get_token(buffer, ifirst, ilast, ierr) result(token) + !! author: David A. Minton + !! + !! Retrieves a character token from an input string. Here a token is defined as any set of contiguous non-blank characters not + !! beginning with or containing "!". If "!" is present, any remaining part of the buffer including the "!" is ignored + !! + !! Adapted from David E. Kaufmann's Swifter routine io_get_token.f90 + implicit none + ! Arguments + character(len=*), intent(in) :: buffer !! Input string buffer + integer(I4B), intent(inout) :: ifirst !! Index of the buffer at which to start the search for a token + integer(I4B), intent(out) :: ilast !! Index of the buffer at the end of the returned token + integer(I4B), intent(out) :: ierr !! Error code + ! Result + character(len=:), allocatable :: token !! Returned token string + ! Internals + integer(I4B) :: i,ilength + + ilength = len(buffer) + + if (ifirst > ilength) then + ilast = ifirst + ierr = -1 !! Bad input + token = '' + return + end if + do i = ifirst, ilength + if (buffer(i:i) /= ' ') exit + end do + if ((i > ilength) .or. (buffer(i:i) == '!')) then + ifirst = i + ilast = i + ierr = -2 !! No valid token + token = '' + return + end if + ifirst = i + do i = ifirst, ilength + if ((buffer(i:i) == ' ') .or. (buffer(i:i) == '!')) exit + end do + ilast = i - 1 + ierr = 0 + + token = buffer(ifirst:ilast) + + return + end function io_get_token + + module subroutine io_param_reader(self, unit, iotype, v_list, iostat, iomsg) !! author: The Purdue Swiftest Team - David A. Minton, Carlisle A. Wishard, Jennifer L.L. Pouplin, and Jacob R. Elliott !! @@ -99,7 +417,9 @@ module subroutine io_param_reader(self, unit, iotype, v_list, iostat, iomsg) param_value = io_get_token(line, ifirst, ilast, iostat) read(param_value, *) self%qmin_ahi case ("ENC_OUT") - self%encounter_file = param_value + self%enc_out = param_value + case ("DISCARD_OUT") + self%discard_out = param_value case ("EXTRA_FORCE") call io_toupper(param_value) if (param_value == "YES" .or. param_value == 'T') self%lextra_force = .true. @@ -225,9 +545,7 @@ module subroutine io_param_reader(self, unit, iotype, v_list, iostat, iomsg) write(*,*) "CHK_QMIN = ",self%qmin write(*,*) "CHK_QMIN_COORD = ",trim(adjustl(self%qmin_coord)) write(*,*) "CHK_QMIN_RANGE = ",self%qmin_alo, self%qmin_ahi - write(*,*) "ENC_OUT = ",trim(adjustl(self%encounter_file)) write(*,*) "EXTRA_FORCE = ",self%lextra_force - write(*,*) "BIG_DISCARD = ",self%lbig_discard write(*,*) "RHILL_PRESENT = ",self%lrhill_present write(*,*) "ROTATION = ", self%lrotation write(*,*) "TIDES = ", self%ltides @@ -235,6 +553,18 @@ module subroutine io_param_reader(self, unit, iotype, v_list, iostat, iomsg) write(*,*) "MU2KG = ",self%MU2KG write(*,*) "TU2S = ",self%TU2S write(*,*) "DU2M = ",self%DU2M + if (trim(adjustl(self%enc_out)) /= "") then + write(*,*) "ENC_OUT = ",trim(adjustl(self%enc_out)) + else + write(*,*) "! ENC_OUT not set: Encounters will not be recorded to file" + end if + if (trim(adjustl(self%discard_out)) /= "") then + write(*,*) "DISCARD_OUT = ",trim(adjustl(self%discard_out)) + write(*,*) "BIG_DISCARD = ",self%lbig_discard + else + write(*,*) "! DISCARD_OUT not set: Discards will not be recorded to file" + write(*,*) "! BIG_DISCARD = ",self%lbig_discard + end if if ((self%MU2KG < 0.0_DP) .or. (self%TU2S < 0.0_DP) .or. (self%DU2M < 0.0_DP)) then write(iomsg,*) 'Invalid unit conversion factor' @@ -245,342 +575,110 @@ module subroutine io_param_reader(self, unit, iotype, v_list, iostat, iomsg) ! Calculate the G for the system units self%GU = GC / (self%DU2M**3 / (self%MU2KG * self%TU2S**2)) - ! Calculate the inverse speed of light in the system units - self%inv_c2 = einsteinC * self%TU2S / self%DU2M - self%inv_c2 = (self%inv_c2)**(-2) - - associate(integrator => v_list(1)) - if (integrator == RMVS) then - if (.not.self%lclose) then - write(iomsg,*) 'This integrator requires CHK_CLOSE to be enabled.' - iostat = -1 - return - end if - end if - - ! Determine if the GR flag is set correctly for this integrator - select case(integrator) - case(WHM, RMVS, HELIO, SYMBA) - write(*,*) "GR = ", self%lgr - case default - if (self%lgr) write(iomsg, *) 'GR is not yet implemented for this integrator. This parameter will be ignored.' - self%lgr = .false. - end select - end associate - - iostat = 0 - - return - end subroutine io_param_reader - - - module subroutine io_param_writer(self, unit, iotype, v_list, iostat, iomsg) - !! author: David A. Minton - !! - !! Dump integration parameters to file - !! - !! Adapted from David E. Kaufmann's Swifter routine io_dump_param.f90 - !! Adapted from Martin Duncan's Swift routine io_dump_param.f - implicit none - ! Arguments - class(swiftest_parameters),intent(in) :: self !! Collection of parameters - integer, intent(in) :: unit !! File unit number - character(len=*), intent(in) :: iotype !! Dummy argument passed to the input/output procedure contains the text from the char-literal-constant, prefixed with DT. - !! If you do not include a char-literal-constant, the iotype argument contains only DT. - integer, intent(in) :: v_list(:) !! Not used in this procedure - integer, intent(out) :: iostat !! IO status code - character(len=*), intent(inout) :: iomsg !! Message to pass if iostat /= 0 - ! Internals - character(*),parameter :: Ifmt = '(I0)' !! Format label for integer values - character(*),parameter :: Rfmt = '(ES25.17)' !! Format label for real values - character(*),parameter :: Rarrfmt = '(3(ES25.17,1X))' !! Format label for real values - character(*),parameter :: Lfmt = '(L1)' !! Format label for logical values - character(len=*), parameter :: Afmt = '(A25,1X,64(:,A25,1X))' - character(256) :: param_name, param_value - type character_array - character(25) :: value - end type character_array - type(character_array), dimension(:), allocatable :: param_array - integer(I4B) :: i - - associate(param => self) - write(param_name, Afmt) "T0"; write(param_value,Rfmt) param%t0; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "TSTOP"; write(param_value, Rfmt) param%tstop; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "DT"; write(param_value, Rfmt) param%dt; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "PL_IN"; write(param_value, Afmt) trim(adjustl(param%inplfile)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "TP_in"; write(param_value, Afmt) trim(adjustl(param%intpfile)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "IN_TYPE"; write(param_value, Afmt) trim(adjustl(param%in_type)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) - if (param%istep_out > 0) then - write(param_name, Afmt) "ISTEP_OUT"; write(param_value, Ifmt) param%istep_out; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "BIN_OUT"; write(param_value, Afmt) trim(adjustl(param%outfile)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "OUT_TYPE"; write(param_value, Afmt) trim(adjustl(param%out_type)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "OUT_FORM"; write(param_value, Afmt) trim(adjustl(param%out_form)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "OUT_STAT"; write(param_value, Afmt) "APPEND"; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - end if - write(param_name, Afmt) "ENC_OUT"; write(param_value, Afmt) trim(adjustl(param%encounter_file)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) - if (param%istep_dump > 0) then - write(param_name, Afmt) "ISTEP_DUMP"; write(param_value, Ifmt) param%istep_dump; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - end if - write(param_name, Afmt) "CHK_RMIN"; write(param_value, Rfmt) param%rmin; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "CHK_RMAX"; write(param_value, Rfmt) param%rmax; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "CHK_EJECT"; write(param_value, Rfmt) param%rmaxu; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "CHK_QMIN"; write(param_value, Rfmt) param%qmin; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - if (param%qmin >= 0.0_DP) then - write(param_name, Afmt) "CHK_QMIN_COORD"; write(param_value, Afmt) trim(adjustl(param%qmin_coord)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) - allocate(param_array(2)) - write(param_array(1)%value, Rfmt) param%qmin_alo - write(param_array(2)%value, Rfmt) param%qmin_ahi - write(param_name, Afmt) "CHK_QMIN_RANGE"; write(unit, Afmt) adjustl(param_name), adjustl(param_array(1)%value), adjustl(param_array(2)%value) - end if - write(param_name, Afmt) "MU2KG"; write(param_value, Rfmt) param%MU2KG; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "TU2S"; write(param_value, Rfmt) param%TU2S ; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "DU2M"; write(param_value, Rfmt) param%DU2M; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "RHILL_PRESENT"; write(param_value, Lfmt) param%lrhill_present; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "EXTRA_FORCE"; write(param_value, Lfmt) param%lextra_force; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "BIG_DISCARD"; write(param_value, Lfmt) param%lbig_discard; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "CHK_CLOSE"; write(param_value, Lfmt) param%lclose; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "ENERGY"; write(param_value, Lfmt) param%lenergy; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "GR"; write(param_value, Lfmt) param%lgr; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "ROTATION"; write(param_value, Lfmt) param%lrotation; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - write(param_name, Afmt) "TIDES"; write(param_value, Lfmt) param%ltides; write(unit, Afmt) adjustl(param_name), adjustl(param_value) - iostat = 0 - iomsg = "UDIO not implemented" - end associate - - return - end subroutine io_param_writer - - - module subroutine io_dump_param(self, param_file_name) - !! author: David A. Minton - !! - !! Dump integration parameters to file - !! - !! Adapted from David E. Kaufmann's Swifter routine io_dump_param.f90 - !! Adapted from Martin Duncan's Swift routine io_dump_param.f - implicit none - ! Arguments - class(swiftest_parameters),intent(in) :: self !! Output collection of parameters - character(len=*), intent(in) :: param_file_name !! Parameter input file name (i.e. param.in) - ! Internals - integer(I4B), parameter :: LUN = 7 !! Unit number of output file - integer(I4B) :: ierr !! Error code - character(STRMAX) :: error_message !! Error message in UDIO procedure - - open(unit = LUN, file = param_file_name, status='replace', form = 'FORMATTED', iostat =ierr) - if (ierr /=0) then - write(*,*) 'Swiftest error.' - write(*,*) ' Could not open dump file: ',trim(adjustl(param_file_name)) - call util_exit(FAILURE) - end if - - !! todo: Currently this procedure does not work in user-defined derived-type input mode - !! due to compiler incompatabilities - !write(LUN,'(DT)') param - call self%writer(LUN, iotype = "none", v_list = [0], iostat = ierr, iomsg = error_message) - if (ierr /= 0) then - write(*,*) trim(adjustl(error_message)) - call util_exit(FAILURE) - end if - close(LUN) - - return - end subroutine io_dump_param - - - module subroutine io_dump_swiftest(self, param, msg) - !! author: David A. Minton - !! - !! Dump massive body data to files - !! - !! Adapted from David E. Kaufmann's Swifter routine: io_dump_pl.f90 and io_dump_tp.f90 - !! Adapted from Hal Levison's Swift routine io_dump_pl.f and io_dump_tp.f - implicit none - ! Arguments - class(swiftest_base), intent(inout) :: self !! Swiftest base object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters - character(*), optional, intent(in) :: msg !! Message to display with dump operation - ! Internals - integer(I4B) :: ierr !! Error code - integer(I4B),parameter :: LUN = 7 !! Unit number for dump file - integer(I4B) :: iu = LUN - character(len=:), allocatable :: dump_file_name - - select type(self) - class is(swiftest_cb) - dump_file_name = trim(adjustl(param%incbfile)) - class is (swiftest_pl) - dump_file_name = trim(adjustl(param%inplfile)) - class is (swiftest_tp) - dump_file_name = trim(adjustl(param%intpfile)) - end select - open(unit = iu, file = dump_file_name, form = "UNFORMATTED", status = 'replace', iostat = ierr) - if (ierr /= 0) then - write(*, *) "Swiftest error:" - write(*, *) " Unable to open binary dump file " // dump_file_name - call util_exit(FAILURE) - end if - call self%write_frame(iu, param) - close(LUN) - - return - end subroutine io_dump_swiftest - - - module subroutine io_dump_system(self, param, msg) - !! author: David A. Minton - !! - !! Dumps the state of the system to files in case the simulation is interrupted. - !! As a safety mechanism, there are two dump files that are written in alternating order - !! so that if a dump file gets corrupted during writing, the user can restart from the older one. - implicit none - ! Arguments - class(swiftest_nbody_system), intent(inout) :: self !! Swiftest system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters - character(*), optional, intent(in) :: msg !! Message to display with dump operation - ! Internals - class(swiftest_parameters), allocatable :: dump_param !! Local parameters variable used to parameters change input file names - !! to dump file-specific values without changing the user-defined values - integer(I4B), save :: idx = 1 !! Index of current dump file. Output flips between 2 files for extra security - !! in case the program halts during writing - character(len=:), allocatable :: param_file_name - real(DP) :: tfrac - - allocate(dump_param, source=param) - param_file_name = trim(adjustl(DUMP_PARAM_FILE(idx))) - dump_param%incbfile = trim(adjustl(DUMP_CB_FILE(idx))) - dump_param%inplfile = trim(adjustl(DUMP_PL_FILE(idx))) - dump_param%intpfile = trim(adjustl(DUMP_TP_FILE(idx))) - dump_param%out_form = XV - dump_param%out_stat = 'APPEND' - dump_param%T0 = param%t - call dump_param%dump(param_file_name) - - call self%cb%dump(dump_param) - if (self%pl%nbody > 0) call self%pl%dump(dump_param) - if (self%tp%nbody > 0) call self%tp%dump(dump_param) - - idx = idx + 1 - if (idx > NDUMPFILES) idx = 1 - - ! Print the status message (format code passed in from main driver) - tfrac = (param%t - param%t0) / (param%tstop - param%t0) - write(*,msg) param%t, tfrac, self%pl%nbody, self%tp%nbody - - return - end subroutine io_dump_system - - - module function io_get_args(integrator, param_file_name) result(ierr) - !! author: David A. Minton - !! - !! Reads in the name of the parameter file from command line arguments. - implicit none - ! Arguments - integer(I4B) :: integrator !! Symbolic code of the requested integrator - character(len=:), allocatable :: param_file_name !! Name of the input parameters file - ! Result - integer(I4B) :: ierr !! I/O error code - ! Internals - character(len=STRMAX) :: arg1, arg2 - integer :: narg,ierr_arg1, ierr_arg2 - character(len=*),parameter :: linefmt = '(A)' + ! Calculate the inverse speed of light in the system units + self%inv_c2 = einsteinC * self%TU2S / self%DU2M + self%inv_c2 = (self%inv_c2)**(-2) - ierr = -1 ! Default is to fail - narg = command_argument_count() ! - if (narg == 2) then - call get_command_argument(1, arg1, status = ierr_arg1) - call get_command_argument(2, arg2, status = ierr_arg2) - if ((ierr_arg1 == 0) .and. (ierr_arg2 == 0)) then - ierr = 0 - call io_toupper(arg1) - select case(arg1) - case('BS') - integrator = BS - case('HELIO') - integrator = HELIO - case('RA15') - integrator = RA15 - case('TU4') - integrator = TU4 - case('WHM') - integrator = WHM - case('RMVS') - integrator = RMVS - case('SYMBA') - integrator = SYMBA - case('RINGMOONS') - integrator = RINGMOONS - case default - integrator = UNKNOWN_INTEGRATOR - write(*,*) trim(adjustl(arg1)) // ' is not a valid integrator.' - ierr = -1 - end select - param_file_name = trim(adjustl(arg2)) - end if - else - call get_command_argument(1, arg1, status = ierr_arg1) - if (ierr_arg1 == 0) then - if (arg1 == '-v' .or. arg1 == '--version') then - call util_version() - else if (arg1 == '-h' .or. arg1 == '--help') then - call util_exit(HELP) + associate(integrator => v_list(1)) + if (integrator == RMVS) then + if (.not.self%lclose) then + write(iomsg,*) 'This integrator requires CHK_CLOSE to be enabled.' + iostat = -1 + return end if end if - end if - if (ierr /= 0) call util_exit(USAGE) + + ! Determine if the GR flag is set correctly for this integrator + select case(integrator) + case(WHM, RMVS, HELIO, SYMBA) + write(*,*) "GR = ", self%lgr + case default + if (self%lgr) write(iomsg, *) 'GR is not yet implemented for this integrator. This parameter will be ignored.' + self%lgr = .false. + end select + end associate - return - end function io_get_args + iostat = 0 + + return + end subroutine io_param_reader - module function io_get_token(buffer, ifirst, ilast, ierr) result(token) + module subroutine io_param_writer(self, unit, iotype, v_list, iostat, iomsg) !! author: David A. Minton !! - !! Retrieves a character token from an input string. Here a token is defined as any set of contiguous non-blank characters not - !! beginning with or containing "!". If "!" is present, any remaining part of the buffer including the "!" is ignored + !! Dump integration parameters to file !! - !! Adapted from David E. Kaufmann's Swifter routine io_get_token.f90 + !! Adapted from David E. Kaufmann's Swifter routine io_dump_param.f90 + !! Adapted from Martin Duncan's Swift routine io_dump_param.f implicit none ! Arguments - character(len=*), intent(in) :: buffer !! Input string buffer - integer(I4B), intent(inout) :: ifirst !! Index of the buffer at which to start the search for a token - integer(I4B), intent(out) :: ilast !! Index of the buffer at the end of the returned token - integer(I4B), intent(out) :: ierr !! Error code - ! Result - character(len=:), allocatable :: token !! Returned token string + class(swiftest_parameters),intent(in) :: self !! Collection of parameters + integer, intent(in) :: unit !! File unit number + character(len=*), intent(in) :: iotype !! Dummy argument passed to the input/output procedure contains the text from the char-literal-constant, prefixed with DT. + !! If you do not include a char-literal-constant, the iotype argument contains only DT. + integer, intent(in) :: v_list(:) !! Not used in this procedure + integer, intent(out) :: iostat !! IO status code + character(len=*), intent(inout) :: iomsg !! Message to pass if iostat /= 0 ! Internals - integer(I4B) :: i,ilength - - ilength = len(buffer) - - if (ifirst > ilength) then - ilast = ifirst - ierr = -1 !! Bad input - token = '' - return - end if - do i = ifirst, ilength - if (buffer(i:i) /= ' ') exit - end do - if ((i > ilength) .or. (buffer(i:i) == '!')) then - ifirst = i - ilast = i - ierr = -2 !! No valid token - token = '' - return - end if - ifirst = i - do i = ifirst, ilength - if ((buffer(i:i) == ' ') .or. (buffer(i:i) == '!')) exit - end do - ilast = i - 1 - ierr = 0 - - token = buffer(ifirst:ilast) + character(*),parameter :: Ifmt = '(I0)' !! Format label for integer values + character(*),parameter :: Rfmt = '(ES25.17)' !! Format label for real values + character(*),parameter :: Rarrfmt = '(3(ES25.17,1X))' !! Format label for real values + character(*),parameter :: Lfmt = '(L1)' !! Format label for logical values + character(len=*), parameter :: Afmt = '(A25,1X,64(:,A25,1X))' + character(256) :: param_name, param_value + type character_array + character(25) :: value + end type character_array + type(character_array), dimension(:), allocatable :: param_array + integer(I4B) :: i + + associate(param => self) + write(param_name, Afmt) "T0"; write(param_value,Rfmt) param%t0; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "TSTOP"; write(param_value, Rfmt) param%tstop; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "DT"; write(param_value, Rfmt) param%dt; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "PL_IN"; write(param_value, Afmt) trim(adjustl(param%inplfile)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "TP_in"; write(param_value, Afmt) trim(adjustl(param%intpfile)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "IN_TYPE"; write(param_value, Afmt) trim(adjustl(param%in_type)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) + if (param%istep_out > 0) then + write(param_name, Afmt) "ISTEP_OUT"; write(param_value, Ifmt) param%istep_out; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "BIN_OUT"; write(param_value, Afmt) trim(adjustl(param%outfile)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "OUT_TYPE"; write(param_value, Afmt) trim(adjustl(param%out_type)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "OUT_FORM"; write(param_value, Afmt) trim(adjustl(param%out_form)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "OUT_STAT"; write(param_value, Afmt) "APPEND"; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + end if + write(param_name, Afmt) "ENC_OUT"; write(param_value, Afmt) trim(adjustl(param%enc_out)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) + if (param%istep_dump > 0) then + write(param_name, Afmt) "ISTEP_DUMP"; write(param_value, Ifmt) param%istep_dump; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + end if + write(param_name, Afmt) "CHK_RMIN"; write(param_value, Rfmt) param%rmin; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "CHK_RMAX"; write(param_value, Rfmt) param%rmax; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "CHK_EJECT"; write(param_value, Rfmt) param%rmaxu; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "CHK_QMIN"; write(param_value, Rfmt) param%qmin; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + if (param%qmin >= 0.0_DP) then + write(param_name, Afmt) "CHK_QMIN_COORD"; write(param_value, Afmt) trim(adjustl(param%qmin_coord)); write(unit, Afmt) adjustl(param_name), adjustl(param_value) + allocate(param_array(2)) + write(param_array(1)%value, Rfmt) param%qmin_alo + write(param_array(2)%value, Rfmt) param%qmin_ahi + write(param_name, Afmt) "CHK_QMIN_RANGE"; write(unit, Afmt) adjustl(param_name), adjustl(param_array(1)%value), adjustl(param_array(2)%value) + end if + write(param_name, Afmt) "MU2KG"; write(param_value, Rfmt) param%MU2KG; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "TU2S"; write(param_value, Rfmt) param%TU2S ; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "DU2M"; write(param_value, Rfmt) param%DU2M; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "RHILL_PRESENT"; write(param_value, Lfmt) param%lrhill_present; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "EXTRA_FORCE"; write(param_value, Lfmt) param%lextra_force; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "BIG_DISCARD"; write(param_value, Lfmt) param%lbig_discard; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "CHK_CLOSE"; write(param_value, Lfmt) param%lclose; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "ENERGY"; write(param_value, Lfmt) param%lenergy; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "GR"; write(param_value, Lfmt) param%lgr; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "ROTATION"; write(param_value, Lfmt) param%lrotation; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "TIDES"; write(param_value, Lfmt) param%ltides; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + iostat = 0 + iomsg = "UDIO not implemented" + end associate return - end function io_get_token + end subroutine io_param_writer module subroutine io_read_body_in(self, param) @@ -718,50 +816,9 @@ module subroutine io_read_cb_in(self, param) end subroutine io_read_cb_in - module subroutine io_read_param_in(self, param_file_name) - !! author: David A. Minton - !! - !! Read in parameters for the integration - !! - !! Adapted from David E. Kaufmann's Swifter routine io_init_param.f90 - !! Adapted from Martin Duncan's Swift routine io_init_param.f - implicit none - ! Arguments - class(swiftest_parameters),intent(inout) :: self !! Current run configuration parameters - character(len=*), intent(in) :: param_file_name !! Parameter input file name (i.e. param.in) - ! Internals - integer(I4B), parameter :: LUN = 7 !! Unit number of input file - integer(I4B) :: ierr = 0 !! Input error code - character(STRMAX) :: error_message !! Error message in UDIO procedure - - ! Read in name of parameter file - write(*, *) 'Parameter input file is ', trim(adjustl(param_file_name)) - write(*, *) ' ' - 100 format(A) - open(unit = LUN, file = param_file_name, status = 'old', iostat = ierr) - if (ierr /= 0) then - write(*,*) 'Swiftest error: ', ierr - write(*,*) ' Unable to open file ',trim(adjustl(param_file_name)) - call util_exit(FAILURE) - end if - - !! todo: Currently this procedure does not work in user-defined derived-type input mode - !! as the newline characters are ignored in the input file when compiled in ifort. - - !read(LUN,'(DT)', iostat= ierr, iomsg = error_message) param - call self%reader(LUN, iotype= "none", v_list = [self%integrator], iostat = ierr, iomsg = error_message) - if (ierr /= 0) then - write(*,*) 'Swiftest error reading ', trim(adjustl(param_file_name)) - write(*,*) ierr,trim(adjustl(error_message)) - call util_exit(FAILURE) - end if - - return - end subroutine io_read_param_in - function io_read_encounter(t, name1, name2, mass1, mass2, radius1, radius2, & - xh1, xh2, vh1, vh2, encounter_file, out_type) result(ierr) + xh1, xh2, vh1, vh2, enc_out, out_type) result(ierr) !! author: David A. Minton !! !! Read close encounter data from input binary files @@ -773,7 +830,7 @@ function io_read_encounter(t, name1, name2, mass1, mass2, radius1, radius2, & integer(I4B), intent(out) :: name1, name2 real(DP), intent(out) :: t, mass1, mass2, radius1, radius2 real(DP), dimension(:), intent(out) :: xh1, xh2, vh1, vh2 - character(*), intent(in) :: encounter_file, out_type + character(*), intent(in) :: enc_out, out_type ! Result integer(I4B) :: ierr ! Internals @@ -782,7 +839,7 @@ function io_read_encounter(t, name1, name2, mass1, mass2, radius1, radius2, & integer(I4B), save :: iu = lun if (lfirst) then - open(unit = iu, file = encounter_file, status = 'OLD', form = 'UNFORMATTED', iostat = ierr) + open(unit = iu, file = enc_out, status = 'OLD', form = 'UNFORMATTED', iostat = ierr) if (ierr /= 0) then write(*, *) "Swiftest Error:" write(*, *) " unable to open binary encounter file" @@ -795,7 +852,7 @@ function io_read_encounter(t, name1, name2, mass1, mass2, radius1, radius2, & close(unit = iu, iostat = ierr) return end if - + read(iu, iostat = ierr) name1, xh1(1), xh1(2), xh1(3), vh1(1), vh1(2), vh1(3), mass1, radius1 if (ierr /= 0) then close(unit = iu, iostat = ierr) @@ -827,7 +884,7 @@ module subroutine io_read_frame_body(self, iu, param, form, ierr) integer(I4B), intent(out) :: ierr !! Error code associate(n => self%nbody) - read(iu, iostat=ierr, err=100) self%id(1:n) + read(iu, iostat=ierr, err=100) self%id(:) !read(iu, iostat=ierr, err=100) self%name(1:n) select case (form) case (EL) @@ -837,33 +894,33 @@ module subroutine io_read_frame_body(self, iu, param, form, ierr) if (.not.allocated(self%capom)) allocate(self%capom(n)) if (.not.allocated(self%omega)) allocate(self%omega(n)) if (.not.allocated(self%capm)) allocate(self%capm(n)) - read(iu, iostat=ierr, err=100) self%a(1:n) - read(iu, iostat=ierr, err=100) self%e(1:n) - read(iu, iostat=ierr, err=100) self%inc(1:n) + read(iu, iostat=ierr, err=100) self%a(:) + read(iu, iostat=ierr, err=100) self%e(:) + read(iu, iostat=ierr, err=100) self%inc(:) read(iu, iostat=ierr, err=100) self%capom(:) read(iu, iostat=ierr, err=100) self%omega(:) read(iu, iostat=ierr, err=100) self%capm(:) case (XV) - read(iu, iostat=ierr, err=100) self%xh(1, 1:n) - read(iu, iostat=ierr, err=100) self%xh(2, 1:n) - read(iu, iostat=ierr, err=100) self%xh(3, 1:n) - read(iu, iostat=ierr, err=100) self%vh(1, 1:n) - read(iu, iostat=ierr, err=100) self%vh(2, 1:n) - read(iu, iostat=ierr, err=100) self%vh(3, 1:n) + read(iu, iostat=ierr, err=100) self%xh(1, :) + read(iu, iostat=ierr, err=100) self%xh(2, :) + read(iu, iostat=ierr, err=100) self%xh(3, :) + read(iu, iostat=ierr, err=100) self%vh(1, :) + read(iu, iostat=ierr, err=100) self%vh(2, :) + read(iu, iostat=ierr, err=100) self%vh(3, :) end select select type(pl => self) class is (swiftest_pl) ! Additional output if the passed polymorphic object is a massive body - read(iu, iostat=ierr, err=100) pl%Gmass(1:n) - pl%mass(1:n) = pl%Gmass / param%GU - if (param%lrhill_present) read(iu, iostat=ierr, err=100) pl%rhill(1:n) - read(iu, iostat=ierr, err=100) pl%radius(1:n) + read(iu, iostat=ierr, err=100) pl%Gmass(:) + pl%mass(:) = pl%Gmass(:) / param%GU + if (param%lrhill_present) read(iu, iostat=ierr, err=100) pl%rhill(:) + read(iu, iostat=ierr, err=100) pl%radius(:) if (param%lrotation) then - read(iu, iostat=ierr, err=100) pl%rot(1, 1:n) - read(iu, iostat=ierr, err=100) pl%rot(2, 1:n) - read(iu, iostat=ierr, err=100) pl%rot(3, 1:n) - read(iu, iostat=ierr, err=100) pl%Ip(1, 1:n) - read(iu, iostat=ierr, err=100) pl%Ip(2, 1:n) - read(iu, iostat=ierr, err=100) pl%Ip(3, 1:n) + read(iu, iostat=ierr, err=100) pl%rot(1, :) + read(iu, iostat=ierr, err=100) pl%rot(2, :) + read(iu, iostat=ierr, err=100) pl%rot(3, :) + read(iu, iostat=ierr, err=100) pl%Ip(1, :) + read(iu, iostat=ierr, err=100) pl%Ip(2, :) + read(iu, iostat=ierr, err=100) pl%Ip(3, :) end if if (param%ltides) then read(iu, iostat=ierr, err=100) pl%k2(1:n) @@ -919,7 +976,7 @@ module subroutine io_read_frame_cb(self, iu, param, form, ierr) return end subroutine io_read_frame_cb - + module subroutine io_read_frame_system(self, iu, param, form, ierr) !! author: The Purdue Swiftest Team - David A. Minton, Carlisle A. Wishard, Jennifer L.L. Pouplin, and Jacob R. Elliott !! @@ -997,6 +1054,47 @@ function io_read_hdr(iu, t, npl, ntp, out_form, out_type) result(ierr) return end function io_read_hdr + module subroutine io_read_param_in(self, param_file_name) + !! author: David A. Minton + !! + !! Read in parameters for the integration + !! + !! Adapted from David E. Kaufmann's Swifter routine io_init_param.f90 + !! Adapted from Martin Duncan's Swift routine io_init_param.f + implicit none + ! Arguments + class(swiftest_parameters),intent(inout) :: self !! Current run configuration parameters + character(len=*), intent(in) :: param_file_name !! Parameter input file name (i.e. param.in) + ! Internals + integer(I4B), parameter :: LUN = 7 !! Unit number of input file + integer(I4B) :: ierr = 0 !! Input error code + character(STRMAX) :: error_message !! Error message in UDIO procedure + + ! Read in name of parameter file + write(*, *) 'Parameter input file is ', trim(adjustl(param_file_name)) + write(*, *) ' ' + 100 format(A) + open(unit = LUN, file = param_file_name, status = 'old', iostat = ierr) + if (ierr /= 0) then + write(*,*) 'Swiftest error: ', ierr + write(*,*) ' Unable to open file ',trim(adjustl(param_file_name)) + call util_exit(FAILURE) + end if + + !! todo: Currently this procedure does not work in user-defined derived-type input mode + !! as the newline characters are ignored in the input file when compiled in ifort. + + !read(LUN,'(DT)', iostat= ierr, iomsg = error_message) param + call self%reader(LUN, iotype= "none", v_list = [self%integrator], iostat = ierr, iomsg = error_message) + if (ierr /= 0) then + write(*,*) 'Swiftest error reading ', trim(adjustl(param_file_name)) + write(*,*) ierr,trim(adjustl(error_message)) + call util_exit(FAILURE) + end if + + return + end subroutine io_read_param_in + module subroutine io_toupper(string) !! author: David A. Minton @@ -1046,31 +1144,27 @@ module subroutine io_write_discard(self, param) character(*), parameter :: PLNAMEFMT = '(I8, 2(1X, E23.16))' class(swiftest_body), allocatable :: pltemp - associate(t => param%t, discards => self%tp_discards, nsp => self%tp_discards%nbody, dxh => self%tp_discards%xh, dvh => self%tp_discards%vh, & - dname => self%tp_discards%id, dstatus => self%tp_discards%status) - + associate(tp_discards => self%tp_discards, nsp => self%tp_discards%nbody, pl => self%pl, npl => self%pl%nbody) + if (nsp == 0) return select case(param%out_stat) case('APPEND') - open(unit = LUN, file = param%outfile, status = 'OLD', position = 'APPEND', form = 'UNFORMATTED', iostat = ierr) + open(unit = LUN, file = param%discard_out, status = 'OLD', position = 'APPEND', form = 'FORMATTED', iostat = ierr) case('NEW', 'REPLACE', 'UNKNOWN') - open(unit = LUN, file = param%outfile, status = param%out_stat, form = 'UNFORMATTED', iostat = ierr) + open(unit = LUN, file = param%discard_out, status = param%out_stat, form = 'FORMATTED', iostat = ierr) case default write(*,*) 'Invalid status code for OUT_STAT: ',trim(adjustl(param%out_stat)) call util_exit(FAILURE) end select lfirst = .false. - if (param%lgr) call discards%pv2v(param) + if (param%lgr) call tp_discards%pv2v(param) - write(LUN, HDRFMT) t, nsp, param%lbig_discard + write(LUN, HDRFMT) param%t, nsp, param%lbig_discard do i = 1, nsp - write(LUN, NAMEFMT) sub, dname(i), dstatus(i) - write(LUN, VECFMT) dxh(1, i), dxh(2, i), dxh(3, i) - write(LUN, VECFMT) dvh(1, i), dvh(2, i), dvh(3, i) + write(LUN, NAMEFMT) SUB, tp_discards%id(i), tp_discards%status(i) + write(LUN, VECFMT) tp_discards%xh(1, i), tp_discards%xh(2, i), tp_discards%xh(3, i) + write(LUN, VECFMT) tp_discards%vh(1, i), tp_discards%vh(2, i), tp_discards%vh(3, i) end do if (param%lbig_discard) then - associate(npl => self%pl%nbody, pl => self%pl, GMpl => self%pl%Gmass, & - Rpl => self%pl%radius, name => self%pl%id, xh => self%pl%xh) - if (param%lgr) then allocate(pltemp, source = pl) call pltemp%pv2v(param) @@ -1082,12 +1176,11 @@ module subroutine io_write_discard(self, param) write(LUN, NPLFMT) npl do i = 1, npl - write(LUN, PLNAMEFMT) name(i), GMpl(i), Rpl(i) - write(LUN, VECFMT) xh(1, i), xh(2, i), xh(3, i) + write(LUN, PLNAMEFMT) pl%id(i), pl%Gmass(i), pl%radius(i) + write(LUN, VECFMT) pl%xh(1, i), pl%xh(2, i), pl%xh(3, i) write(LUN, VECFMT) vh(1, i), vh(2, i), vh(3, i) end do deallocate(vh) - end associate end if close(LUN) end associate @@ -1097,7 +1190,7 @@ end subroutine io_write_discard module subroutine io_write_encounter(t, name1, name2, mass1, mass2, radius1, radius2, & - xh1, xh2, vh1, vh2, encounter_file, out_type) + xh1, xh2, vh1, vh2, enc_out, out_type) !! author: David A. Minton !! !! Write close encounter data to output binary files @@ -1110,16 +1203,16 @@ module subroutine io_write_encounter(t, name1, name2, mass1, mass2, radius1, rad integer(I4B), intent(in) :: name1, name2 real(DP), intent(in) :: t, mass1, mass2, radius1, radius2 real(DP), dimension(:), intent(in) :: xh1, xh2, vh1, vh2 - character(*), intent(in) :: encounter_file, out_type + character(*), intent(in) :: enc_out, out_type ! Internals logical , save :: lfirst = .true. integer(I4B), parameter :: lun = 30 integer(I4B) :: ierr integer(I4B), save :: iu = lun - open(unit = iu, file = encounter_file, status = 'OLD', position = 'APPEND', form = 'UNFORMATTED', iostat = ierr) + open(unit = iu, file = enc_out, status = 'OLD', position = 'APPEND', form = 'UNFORMATTED', iostat = ierr) if ((ierr /= 0) .and. lfirst) then - open(unit = iu, file = encounter_file, status = 'NEW', form = 'UNFORMATTED', iostat = ierr) + open(unit = iu, file = enc_out, status = 'NEW', form = 'UNFORMATTED', iostat = ierr) end if if (ierr /= 0) then write(*, *) "Swiftest Error:" diff --git a/src/main/swiftest_driver.f90 b/src/main/swiftest_driver.f90 index 805264c2c..55eb1bc89 100644 --- a/src/main/swiftest_driver.f90 +++ b/src/main/swiftest_driver.f90 @@ -67,7 +67,7 @@ program swiftest_driver t = t0 + iloop * dt - !> Evaluate any discards or mergers + !> Evaluate any discards or collisional outcomes call nbody_system%discard(param) !> If the loop counter is at the output cadence value, append the data file with a single frame diff --git a/src/modules/rmvs_classes.f90 b/src/modules/rmvs_classes.f90 index 88e3ee217..4f7255237 100644 --- a/src/modules/rmvs_classes.f90 +++ b/src/modules/rmvs_classes.f90 @@ -70,8 +70,10 @@ module rmvs_classes procedure :: encounter_check => rmvs_encounter_check_tp !! Checks if any test particles are undergoing a close encounter with a massive body procedure :: accel => rmvs_kick_getacch_tp !! Calculates either the standard or modified version of the acceleration depending if the !! if the test particle is undergoing a close encounter or not - procedure :: setup => rmvs_setup_tp !! Constructor method - Allocates space for number of particles - procedure :: fill => rmvs_util_fill_tp !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) + procedure :: setup => rmvs_setup_tp !! Constructor method - Allocates space for the input number of bodiess + procedure :: append => rmvs_util_append_tp !! Appends elements from one structure to another + procedure :: fill => rmvs_util_fill_tp !! "Fills" bodies from one object into another depending on the results of a mask (uses the UNPACK intrinsic) + procedure :: resize => rmvs_util_resize_tp !! Checks the current size of a Swiftest body against the requested size and resizes it if it is too small. procedure :: sort => rmvs_util_sort_tp !! Sorts body arrays by a sortable componen procedure :: rearrange => rmvs_util_sort_rearrange_tp !! Rearranges the order of array elements of body based on an input index array. Used in sorting methods procedure :: spill => rmvs_util_spill_tp !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) @@ -91,10 +93,12 @@ module rmvs_classes class(rmvs_nbody_system), dimension(:), allocatable :: planetocentric !! Planetocentric version of the massive body objects (one for each massive body) logical :: lplanetocentric = .false. !! Flag that indicates that the object is a planetocentric set of masive bodies used for close encounter calculations contains - procedure :: setup => rmvs_setup_pl !! Constructor method - Allocates space for number of particles + procedure :: setup => rmvs_setup_pl !! Constructor method - Allocates space for the input number of bodiess + procedure :: append => rmvs_util_append_pl !! Appends elements from one structure to another + procedure :: fill => rmvs_util_fill_pl !! "Fills" bodies from one object into another depending on the results of a mask (uses the UNPACK intrinsic) + procedure :: resize => rmvs_util_resize_pl !! Checks the current size of a Swiftest body against the requested size and resizes it if it is too small. procedure :: sort => rmvs_util_sort_pl !! Sorts body arrays by a sortable componen procedure :: rearrange => rmvs_util_sort_rearrange_pl !! Rearranges the order of array elements of body based on an input index array. Used in sorting methods - procedure :: fill => rmvs_util_fill_pl !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) procedure :: spill => rmvs_util_spill_pl !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) end type rmvs_pl @@ -110,7 +114,7 @@ module subroutine rmvs_discard_tp(self, system, param) implicit none class(rmvs_tp), intent(inout) :: self !! RMVS test particle object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters end subroutine rmvs_discard_tp module function rmvs_encounter_check_tp(self, system, dt) result(lencounter) @@ -154,11 +158,27 @@ module subroutine rmvs_setup_tp(self, n, param) class(swiftest_parameters), intent(in) :: param !! Current run configuration parametere end subroutine rmvs_setup_tp + module subroutine rmvs_util_append_pl(self, source, lsource_mask) + use swiftest_classes, only : swiftest_body + implicit none + class(rmvs_pl), intent(inout) :: self !! RMVS massive body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine rmvs_util_append_pl + + module subroutine rmvs_util_append_tp(self, source, lsource_mask) + use swiftest_classes, only : swiftest_body + implicit none + class(rmvs_tp), intent(inout) :: self !! RMVS test particle object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine rmvs_util_append_tp + module subroutine rmvs_util_fill_pl(self, inserts, lfill_list) use swiftest_classes, only : swiftest_body implicit none class(rmvs_pl), intent(inout) :: self !! RMVS massive body object - class(swiftest_body), intent(inout) :: inserts !! Inserted object + class(swiftest_body), intent(in) :: inserts !! Inserted object logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps end subroutine rmvs_util_fill_pl @@ -166,10 +186,22 @@ module subroutine rmvs_util_fill_tp(self, inserts, lfill_list) use swiftest_classes, only : swiftest_body implicit none class(rmvs_tp), intent(inout) :: self !! RMVS massive body object - class(swiftest_body), intent(inout) :: inserts !! Inserted object + class(swiftest_body), intent(in) :: inserts !! Inserted object logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps end subroutine rmvs_util_fill_tp + module subroutine rmvs_util_resize_pl(self, nnew) + implicit none + class(rmvs_pl), intent(inout) :: self !! RMVS massive body object + integer(I4B), intent(in) :: nnew !! New size neded + end subroutine rmvs_util_resize_pl + + module subroutine rmvs_util_resize_tp(self, nnew) + implicit none + class(rmvs_tp), intent(inout) :: self !! RMVS test particle object + integer(I4B), intent(in) :: nnew !! New size neded + end subroutine rmvs_util_resize_tp + module subroutine rmvs_util_sort_pl(self, sortby, ascending) implicit none class(rmvs_pl), intent(inout) :: self !! RMVS massive body object @@ -196,20 +228,22 @@ module subroutine rmvs_util_sort_rearrange_tp(self, ind) integer(I4B), dimension(:), intent(in) :: ind !! Index array used to restructure the body (should contain all 1:n index values in the desired order) end subroutine rmvs_util_sort_rearrange_tp - module subroutine rmvs_util_spill_pl(self, discards, lspill_list) + module subroutine rmvs_util_spill_pl(self, discards, lspill_list, ldestructive) use swiftest_classes, only : swiftest_body implicit none - class(rmvs_pl), intent(inout) :: self !! RMVS massive body object - class(swiftest_body), intent(inout) :: discards !! Discarded object - logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + class(rmvs_pl), intent(inout) :: self !! RMVS massive body object + class(swiftest_body), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not end subroutine rmvs_util_spill_pl - module subroutine rmvs_util_spill_tp(self, discards, lspill_list) + module subroutine rmvs_util_spill_tp(self, discards, lspill_list, ldestructive) use swiftest_classes, only : swiftest_body implicit none - class(rmvs_tp), intent(inout) :: self !! RMVS test particle object - class(swiftest_body), intent(inout) :: discards !! Discarded object - logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + class(rmvs_tp), intent(inout) :: self !! RMVS test particle object + class(swiftest_body), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not end subroutine rmvs_util_spill_tp module subroutine rmvs_step_system(self, param, t, dt) diff --git a/src/modules/swiftest_classes.f90 b/src/modules/swiftest_classes.f90 index 913d678eb..dcff0f6d8 100644 --- a/src/modules/swiftest_classes.f90 +++ b/src/modules/swiftest_classes.f90 @@ -38,14 +38,16 @@ module swiftest_classes character(STRMAX) :: qmin_coord = 'HELIO' !! Coordinate frame to use for qmin real(DP) :: qmin_alo = -1.0_DP !! Minimum semimajor axis for qmin real(DP) :: qmin_ahi = -1.0_DP !! Maximum semimajor axis for qmin - character(STRMAX) :: encounter_file = ENC_OUTFILE !! Name of output file for encounters + character(STRMAX) :: enc_out = "" !! Name of output file for encounters + character(STRMAX) :: discard_out = "" !! Name of output file for discards real(QP) :: MU2KG = -1.0_QP !! Converts mass units to grams real(QP) :: TU2S = -1.0_QP !! Converts time units to seconds real(QP) :: DU2M = -1.0_QP !! Converts distance unit to centimeters real(DP) :: GU = -1.0_DP !! Universal gravitational constant in the system units real(DP) :: inv_c2 = -1.0_DP !! Inverse speed of light squared in the system units + character(STRMAX) :: ennergy_out = "" !! Name of output energy and momentum report file - !Logical flags to turn on or off various features of the code + ! Logical flags to turn on or off various features of the code logical :: lrhill_present = .false. !! Hill radii are given as an input rather than calculated by the code (can be used to inflate close encounter regions manually) logical :: lextra_force = .false. !! User defined force function turned on logical :: lbig_discard = .false. !! Save big bodies on every discard @@ -55,6 +57,16 @@ module swiftest_classes logical :: lrotation = .false. !! Include rotation states of big bodies logical :: ltides = .false. !! Include tidal dissipation + ! Initial values to pass to the energy report subroutine (usually only used in the case of a restart, otherwise these will be updated with initial conditions values) + real(DP) :: Eorbit_orig = 0.0_DP !! Initial orbital energy + real(DP) :: Mtot_orig = 0.0_DP !! Initial system mass + real(DP) :: Lmag_orig = 0.0_DP !! Initial total angular momentum magnitude + real(DP), dimension(NDIM) :: Ltot_orig = 0.0_DP !! Initial total angular momentum vector + real(DP), dimension(NDIM) :: Lorbit_orig = 0.0_DP !! Initial orbital angular momentum + real(DP), dimension(NDIM) :: Lspin_orig = 0.0_DP !! Initial spin angular momentum vector + logical :: lfirstenergy = .true. !! This is the first time computing energe + logical :: lfirstkick = .true. !! Initiate the first kick in a symplectic step + ! Future features not implemented or in development logical :: lgr = .false. !! Turn on GR logical :: lyarkovsky = .false. !! Turn on Yarkovsky effect @@ -74,7 +86,7 @@ module swiftest_classes logical :: lintegrate = .false. !! Flag indicating that this object should be integrated in the current step contains !! The minimal methods that all systems must have - procedure :: dump => io_dump_swiftest + procedure :: dump => io_dump_swiftest procedure(abstract_initialize), deferred :: initialize procedure(abstract_read_frame), deferred :: read_frame procedure(abstract_write_frame), deferred :: write_frame @@ -85,7 +97,7 @@ module swiftest_classes !******************************************************************************************************************************** !> A concrete lass for the central body in a Swiftest simulation type, abstract, extends(swiftest_base) :: swiftest_cb - character(len=STRMAX) :: name !! Non-unique name + character(len=STRMAX) :: name !! Non-unique name integer(I4B) :: id = 0 !! External identifier (unique) real(DP) :: mass = 0.0_DP !! Central body mass (units MU) real(DP) :: Gmass = 0.0_DP !! Central mass gravitational term G * mass (units GU * MU) @@ -127,6 +139,8 @@ module swiftest_classes integer(I4B), dimension(:), allocatable :: id !! External identifier (unique) integer(I4B), dimension(:), allocatable :: status !! An integrator-specific status indicator logical, dimension(:), allocatable :: ldiscard !! Body should be discarded + logical, dimension(:), allocatable :: lmask !! Logical mask used to select a subset of bodies when performing certain operations (drift, kick, accel, etc.) + real(DP), dimension(:), allocatable :: mu !! G * (Mcb + [m]) real(DP), dimension(:,:), allocatable :: xh !! Heliocentric position real(DP), dimension(:,:), allocatable :: vh !! Heliocentric velocity real(DP), dimension(:,:), allocatable :: xb !! Barycentric position @@ -142,8 +156,6 @@ module swiftest_classes real(DP), dimension(:), allocatable :: capom !! Longitude of ascending node real(DP), dimension(:), allocatable :: omega !! Argument of pericenter real(DP), dimension(:), allocatable :: capm !! Mean anomaly - real(DP), dimension(:), allocatable :: mu !! G * (Mcb + [m]) - logical, dimension(:), allocatable :: lmask !! Logical mask used to select a subset of bodies when performing certain operations (drift, kick, accel, etc.) !! Note to developers: If you add components to this class, be sure to update methods and subroutines that traverse the !! component list, such as setup_body and util_spill contains @@ -164,7 +176,9 @@ module swiftest_classes procedure :: xv2el => orbel_xv2el_vec !! Convert position and velocity vectors to orbital elements procedure :: setup => setup_body !! A constructor that sets the number of bodies and allocates all allocatable arrays procedure :: accel_user => user_kick_getacch_body !! Add user-supplied heliocentric accelerations to planets - procedure :: fill => util_fill_body !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) + procedure :: append => util_append_body !! Appends elements from one structure to another + procedure :: fill => util_fill_body !! "Fills" bodies from one object into another depending on the results of a mask (uses the UNPACK intrinsic) + procedure :: resize => util_resize_body !! Checks the current size of a Swiftest body against the requested size and resizes it if it is too small. procedure :: set_ir3 => util_set_ir3h !! Sets the inverse heliocentric radius term (1/rh**3) procedure :: sort => util_sort_body !! Sorts body arrays by a sortable componen procedure :: rearrange => util_sort_rearrange_body !! Rearranges the order of array elements of body based on an input index array. Used in sorting methods @@ -203,9 +217,11 @@ module swiftest_classes procedure :: accel_obl => obl_acc_pl !! Compute the barycentric accelerations of bodies due to the oblateness of the central body procedure :: setup => setup_pl !! A base constructor that sets the number of bodies and allocates and initializes all arrays procedure :: accel_tides => tides_kick_getacch_pl !! Compute the accelerations of bodies due to tidal interactions with the central body + procedure :: append => util_append_pl !! Appends elements from one structure to another procedure :: h2b => util_coord_h2b_pl !! Convert massive bodies from heliocentric to barycentric coordinates (position and velocity) procedure :: b2h => util_coord_b2h_pl !! Convert massive bodies from barycentric to heliocentric coordinates (position and velocity) - procedure :: fill => util_fill_pl !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) + procedure :: fill => util_fill_pl !! "Fills" bodies from one object into another depending on the results of a mask (uses the UNPACK intrinsic) + procedure :: resize => util_resize_pl !! Checks the current size of a Swiftest body against the requested size and resizes it if it is too small. procedure :: set_beg_end => util_set_beg_end_pl !! Sets the beginning and ending positions and velocities of planets. procedure :: set_mu => util_set_mu_pl !! Method used to construct the vectorized form of the central body mass procedure :: set_rhill => util_set_rhill !! Calculates the Hill's radii for each body @@ -228,18 +244,20 @@ module swiftest_classes contains ! Test particle-specific concrete methods ! These are concrete because they are the same implemenation for all integrators - procedure :: discard => discard_tp !! Check to see if test particles should be discarded based on their positions relative to the massive bodies - procedure :: accel_int => kick_getacch_int_tp !! Compute direct cross (third) term heliocentric accelerations of test particles by massive bodies - procedure :: accel_obl => obl_acc_tp !! Compute the barycentric accelerations of bodies due to the oblateness of the central body - procedure :: setup => setup_tp !! A base constructor that sets the number of bodies and - procedure :: h2b => util_coord_h2b_tp !! Convert test particles from heliocentric to barycentric coordinates (position and velocity) - procedure :: b2h => util_coord_b2h_tp !! Convert test particles from barycentric to heliocentric coordinates (position and velocity) - procedure :: fill => util_fill_tp !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) - procedure :: get_peri => util_peri_tp !! Determine system pericenter passages for test particles - procedure :: set_mu => util_set_mu_tp !! Method used to construct the vectorized form of the central body mass - procedure :: sort => util_sort_tp !! Sorts body arrays by a sortable component - procedure :: rearrange => util_sort_rearrange_tp !! Rearranges the order of array elements of body based on an input index array. Used in sorting methods - procedure :: spill => util_spill_tp !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) + procedure :: discard => discard_tp !! Check to see if test particles should be discarded based on their positions relative to the massive bodies + procedure :: accel_int => kick_getacch_int_tp !! Compute direct cross (third) term heliocentric accelerations of test particles by massive bodies + procedure :: accel_obl => obl_acc_tp !! Compute the barycentric accelerations of bodies due to the oblateness of the central body + procedure :: setup => setup_tp !! A base constructor that sets the number of bodies and + procedure :: append => util_append_tp !! Appends elements from one structure to another + procedure :: h2b => util_coord_h2b_tp !! Convert test particles from heliocentric to barycentric coordinates (position and velocity) + procedure :: b2h => util_coord_b2h_tp !! Convert test particles from barycentric to heliocentric coordinates (position and velocity) + procedure :: fill => util_fill_tp !! "Fills" bodies from one object into another depending on the results of a mask (uses the UNPACK intrinsic) + procedure :: get_peri => util_peri_tp !! Determine system pericenter passages for test particles + procedure :: resize => util_resize_tp !! Checks the current size of a Swiftest body against the requested size and resizes it if it is too small. + procedure :: set_mu => util_set_mu_tp !! Method used to construct the vectorized form of the central body mass + procedure :: sort => util_sort_tp !! Sorts body arrays by a sortable component + procedure :: rearrange => util_sort_rearrange_tp !! Rearranges the order of array elements of body based on an input index array. Used in sorting methods + procedure :: spill => util_spill_tp !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) end type swiftest_tp !******************************************************************************************************************************** @@ -252,7 +270,7 @@ module swiftest_classes class(swiftest_pl), allocatable :: pl !! Massive body data structure class(swiftest_tp), allocatable :: tp !! Test particle data structure class(swiftest_tp), allocatable :: tp_discards !! Discarded test particle data structure - real(DP) :: msys = 0.0_DP !! Total system mass - used for barycentric coordinate conversion + real(DP) :: Gmtot = 0.0_DP !! Total system mass - used for barycentric coordinate conversion real(DP) :: ke = 0.0_DP !! System kinetic energy real(DP) :: pe = 0.0_DP !! System potential energy real(DP) :: te = 0.0_DP !! System total energy @@ -269,22 +287,41 @@ module swiftest_classes procedure(abstract_step_system), deferred :: step ! Concrete classes that are common to the basic integrator (only test particles considered for discard) - procedure :: discard => discard_system !! Perform a discard step on the system - procedure :: dump => io_dump_system !! Dump the state of the system to a file - procedure :: read_frame => io_read_frame_system !! Append a frame of output data to file - procedure :: write_discard => io_write_discard !! Append a frame of output data to file - procedure :: write_frame => io_write_frame_system !! Append a frame of output data to file - procedure :: initialize => setup_initialize_system !! Initialize the system from input files - procedure :: step_spin => tides_step_spin_system !! Steps the spins of the massive & central bodies due to tides. - procedure :: set_msys => util_set_msys !! Sets the value of msys from the masses of system bodies. + procedure :: discard => discard_system !! Perform a discard step on the system + procedure :: conservation_report => io_conservation_report !! Compute energy and momentum and print out the change with time + procedure :: dump => io_dump_system !! Dump the state of the system to a file + procedure :: read_frame => io_read_frame_system !! Read in a frame of input data from file + procedure :: write_discard => io_write_discard !! Write out information about discarded test particles + procedure :: write_frame => io_write_frame_system !! Append a frame of output data to file + procedure :: initialize => setup_initialize_system !! Initialize the system from input files + procedure :: step_spin => tides_step_spin_system !! Steps the spins of the massive & central bodies due to tides. + procedure :: set_msys => util_set_msys !! Sets the value of msys from the masses of system bodies. + procedure :: get_energy_and_momentum => util_get_energy_momentum_system !! Calculates the total system energy and momentum end type swiftest_nbody_system + type :: swiftest_encounter + integer(I4B) :: nenc !! Total number of encounters + logical, dimension(:), allocatable :: lvdotr !! relative vdotr flag + integer(I4B), dimension(:), allocatable :: status !! status of the interaction + integer(I4B), dimension(:), allocatable :: index1 !! position of the first body in the encounter + integer(I4B), dimension(:), allocatable :: index2 !! position of the second body in the encounter + real(DP), dimension(:,:), allocatable :: x1 !! the position of body 1 in the encounter + real(DP), dimension(:,:), allocatable :: x2 !! the position of body 2 in the encounter + real(DP), dimension(:,:), allocatable :: v1 !! the velocity of body 1 in the encounter + real(DP), dimension(:,:), allocatable :: v2 !! the velocity of body 2 in the encounter + contains + procedure :: setup => setup_encounter !! A constructor that sets the number of encounters and allocates and initializes all arrays + procedure :: copy => util_copy_encounter !! Copies elements from the source encounter list into self. + procedure :: spill => util_spill_encounter !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) + procedure :: resize => util_resize_encounter !! Checks the current size of the encounter list against the required size and extends it by a factor of 2 more than requested if it is too small. + end type swiftest_encounter + abstract interface subroutine abstract_discard_body(self, system, param) import swiftest_body, swiftest_nbody_system, swiftest_parameters class(swiftest_body), intent(inout) :: self !! Swiftest body object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters end subroutine abstract_discard_body subroutine abstract_accel(self, system, param, t, lbeg) @@ -360,20 +397,20 @@ module subroutine discard_pl(self, system, param) implicit none class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameter + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameter end subroutine discard_pl module subroutine discard_system(self, param) implicit none class(swiftest_nbody_system), intent(inout) :: self !! Swiftest system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters end subroutine discard_system module subroutine discard_tp(self, system, param) implicit none class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters end subroutine discard_tp module pure subroutine drift_all(mu, x, v, n, param, dt, mask, iflag) @@ -463,6 +500,13 @@ module pure subroutine gr_vh2pv_body(self, param) class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine gr_vh2pv_body + module subroutine io_conservation_report(self, param, lterminal) + implicit none + class(swiftest_nbody_system), intent(inout) :: self !! Swiftest nbody system object + class(swiftest_parameters), intent(inout) :: param !! Input colleciton of user-defined parameters + logical, intent(in) :: lterminal !! Indicates whether to output information to the terminal screen + end subroutine io_conservation_report + module subroutine io_dump_param(self, param_file_name) implicit none class(swiftest_parameters),intent(in) :: self !! Output collection of parameters @@ -578,12 +622,12 @@ module subroutine io_toupper(string) end subroutine io_toupper module subroutine io_write_encounter(t, name1, name2, mass1, mass2, radius1, radius2, & - xh1, xh2, vh1, vh2, encounter_file, out_type) + xh1, xh2, vh1, vh2, enc_out, out_type) implicit none integer(I4B), intent(in) :: name1, name2 real(DP), intent(in) :: t, mass1, mass2, radius1, radius2 real(DP), dimension(:), intent(in) :: xh1, xh2, vh1, vh2 - character(*), intent(in) :: encounter_file, out_type + character(*), intent(in) :: enc_out, out_type end subroutine io_write_encounter module subroutine io_write_frame_body(self, iu, param) @@ -638,6 +682,17 @@ module subroutine obl_acc_tp(self, system) class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object end subroutine obl_acc_tp + module subroutine obl_pot(npl, Mcb, Mpl, j2rp2, j4rp4, xh, irh, oblpot) + implicit none + integer(I4B), intent(in) :: npl + real(DP), intent(in) :: Mcb + real(DP), dimension(:), intent(in) :: Mpl + real(DP), intent(in) :: j2rp2, j4rp4 + real(DP), dimension(:), intent(in) :: irh + real(DP), dimension(:, :), intent(in) :: xh + real(DP), intent(out) :: oblpot + end subroutine obl_pot + module subroutine orbel_el2xv_vec(self, cb) implicit none class(swiftest_body), intent(inout) :: self !! Swiftest body object @@ -690,6 +745,12 @@ module subroutine setup_construct_system(system, param) class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine setup_construct_system + module subroutine setup_encounter(self, n) + implicit none + class(swiftest_encounter), intent(inout) :: self !! Swiftest encounter structure + integer(I4B), intent(in) :: n !! Number of encounters to allocate space for + end subroutine setup_encounter + module subroutine setup_initialize_system(self, param) implicit none class(swiftest_nbody_system), intent(inout) :: self !! Swiftest system object @@ -732,6 +793,66 @@ module subroutine user_kick_getacch_body(self, system, param, t, lbeg) real(DP), intent(in) :: t !! Current time logical, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step end subroutine user_kick_getacch_body + end interface + + interface util_append + module subroutine util_append_arr_char_string(arr, source, lsource_mask) + implicit none + character(len=STRMAX), dimension(:), allocatable, intent(inout) :: arr !! Destination array + character(len=STRMAX), dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine util_append_arr_char_string + + module subroutine util_append_arr_DP(arr, source, lsource_mask) + implicit none + real(DP), dimension(:), allocatable, intent(inout) :: arr !! Destination array + real(DP), dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine util_append_arr_DP + + module subroutine util_append_arr_DPvec(arr, source, lsource_mask) + implicit none + real(DP), dimension(:,:), allocatable, intent(inout) :: arr !! Destination array + real(DP), dimension(:,:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine util_append_arr_DPvec + + module subroutine util_append_arr_I4B(arr, source, lsource_mask) + implicit none + integer(I4B), dimension(:), allocatable, intent(inout) :: arr !! Destination array + integer(I4B), dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine util_append_arr_I4B + + module subroutine util_append_arr_logical(arr, source, lsource_mask) + implicit none + logical, dimension(:), allocatable, intent(inout) :: arr !! Destination array + logical, dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine util_append_arr_logical + end interface + + interface + module subroutine util_append_body(self, source, lsource_mask) + implicit none + class(swiftest_body), intent(inout) :: self !! Swiftest body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine util_append_body + + module subroutine util_append_pl(self, source, lsource_mask) + implicit none + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine util_append_pl + + module subroutine util_append_tp(self, source, lsource_mask) + implicit none + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine util_append_tp module subroutine util_coord_b2h_pl(self, cb) implicit none @@ -757,6 +878,12 @@ module subroutine util_coord_h2b_tp(self, cb) class(swiftest_cb), intent(in) :: cb !! Swiftest central body object end subroutine util_coord_h2b_tp + module subroutine util_copy_encounter(self, source) + implicit none + class(swiftest_encounter), intent(inout) :: self !! Encounter list + class(swiftest_encounter), intent(in) :: source !! Source object to copy into + end subroutine util_copy_encounter + module subroutine util_exit(code) implicit none integer(I4B), intent(in) :: code !! Failure exit code @@ -765,30 +892,138 @@ end subroutine util_exit module subroutine util_fill_body(self, inserts, lfill_list) implicit none class(swiftest_body), intent(inout) :: self !! Swiftest body object - class(swiftest_body), intent(inout) :: inserts !! Swiftest body object to be inserted + class(swiftest_body), intent(in) :: inserts !! Swiftest body object to be inserted logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps end subroutine util_fill_body module subroutine util_fill_pl(self, inserts, lfill_list) implicit none class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object - class(swiftest_body), intent(inout) :: inserts !! Swiftest body object to be inserted + class(swiftest_body), intent(in) :: inserts !! Swiftest body object to be inserted logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps end subroutine util_fill_pl module subroutine util_fill_tp(self, inserts, lfill_list) implicit none class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object - class(swiftest_body), intent(inout) :: inserts !! Swiftest body object to be inserted + class(swiftest_body), intent(in) :: inserts !! Swiftest body object to be inserted logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps end subroutine util_fill_tp + end interface + + interface util_fill + module subroutine util_fill_arr_char_string(keeps, inserts, lfill_list) + implicit none + character(len=STRMAX), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + character(len=STRMAX), dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + end subroutine util_fill_arr_char_string + + module subroutine util_fill_arr_DP(keeps, inserts, lfill_list) + implicit none + real(DP), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + real(DP), dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + end subroutine util_fill_arr_DP + + module subroutine util_fill_arr_DPvec(keeps, inserts, lfill_list) + implicit none + real(DP), dimension(:,:), allocatable, intent(inout) :: keeps !! Array of values to keep + real(DP), dimension(:,:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + end subroutine util_fill_arr_DPvec + + module subroutine util_fill_arr_I4B(keeps, inserts, lfill_list) + implicit none + integer(I4B), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + integer(I4B), dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + end subroutine util_fill_arr_I4B + + module subroutine util_fill_arr_logical(keeps, inserts, lfill_list) + implicit none + logical, dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + logical, dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + end subroutine util_fill_arr_logical + end interface + interface module subroutine util_peri_tp(self, system, param) implicit none class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine util_peri_tp + end interface + + interface util_resize + module subroutine util_resize_arr_char_string(arr, nnew) + implicit none + character(len=STRMAX), dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + end subroutine util_resize_arr_char_string + + module subroutine util_resize_arr_DP(arr, nnew) + implicit none + real(DP), dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + end subroutine util_resize_arr_DP + + module subroutine util_resize_arr_DPvec(arr, nnew) + implicit none + real(DP), dimension(:,:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + end subroutine util_resize_arr_DPvec + + module subroutine util_resize_arr_I4B(arr, nnew) + implicit none + integer(I4B), dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + end subroutine util_resize_arr_I4B + + module subroutine util_resize_arr_logical(arr, nnew) + implicit none + logical, dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + end subroutine util_resize_arr_logical + end interface + + interface + module subroutine util_resize_body(self, nnew) + implicit none + class(swiftest_body), intent(inout) :: self !! Swiftest body object + integer(I4B), intent(in) :: nnew !! New size neded + end subroutine util_resize_body + + module subroutine util_resize_encounter(self, nnew) + implicit none + class(swiftest_encounter), intent(inout) :: self !! Swiftest encounter list + integer(I4B), intent(in) :: nnew !! New size of list needed + end subroutine util_resize_encounter + + module subroutine util_resize_pl(self, nnew) + implicit none + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + integer(I4B), intent(in) :: nnew !! New size neded + end subroutine util_resize_pl + + module subroutine util_resize_tp(self, nnew) + implicit none + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object + integer(I4B), intent(in) :: nnew !! New size neded + end subroutine util_resize_tp + + module subroutine util_get_energy_momentum_system(self, param, ke_orbit, ke_spin, pe, Lorbit, Lspin) + implicit none + class(swiftest_nbody_system), intent(inout) :: self !! Swiftest nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(out) :: ke_orbit !! Orbital kinetic energy + real(DP), intent(out) :: ke_spin !! Spin kinetic energy + real(DP), intent(out) :: pe !! Potential energy + real(DP), dimension(:), intent(out) :: Lorbit !! Orbital angular momentum + real(DP), dimension(:), intent(out) :: Lspin !! Spin angular momentum + end subroutine util_get_energy_momentum_system module subroutine util_set_beg_end_pl(self, xbeg, xend, vbeg) implicit none @@ -907,26 +1142,81 @@ module subroutine util_sort_tp(self, sortby, ascending) character(*), intent(in) :: sortby !! Sorting attribute logical, intent(in) :: ascending !! Logical flag indicating whether or not the sorting should be in ascending or descending order end subroutine util_sort_tp - - module subroutine util_spill_body(self, discards, lspill_list) + end interface + + interface util_spill + module subroutine util_spill_arr_char_string(keeps, discards, lspill_list, ldestructive) + implicit none + character(len=STRMAX), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + character(len=STRMAX), dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discardss + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + end subroutine util_spill_arr_char_string + + module subroutine util_spill_arr_DP(keeps, discards, lspill_list, ldestructive) + implicit none + real(DP), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + real(DP), dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + end subroutine util_spill_arr_DP + + module subroutine util_spill_arr_DPvec(keeps, discards, lspill_list, ldestructive) + implicit none + real(DP), dimension(:,:), allocatable, intent(inout) :: keeps !! Array of values to keep + real(DP), dimension(:,:), allocatable, intent(inout) :: discards !! Array discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + end subroutine util_spill_arr_DPvec + + module subroutine util_spill_arr_I4B(keeps, discards, lspill_list, ldestructive) + implicit none + integer(I4B), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + integer(I4B), dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discardss + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + end subroutine util_spill_arr_I4B + + module subroutine util_spill_arr_logical(keeps, discards, lspill_list, ldestructive) + implicit none + logical, dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + logical, dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discardss + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + end subroutine util_spill_arr_logical + end interface + + interface + module subroutine util_spill_body(self, discards, lspill_list, ldestructive) implicit none class(swiftest_body), intent(inout) :: self !! Swiftest body object class(swiftest_body), intent(inout) :: discards !! Discarded object logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not end subroutine util_spill_body - module subroutine util_spill_pl(self, discards, lspill_list) + module subroutine util_spill_encounter(self, discards, lspill_list, ldestructive) + implicit none + class(swiftest_encounter), intent(inout) :: self !! Swiftest encounter list + class(swiftest_encounter), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter body by removing the discard list + end subroutine util_spill_encounter + + module subroutine util_spill_pl(self, discards, lspill_list, ldestructive) implicit none class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object class(swiftest_body), intent(inout) :: discards !! Discarded object logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not end subroutine util_spill_pl - module subroutine util_spill_tp(self, discards, lspill_list) + module subroutine util_spill_tp(self, discards, lspill_list, ldestructive) implicit none class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object class(swiftest_body), intent(inout) :: discards !! Discarded object logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not end subroutine util_spill_tp module subroutine util_valid(pl, tp) diff --git a/src/modules/symba_classes.f90 b/src/modules/symba_classes.f90 index 2b131ef76..f920fff87 100644 --- a/src/modules/symba_classes.f90 +++ b/src/modules/symba_classes.f90 @@ -4,7 +4,7 @@ module symba_classes !! Definition of classes and methods specific to the Democratic SyMBAcentric Method !! Adapted from David E. Kaufmann's Swifter routine: helio.f90 use swiftest_globals - use swiftest_classes, only : swiftest_parameters, swiftest_base + use swiftest_classes, only : swiftest_parameters, swiftest_base, swiftest_encounter use helio_classes, only : helio_cb, helio_pl, helio_tp, helio_nbody_system use rmvs_classes, only : rmvs_chk_ind implicit none @@ -91,11 +91,25 @@ module symba_classes procedure :: drift => symba_drift_pl !! Method for Danby drift in Democratic Heliocentric coordinates. Sets the mask to the current recursion level procedure :: encounter_check => symba_encounter_check_pl !! Checks if massive bodies are going through close encounters with each other procedure :: accel => symba_kick_getacch_pl !! Compute heliocentric accelerations of massive bodies - procedure :: setup => symba_setup_pl !! Constructor method - Allocates space for number of particle + procedure :: setup => symba_setup_pl !! Constructor method - Allocates space for the input number of bodies + procedure :: append => symba_util_append_pl !! Appends elements from one structure to another + procedure :: fill => symba_util_fill_pl !! "Fills" bodies from one object into another depending on the results of a mask (uses the UNPACK intrinsic) + procedure :: get_peri => symba_util_peri_pl !! Determine system pericenter passages for massive bodies + procedure :: rearray => symba_util_rearray_pl !! Clean up the massive body structures to remove discarded bodies and add new bodies + procedure :: resize => symba_util_resize_pl !! Checks the current size of a SyMBA massive body against the requested size and resizes it if it is too small. procedure :: sort => symba_util_sort_pl !! Sorts body arrays by a sortable componen procedure :: rearrange => symba_util_sort_rearrange_pl !! Rearranges the order of array elements of body based on an input index array. Used in sorting methods + procedure :: spill => symba_util_spill_pl !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) end type symba_pl + type, extends(symba_pl) :: symba_merger + integer(I4B), dimension(:), allocatable :: ncomp + contains + procedure :: append => symba_util_append_merger !! Appends elements from one structure to another + procedure :: resize => symba_util_resize_merger !! Checks the current size of a SyMBA merger list against the requested size and resizes it if it is too small. + procedure :: setup => symba_setup_merger !! Constructor method - Allocates space for the input number of bodies + end type symba_merger + !******************************************************************************************************************************** ! symba_tp class definitions and method interfaces !******************************************************************************************************************************* @@ -108,29 +122,27 @@ module symba_classes procedure :: drift => symba_drift_tp !! Method for Danby drift in Democratic Heliocentric coordinates. Sets the mask to the current recursion level procedure :: encounter_check => symba_encounter_check_tp !! Checks if any test particles are undergoing a close encounter with a massive body procedure :: accel => symba_kick_getacch_tp !! Compute heliocentric accelerations of test particles - procedure :: setup => symba_setup_tp !! Constructor method - Allocates space for number of particle + procedure :: setup => symba_setup_tp !! Constructor method - Allocates space for the input number of bodies + procedure :: append => symba_util_append_tp !! Appends elements from one structure to another + procedure :: fill => symba_util_fill_tp !! "Fills" bodies from one object into another depending on the results of a mask (uses the UNPACK intrinsic) + procedure :: resize => symba_util_resize_tp !! Checks the current size of a Swiftest body against the requested size and resizes it if it is too small. procedure :: sort => symba_util_sort_tp !! Sorts body arrays by a sortable componen procedure :: rearrange => symba_util_sort_rearrange_tp !! Rearranges the order of array elements of body based on an input index array. Used in sorting methods + procedure :: spill => symba_util_spill_tp !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) end type symba_tp !******************************************************************************************************************************** ! symba_pltpenc class definitions and method interfaces !******************************************************************************************************************************* !> SyMBA class for tracking pl-tp close encounters in a step - type :: symba_pltpenc - integer(I4B) :: nenc !! Total number of encounters - logical, dimension(:), allocatable :: lvdotr !! relative vdotr flag - integer(I4B), dimension(:), allocatable :: status !! status of the interaction + type, extends(swiftest_encounter) :: symba_pltpenc integer(I4B), dimension(:), allocatable :: level !! encounter recursion level - integer(I4B), dimension(:), allocatable :: index1 !! position of the planet in encounter - integer(I4B), dimension(:), allocatable :: index2 !! position of the test particle in encounter contains procedure :: collision_check => symba_collision_check_pltpenc !! Checks if a test particle is going to collide with a massive body procedure :: encounter_check => symba_encounter_check_pltpenc !! Checks if massive bodies are going through close encounters with each other procedure :: kick => symba_kick_pltpenc !! Kick barycentric velocities of active test particles within SyMBA recursion procedure :: setup => symba_setup_pltpenc !! A constructor that sets the number of encounters and allocates and initializes all arrays - procedure :: copy => symba_util_copy_pltpenc !! Copies all elements of one pltpenc list to another - procedure :: resize => symba_util_resize_pltpenc !! Checks the current size of the pltpenc_list against the required size and extends it by a factor of 2 more than requested if it is too small + procedure :: spill => symba_util_spill_pltpenc !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) end type symba_pltpenc !******************************************************************************************************************************** @@ -138,33 +150,29 @@ module symba_classes !******************************************************************************************************************************* !> SyMBA class for tracking pl-pl close encounters in a step type, extends(symba_pltpenc) :: symba_plplenc - real(DP), dimension(:,:), allocatable :: xh1 !! the heliocentric position of parent 1 in encounter - real(DP), dimension(:,:), allocatable :: xh2 !! the heliocentric position of parent 2 in encounter - real(DP), dimension(:,:), allocatable :: vb1 !! the barycentric velocity of parent 1 in encounter - real(DP), dimension(:,:), allocatable :: vb2 !! the barycentric velocity of parent 2 in encounter contains - procedure :: collision_check => symba_collision_check_plplenc !! Checks if two massive bodies are going to collide - procedure :: setup => symba_setup_plplenc !! A constructor that sets the number of encounters and allocates and initializes all arrays - procedure :: copy => symba_util_copy_plplenc !! Copies all elements of one plplenc list to another + procedure :: scrub_non_collision => symba_collision_encounter_scrub !! Processes the pl-pl encounter list remove only those encounters that led to a collision + procedure :: resolve_fragmentations => symba_collision_resolve_fragmentations !! Process list of collisions, determine the collisional regime, and then create fragments + procedure :: resolve_mergers => symba_collision_resolve_mergers !! Process list of collisions and merge colliding bodies together end type symba_plplenc !******************************************************************************************************************************** ! symba_nbody_system class definitions and method interfaces !******************************************************************************************************************************** type, extends(helio_nbody_system) :: symba_nbody_system - class(symba_pl), allocatable :: mergeadd_list !! List of added bodies in mergers or collisions - class(symba_pl), allocatable :: mergesub_list !! List of subtracted bodies in mergers or collisions + class(symba_merger), allocatable :: mergeadd_list !! List of added bodies in mergers or collisions + class(symba_merger), allocatable :: mergesub_list !! List of subtracted bodies in mergers or collisions class(symba_pltpenc), allocatable :: pltpenc_list !! List of massive body-test particle encounters in a single step class(symba_plplenc), allocatable :: plplenc_list !! List of massive body-massive body encounters in a single step - class(symba_pl), allocatable :: pl_discards !! Discarded test particle data structure integer(I4B) :: irec !! System recursion level contains - procedure :: initialize => symba_setup_initialize_system !! Performs SyMBA-specific initilization steps - procedure :: step => symba_step_system !! Advance the SyMBA nbody system forward in time by one step - procedure :: interp => symba_step_interp_system !! Perform an interpolation step on the SymBA nbody system - procedure :: set_recur_levels => symba_step_set_recur_levels_system !! Sets recursion levels of bodies and encounter lists to the current system level - procedure :: recursive_step => symba_step_recur_system !! Step interacting planets and active test particles ahead in democratic heliocentric coordinates at the current recursion level, if applicable, and descend to the next deeper level if necessary - procedure :: reset => symba_step_reset_system !! Resets pl, tp,and encounter structures at the start of a new step + procedure :: write_discard => symba_io_write_discard !! Write out information about discarded and merged planets and test particles in SyMBA + procedure :: initialize => symba_setup_initialize_system !! Performs SyMBA-specific initilization steps + procedure :: step => symba_step_system !! Advance the SyMBA nbody system forward in time by one step + procedure :: interp => symba_step_interp_system !! Perform an interpolation step on the SymBA nbody system + procedure :: set_recur_levels => symba_step_set_recur_levels_system !! Sets recursion levels of bodies and encounter lists to the current system level + procedure :: recursive_step => symba_step_recur_system !! Step interacting planets and active test particles ahead in democratic heliocentric coordinates at the current recursion level, if applicable, and descend to the next deeper level if necessary + procedure :: reset => symba_step_reset_system !! Resets pl, tp,and encounter structures at the start of a new step end type symba_nbody_system interface @@ -179,16 +187,12 @@ module subroutine symba_collision_check_pltpenc(self, system, param, t, dt, irec integer(I4B), intent(in) :: irec !! Current recursion level end subroutine symba_collision_check_pltpenc - module subroutine symba_collision_check_plplenc(self, system, param, t, dt, irec) - use swiftest_classes, only : swiftest_parameters + module subroutine symba_collision_encounter_scrub(self, system, param) implicit none - class(symba_plplenc), intent(inout) :: self !! SyMBA pl-tp encounter list object + class(symba_plplenc), intent(inout) :: self !! SyMBA pl-pl encounter list class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters - real(DP), intent(in) :: t !! current time - real(DP), intent(in) :: dt !! step size - integer(I4B), intent(in) :: irec !! Current recursion level - end subroutine symba_collision_check_plplenc + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameterss + end subroutine module subroutine symba_collision_make_family_pl(self,idx) implicit none @@ -196,12 +200,26 @@ module subroutine symba_collision_make_family_pl(self,idx) integer(I4B), dimension(2), intent(in) :: idx !! Array holding the indices of the two bodies involved in the collision end subroutine symba_collision_make_family_pl + module subroutine symba_collision_resolve_fragmentations(self, system, param) + implicit none + class(symba_plplenc), intent(inout) :: self !! SyMBA pl-pl encounter list + class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object + class(symba_parameters), intent(in) :: param !! Current run configuration parameters with SyMBA additions + end subroutine symba_collision_resolve_fragmentations + + module subroutine symba_collision_resolve_mergers(self, system, param) + implicit none + class(symba_plplenc), intent(inout) :: self !! SyMBA pl-pl encounter list + class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object + class(symba_parameters), intent(in) :: param !! Current run configuration parameters with SyMBA additions + end subroutine symba_collision_resolve_mergers + module subroutine symba_discard_pl(self, system, param) use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters implicit none class(symba_pl), intent(inout) :: self !! SyMBA test particle object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters end subroutine symba_discard_pl module subroutine symba_drift_pl(self, system, param, dt) @@ -257,34 +275,22 @@ module function symba_encounter_check_tp(self, system, dt, irec) result(lany_enc logical :: lany_encounter !! Returns true if there is at least one close encounter end function symba_encounter_check_tp - module subroutine symba_kick_getacch_pl(self, system, param, t, lbeg) - use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters + module function symba_fragmentation_casemerge(system, param, family, x, v, mass, radius, L_spin, Ip) result(status) implicit none - class(symba_pl), intent(inout) :: self !! SyMBA massive body particle data structure - class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters - real(DP), intent(in) :: t !! Current simulation time - logical, intent(in) :: lbeg !! Logical flag that determines whether or not this is the beginning or end of the step - end subroutine symba_kick_getacch_pl + class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object + class(symba_parameters), intent(in) :: param !! Current run configuration parameters with SyMBA additions + integer(I4B), dimension(:), intent(in) :: family !! List of indices of all bodies inovlved in the collision + real(DP), dimension(:,:), intent(in) :: x, v, L_spin, Ip !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(in) :: mass, radius !! Input values that represent a 2-body equivalent of a possibly 2+ body collisio + integer(I4B) :: status !! Status flag assigned to this outcome + end function symba_fragmentation_casemerge - module subroutine symba_kick_getacch_tp(self, system, param, t, lbeg) - use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters - implicit none - class(symba_tp), intent(inout) :: self !! SyMBA test particle data structure - class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters - real(DP), intent(in) :: t !! Current time - logical, intent(in) :: lbeg !! Logical flag that determines whether or not this is the beginning or end of the step - end subroutine symba_kick_getacch_tp - - module subroutine symba_kick_pltpenc(self, system, dt, irec, sgn) + module subroutine symba_io_write_discard(self, param) + use swiftest_classes, only : swiftest_parameters implicit none - class(symba_pltpenc), intent(in) :: self !! SyMBA pl-tp encounter list object - class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object - real(DP), intent(in) :: dt !! step size - integer(I4B), intent(in) :: irec !! Current recursion level - integer(I4B), intent(in) :: sgn !! sign to be applied to acceleration - end subroutine symba_kick_pltpenc + class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + end subroutine symba_io_write_discard module subroutine symba_io_dump_particle_info(self, param, msg) use swiftest_classes, only : swiftest_parameters @@ -296,7 +302,7 @@ end subroutine symba_io_dump_particle_info module subroutine symba_io_param_reader(self, unit, iotype, v_list, iostat, iomsg) implicit none - class(symba_parameters), intent(inout) :: self !! Collection of parameters + class(symba_parameters), intent(inout) :: self !! Current run configuration parameters with SyMBA additionss integer, intent(in) :: unit !! File unit number character(len=*), intent(in) :: iotype !! Dummy argument passed to the input/output procedure contains the text from the char-literal-constant, prefixed with DT. !! If you do not include a char-literal-constant, the iotype argument contains only DT. @@ -307,7 +313,7 @@ end subroutine symba_io_param_reader module subroutine symba_io_param_writer(self, unit, iotype, v_list, iostat, iomsg) implicit none - class(symba_parameters),intent(in) :: self !! Collection of SyMBA parameters + class(symba_parameters),intent(in) :: self !! Current run configuration parameters with SyMBA additions integer, intent(in) :: unit !! File unit number character(len=*), intent(in) :: iotype !! Dummy argument passed to the input/output procedure contains the text from the char-literal-constant, prefixed with DT. !! If you do not include a char-literal-constant, the iotype argument contains only DT. @@ -333,6 +339,35 @@ module subroutine symba_io_read_frame_info(self, iu, param, form, ierr) integer(I4B), intent(out) :: ierr !! Error code end subroutine symba_io_read_frame_info + module subroutine symba_kick_getacch_pl(self, system, param, t, lbeg) + use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters + implicit none + class(symba_pl), intent(inout) :: self !! SyMBA massive body particle data structure + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(in) :: t !! Current simulation time + logical, intent(in) :: lbeg !! Logical flag that determines whether or not this is the beginning or end of the step + end subroutine symba_kick_getacch_pl + + module subroutine symba_kick_getacch_tp(self, system, param, t, lbeg) + use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters + implicit none + class(symba_tp), intent(inout) :: self !! SyMBA test particle data structure + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(in) :: t !! Current time + logical, intent(in) :: lbeg !! Logical flag that determines whether or not this is the beginning or end of the step + end subroutine symba_kick_getacch_tp + + module subroutine symba_kick_pltpenc(self, system, dt, irec, sgn) + implicit none + class(symba_pltpenc), intent(in) :: self !! SyMBA pl-tp encounter list object + class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object + real(DP), intent(in) :: dt !! step size + integer(I4B), intent(in) :: irec !! Current recursion level + integer(I4B), intent(in) :: sgn !! sign to be applied to acceleration + end subroutine symba_kick_pltpenc + module subroutine symba_io_write_frame_info(self, iu, param) use swiftest_classes, only : swiftest_parameters implicit none @@ -341,11 +376,26 @@ module subroutine symba_io_write_frame_info(self, iu, param) class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine symba_io_write_frame_info + module subroutine symba_setup_initialize_system(self, param) + use swiftest_classes, only : swiftest_parameters + implicit none + class(symba_nbody_system), intent(inout) :: self !! SyMBA system object + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters + end subroutine symba_setup_initialize_system + + module subroutine symba_setup_merger(self, n, param) + use swiftest_classes, only : swiftest_parameters + implicit none + class(symba_merger), intent(inout) :: self !! SyMBA merger list object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + end subroutine symba_setup_merger + module subroutine symba_setup_pl(self, n, param) use swiftest_classes, only : swiftest_parameters implicit none - class(symba_pl), intent(inout) :: self !! SyMBA massive body object - integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine symba_setup_pl @@ -355,19 +405,6 @@ module subroutine symba_setup_pltpenc(self,n) integer(I4B), intent(in) :: n !! Number of encounters to allocate space for end subroutine symba_setup_pltpenc - module subroutine symba_setup_plplenc(self,n) - implicit none - class(symba_plplenc), intent(inout) :: self !! SyMBA pl-tp encounter structure - integer(I4B), intent(in) :: n !! Number of encounters to allocate space for - end subroutine symba_setup_plplenc - - module subroutine symba_setup_initialize_system(self, param) - use swiftest_classes, only : swiftest_parameters - implicit none - class(symba_nbody_system), intent(inout) :: self !! SyMBA system object - class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters - end subroutine symba_setup_initialize_system - module subroutine symba_setup_tp(self, n, param) use swiftest_classes, only : swiftest_parameters implicit none @@ -413,24 +450,131 @@ module subroutine symba_step_reset_system(self) implicit none class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody system object end subroutine symba_step_reset_system + end interface + + interface util_append + module subroutine symba_util_append_arr_info(arr, source, lsource_mask) + implicit none + type(symba_particle_info), dimension(:), allocatable, intent(inout) :: arr !! Destination array + type(symba_particle_info), dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine symba_util_append_arr_info + + module subroutine symba_util_append_arr_kin(arr, source, lsource_mask) + implicit none + type(symba_kinship), dimension(:), allocatable, intent(inout) :: arr !! Destination array + type(symba_kinship), dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine symba_util_append_arr_kin + end interface + + interface + module subroutine symba_util_append_merger(self, source, lsource_mask) + use swiftest_classes, only : swiftest_body + implicit none + class(symba_merger), intent(inout) :: self !! SyMBA massive body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine symba_util_append_merger + + module subroutine symba_util_append_pl(self, source, lsource_mask) + use swiftest_classes, only : swiftest_body + implicit none + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine symba_util_append_pl + + module subroutine symba_util_append_tp(self, source, lsource_mask) + use swiftest_classes, only : swiftest_body + implicit none + class(symba_tp), intent(inout) :: self !! SyMBA test particle object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine symba_util_append_tp + end interface + + interface util_fill + module subroutine symba_util_fill_arr_info(keeps, inserts, lfill_list) + implicit none + type(symba_particle_info), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + type(symba_particle_info), dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + end subroutine symba_util_fill_arr_info + + module subroutine symba_util_fill_arr_kin(keeps, inserts, lfill_list) + implicit none + type(symba_kinship), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + type(symba_kinship), dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + end subroutine symba_util_fill_arr_kin + end interface + + interface + module subroutine symba_util_fill_pl(self, inserts, lfill_list) + use swiftest_classes, only : swiftest_body + implicit none + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + class(swiftest_body), intent(in) :: inserts !! Inserted object + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + end subroutine symba_util_fill_pl + + module subroutine symba_util_fill_tp(self, inserts, lfill_list) + use swiftest_classes, only : swiftest_body + implicit none + class(symba_tp), intent(inout) :: self !! SyMBA test particle object + class(swiftest_body), intent(in) :: inserts !! Inserted object + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + end subroutine symba_util_fill_tp + + module subroutine symba_util_peri_pl(self, system, param) + use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters + implicit none + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + end subroutine symba_util_peri_pl - module subroutine symba_util_copy_pltpenc(self, source) + module subroutine symba_util_rearray_pl(self, system, param) implicit none - class(symba_pltpenc), intent(inout) :: self !! SyMBA pl-tp encounter list - class(symba_pltpenc), intent(in) :: source !! Source object to copy into - end subroutine symba_util_copy_pltpenc + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object + class(symba_parameters), intent(in) :: param !! Current run configuration parameters with SyMBA additions + end subroutine symba_util_rearray_pl + end interface - module subroutine symba_util_copy_plplenc(self, source) + interface util_resize + module subroutine symba_util_resize_arr_info(arr, nnew) implicit none - class(symba_plplenc), intent(inout) :: self !! SyMBA pl-pl encounter list - class(symba_pltpenc), intent(in) :: source !! Source object to copy into - end subroutine symba_util_copy_plplenc + type(symba_particle_info), dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + end subroutine symba_util_resize_arr_info - module subroutine symba_util_resize_pltpenc(self, nrequested) + module subroutine symba_util_resize_arr_kin(arr, nnew) implicit none - class(symba_pltpenc), intent(inout) :: self !! SyMBA pl-tp encounter list - integer(I4B), intent(in) :: nrequested !! New size of list needed - end subroutine symba_util_resize_pltpenc + type(symba_kinship), dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + end subroutine symba_util_resize_arr_kin + end interface + + interface + module subroutine symba_util_resize_merger(self, nnew) + implicit none + class(symba_merger), intent(inout) :: self !! SyMBA merger list object + integer(I4B), intent(in) :: nnew !! New size neded + end subroutine symba_util_resize_merger + + module subroutine symba_util_resize_pl(self, nnew) + implicit none + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + integer(I4B), intent(in) :: nnew !! New size neded + end subroutine symba_util_resize_pl + + module subroutine symba_util_resize_tp(self, nnew) + implicit none + class(symba_tp), intent(inout) :: self !! SyMBA massive body object + integer(I4B), intent(in) :: nnew !! New size neded + end subroutine symba_util_resize_tp module subroutine symba_util_sort_pl(self, sortby, ascending) implicit none @@ -457,7 +601,53 @@ module subroutine symba_util_sort_rearrange_tp(self, ind) class(symba_tp), intent(inout) :: self !! SyMBA massive body object integer(I4B), dimension(:), intent(in) :: ind !! Index array used to restructure the body (should contain all 1:n index values in the desired order) end subroutine symba_util_sort_rearrange_tp + end interface + + interface util_spill + module subroutine symba_util_spill_arr_info(keeps, discards, lspill_list, ldestructive) + implicit none + type(symba_particle_info), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + type(symba_particle_info), dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discardss + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + end subroutine symba_util_spill_arr_info + module subroutine symba_util_spill_arr_kin(keeps, discards, lspill_list, ldestructive) + implicit none + type(symba_kinship), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + type(symba_kinship), dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discardss + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + end subroutine symba_util_spill_arr_kin + end interface + + interface + module subroutine symba_util_spill_pl(self, discards, lspill_list, ldestructive) + use swiftest_classes, only : swiftest_body + implicit none + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + class(swiftest_body), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + end subroutine symba_util_spill_pl + + module subroutine symba_util_spill_pltpenc(self, discards, lspill_list, ldestructive) + use swiftest_classes, only : swiftest_encounter + implicit none + class(symba_pltpenc), intent(inout) :: self !! SyMBA pl-tp encounter list + class(swiftest_encounter), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter body by removing the discard list + end subroutine symba_util_spill_pltpenc + + module subroutine symba_util_spill_tp(self, discards, lspill_list, ldestructive) + use swiftest_classes, only : swiftest_body + implicit none + class(symba_tp), intent(inout) :: self !! SyMBA test particle object + class(swiftest_body), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + end subroutine symba_util_spill_tp end interface end module symba_classes \ No newline at end of file diff --git a/src/modules/whm_classes.f90 b/src/modules/whm_classes.f90 index 5509a3afe..a79f52bca 100644 --- a/src/modules/whm_classes.f90 +++ b/src/modules/whm_classes.f90 @@ -34,18 +34,20 @@ module whm_classes procedure :: j2h => whm_coord_j2h_pl !! Convert position and velcoity vectors from Jacobi to helliocentric coordinates procedure :: vh2vj => whm_coord_vh2vj_pl !! Convert velocity vectors from heliocentric to Jacobi coordinates procedure :: drift => whm_drift_pl !! Loop through massive bodies and call Danby drift routine to jacobi coordinates - procedure :: fill => whm_util_fill_pl !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) - procedure :: accel => whm_kick_getacch_pl !! Compute heliocentric accelerations of massive bodies - procedure :: kick => whm_kick_vh_pl !! Kick heliocentric velocities of massive bodies procedure :: accel_gr => whm_gr_kick_getacch_pl !! Acceleration term arising from the post-Newtonian correction procedure :: gr_pos_kick => whm_gr_p4_pl !! Position kick due to p**4 term in the post-Newtonian correction - procedure :: setup => whm_setup_pl !! Constructor method - Allocates space for number of particles - procedure :: set_mu => whm_util_set_mu_eta_pl !! Sets the Jacobi mass value for all massive bodies. + procedure :: accel => whm_kick_getacch_pl !! Compute heliocentric accelerations of massive bodies + procedure :: kick => whm_kick_vh_pl !! Kick heliocentric velocities of massive bodies + procedure :: append => whm_util_append_pl !! Appends elements from one structure to another + procedure :: fill => whm_util_fill_pl !! "Fills" bodies from one object into another depending on the results of a mask (uses the UNPACK intrinsic) + procedure :: resize => whm_util_resize_pl !! Checks the current size of a Swiftest body against the requested size and resizes it if it is too small. procedure :: set_ir3 => whm_util_set_ir3j !! Sets both the heliocentric and jacobi inverse radius terms (1/rj**3 and 1/rh**3) + procedure :: set_mu => whm_util_set_mu_eta_pl !! Sets the Jacobi mass value for all massive bodies. procedure :: sort => whm_util_sort_pl !! Sort a WHM massive body object in-place. procedure :: rearrange => whm_util_sort_rearrange_pl !! Rearranges the order of array elements of body based on an input index array. Used in sorting methods - procedure :: step => whm_step_pl !! Steps the body forward one stepsize procedure :: spill => whm_util_spill_pl !!"Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) + procedure :: setup => whm_setup_pl !! Constructor method - Allocates space for the input number of bodiess + procedure :: step => whm_step_pl !! Steps the body forward one stepsize end type whm_pl !******************************************************************************************************************************** @@ -57,10 +59,10 @@ module whm_classes !! Note to developers: If you add componenets to this class, be sure to update methods and subroutines that traverse the !! component list, such as whm_util_spill_tp contains - procedure :: accel => whm_kick_getacch_tp !! Compute heliocentric accelerations of test particles - procedure :: kick => whm_kick_vh_tp !! Kick heliocentric velocities of test particles procedure :: accel_gr => whm_gr_kick_getacch_tp !! Acceleration term arising from the post-Newtonian correction procedure :: gr_pos_kick => whm_gr_p4_tp !! Position kick due to p**4 term in the post-Newtonian correction + procedure :: accel => whm_kick_getacch_tp !! Compute heliocentric accelerations of test particles + procedure :: kick => whm_kick_vh_tp !! Kick heliocentric velocities of test particles procedure :: step => whm_step_tp !! Steps the particle forward one stepsize end type whm_tp @@ -106,14 +108,6 @@ module subroutine whm_drift_pl(self, system, param, dt) real(DP), intent(in) :: dt !! Stepsize end subroutine whm_drift_pl - module subroutine whm_util_fill_pl(self, inserts, lfill_list) - use swiftest_classes, only : swiftest_body - implicit none - class(whm_pl), intent(inout) :: self !! WHM massive body object - class(swiftest_body), intent(inout) :: inserts !! inserted object - logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps - end subroutine whm_util_fill_pl - !> Get heliocentric accelration of massive bodies module subroutine whm_kick_getacch_pl(self, system, param, t, lbeg) use swiftest_classes, only : swiftest_cb, swiftest_parameters @@ -197,31 +191,6 @@ module subroutine whm_setup_pl(self, n, param) class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine whm_setup_pl - module subroutine whm_util_set_ir3j(self) - implicit none - class(whm_pl), intent(inout) :: self !! WHM massive body object - end subroutine whm_util_set_ir3j - - module subroutine whm_util_set_mu_eta_pl(self, cb) - use swiftest_classes, only : swiftest_cb - implicit none - class(whm_pl), intent(inout) :: self !! WHM massive body object - class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object - end subroutine whm_util_set_mu_eta_pl - - module subroutine whm_util_sort_pl(self, sortby, ascending) - implicit none - class(whm_pl), intent(inout) :: self !! WHM massive body object - character(*), intent(in) :: sortby !! Sorting attribute - logical, intent(in) :: ascending !! Logical flag indicating whether or not the sorting should be in ascending or descending order - end subroutine whm_util_sort_pl - - module subroutine whm_util_sort_rearrange_pl(self, ind) - implicit none - class(whm_pl), intent(inout) :: self !! WHM massive body object - integer(I4B), dimension(:), intent(in) :: ind !! Index array used to restructure the body (should contain all 1:n index values in the desired order) - end subroutine whm_util_sort_rearrange_pl - module subroutine whm_setup_initialize_system(self, param) use swiftest_classes, only : swiftest_parameters implicit none @@ -239,6 +208,15 @@ module subroutine whm_step_pl(self, system, param, t, dt) real(DP), intent(in) :: dt !! Current stepsize end subroutine whm_step_pl + module subroutine whm_step_system(self, param, t, dt) + use swiftest_classes, only : swiftest_parameters + implicit none + class(whm_nbody_system), intent(inout) :: self !! WHM system object + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters + real(DP), intent(in) :: t !! Simulation time + real(DP), intent(in) :: dt !! Current stepsize + end subroutine whm_step_system + module subroutine whm_step_tp(self, system, param, t, dt) use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters implicit none @@ -249,23 +227,61 @@ module subroutine whm_step_tp(self, system, param, t, dt) real(DP), intent(in) :: dt !! Stepsize end subroutine whm_step_tp - module subroutine whm_util_spill_pl(self, discards, lspill_list) + module subroutine whm_util_append_pl(self, source, lsource_mask) + use swiftest_classes, only : swiftest_body + implicit none + class(whm_pl), intent(inout) :: self !! WHM massive body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + end subroutine whm_util_append_pl + + module subroutine whm_util_spill_pl(self, discards, lspill_list, ldestructive) use swiftest_classes, only : swiftest_body implicit none class(whm_pl), intent(inout) :: self !! WHM massive body object class(swiftest_body), intent(inout) :: discards !! Discarded object logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not end subroutine whm_util_spill_pl - !> Steps the Swiftest nbody system forward in time one stepsize - module subroutine whm_step_system(self, param, t, dt) - use swiftest_classes, only : swiftest_parameters + module subroutine whm_util_fill_pl(self, inserts, lfill_list) + use swiftest_classes, only : swiftest_body implicit none - class(whm_nbody_system), intent(inout) :: self !! WHM system object - class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters - real(DP), intent(in) :: t !! Simulation time - real(DP), intent(in) :: dt !! Current stepsize - end subroutine whm_step_system + class(whm_pl), intent(inout) :: self !! WHM massive body object + class(swiftest_body), intent(in) :: inserts !! inserted object + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + end subroutine whm_util_fill_pl + + module subroutine whm_util_resize_pl(self, nnew) + implicit none + class(whm_pl), intent(inout) :: self !! WHM massive body object + integer(I4B), intent(in) :: nnew !! New size neded + end subroutine whm_util_resize_pl + + module subroutine whm_util_set_ir3j(self) + implicit none + class(whm_pl), intent(inout) :: self !! WHM massive body object + end subroutine whm_util_set_ir3j + + module subroutine whm_util_set_mu_eta_pl(self, cb) + use swiftest_classes, only : swiftest_cb + implicit none + class(whm_pl), intent(inout) :: self !! WHM massive body object + class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object + end subroutine whm_util_set_mu_eta_pl + + module subroutine whm_util_sort_pl(self, sortby, ascending) + implicit none + class(whm_pl), intent(inout) :: self !! WHM massive body object + character(*), intent(in) :: sortby !! Sorting attribute + logical, intent(in) :: ascending !! Logical flag indicating whether or not the sorting should be in ascending or descending order + end subroutine whm_util_sort_pl + + module subroutine whm_util_sort_rearrange_pl(self, ind) + implicit none + class(whm_pl), intent(inout) :: self !! WHM massive body object + integer(I4B), dimension(:), intent(in) :: ind !! Index array used to restructure the body (should contain all 1:n index values in the desired order) + end subroutine whm_util_sort_rearrange_pl end interface end module whm_classes diff --git a/src/obl/obl.f90 b/src/obl/obl.f90 index 91b20b62b..035a54b18 100644 --- a/src/obl/obl.f90 +++ b/src/obl/obl.f90 @@ -106,5 +106,45 @@ module subroutine obl_acc_tp(self, system) end subroutine obl_acc_tp + module subroutine obl_pot(npl, Mcb, Mpl, j2rp2, j4rp4, xh, irh, oblpot) + !! author: David A. Minton + !! + !! Compute the contribution to the total gravitational potential due solely to the oblateness of the central body + !! Returned value does not include monopole term or terms higher than J4 + !! + !! Reference: MacMillan, W. D. 1958. The Theory of the Potential, (Dover Publications), 363. + !! + !! Adapted from David E. Kaufmann's Swifter routine: obl_pot.f90 + !! Adapted from Hal Levison's Swift routine obl_pot.f + implicit none + ! Arguments + integer(I4B), intent(in) :: npl + real(DP), intent(in) :: Mcb + real(DP), dimension(:), intent(in) :: Mpl + real(DP), intent(in) :: j2rp2, j4rp4 + real(DP), dimension(:), intent(in) :: irh + real(DP), dimension(:, :), intent(in) :: xh + real(DP), intent(out) :: oblpot + + ! Internals + integer(I4B) :: i + real(DP) :: rinv2, t0, t1, t2, t3, p2, p4, mu + + oblpot = 0.0_DP + mu = Mcb + do i = 1, npl + rinv2 = irh(i)**2 + t0 = mu * Mpl(i) * rinv2 * irh(i) + t1 = j2rp2 + t2 = xh(3, i) * xh(3, i) * rinv2 + t3 = j4rp4 * rinv2 + p2 = 0.5_DP * (3 * t2 - 1.0_DP) + p4 = 0.125_DP * ((35 * t2 - 30.0_DP) * t2 + 3.0_DP) + oblpot = oblpot + t0 * (t1 * p2 + t3 * p4) + end do + + return + end subroutine obl_pot + end submodule s_obl diff --git a/src/rmvs/rmvs_discard.f90 b/src/rmvs/rmvs_discard.f90 index 551cdab92..bcdb9f902 100644 --- a/src/rmvs/rmvs_discard.f90 +++ b/src/rmvs/rmvs_discard.f90 @@ -13,7 +13,7 @@ module subroutine rmvs_discard_tp(self, system, param) ! Arguments class(rmvs_tp), intent(inout) :: self !! RMVS test particle object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters ! Internals integer(I4B) :: i diff --git a/src/rmvs/rmvs_setup.f90 b/src/rmvs/rmvs_setup.f90 index 778ba3714..92043e0fe 100644 --- a/src/rmvs/rmvs_setup.f90 +++ b/src/rmvs/rmvs_setup.f90 @@ -147,10 +147,16 @@ module subroutine rmvs_setup_tp(self, n, param) call setup_tp(self, n, param) if (n <= 0) return + if (allocated(self%lperi)) deallocate(self%lperi) + if (allocated(self%plperP)) deallocate(self%plperP) + if (allocated(self%plencP)) deallocate(self%plencP) + allocate(self%lperi(n)) allocate(self%plperP(n)) allocate(self%plencP(n)) + if (self%lplanetocentric) then + if (allocated(self%xheliocentric)) deallocate(self%xheliocentric) allocate(self%xheliocentric(NDIM, n)) end if diff --git a/src/rmvs/rmvs_step.f90 b/src/rmvs/rmvs_step.f90 index 113b4d02f..0385aeecc 100644 --- a/src/rmvs/rmvs_step.f90 +++ b/src/rmvs/rmvs_step.f90 @@ -442,6 +442,7 @@ subroutine rmvs_make_planetocentric(param, cb, pl, tp) tpenci%cb_heliocentric = cb tpenci%ipleP = i tpenci%lmask(:) = .true. + tpenci%status(:) = ACTIVE ! Grab all the encountering test particles and convert them to a planetocentric frame tpenci%id(:) = pack(tp%id(:), encmask(:)) do j = 1, NDIM @@ -538,7 +539,7 @@ subroutine rmvs_peri_tp(tp, pl, t, dt, lfirst, inner_index, ipleP, param) call orbel_xv2aqt(mu, xpc(:, i), vpc(:, i), a, peri, capm, tperi) r2 = dot_product(xpc(:, i), xpc(:, i)) if ((abs(tperi) > FACQDT * dt) .or. (r2 > rhill2)) peri = sqrt(r2) - if (param%encounter_file /= "") then + if (param%enc_out /= "") then id1 = pl%id(ipleP) rpl = pl%radius(ipleP) xh1(:) = pl%inner(inner_index)%x(:, ipleP) @@ -547,7 +548,7 @@ subroutine rmvs_peri_tp(tp, pl, t, dt, lfirst, inner_index, ipleP, param) xh2(:) = xpc(:, i) + xh1(:) vh2(:) = xpc(:, i) + vh1(:) call io_write_encounter(t, id1, id2, mu, 0.0_DP, rpl, 0.0_DP, xh1(:), xh2(:), vh1(:), vh2(:), & - param%encounter_file, param%out_type) + param%enc_out, param%out_type) end if if (tp%lperi(i)) then if (peri < tp%peri(i)) then diff --git a/src/rmvs/rmvs_util.f90 b/src/rmvs/rmvs_util.f90 index 27b6bd4b3..9f9cf0037 100644 --- a/src/rmvs/rmvs_util.f90 +++ b/src/rmvs/rmvs_util.f90 @@ -2,6 +2,66 @@ use swiftest contains + module subroutine rmvs_util_append_pl(self, source, lsource_mask) + !! author: David A. Minton + !! + !! Append components from one massive body object to another. + !! This method will automatically resize the destination body if it is too small + implicit none + !! Arguments + class(rmvs_pl), intent(inout) :: self !! RMVS massive body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + + select type(source) + class is (rmvs_pl) + call whm_util_append_pl(self, source, lsource_mask) + + call util_append(self%nenc, source%nenc, lsource_mask) + call util_append(self%tpenc1P, source%tpenc1P, lsource_mask) + call util_append(self%plind, source%plind, lsource_mask) + + ! The following are not implemented as RMVS doesn't make use of fill operations on pl type + ! So they are here as a placeholder in case someone wants to extend the RMVS class for some reason + !call util_append(self%outer, source%outer, lsource_mask) + !call util_append(self%inner, source%inner, lsource_mask) + !call util_append(self%planetocentric, source%planetocentric, lsource_mask) + class default + write(*,*) "Invalid object passed to the append method. Source must be of class rmvs_pl or its descendents!" + call util_exit(FAILURE) + end select + + return + end subroutine rmvs_util_append_pl + + + module subroutine rmvs_util_append_tp(self, source, lsource_mask) + !! author: David A. Minton + !! + !! Append components from test particle object to another. + !! This method will automatically resize the destination body if it is too small + implicit none + !! Arguments + class(rmvs_tp), intent(inout) :: self !! RMVS test particle object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + + select type(source) + class is (rmvs_tp) + call util_append_tp(self, source, lsource_mask) ! Note: whm_tp does not have its own append method, so we skip back to the base class + + call util_append(self%lperi, source%lperi, lsource_mask) + call util_append(self%plperP, source%plperP, lsource_mask) + call util_append(self%plencP, source%plencP, lsource_mask) + class default + write(*,*) "Invalid object passed to the append method. Source must be of class rmvs_tp or its descendents!" + call util_exit(FAILURE) + end select + + return + end subroutine rmvs_util_append_tp + + module subroutine rmvs_util_fill_pl(self, inserts, lfill_list) !! author: David A. Minton !! @@ -11,7 +71,7 @@ module subroutine rmvs_util_fill_pl(self, inserts, lfill_list) implicit none ! Arguments class(rmvs_pl), intent(inout) :: self !! RMVS massive body object - class(swiftest_body), intent(inout) :: inserts !! Inserted object + class(swiftest_body), intent(in) :: inserts !! Inserted object logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps ! Internals integer(I4B) :: i @@ -19,13 +79,20 @@ module subroutine rmvs_util_fill_pl(self, inserts, lfill_list) associate(keeps => self) select type(inserts) class is (rmvs_pl) + call util_fill(keeps%nenc, inserts%nenc, lfill_list) + call util_fill(keeps%tpenc1P, inserts%tpenc1P, lfill_list) + call util_fill(keeps%plind, inserts%plind, lfill_list) + + ! The following are not implemented as RMVS doesn't make use of fill operations on pl type + ! So they are here as a placeholder in case someone wants to extend the RMVS class for some reason + !call util_fill(keeps%outer, inserts%outer, lfill_list) + !call util_fill(keeps%inner, inserts%inner, lfill_list) + !call util_fill(keeps%planetocentric, inserts%planetocentric, lfill_list) - keeps%nenc(:) = unpack(keeps%nenc(:), .not.lfill_list(:), keeps%nenc(:)) - keeps%nenc(:) = unpack(inserts%nenc(:), lfill_list(:), keeps%nenc(:)) - call whm_util_fill_pl(keeps, inserts, lfill_list) class default - write(*,*) 'Error! spill method called for incompatible return type on rmvs_pl' + write(*,*) "Invalid object passed to the fill method. Source must be of class rmvs_pl or its descendents!" + call util_exit(FAILURE) end select end associate @@ -42,31 +109,72 @@ module subroutine rmvs_util_fill_tp(self, inserts, lfill_list) implicit none ! Arguments class(rmvs_tp), intent(inout) :: self !! RMVS test particle object - class(swiftest_body), intent(inout) :: inserts !! Inserted object + class(swiftest_body), intent(in) :: inserts !! Inserted object logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps associate(keeps => self) select type(inserts) class is (rmvs_tp) - - keeps%lperi(:) = unpack(keeps%lperi(:), .not.lfill_list(:), keeps%lperi(:)) - keeps%lperi(:) = unpack(inserts%lperi(:), lfill_list(:), keeps%lperi(:)) - - keeps%plperP(:) = unpack(keeps%plperP(:), .not.lfill_list(:), keeps%plperP(:)) - keeps%plperP(:) = unpack(inserts%plperP(:), lfill_list(:), keeps%plperP(:)) + call util_fill(keeps%lperi, inserts%lperi, lfill_list) + call util_fill(keeps%plperP, inserts%plperP, lfill_list) + call util_fill(keeps%plencP, inserts%plencP, lfill_list) - keeps%plencP(:) = unpack(keeps%plencP(:), .not.lfill_list(:), keeps%plencP(:)) - keeps%plencP(:) = unpack(inserts%plencP(:), lfill_list(:), keeps%plencP(:)) - - call util_fill_tp(keeps, inserts, lfill_list) + call util_fill_tp(keeps, inserts, lfill_list) ! Note: whm_tp does not have its own fill method, so we skip back to the base class class default - write(*,*) 'Error! fill method called for incompatible return type on rmvs_tp' + write(*,*) "Invalid object passed to the fill method. Source must be of class rmvs_tp or its descendents!" + call util_exit(FAILURE) end select end associate return end subroutine rmvs_util_fill_tp + + module subroutine rmvs_util_resize_pl(self, nnew) + !! author: David A. Minton + !! + !! Checks the current size of a massive body object against the requested size and resizes it if it is too small. + implicit none + ! Arguments + class(rmvs_pl), intent(inout) :: self !! RMVS massive body object + integer(I4B), intent(in) :: nnew !! New size neded + + call whm_util_resize_pl(self, nnew) + + call util_resize(self%nenc, nnew) + call util_resize(self%tpenc1P, nnew) + call util_resize(self%plind, nnew) + + ! The following are not implemented as RMVS doesn't make use of resize operations on pl type + ! So they are here as a placeholder in case someone wants to extend the RMVS class for some reason + !call util_resize(self%outer, nnew) + !call util_resize(self%inner, nnew) + !call util_resize(self%planetocentric, nnew) + + return + end subroutine rmvs_util_resize_pl + + + module subroutine rmvs_util_resize_tp(self, nnew) + !! author: David A. Minton + !! + !! Checks the current size of a test particle object against the requested size and resizes it if it is too small. + implicit none + ! Arguments + class(rmvs_tp), intent(inout) :: self !! RMVS test particle object + integer(I4B), intent(in) :: nnew !! New size neded + + call util_resize_tp(self, nnew) + + call util_resize(self%lperi, nnew) + call util_resize(self%plperP, nnew) + call util_resize(self%plencP, nnew) + call util_resize(self%xheliocentric, nnew) + + return + end subroutine rmvs_util_resize_tp + + module subroutine rmvs_util_sort_pl(self, sortby, ascending) !! author: David A. Minton !! @@ -166,11 +274,9 @@ module subroutine rmvs_util_sort_rearrange_pl(self, ind) associate(pl => self, npl => self%nbody) call util_sort_rearrange_pl(pl,ind) allocate(pl_sorted, source=self) - pl%eta(1:npl) = pl_sorted%eta(ind(1:npl)) - pl%xj(:,1:npl) = pl_sorted%xj(:,ind(1:npl)) - pl%vj(:,1:npl) = pl_sorted%vj(:,ind(1:npl)) - pl%muj(1:npl) = pl_sorted%muj(ind(1:npl)) - pl%ir3j(1:npl) = pl_sorted%ir3j(ind(1:npl)) + if (allocated(pl%nenc)) pl%nenc(1:npl) = pl_sorted%nenc(ind(1:npl)) + if (allocated(pl%tpenc1P)) pl%tpenc1P(1:npl) = pl_sorted%tpenc1P(ind(1:npl)) + if (allocated(pl%plind)) pl%plind(1:npl) = pl_sorted%plind(ind(1:npl)) deallocate(pl_sorted) end associate @@ -195,10 +301,10 @@ module subroutine rmvs_util_sort_rearrange_tp(self, ind) associate(tp => self, ntp => self%nbody) call util_sort_rearrange_tp(tp,ind) allocate(tp_sorted, source=self) - tp%lperi(1:ntp) = tp_sorted%lperi(ind(1:ntp)) - tp%plperP(1:ntp) = tp_sorted%plperP(ind(1:ntp)) - tp%plencP(1:ntp) = tp_sorted%plencP(ind(1:ntp)) - tp%xheliocentric(:,1:ntp) = tp_sorted%xheliocentric(:,ind(1:ntp)) + if (allocated(tp%lperi)) tp%lperi(1:ntp) = tp_sorted%lperi(ind(1:ntp)) + if (allocated(tp%plperP)) tp%plperP(1:ntp) = tp_sorted%plperP(ind(1:ntp)) + if (allocated(tp%plencP)) tp%plencP(1:ntp) = tp_sorted%plencP(ind(1:ntp)) + if (allocated(tp%xheliocentric)) tp%xheliocentric(:,1:ntp) = tp_sorted%xheliocentric(:,ind(1:ntp)) deallocate(tp_sorted) end associate @@ -206,7 +312,7 @@ module subroutine rmvs_util_sort_rearrange_tp(self, ind) end subroutine rmvs_util_sort_rearrange_tp - module subroutine rmvs_util_spill_pl(self, discards, lspill_list) + module subroutine rmvs_util_spill_pl(self, discards, lspill_list, ldestructive) !! author: David A. Minton !! !! Move spilled (discarded) RMVS test particle structure from active list to discard list @@ -214,22 +320,24 @@ module subroutine rmvs_util_spill_pl(self, discards, lspill_list) !! Adapted from David E. Kaufmann's Swifter routine discard_discard_spill.f90 implicit none ! Arguments - class(rmvs_pl), intent(inout) :: self !! RMVS massive body body object - class(swiftest_body), intent(inout) :: discards !! Discarded object - logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + class(rmvs_pl), intent(inout) :: self !! RMVS massive body body object + class(swiftest_body), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not ! Internals integer(I4B) :: i associate(keeps => self) select type(discards) class is (rmvs_pl) - discards%nenc(:) = pack(keeps%nenc(:), lspill_list(:)) - if (count(.not.lspill_list(:)) > 0) then - keeps%nenc(:) = pack(keeps%nenc(:), .not. lspill_list(:)) - end if - call whm_util_spill_pl(keeps, discards, lspill_list) + call util_spill(keeps%nenc, discards%nenc, lspill_list, ldestructive) + call util_spill(keeps%tpenc1P, discards%tpenc1P, lspill_list, ldestructive) + call util_spill(keeps%plind, discards%plind, lspill_list, ldestructive) + + call whm_util_spill_pl(keeps, discards, lspill_list, ldestructive) class default - write(*,*) 'Error! spill method called for incompatible return type on rmvs_pl' + write(*,*) "Invalid object passed to the spill method. Source must be of class rmvs_pl or its descendents!" + call util_exit(FAILURE) end select end associate @@ -237,7 +345,7 @@ module subroutine rmvs_util_spill_pl(self, discards, lspill_list) end subroutine rmvs_util_spill_pl - module subroutine rmvs_util_spill_tp(self, discards, lspill_list) + module subroutine rmvs_util_spill_tp(self, discards, lspill_list, ldestructive) !! author: David A. Minton !! !! Move spilled (discarded) RMVS test particle structure from active list to discard list @@ -248,24 +356,21 @@ module subroutine rmvs_util_spill_tp(self, discards, lspill_list) class(rmvs_tp), intent(inout) :: self !! RMVS test particle object class(swiftest_body), intent(inout) :: discards !! Discarded object logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not ! Internals integer(I4B) :: i associate(keeps => self) select type(discards) class is (rmvs_tp) - discards%lperi(:) = pack(keeps%lperi(:), lspill_list(:)) - discards%plperP(:) = pack(keeps%plperP(:), lspill_list(:)) - discards%plencP(:) = pack(keeps%plencP(:), lspill_list(:)) - if (count(.not.lspill_list(:)) > 0) then - keeps%lperi(:) = pack(keeps%lperi(:), .not. lspill_list(:)) - keeps%plperP(:) = pack(keeps%plperP(:), .not. lspill_list(:)) - keeps%plencP(:) = pack(keeps%plencP(:), .not. lspill_list(:)) - end if - - call util_spill_tp(keeps, discards, lspill_list) + call util_spill(keeps%lperi, discards%lperi, lspill_list, ldestructive) + call util_spill(keeps%plperP, discards%plperP, lspill_list, ldestructive) + call util_spill(keeps%plencP, discards%plencP, lspill_list, ldestructive) + + call util_spill_tp(keeps, discards, lspill_list, ldestructive) class default - write(*,*) 'Error! spill method called for incompatible return type on rmvs_tp' + write(*,*) "Invalid object passed to the spill method. Source must be of class rmvs_tp or its descendents!" + call util_exit(FAILURE) end select end associate diff --git a/src/setup/setup.f90 b/src/setup/setup.f90 index 50da6ce1c..edb641907 100644 --- a/src/setup/setup.f90 +++ b/src/setup/setup.f90 @@ -53,10 +53,9 @@ module subroutine setup_construct_system(system, param) allocate(symba_cb :: system%cb) allocate(symba_pl :: system%pl) allocate(symba_tp :: system%tp) - allocate(symba_pl :: system%pl_discards) allocate(symba_tp :: system%tp_discards) - allocate(symba_pl :: system%mergeadd_list) - allocate(symba_pl :: system%mergesub_list) + allocate(symba_merger :: system%mergeadd_list) + allocate(symba_merger :: system%mergesub_list) allocate(symba_plplenc :: system%plplenc_list) allocate(symba_pltpenc :: system%pltpenc_list) end select @@ -71,6 +70,50 @@ module subroutine setup_construct_system(system, param) end subroutine setup_construct_system + module subroutine setup_encounter(self, n) + !! author: David A. Minton + !! + !! A constructor that sets the number of encounters and allocates and initializes all arrays + !! + implicit none + ! Arguments + class(swiftest_encounter), intent(inout) :: self !! Swiftest encounter structure + integer(I4B), intent(in) :: n !! Number of encounters to allocate space for + + self%nenc = n + if (n == 0) return + + if (allocated(self%lvdotr)) deallocate(self%lvdotr) + if (allocated(self%status)) deallocate(self%status) + if (allocated(self%index1)) deallocate(self%index1) + if (allocated(self%index2)) deallocate(self%index2) + if (allocated(self%x1)) deallocate(self%x1) + if (allocated(self%x2)) deallocate(self%x2) + if (allocated(self%v1)) deallocate(self%v1) + if (allocated(self%v2)) deallocate(self%v2) + + allocate(self%lvdotr(n)) + allocate(self%status(n)) + allocate(self%index1(n)) + allocate(self%index2(n)) + allocate(self%x1(NDIM,n)) + allocate(self%x2(NDIM,n)) + allocate(self%v1(NDIM,n)) + allocate(self%v2(NDIM,n)) + + self%lvdotr(:) = .false. + self%status(:) = INACTIVE + self%index1(:) = 0 + self%index2(:) = 0 + self%x1(:,:) = 0.0_DP + self%x2(:,:) = 0.0_DP + self%v1(:,:) = 0.0_DP + self%v2(:,:) = 0.0_DP + + return + end subroutine setup_encounter + + module subroutine setup_initialize_system(self, param) !! author: David A. Minton !! @@ -110,6 +153,19 @@ module subroutine setup_body(self, n, param) if (n <= 0) return self%lfirst = .true. + if (allocated(self%id)) deallocate(self%id) + if (allocated(self%name)) deallocate(self%name) + if (allocated(self%status)) deallocate(self%status) + if (allocated(self%ldiscard)) deallocate(self%ldiscard) + if (allocated(self%xh)) deallocate(self%xh) + if (allocated(self%vh)) deallocate(self%vh) + if (allocated(self%xb)) deallocate(self%xb) + if (allocated(self%vb)) deallocate(self%vb) + if (allocated(self%ah)) deallocate(self%ah) + if (allocated(self%ir3h)) deallocate(self%ir3h) + if (allocated(self%mu)) deallocate(self%mu) + if (allocated(self%lmask)) deallocate(self%lmask) + allocate(self%id(n)) allocate(self%name(n)) allocate(self%status(n)) @@ -137,14 +193,17 @@ module subroutine setup_body(self, n, param) self%mu(:) = 0.0_DP if (param%loblatecb) then + if (allocated(self%aobl)) deallocate(self%aobl) allocate(self%aobl(NDIM, n)) self%aobl(:,:) = 0.0_DP end if if (param%ltides) then + if (allocated(self%atide)) deallocate(self%lmask) allocate(self%atide(NDIM, n)) self%atide(:,:) = 0.0_DP end if if (param%lgr) then + if (allocated(self%agr)) deallocate(self%lmask) allocate(self%agr(NDIM, n)) self%agr(:,:) = 0.0_DP end if @@ -169,6 +228,10 @@ module subroutine setup_pl(self, n, param) call setup_body(self, n, param) if (n <= 0) return + if (allocated(self%mass)) deallocate(self%mass) + if (allocated(self%Gmass)) deallocate(self%Gmass) + if (allocated(self%rhill)) deallocate(self%rhill) + allocate(self%mass(n)) allocate(self%Gmass(n)) allocate(self%rhill(n)) @@ -180,6 +243,8 @@ module subroutine setup_pl(self, n, param) self%nplpl = 0 if (param%lclose) then + if (allocated(self%radius)) deallocate(self%radius) + if (allocated(self%density)) deallocate(self%density) allocate(self%radius(n)) allocate(self%density(n)) self%radius(:) = 0.0_DP @@ -187,6 +252,8 @@ module subroutine setup_pl(self, n, param) end if if (param%lrotation) then + if (allocated(self%rot)) deallocate(self%rhill) + if (allocated(self%Ip)) deallocate(self%rhill) allocate(self%rot(NDIM, n)) allocate(self%Ip(NDIM, n)) self%rot(:,:) = 0.0_DP @@ -194,6 +261,9 @@ module subroutine setup_pl(self, n, param) end if if (param%ltides) then + if (allocated(self%k2)) deallocate(self%rhill) + if (allocated(self%Q)) deallocate(self%rhill) + if (allocated(self%tlag)) deallocate(self%rhill) allocate(self%k2(n)) allocate(self%Q(n)) allocate(self%tlag(n)) @@ -222,6 +292,10 @@ module subroutine setup_tp(self, n, param) call setup_body(self, n, param) if (n <= 0) return + if (allocated(self%isperi)) deallocate(self%isperi) + if (allocated(self%peri)) deallocate(self%peri) + if (allocated(self%atp)) deallocate(self%atp) + allocate(self%isperi(n)) allocate(self%peri(n)) allocate(self%atp(n)) diff --git a/src/symba/symba_collision.f90 b/src/symba/symba_collision.f90 index d601e853a..ad0e64079 100644 --- a/src/symba/symba_collision.f90 +++ b/src/symba/symba_collision.f90 @@ -2,79 +2,12 @@ use swiftest contains - module subroutine symba_collision_check_plplenc(self, system, param, t, dt, irec) - !! author: Jennifer L.L. Pouplin, Carlisle A. wishard, and David A. Minton - !! - !! Check for merger between massive bodies in SyMBA. If the user has turned on the FRAGMENTATION feature, it will call the - !! symba_regime subroutine to determine what kind of collision will occur. - !! - !! Adapted from David E. Kaufmann's Swifter routine symba_merge_pl.f90 - !! - !! Adapted from Hal Levison's Swift routine symba5_merge.f - implicit none - ! Arguments - class(symba_plplenc), intent(inout) :: self !! SyMBA pl-tp encounter list object - class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters - real(DP), intent(in) :: t !! current time - real(DP), intent(in) :: dt !! step size - integer(I4B), intent(in) :: irec !! Current recursion level - ! Internals - logical, dimension(:), allocatable :: lcollision, lmask - real(DP), dimension(NDIM) :: xr, vr - integer(I4B) :: k - real(DP) :: rlim, mtot - - if (self%nenc == 0) return - - select type(pl => system%pl) - class is (symba_pl) - associate(plplenc_list => self, nplplenc => self%nenc, ind1 => self%index1, ind2 => self%index2) - allocate(lmask(nplplenc)) - lmask(:) = ((plplenc_list%status(1:nplplenc) == ACTIVE) & - .and. (pl%levelg(ind1(1:nplplenc)) >= irec) & - .and. (pl%levelg(ind2(1:nplplenc)) >= irec)) - if (.not.any(lmask(:))) return - - allocate(lcollision(nplplenc)) - lcollision(:) = .false. - - do concurrent(k = 1:nplplenc, lmask(k)) - xr(:) = pl%xh(:, ind1(k)) - pl%xh(:, ind2(k)) - vr(:) = pl%vb(:, ind1(k)) - pl%vb(:, ind2(k)) - rlim = pl%radius(ind1(k)) + pl%radius(ind2(k)) - mtot = pl%Gmass(ind1(k)) + pl%Gmass(ind2(k)) - lcollision(k) = symba_collision_check_one(xr(1), xr(2), xr(3), vr(1), vr(2), vr(3), mtot, rlim, dt, plplenc_list%lvdotr(k)) - end do - - if (any(lcollision(:))) then - do k = 1, nplplenc - if (plplenc_list%status(k) /= COLLISION) cycle - plplenc_list%status(k) = COLLISION - plplenc_list%xh1(:,k) = pl%xh(:,ind1(k)) - plplenc_list%vb1(:,k) = pl%vb(:,ind1(k)) - plplenc_list%xh2(:,k) = pl%xh(:,ind2(k)) - plplenc_list%vb2(:,k) = pl%vb(:,ind2(k)) - if (pl%lcollision(ind1(k)) .or. pl%lcollision(ind2(k))) call pl%make_family([ind1(k),ind2(k)]) - pl%lcollision(ind1(k)) = .true. - pl%lcollision(ind2(k)) = .true. - end do - end if - end associate - end select - - return - - return - end subroutine symba_collision_check_plplenc - - module subroutine symba_collision_check_pltpenc(self, system, param, t, dt, irec) !! author: David A. Minton !! !! Check for merger between massive bodies and test particles in SyMBA !! - !! Adapted from David E. Kaufmann's Swifter routine symba_merge_tp.f90 + !! Adapted from David E. Kaufmann's Swifter routine symba_merge.f90 and symba_merge_tp.f90 !! !! Adapted from Hal Levison's Swift routine symba5_merge.f implicit none @@ -89,39 +22,74 @@ module subroutine symba_collision_check_pltpenc(self, system, param, t, dt, irec logical, dimension(:), allocatable :: lcollision, lmask real(DP), dimension(NDIM) :: xr, vr integer(I4B) :: k + real(DP) :: rlim, mtot + logical :: isplpl if (self%nenc == 0) return + select type(self) + class is (symba_plplenc) + isplpl = .true. + class default + isplpl = .false. + end select select type(pl => system%pl) class is (symba_pl) select type(tp => system%tp) class is (symba_tp) - associate(pltpenc_list => self, npltpenc => self%nenc, plind => self%index1, tpind => self%index2) - allocate(lmask(npltpenc)) - lmask(:) = ((pltpenc_list%status(1:npltpenc) == ACTIVE) & - .and. (pl%levelg(plind(1:npltpenc)) >= irec) & - .and. (tp%levelg(tpind(1:npltpenc)) >= irec)) + associate(nenc => self%nenc, ind1 => self%index1, ind2 => self%index2) + allocate(lmask(nenc)) + lmask(:) = ((self%status(1:nenc) == ACTIVE) .and. (pl%levelg(ind1(1:nenc)) >= irec)) + if (isplpl) then + lmask(:) = lmask(:) .and. (pl%levelg(ind2(1:nenc)) >= irec) + else + lmask(:) = lmask(:) .and. (tp%levelg(ind2(1:nenc)) >= irec) + end if if (.not.any(lmask(:))) return - allocate(lcollision(npltpenc)) + allocate(lcollision(nenc)) lcollision(:) = .false. - do concurrent(k = 1:npltpenc, lmask(k)) - xr(:) = pl%xh(:, plind(k)) - tp%xh(:, tpind(k)) - vr(:) = pl%vb(:, plind(k)) - tp%vb(:, tpind(k)) - lcollision(k) = symba_collision_check_one(xr(1), xr(2), xr(3), vr(1), vr(2), vr(3), pl%Gmass(plind(k)), pl%radius(plind(k)), dt, pltpenc_list%lvdotr(k)) - end do + if (isplpl) then + do concurrent(k = 1:nenc, lmask(k)) + xr(:) = pl%xh(:, ind1(k)) - pl%xh(:, ind2(k)) + vr(:) = pl%vb(:, ind1(k)) - pl%vb(:, ind2(k)) + rlim = pl%radius(ind1(k)) + pl%radius(ind2(k)) + mtot = pl%Gmass(ind1(k)) + pl%Gmass(ind2(k)) + lcollision(k) = symba_collision_check_one(xr(1), xr(2), xr(3), vr(1), vr(2), vr(3), mtot, rlim, dt, self%lvdotr(k)) + end do + else + do concurrent(k = 1:nenc, lmask(k)) + xr(:) = pl%xh(:, ind1(k)) - tp%xh(:, ind2(k)) + vr(:) = pl%vb(:, ind1(k)) - tp%vb(:, ind2(k)) + lcollision(k) = symba_collision_check_one(xr(1), xr(2), xr(3), vr(1), vr(2), vr(3), pl%Gmass(ind1(k)), pl%radius(ind1(k)), dt, self%lvdotr(k)) + end do + end if if (any(lcollision(:))) then - where(lcollision(1:npltpenc)) - pltpenc_list%status(1:npltpenc) = COLLISION - tp%status(tpind(1:npltpenc)) = DISCARDED_PLR - tp%ldiscard(tpind(1:npltpenc)) = .true. - end where - - do k = 1, npltpenc - if (pltpenc_list%status(k) /= COLLISION) cycle - write(*,*) 'Test particle ',tp%id(tpind(k)), ' collided with massive body ',pl%id(plind(k)), ' at time ',t + do k = 1, nenc + if (.not.lcollision(k)) cycle + self%status(k) = COLLISION + self%x1(:,k) = pl%xh(:,ind1(k)) + self%v1(:,k) = pl%vb(:,ind1(k)) + if (isplpl) then + self%x2(:,k) = pl%xh(:,ind2(k)) + self%v2(:,k) = pl%vb(:,ind2(k)) + + ! Check to see if either of these bodies has been involved with a collision before, and if so, make this a collisional family + if (pl%lcollision(ind1(k)) .or. pl%lcollision(ind2(k))) call pl%make_family([ind1(k),ind2(k)]) + + ! Set the collision flag for these to bodies to true in case they become involved in another collision later in the step + pl%lcollision([ind1(k), ind2(k)]) = .true. + pl%ldiscard([ind1(k), ind2(k)]) = .true. + pl%status([ind1(k), ind2(k)]) = COLLISION + else + self%x2(:,k) = tp%xh(:,ind2(k)) + self%v2(:,k) = tp%vb(:,ind2(k)) + tp%status(ind2(k)) = DISCARDED_PLR + tp%ldiscard(ind2(k)) = .true. + write(*,*) 'Test particle ',tp%id(ind2(k)), ' collided with massive body ',pl%id(ind1(k)), ' at time ',t + end if end do end if end associate @@ -174,6 +142,199 @@ pure elemental function symba_collision_check_one(xr, yr, zr, vxr, vyr, vzr, Gmt return end function symba_collision_check_one + + function symba_collision_consolidate_familes(pl, param, idx_parent, family, x, v, mass, radius, L_spin, Ip) result(lflag) + !! author: David A. Minton + !! + !! Loops through the pl-pl collision list and groups families together by index. Outputs the indices of all family members, + !! and pairs of quantities (x and v vectors, mass, radius, L_spin, and Ip) that can be used to resolve the collisional outcome. + implicit none + ! Arguments + class(symba_pl), intent(inout) :: pl !! SyMBA massive body object + class(symba_parameters), intent(in) :: param !! Current run configuration parameters with SyMBA additions + integer(I4B), dimension(2), intent(inout) :: idx_parent !! Index of the two bodies considered the "parents" of the collision + integer(I4B), dimension(:), allocatable, intent(out) :: family !! List of indices of all bodies inovlved in the collision + real(DP), dimension(NDIM,2), intent(out) :: x, v, L_spin, Ip !! Output values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(2), intent(out) :: mass, radius !! Output values that represent a 2-body equivalent of a possibly 2+ body collision + ! Result + logical :: lflag !! Logical flag indicating whether a family was successfully created or not + ! Internals + type family_array + integer(I4B), dimension(:), allocatable :: id + integer(I4B), dimension(:), allocatable :: idx + end type family_array + type(family_array), dimension(2) :: parent_child_index_array + integer(I4B), dimension(2) :: nchild + integer(I4B) :: i, j, fam_size, idx_child + real(DP), dimension(2) :: volume, density + real(DP) :: mchild, mtot, volchild + real(DP), dimension(NDIM) :: xc, vc, xcom, vcom, xchild, vchild, xcrossv + + nchild(:) = pl%kin(idx_parent(:))%nchild + ! If all of these bodies share a parent, but this is still a unique collision, move the last child + ! out of the parent's position and make it the secondary body + if (idx_parent(1) == idx_parent(2)) then + if (nchild(1) == 0) then ! There is only one valid body recorded in this pair (this could happen due to restructuring of the kinship relationships, though it should be rare) + lflag = .false. + return + end if + idx_parent(2) = pl%kin(idx_parent(1))%child(nchild(1)) + nchild(1) = nchild(1) - 1 + nchild(2) = 0 + pl%kin(idx_parent(:))%nchild = nchild(:) + pl%kin(idx_parent(2))%parent = idx_parent(1) + end if + + mass(:) = pl%mass(idx_parent(:)) ! Note: This is meant to mass, not G*mass, as the collisional regime determination uses mass values that will be converted to Si + radius(:) = pl%radius(idx_parent(:)) + volume(:) = (4.0_DP / 3.0_DP) * PI * radius(:)**3 + + ! Group together the ids and indexes of each collisional parent and its children + do j = 1, 2 + allocate(parent_child_index_array(j)%idx(nchild(j)+ 1)) + allocate(parent_child_index_array(j)%id(nchild(j)+ 1)) + associate(idx_arr => parent_child_index_array(j)%idx, & + id_arr => parent_child_index_array(j)%id, & + ncj => nchild(j), & + pl => pl, & + plkinj => pl%kin(idx_parent(j))) + idx_arr(1) = idx_parent(j) + if (ncj > 0) idx_arr(2:ncj + 1) = plkinj%child(1:ncj) + id_arr(:) = pl%id(idx_arr(:)) + end associate + end do + + ! Consolidate the groups of collsional parents with any children they may have into a single "family" index array + fam_size = 2 + sum(nchild(:)) + allocate(family(fam_size)) + family = [parent_child_index_array(1)%idx(:),parent_child_index_array(2)%idx(:)] + fam_size = count(pl%lcollision(family(:))) + family = pack(family(:), pl%lcollision(family(:))) + L_spin(:,:) = 0.0_DP + Ip(:,:) = 0.0_DP + + ! Find the barycenter of each body along with its children, if it has any + do j = 1, 2 + x(:, j) = pl%xb(:, idx_parent(j)) + v(:, j) = pl%vb(:, idx_parent(j)) + ! Assume principal axis rotation about axis corresponding to highest moment of inertia (3rd Ip) + if (param%lrotation) then + Ip(:, j) = mass(j) * pl%Ip(:, idx_parent(j)) + L_spin(:, j) = Ip(3, j) * radius(j)**2 * pl%rot(:, idx_parent(j)) + end if + + if (nchild(j) > 0) then + do i = 1, nchild(j) ! Loop over all children and take the mass weighted mean of the properties + idx_child = parent_child_index_array(j)%idx(i + 1) + if (.not. pl%lcollision(idx_child)) cycle + mchild = pl%mass(idx_child) + xchild(:) = pl%xb(:, idx_child) + vchild(:) = pl%vb(:, idx_child) + volchild = (4.0_DP / 3.0_DP) * PI * pl%radius(idx_child)**3 + volume(j) = volume(j) + volchild + ! Get angular momentum of the child-parent pair and add that to the spin + ! Add the child's spin + if (param%lrotation) then + xcom(:) = (mass(j) * x(:,j) + mchild * xchild(:)) / (mass(j) + mchild) + vcom(:) = (mass(j) * v(:,j) + mchild * vchild(:)) / (mass(j) + mchild) + xc(:) = x(:, j) - xcom(:) + vc(:) = v(:, j) - vcom(:) + xcrossv(:) = xc(:) .cross. vc(:) + L_spin(:, j) = L_spin(:, j) + mass(j) * xcrossv(:) + + xc(:) = xchild(:) - xcom(:) + vc(:) = vchild(:) - vcom(:) + xcrossv(:) = xc(:) .cross. vc(:) + L_spin(:, j) = L_spin(:, j) + mchild * xcrossv(:) + + L_spin(:, j) = L_spin(:, j) + mchild * pl%Ip(3, idx_child) * pl%radius(idx_child)**2 * pl%rot(:, idx_child) + Ip(:, j) = Ip(:, j) + mchild * pl%Ip(:, idx_child) + end if + + ! Merge the child and parent + mass(j) = mass(j) + mchild + x(:, j) = xcom(:) + v(:, j) = vcom(:) + end do + end if + density(j) = mass(j) / volume(j) + radius(j) = ((3 * mass(j)) / (density(j) * 4 * pi))**(1.0_DP / 3.0_DP) + if (param%lrotation) Ip(:, j) = Ip(:, j) / mass(j) + end do + lflag = .true. + + return + end function symba_collision_consolidate_familes + + + module subroutine symba_collision_encounter_scrub(self, system, param) + !! author: David A. Minton + !! + !! Processes the pl-pl encounter list remove only those encounters that led to a collision + !! + implicit none + ! Arguments + class(symba_plplenc), intent(inout) :: self !! SyMBA pl-pl encounter list + class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + ! Internals + logical, dimension(self%nenc) :: lplpl_collision + logical, dimension(:), allocatable :: lplpl_unique_parent + integer(I4B), dimension(:), pointer :: plparent + integer(I4B), dimension(:), allocatable :: collision_idx, unique_parent_idx + integer(I4B) :: i, index_coll, ncollisions, nunique_parent + type(symba_plplenc) :: plplenc_noncollision + + select type (pl => system%pl) + class is (symba_pl) + associate(plplenc_list => self, nplplenc => self%nenc, idx1 => self%index1, idx2 => self%index2, plparent => pl%kin%parent) + lplpl_collision(:) = plplenc_list%status(1:nplplenc) == COLLISION + if (any(lplpl_collision)) then ! Collisions have been detected in this step. So we need to determine which of them are between unique bodies. + + ! Get the subset of pl-pl encounters that lead to a collision + ncollisions = count(lplpl_collision(:)) + allocate(collision_idx(ncollisions)) + collision_idx = pack([(i, i=1, nplplenc)], lplpl_collision) + + ! Get the subset of collisions that involve a unique pair of parents + allocate(lplpl_unique_parent(ncollisions)) + + lplpl_unique_parent(:) = plparent(idx1(collision_idx(:))) /= plparent(idx2(collision_idx(:))) + nunique_parent = count(lplpl_unique_parent(:)) + allocate(unique_parent_idx(nunique_parent)) + unique_parent_idx = pack(collision_idx(:), lplpl_unique_parent(:)) + + ! Scrub all pl-pl collisions involving unique pairs of parents, which will remove all duplicates and leave behind + ! all pairs that have themselves as parents but are not part of the unique parent list. This can hapepn in rare cases + ! due to restructuring of parent/child relationships when there are large numbers of multi-body collisions in a single + ! step + lplpl_unique_parent(:) = .true. + do index_coll = 1, ncollisions + associate(ip1 => plparent(idx1(collision_idx(index_coll))), ip2 => plparent(idx2(collision_idx(index_coll)))) + lplpl_unique_parent(:) = .not. ( any(plparent(idx1(unique_parent_idx(:))) == ip1) .or. & + any(plparent(idx2(unique_parent_idx(:))) == ip1) .or. & + any(plparent(idx1(unique_parent_idx(:))) == ip2) .or. & + any(plparent(idx2(unique_parent_idx(:))) == ip2) ) + end associate + end do + + ! Reassemble collision index list to include only those containing the unique pairs of parents, plus all the non-unique pairs that don't + ! contain a parent body on the unique parent list. + ncollisions = nunique_parent + count(lplpl_unique_parent) + collision_idx = [unique_parent_idx(:), pack(collision_idx(:), lplpl_unique_parent(:))] + + ! Create a mask that contains only the pl-pl encounters that did not result in a collision, and then discard them + lplpl_collision(:) = .false. + lplpl_collision(collision_idx(:)) = .true. + end if + call plplenc_list%spill(plplenc_noncollision, .not.lplpl_collision, ldestructive=.true.) ! Remove any encounters that are not collisions from the list. + end associate + end select + + return + end subroutine symba_collision_encounter_scrub + + module subroutine symba_collision_make_family_pl(self, idx) !! author: Jennifer L.L. Pouplin, Carlisle A. wishard, and David A. Minton !! @@ -235,4 +396,56 @@ module subroutine symba_collision_make_family_pl(self, idx) return end subroutine symba_collision_make_family_pl + + module subroutine symba_collision_resolve_fragmentations(self, system, param) + !! author: David A. Minton + !! + !! Process list of collisions, determine the collisional regime, and then create fragments. + !! + implicit none + ! Arguments + class(symba_plplenc), intent(inout) :: self !! SyMBA pl-pl encounter list + class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object + class(symba_parameters), intent(in) :: param !! Current run configuration parameters with SyMBA additions + ! Internals + + return + end subroutine symba_collision_resolve_fragmentations + + + module subroutine symba_collision_resolve_mergers(self, system, param) + !! author: David A. Minton + !! + !! Process list of collisions and merge colliding bodies together. + !! + implicit none + ! Arguments + class(symba_plplenc), intent(inout) :: self !! SyMBA pl-pl encounter list + class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object + class(symba_parameters), intent(in) :: param !! Current run configuration parameters with SyMBA additions + ! Internals + integer(I4B), dimension(:), allocatable :: family !! List of indices of all bodies inovlved in the collision + integer(I4B), dimension(2) :: idx_parent !! Index of the two bodies considered the "parents" of the collision + real(DP), dimension(NDIM,2) :: x, v, L_spin, Ip !! Output values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(2) :: mass, radius !! Output values that represent a 2-body equivalent of a possibly 2+ body collision + logical :: lgoodcollision + integer(I4B) :: i, status + + associate(plpl_collisions => self, ncollisions => self%nenc, idx1 => self%index1, idx2 => self%index2) + select type(pl => system%pl) + class is (symba_pl) + do i = 1, ncollisions + idx_parent(1) = pl%kin(idx1(i))%parent + idx_parent(2) = pl%kin(idx2(i))%parent + lgoodcollision = symba_collision_consolidate_familes(pl, param, idx_parent, family, x, v, mass, radius, L_spin, Ip) + if (.not. lgoodcollision) cycle + if (any(pl%status(idx_parent(:)) /= COLLISION)) cycle ! One of these two bodies has already been resolved + status = symba_fragmentation_casemerge(system, param, family, x, v, mass, radius, L_spin, Ip) + end do + end select + end associate + + return + end subroutine symba_collision_resolve_mergers + end submodule s_symba_collision \ No newline at end of file diff --git a/src/symba/symba_discard.f90 b/src/symba/symba_discard.f90 index 3f8ada6fe..6ab835e36 100644 --- a/src/symba/symba_discard.f90 +++ b/src/symba/symba_discard.f90 @@ -2,13 +2,321 @@ use swiftest contains + subroutine symba_discard_cb_pl(pl, system, param) + !! author: David A. Minton + !! + !! Check to see if planets should be discarded based on their positions relative to the central body. + !! If a body gets flagged here when it has also been previously flagged for a collision with another massive body, + !! its collisional status will be revoked. Discards due to colliding with or escaping the central body take precedence + !! over pl-pl collisions + !! + !! Adapted from David E. Kaufmann's Swifter routine: symba_discard_sun.f90 + !! Adapted from Hal Levison's Swift routine discard_massive5.f + implicit none + ! Arguments + class(symba_pl), intent(inout) :: pl !! SyMBA massive body object + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + ! Internals + integer(I4B) :: i, j + real(DP) :: energy, vb2, rb2, rh2, rmin2, rmax2, rmaxu2 + + associate(npl => pl%nbody, cb => system%cb) + call system%set_msys() + rmin2 = param%rmin**2 + rmax2 = param%rmax*2 + rmaxu2 = param%rmaxu**2 + do i = 1, npl + if (pl%status(i) == ACTIVE) then + rh2 = dot_product(pl%xh(:,i), pl%xh(:,i)) + if ((param%rmax >= 0.0_DP) .and. (rh2 > rmax2)) then + pl%ldiscard(i) = .true. + pl%lcollision(i) = .false. + pl%status(i) = DISCARDED_RMAX + write(*, *) "Massive body ", pl%id(i), " too far from the central body at t = ", param%t + else if ((param%rmin >= 0.0_DP) .and. (rh2 < rmin2)) then + pl%ldiscard(i) = .true. + pl%lcollision(i) = .false. + pl%status(i) = DISCARDED_RMIN + write(*, *) "Massive body ", pl%id(i), " too close to the central body at t = ", param%t + else if (param%rmaxu >= 0.0_DP) then + rb2 = dot_product(pl%xb(:,i), pl%xb(:,i)) + vb2 = dot_product(pl%vb(:,i), pl%vb(:,i)) + energy = 0.5_DP * vb2 - system%Gmtot / sqrt(rb2) + if ((energy > 0.0_DP) .and. (rb2 > rmaxu2)) then + pl%ldiscard(i) = .true. + pl%lcollision(i) = .false. + pl%status(i) = DISCARDED_RMAXU + write(*, *) "Massive body ", pl%id(i), " is unbound and too far from barycenter at t = ", param%t + end if + end if + end if + end do + end associate + + return + end subroutine symba_discard_cb_pl + + + subroutine symba_discard_conserve_mtm(pl, system, param, ipl, lescape_body) + !! author: David A. Minton + !! + !! Conserves system momentum when a body is lost from the system or collides with central body + implicit none + ! Arguments + class(symba_pl), intent(inout) :: pl + class(symba_nbody_system), intent(inout) :: system + class(symba_parameters), intent(inout) :: param + integer(I4B), intent(in) :: ipl + logical, intent(in) :: lescape_body + ! Internals + real(DP), dimension(NDIM) :: Lpl, Ltot, Lcb, xcom, vcom + real(DP) :: pe, ke_orbit, ke_spin + integer(I4B) :: i, oldstat + + select type(cb => system%cb) + class is (symba_cb) + + ! Add the potential and kinetic energy of the lost body to the records + pe = -cb%mass * pl%mass(ipl) / norm2(pl%xb(:, ipl) - cb%xb(:)) + ke_orbit = 0.5_DP * pl%mass(ipl) * dot_product(pl%vb(:, ipl), pl%vb(:, ipl)) + if (param%lrotation) then + ke_spin = 0.5_DP * pl%mass(ipl) * pl%radius(ipl)**2 * pl%Ip(3, ipl) * dot_product(pl%rot(:, ipl), pl%rot(:, ipl)) + else + ke_spin = 0.0_DP + end if + + ! Add the pre-collision ke of the central body to the records + ! Add planet mass to central body accumulator + if (lescape_body) then + system%Mescape = system%Mescape + pl%mass(ipl) + do i = 1, pl%nbody + if (i == ipl) cycle + pe = pe - pl%mass(i) * pl%mass(ipl) / norm2(pl%xb(:, ipl) - pl%xb(:, i)) + end do + + Ltot(:) = 0.0_DP + do i = 1, pl%nbody + Lpl(:) = pL%mass(i) * pl%xb(:,i) .cross. pl%vb(:, i) + Ltot(:) = Ltot(:) + Lpl(:) + end do + Ltot(:) = Ltot(:) + cb%mass * cb%xb(:) .cross. cb%vb(:) + call pl%b2h(cb) + oldstat = pl%status(ipl) + pl%status(ipl) = INACTIVE + call pl%h2b(cb) + pl%status(ipl) = oldstat + do i = 1, pl%nbody + if (i == ipl) cycle + Lpl(:) = pl%mass(i) * pl%xb(:,i) .cross. pl%vb(:, i) + Ltot(:) = Ltot(:) - Lpl(:) + end do + Ltot(:) = Ltot(:) - cb%mass * cb%xb(:) .cross. cb%vb(:) + system%Lescape(:) = system%Lescape(:) + system%Ltot(:) + if (param%lrotation) system%Lescape(:) = system%Lescape + pl%mass(ipl) * pl%radius(ipl)**2 * pl%Ip(3, ipl) * pl%rot(:, ipl) + + else + xcom(:) = (pl%mass(ipl) * pl%xb(:, ipl) + cb%mass * cb%xb(:)) / (cb%mass + pl%mass(ipl)) + vcom(:) = (pl%mass(ipl) * pl%vb(:, ipl) + cb%mass * cb%vb(:)) / (cb%mass + pl%mass(ipl)) + Lpl(:) = (pl%xb(:,ipl) - xcom(:)) .cross. pL%vb(:,ipl) - vcom(:) + if (param%lrotation) Lpl(:) = pl%mass(ipl) * (Lpl(:) + pl%radius(ipl)**2 * pl%Ip(3,ipl) * pl%rot(:, ipl)) + + Lcb(:) = cb%mass * (cb%xb(:) - xcom(:)) .cross. (cb%vb(:) - vcom(:)) + + ke_orbit = ke_orbit + 0.5_DP * cb%mass * dot_product(cb%vb(:), cb%vb(:)) + if (param%lrotation) ke_spin = ke_spin + 0.5_DP * cb%mass * cb%radius**2 * cb%Ip(3) * dot_product(cb%rot(:), cb%rot(:)) + ! Update mass of central body to be consistent with its total mass + cb%dM = cb%dM + pl%mass(ipl) + cb%dR = cb%dR + 1.0_DP / 3.0_DP * (pl%radius(ipl) / cb%radius)**3 - 2.0_DP / 9.0_DP * (pl%radius(ipl) / cb%radius)**6 + cb%mass = cb%M0 + cb%dM + cb%Gmass = param%GU * cb%mass + cb%radius = cb%R0 + cb%dR + param%rmin = cb%radius + ! Add planet angular momentum to central body accumulator + cb%dL(:) = Lpl(:) + Lcb(:) + cb%dL(:) + ! Update rotation of central body to by consistent with its angular momentum + if (param%lrotation) then + cb%rot(:) = (cb%L0(:) + cb%dL(:)) / (cb%Ip(3) * cb%mass * cb%radius**2) + ke_spin = ke_spin - 0.5_DP * cb%mass * cb%radius**2 * cb%Ip(3) * dot_product(cb%rot(:), cb%rot(:)) + end if + cb%xb(:) = xcom(:) + cb%vb(:) = vcom(:) + ke_orbit = ke_orbit - 0.5_DP * cb%mass * dot_product(cb%vb(:), cb%vb(:)) + end if + call pl%b2h(cb) + + ! We must do this for proper book-keeping, since we can no longer track this body's contribution to energy directly + if (lescape_body) then + system%Ecollisions = system%Ecollisions + ke_orbit + ke_spin + pe + system%Euntracked = system%Euntracked - (ke_orbit + ke_spin + pe) + else + system%Ecollisions = system%Ecollisions + pe + system%Euntracked = system%Euntracked - pe + end if + + end select + return + + end subroutine symba_discard_conserve_mtm + + + subroutine symba_discard_nonplpl(pl, system, param) + !! author: David A. Minton + !! + !! Check to see if planets should be discarded based on their positions or because they are unbound + !s + !! + !! Adapted from David E. Kaufmann's Swifter routine: symba_discard_pl.f90 + !! Adapted from Hal Levison's Swift routine discard_massive5.f + implicit none + ! Arguments + class(symba_pl), intent(inout) :: pl !! SyMBA test particle object + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + + ! First check for collisions with the central body + associate(npl => pl%nbody, cb => system%cb) + if (npl == 0) return + if ((param%rmin >= 0.0_DP) .or. (param%rmax >= 0.0_DP) .or. & + (param%rmaxu >= 0.0_DP) .or. ((param%qmin >= 0.0_DP) .and. (param%qmin_coord == "BARY"))) then + call pl%h2b(cb) + end if + if ((param%rmin >= 0.0_DP) .or. (param%rmax >= 0.0_DP) .or. (param%rmaxu >= 0.0_DP)) then + call symba_discard_cb_pl(pl, system, param) + end if + if (param%qmin >= 0.0_DP .and. npl > 0) call symba_discard_peri_pl(pl, system, param) + end associate + + return + end subroutine symba_discard_nonplpl + + + subroutine symba_discard_nonplpl_conservation(pl, system, param) + !! author: David A. Minton + !! + !! If there are any bodies that are removed due to either colliding with the central body or escaping the systme, + !! we need to track the conserved quantities with the system bookkeeping terms. + implicit none + ! Arguments + class(symba_pl), intent(inout) :: pl !! SyMBA test particle object + class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object + class(symba_parameters), intent(inout) :: param !! Current run configuration parameters + ! Internals + integer(I4B) :: i, ndiscard, dstat + logical :: lescape + logical, dimension(pl%nbody) :: discard_l_pl + integer(I4B), dimension(:), allocatable :: discard_index_list + + associate(npl => pl%nbody) + discard_l_pl(1:npl) = pl%ldiscard(1:npl) .and. .not. pl%lcollision(1:npl) ! These are bodies that are discarded but not flagged as pl-pl collision + ndiscard = count(discard_l_pl(:)) + allocate(discard_index_list(ndiscard)) + discard_index_list(:) = pack([(i, i = 1, npl)], discard_l_pl(1:npl)) + do i = 1, ndiscard + dstat = pl%status(discard_index_list(i)) + if ((dstat == DISCARDED_RMIN) .or. (dstat == DISCARDED_PERI)) then + lescape = .false. + else if ((dstat == DISCARDED_RMAX) .or. (dstat == DISCARDED_RMAXU)) then + lescape = .true. + else + cycle + end if + ! Conserve all the quantities + call symba_discard_conserve_mtm(pl, system, param, discard_index_list(i), lescape) + end do + end associate + + return + end subroutine symba_discard_nonplpl_conservation + + + subroutine symba_discard_peri_pl(pl, system, param) + !! author: David A. Minton + !! + !! Check to see if a test particle should be discarded because its perihelion distance becomes too small + !! + !! Adapted from David E. Kaufmann's Swifter routine: symba_discard_peri_pl.f90 + !! Adapted from Hal Levison's Swift routine discard_mass_peri.f + implicit none + ! Arguments + class(symba_pl), intent(inout) :: pl !! SyMBA massive body object + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + ! Internals + logical, save :: lfirst = .true. + logical :: lfirst_orig + integer(I4B) :: i + + + lfirst_orig = pl%lfirst + pl%lfirst = lfirst + if (lfirst) then + call pl%get_peri(system, param) + lfirst = .false. + else + call pl%get_peri(system, param) + do i = 1, pl%nbody + if (pl%status(i) == ACTIVE) then + if ((pl%isperi(i) == 0) .and. (pl%nplenc(i)== 0)) then + if ((pl%atp(i) >= param%qmin_alo) .and. (pl%atp(i) <= param%qmin_ahi) .and. (pl%peri(i) <= param%qmin)) then + pl%ldiscard(i) = .true. + pl%lcollision(i) = .false. + pl%status(i) = DISCARDED_PERI + write(*, *) "Particle ", pl%id(i), " perihelion distance too small at t = ", param%t + end if + end if + end if + end do + end if + pl%lfirst = lfirst_orig + + return + + end subroutine symba_discard_peri_pl + + module subroutine symba_discard_pl(self, system, param) + !! author: David A. Minton + !! + !! Call the various flavors of discards for massive bodies in SyMBA runs, including discards due to colling with the central body, + !! escaping the system, or colliding with each other. implicit none ! Arguments class(symba_pl), intent(inout) :: self !! SyMBA test particle object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters - + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters + + select type(system) + class is (symba_nbody_system) + select type(param) + class is (symba_parameters) + associate(pl => self, plplenc_list => system%plplenc_list) + call pl%h2b(system%cb) + + ! First deal with the non pl-pl collisions + call symba_discard_nonplpl(self, system, param) + + ! Scrub the pl-pl encounter list of any encounters that did not lead to a collision + call plplenc_list%scrub_non_collision(system, param) + + if ((plplenc_list%nenc > 0) .and. any(pl%lcollision(:))) then + write(*, *) "Collision between massive bodies detected at time t = ",param%t + if (param%lfragmentation) then + call plplenc_list%resolve_fragmentations(system, param) + else + call plplenc_list%resolve_mergers(system, param) + end if + end if + + if (any(pl%ldiscard(:))) then + call symba_discard_nonplpl_conservation(self, system, param) + call pl%rearray(system, param) + end if + + end associate + end select + end select + return end subroutine symba_discard_pl diff --git a/src/symba/symba_drift.f90 b/src/symba/symba_drift.f90 index ac06cbb6a..c4efee05f 100644 --- a/src/symba/symba_drift.f90 +++ b/src/symba/symba_drift.f90 @@ -17,9 +17,9 @@ module subroutine symba_drift_pl(self, system, param, dt) select type(system) class is (symba_nbody_system) - self%lmask(:) = self%status(:) == ACTIVE .and. self%levelg(:) == system%irec + self%lmask(:) = self%status(:) /= INACTIVE .and. self%levelg(:) == system%irec call helio_drift_body(self, system, param, dt) - self%lmask(:) = self%status(:) == ACTIVE + self%lmask(:) = self%status(:) /= INACTIVE end select return @@ -41,9 +41,9 @@ module subroutine symba_drift_tp(self, system, param, dt) select type(system) class is (symba_nbody_system) - self%lmask(:) = self%status(:) == ACTIVE .and. self%levelg(:) == system%irec + self%lmask(:) = self%status(:) /= INACTIVE .and. self%levelg(:) == system%irec call helio_drift_body(self, system, param, dt) - self%lmask(:) = self%status(:) == ACTIVE + self%lmask(:) = self%status(:) /= INACTIVE end select return diff --git a/src/symba/symba_fragmentation.f90 b/src/symba/symba_fragmentation.f90 new file mode 100644 index 000000000..f8afffb85 --- /dev/null +++ b/src/symba/symba_fragmentation.f90 @@ -0,0 +1,137 @@ +submodule (symba_classes) s_symba_fragmentation + use swiftest +contains + + module function symba_fragmentation_casemerge(system, param, family, x, v, mass, radius, L_spin, Ip) result(status) + !! author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! Merge planets. + !! + !! Adapted from David E. Kaufmann's Swifter routines symba_merge_pl.f90 and symba_discard_merge_pl.f90 + !! + !! Adapted from Hal Levison's Swift routines symba5_merge.f and discard_mass_merge.f + implicit none + ! Arguments + class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object + class(symba_parameters), intent(in) :: param !! Current run configuration parameters with SyMBA additions + integer(I4B), dimension(:), intent(in) :: family !! List of indices of all bodies inovlved in the collision + real(DP), dimension(:,:), intent(in) :: x, v, L_spin, Ip !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(in) :: mass, radius !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + ! Result + integer(I4B) :: status !! Status flag assigned to this outcome + ! Internals + integer(I4B) :: i, j, ibiggest, nfamily, nstart, nend + real(DP) :: mass_new, radius_new, volume_new, pe + real(DP), dimension(NDIM) :: xcom, vcom, xc, vc, xcrossv + real(DP), dimension(2) :: vol + real(DP), dimension(NDIM) :: L_orb_old, L_spin_old + real(DP), dimension(NDIM) :: L_spin_new, rot_new, Ip_new + logical, dimension(system%pl%nbody) :: lmask + class(symba_pl), allocatable :: plnew + + select type(pl => system%pl) + class is (symba_pl) + associate(mergeadd_list => system%mergeadd_list, mergesub_list => system%mergesub_list, cb => system%cb) + status = MERGED + write(*, '("Merging bodies ",99(I8,",",:))') pl%id(family(:)) + mass_new = sum(mass(:)) + + ! Merged body is created at the barycenter of the original bodies + xcom(:) = (mass(1) * x(:,1) + mass(2) * x(:,2)) / mass_new + vcom(:) = (mass(1) * v(:,1) + mass(2) * v(:,2)) / mass_new + + ! Get mass weighted mean of Ip and + vol(:) = 4._DP / 3._DP * PI * radius(:)**3 + volume_new = sum(vol(:)) + radius_new = (3 * volume_new / (4 * PI))**(1._DP / 3._DP) + + L_orb_old(:) = 0.0_DP + + ! Compute orbital angular momentum of pre-impact system + do i = 1, 2 + xc(:) = x(:, i) - xcom(:) + vc(:) = v(:, i) - vcom(:) + xcrossv(:) = xc(:) .cross. vc(:) + L_orb_old(:) = L_orb_old(:) + mass(i) * xcrossv(:) + end do + + if (param%lrotation) then + Ip_new(:) = (mass(1) * Ip(:,1) + mass(2) * Ip(:,2)) / mass_new + L_spin_old(:) = L_spin(:,1) + L_spin(:,2) + + ! Conserve angular momentum by putting pre-impact orbital momentum into spin of the new body + L_spin_new(:) = L_orb_old(:) + L_spin_old(:) + + ! Assume prinicpal axis rotation on 3rd Ip axis + rot_new(:) = L_spin_new(:) / (Ip_new(3) * mass_new * radius_new**2) + else ! If spin is not enabled, we will consider the lost pre-collision angular momentum as "escaped" and add it to our bookkeeping variable + system%Lescape(:) = system%Lescape(:) + L_orb_old(:) + end if + + ! Keep track of the component of potential energy due to the pre-impact family for book-keeping + nfamily = size(family(:)) + pe = 0.0_DP + do j = 1, nfamily + do i = j + 1, nfamily + pe = pe - pl%mass(i) * pl%mass(j) / norm2(pl%xb(:, i) - pl%xb(:, j)) + end do + end do + system%Ecollisions = system%Ecollisions + pe + system%Euntracked = system%Euntracked - pe + + ! Add the family bodies to the subtraction list + lmask(:) = .false. + lmask(family(:)) = .true. + pl%status(family(:)) = MERGED + nstart = mergesub_list%nbody + 1 + nend = mergesub_list%nbody + nfamily + call mergesub_list%append(pl, lmask) + ! Record how many bodies were subtracted in this event + mergesub_list%ncomp(nstart:nend) = nfamily + + ! Create the new merged body + allocate(plnew, mold=pl) + call plnew%setup(1, param) + + ! The merged body's name will be that of the largest of the two parents + ibiggest = maxloc(pl%Gmass(family(:)), dim=1) + plnew%id(1) = pl%id(family(ibiggest)) + plnew%status(1) = ACTIVE + plnew%lcollision = .false. + plnew%ldiscard = .false. + plnew%xb(:,1) = xcom(:) + plnew%vb(:,1) = vcom(:) + plnew%xh(:,1) = xcom(:) - cb%xb(:) + plnew%vh(:,1) = vcom(:) - cb%vb(:) + plnew%mass(1) = mass_new + plnew%Gmass(1) = param%GU * mass_new + plnew%density(1) = mass_new / volume_new + plnew%radius(1) = radius_new + plnew%info(1) = pl%info(family(ibiggest)) + if (param%lrotation) then + pl%Ip(:,1) = Ip_new(:) + pl%rot(:,1) = rot_new(:) + end if + if (param%ltides) then + plnew%Q = pl%Q(ibiggest) + plnew%k2 = pl%k2(ibiggest) + plnew%tlag = pl%tlag(ibiggest) + end if + + ! Append the new merged body to the list and record how many we made + nstart = mergeadd_list%nbody + 1 + nend = mergeadd_list%nbody + plnew%nbody + call mergeadd_list%append(plnew) + mergeadd_list%ncomp(nstart:nend) = plnew%nbody + + call plnew%setup(0, param) + deallocate(plnew) + + end associate + end select + + return + + end function symba_fragmentation_casemerge + +end submodule s_symba_fragmentation diff --git a/src/symba/symba_io.f90 b/src/symba/symba_io.f90 index 403204017..1f8626242 100644 --- a/src/symba/symba_io.f90 +++ b/src/symba/symba_io.f90 @@ -207,7 +207,77 @@ module subroutine symba_io_read_frame_info(self, iu, param, form, ierr) ierr = 0 end subroutine symba_io_read_frame_info - + + + module subroutine symba_io_write_discard(self, param) + implicit none + class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + ! Internals + integer(I4B), parameter :: LUN = 40 + integer(I4B) :: iadd, isub, j, ierr, nsub, nadd + logical, save :: lfirst = .true. + real(DP), dimension(:,:), allocatable :: vh + character(*), parameter :: HDRFMT = '(E23.16, 1X, I8, 1X, L1)' + character(*), parameter :: NAMEFMT = '(A, 2(1X, I8))' + character(*), parameter :: VECFMT = '(3(E23.16, 1X))' + character(*), parameter :: NPLFMT = '(I8)' + character(*), parameter :: PLNAMEFMT = '(I8, 2(1X, E23.16))' + class(swiftest_body), allocatable :: pltemp + + associate(pl => self%pl, npl => self%pl%nbody, mergesub_list => self%mergesub_list, mergeadd_list => self%mergeadd_list) + if (self%tp_discards%nbody > 0) call io_write_discard(self, param) + + if (mergesub_list%nbody == 0) return + select case(param%out_stat) + case('APPEND') + open(unit = LUN, file = param%discard_out, status = 'OLD', position = 'APPEND', form = 'FORMATTED', iostat = ierr) + case('NEW', 'REPLACE', 'UNKNOWN') + open(unit = LUN, file = param%discard_out, status = param%out_stat, form = 'FORMATTED', iostat = ierr) + case default + write(*,*) 'Invalid status code for OUT_STAT: ',trim(adjustl(param%out_stat)) + call util_exit(FAILURE) + end select + lfirst = .false. + if (param%lgr) then + call mergesub_list%pv2v(param) + call mergeadd_list%pv2v(param) + end if + + write(LUN, HDRFMT) param%t, mergesub_list%nbody, param%lbig_discard + iadd = 1 + isub = 1 + do while (iadd <= mergeadd_list%nbody) + nadd = mergeadd_list%ncomp(iadd) + nsub = mergesub_list%ncomp(isub) + do j = 1, nadd + if (iadd <= mergeadd_list%nbody) then + write(LUN, NAMEFMT) ADD, mergesub_list%id(iadd), mergesub_list%status(iadd) + write(LUN, VECFMT) mergeadd_list%xh(1, iadd), mergeadd_list%xh(2, iadd), mergeadd_list%xh(3, iadd) + write(LUN, VECFMT) mergeadd_list%vh(1, iadd), mergeadd_list%vh(2, iadd), mergeadd_list%vh(3, iadd) + else + exit + end if + iadd = iadd + 1 + end do + do j = 1, nsub + if (isub <= mergesub_list%nbody) then + write(LUN, NAMEFMT) SUB, mergesub_list%id(isub), mergesub_list%status(isub) + write(LUN, VECFMT) mergesub_list%xh(1, isub), mergesub_list%xh(2, isub), mergesub_list%xh(3, isub) + write(LUN, VECFMT) mergesub_list%vh(1, isub), mergesub_list%vh(2, isub), mergesub_list%vh(3, isub) + else + exit + end if + isub = isub + 1 + end do + end do + + close(LUN) + end associate + + return + end subroutine symba_io_write_discard + module subroutine symba_io_write_frame_info(self, iu, param) implicit none @@ -216,6 +286,5 @@ module subroutine symba_io_write_frame_info(self, iu, param) class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine symba_io_write_frame_info - end submodule s_symba_io diff --git a/src/symba/symba_kick.f90 b/src/symba/symba_kick.f90 index aebb6bb2b..8625b3d81 100644 --- a/src/symba/symba_kick.f90 +++ b/src/symba/symba_kick.f90 @@ -124,8 +124,8 @@ module subroutine symba_kick_pltpenc(self, system, dt, irec, sgn) select type(tp => system%tp) class is (symba_tp) associate(ind1 => self%index1, ind2 => self%index2) - if (pl%nbody > 0) pl%lmask(:) = pl%status(:) == ACTIVE - if (tp%nbody > 0) tp%lmask(:) = tp%status(:) == ACTIVE + if (pl%nbody > 0) pl%lmask(:) = pl%status(:) /= INACTIVE + if (tp%nbody > 0) tp%lmask(:) = tp%status(:) /= INACTIVE irm1 = irec - 1 if (sgn < 0) then @@ -145,7 +145,7 @@ module subroutine symba_kick_pltpenc(self, system, dt, irec, sgn) else lgoodlevel = (pl%levelg(ind1(k)) >= irm1) .and. (tp%levelg(ind2(k)) >= irm1) end if - if ((self%status(k) == ACTIVE) .and. lgoodlevel) then + if ((self%status(k) /= INACTIVE) .and. lgoodlevel) then if (isplpl) then ri = ((pl%rhill(ind1(k)) + pl%rhill(ind2(k)))**2) * (RHSCALE**2) * (RSHELL**(2*irecl)) rim1 = ri * (RSHELL**2) diff --git a/src/symba/symba_setup.f90 b/src/symba/symba_setup.f90 index e240be778..021873a70 100644 --- a/src/symba/symba_setup.f90 +++ b/src/symba/symba_setup.f90 @@ -18,8 +18,6 @@ module subroutine symba_setup_initialize_system(self, param) ! Call parent method associate(system => self) call whm_setup_initialize_system(system, param) - call system%mergeadd_list%setup(1, param) - call system%mergesub_list%setup(1, param) call system%pltpenc_list%setup(0) call system%plplenc_list%setup(0) select type(pl => system%pl) @@ -37,6 +35,32 @@ module subroutine symba_setup_initialize_system(self, param) end subroutine symba_setup_initialize_system + module subroutine symba_setup_merger(self, n, param) + !! author: David A. Minton + !! + !! Allocate SyMBA test particle structure + !! + !! Equivalent in functionality to David E. Kaufmann's Swifter routine symba_setup.f90 + implicit none + ! Arguments + class(symba_merger), intent(inout) :: self !! SyMBA merger list object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameter + ! Internals + integer(I4B) :: i + + !> Call allocation method for parent class. In this case, helio_pl does not have its own setup method so we use the base method for swiftest_pl + call symba_setup_pl(self, n, param) + if (n <= 0) return + + if (allocated(self%ncomp)) deallocate(self%ncomp) + allocate(self%ncomp(n)) + self%ncomp(:) = 0 + + return + end subroutine symba_setup_merger + + module subroutine symba_setup_pl(self, n, param) !! author: David A. Minton !! @@ -55,6 +79,19 @@ module subroutine symba_setup_pl(self, n, param) call setup_pl(self, n, param) if (n <= 0) return + if (allocated(self%lcollision)) deallocate(self%lcollision) + if (allocated(self%lencounter)) deallocate(self%lencounter) + if (allocated(self%lmtiny)) deallocate(self%lmtiny) + if (allocated(self%nplenc)) deallocate(self%nplenc) + if (allocated(self%ntpenc)) deallocate(self%ntpenc) + if (allocated(self%levelg)) deallocate(self%levelg) + if (allocated(self%levelm)) deallocate(self%levelm) + if (allocated(self%isperi)) deallocate(self%isperi) + if (allocated(self%peri)) deallocate(self%peri) + if (allocated(self%atp)) deallocate(self%atp) + if (allocated(self%kin)) deallocate(self%kin) + if (allocated(self%info)) deallocate(self%info) + allocate(self%lcollision(n)) allocate(self%lencounter(n)) allocate(self%lmtiny(n)) @@ -94,59 +131,18 @@ module subroutine symba_setup_pltpenc(self, n) class(symba_pltpenc), intent(inout) :: self !! SyMBA pl-tp encounter structure integer(I4B), intent(in) :: n !! Number of encounters to allocate space for - self%nenc = n + call setup_encounter(self, n) if (n == 0) return - if (allocated(self%lvdotr)) deallocate(self%lvdotr) - if (allocated(self%status)) deallocate(self%status) if (allocated(self%level)) deallocate(self%level) - if (allocated(self%index1)) deallocate(self%index1) - if (allocated(self%index2)) deallocate(self%index2) - allocate(self%lvdotr(n)) - allocate(self%status(n)) allocate(self%level(n)) - allocate(self%index1(n)) - allocate(self%index2(n)) - self%lvdotr(:) = .false. - self%status(:) = INACTIVE + self%level(:) = -1 - self%index1(:) = 0 - self%index2(:) = 0 return end subroutine symba_setup_pltpenc - module subroutine symba_setup_plplenc(self, n) - !! author: David A. Minton - !! - !! A constructor that sets the number of encounters and allocates and initializes all arrays - ! - implicit none - ! Arguments - class(symba_plplenc), intent(inout) :: self !! SyMBA pl-tp encounter structure - integer(I4B), intent(in) :: n !! Number of encounters to allocate space for - - call symba_setup_pltpenc(self, n) - if (n == 0) return - - if (allocated(self%xh1)) deallocate(self%xh1) - if (allocated(self%xh2)) deallocate(self%xh2) - if (allocated(self%vb1)) deallocate(self%vb1) - if (allocated(self%vb2)) deallocate(self%vb2) - allocate(self%xh1(NDIM,n)) - allocate(self%xh2(NDIM,n)) - allocate(self%vb1(NDIM,n)) - allocate(self%vb2(NDIM,n)) - self%xh1(:,:) = 0.0_DP - self%xh2(:,:) = 0.0_DP - self%vb1(:,:) = 0.0_DP - self%vb2(:,:) = 0.0_DP - - return - end subroutine symba_setup_plplenc - - module subroutine symba_setup_tp(self, n, param) !! author: David A. Minton !! @@ -163,9 +159,14 @@ module subroutine symba_setup_tp(self, n, param) call setup_tp(self, n, param) if (n <= 0) return + if (allocated(self%nplenc)) deallocate(self%nplenc) + if (allocated(self%levelg)) deallocate(self%levelg) + if (allocated(self%levelm)) deallocate(self%levelm) + allocate(self%nplenc(n)) allocate(self%levelg(n)) allocate(self%levelm(n)) + self%nplenc(:) = 0 self%levelg(:) = -1 self%levelm(:) = -1 diff --git a/src/symba/symba_step.f90 b/src/symba/symba_step.f90 index e8badd577..41e7a3a74 100644 --- a/src/symba/symba_step.f90 +++ b/src/symba/symba_step.f90 @@ -265,8 +265,8 @@ module subroutine symba_step_reset_system(self) pltpenc_list%nenc = 0 end if - mergeadd_list%nbody = 0 - mergesub_list%nbody = 0 + call mergeadd_list%resize(0) + call mergesub_list%resize(0) end select end select end associate diff --git a/src/symba/symba_util.f90 b/src/symba/symba_util.f90 index 7a6f17cbf..98c8889d8 100644 --- a/src/symba/symba_util.f90 +++ b/src/symba/symba_util.f90 @@ -2,85 +2,540 @@ use swiftest contains - module subroutine symba_util_copy_pltpenc(self, source) + module subroutine symba_util_append_arr_info(arr, source, lsource_mask) !! author: David A. Minton !! - !! Copies elements from the source encounter list into self. + !! Append a single array of particle information type onto another. If the destination array is not allocated, or is not big enough, this will allocate space for it. implicit none ! Arguments - class(symba_pltpenc), intent(inout) :: self !! SyMBA pl-tp encounter list - class(symba_pltpenc), intent(in) :: source !! Source object to copy into + type(symba_particle_info), dimension(:), allocatable, intent(inout) :: arr !! Destination array + type(symba_particle_info), dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + ! Internals + integer(I4B) :: narr, nsrc + + if (.not. allocated(source)) return + + if (present(lsource_mask)) then + nsrc = count(lsource_mask) + else + nsrc = size(source) + end if + + if (allocated(arr)) then + narr = size(arr) + else + allocate(arr(nsrc)) + narr = 0 + end if + + call util_resize(arr, narr + nsrc) + + if (present(lsource_mask)) then + arr(narr + 1:narr + nsrc) = pack(source(:), lsource_mask(:)) + else + arr(narr + 1:narr + nsrc) = source(:) + end if + + return + end subroutine symba_util_append_arr_info + + + module subroutine symba_util_append_arr_kin(arr, source, lsource_mask) + !! author: David A. Minton + !! + !! Append a single array of kinship type onto another. If the destination array is not allocated, or is not big enough, this will allocate space for it. + implicit none + ! Arguments + type(symba_kinship), dimension(:), allocatable, intent(inout) :: arr !! Destination array + type(symba_kinship), dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + ! Internals + integer(I4B) :: narr, nsrc + + if (.not. allocated(source)) return + + if (present(lsource_mask)) then + nsrc = count(lsource_mask) + else + nsrc = size(source) + end if + + if (allocated(arr)) then + narr = size(arr) + else + allocate(arr(nsrc)) + narr = 0 + end if + + call util_resize(arr, narr + nsrc) + + if (present(lsource_mask)) then + arr(narr + 1:narr + nsrc) = pack(source(:), lsource_mask(:)) + else + arr(narr + 1:narr + nsrc) = source(:) + end if + + return + end subroutine symba_util_append_arr_kin + + + module subroutine symba_util_append_pl(self, source, lsource_mask) + !! author: David A. Minton + !! + !! Append components from one massive body object to another. + !! This method will automatically resize the destination body if it is too small + implicit none + !! Arguments + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + + select type(source) + class is (symba_pl) + call util_append_pl(self, source, lsource_mask) ! Note: helio_pl does not have its own append method, so we skip back to the base class + + call util_append(self%lcollision, source%lcollision, lsource_mask) + call util_append(self%lencounter, source%lencounter, lsource_mask) + call util_append(self%lmtiny, source%lmtiny, lsource_mask) + call util_append(self%nplenc, source%nplenc, lsource_mask) + call util_append(self%ntpenc, source%ntpenc, lsource_mask) + call util_append(self%levelg, source%levelg, lsource_mask) + call util_append(self%levelm, source%levelm, lsource_mask) + call util_append(self%isperi, source%isperi, lsource_mask) + call util_append(self%peri, source%peri, lsource_mask) + call util_append(self%atp, source%atp, lsource_mask) + call util_append(self%kin, source%kin, lsource_mask) + call util_append(self%info, source%info, lsource_mask) + class default + write(*,*) "Invalid object passed to the append method. Source must be of class symba_pl or its descendents!" + call util_exit(FAILURE) + end select + + return + end subroutine symba_util_append_pl + + + module subroutine symba_util_append_merger(self, source, lsource_mask) + !! author: David A. Minton + !! + !! Append components from one massive body object to another. + !! This method will automatically resize the destination body if it is too small + implicit none + ! Arguments + class(symba_merger), intent(inout) :: self !! SyMBA massive body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + ! Internals + integer(I4B), dimension(:), allocatable :: ncomp_tmp !! Temporary placeholder for ncomp incase we are appending a symba_pl object to a symba_merger + + select type(source) + class is (symba_merger) + call symba_util_append_pl(self, source, lsource_mask) + call util_append(self%ncomp, source%ncomp, lsource_mask) + class is (symba_pl) + call symba_util_append_pl(self, source, lsource_mask) + allocate(ncomp_tmp, mold=source%id) + ncomp_tmp(:) = 0 + call util_append(self%ncomp, ncomp_tmp, lsource_mask) + class default + write(*,*) "Invalid object passed to the append method. Source must be of class symba_pl or its descendents!" + call util_exit(FAILURE) + end select + + return + end subroutine symba_util_append_merger - associate(n => source%nenc) - self%nenc = n - self%lvdotr(1:n) = source%lvdotr(1:n) - self%status(1:n) = source%status(1:n) - self%level(1:n) = source%level(1:n) - self%index1(1:n) = source%index1(1:n) - self%index2(1:n) = source%index2(1:n) - end associate + module subroutine symba_util_append_tp(self, source, lsource_mask) + !! author: David A. Minton + !! + !! Append components from test particle object to another. + !! This method will automatically resize the destination body if it is too small + implicit none + !! Arguments + class(symba_tp), intent(inout) :: self !! SyMBA test particle object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + + select type(source) + class is (symba_tp) + call util_append_tp(self, source, lsource_mask) ! Note: helio_tp does not have its own append method, so we skip back to the base class + + call util_append(self%nplenc, source%nplenc, lsource_mask) + call util_append(self%levelg, source%levelg, lsource_mask) + call util_append(self%levelm, source%levelm, lsource_mask) + class default + write(*,*) "Invalid object passed to the append method. Source must be of class symba_tp or its descendents!" + call util_exit(FAILURE) + end select + + return + end subroutine symba_util_append_tp + + + module subroutine symba_util_fill_arr_info(keeps, inserts, lfill_list) + !! author: David A. Minton + !! + !! Performs a fill operation on a single array of particle origin information types + !! This is the inverse of a spill operation + implicit none + ! Arguments + type(symba_particle_info), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + type(symba_particle_info), dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + + if (.not.allocated(keeps) .or. .not.allocated(inserts)) return + + keeps(:) = unpack(keeps(:), .not.lfill_list(:), keeps(:)) + keeps(:) = unpack(inserts(:), lfill_list(:), keeps(:)) + return - end subroutine symba_util_copy_pltpenc + end subroutine symba_util_fill_arr_info - module subroutine symba_util_copy_plplenc(self, source) + module subroutine symba_util_fill_arr_kin(keeps, inserts, lfill_list) !! author: David A. Minton !! - !! Copies elements from the source encounter list into self. + !! Performs a fill operation on a single array of particle kinship types + !! This is the inverse of a spill operation implicit none ! Arguments - class(symba_plplenc), intent(inout) :: self !! SyMBA pl-pl encounter list - class(symba_pltpenc), intent(in) :: source !! Source object to copy into + type(symba_kinship), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + type(symba_kinship), dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps - call symba_util_copy_pltpenc(self, source) - associate(n => source%nenc) - select type(source) - class is (symba_plplenc) - self%xh1(:,1:n) = source%xh1(:,1:n) - self%xh2(:,1:n) = source%xh2(:,1:n) - self%vb1(:,1:n) = source%vb1(:,1:n) - self%vb2(:,1:n) = source%vb2(:,1:n) + if (.not.allocated(keeps) .or. .not.allocated(inserts)) return + + keeps(:) = unpack(keeps(:), .not.lfill_list(:), keeps(:)) + keeps(:) = unpack(inserts(:), lfill_list(:), keeps(:)) + + return + end subroutine symba_util_fill_arr_kin + + + module subroutine symba_util_fill_pl(self, inserts, lfill_list) + !! author: David A. Minton + !! + !! Insert new SyMBA test particle structure into an old one. + !! This is the inverse of a fill operation. + !! + implicit none + ! Arguments + class(symba_pl), intent(inout) :: self !! SyMBA masive body object + class(swiftest_body), intent(in) :: inserts !! Inserted object + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + + associate(keeps => self) + select type(inserts) + class is (symba_pl) + call util_fill(keeps%lcollision, inserts%lcollision, lfill_list) + call util_fill(keeps%lencounter, inserts%lencounter, lfill_list) + call util_fill(keeps%lmtiny, inserts%lmtiny, lfill_list) + call util_fill(keeps%nplenc, inserts%nplenc, lfill_list) + call util_fill(keeps%ntpenc, inserts%ntpenc, lfill_list) + call util_fill(keeps%levelg, inserts%levelg, lfill_list) + call util_fill(keeps%levelm, inserts%levelm, lfill_list) + call util_fill(keeps%isperi, inserts%isperi, lfill_list) + call util_fill(keeps%peri, inserts%peri, lfill_list) + call util_fill(keeps%atp, inserts%atp, lfill_list) + call util_fill(keeps%kin, inserts%kin, lfill_list) + call util_fill(keeps%info, inserts%info, lfill_list) + + call util_fill_pl(keeps, inserts, lfill_list) ! Note: helio_pl does not have its own fill method, so we skip back to the base class + class default + write(*,*) "Invalid object passed to the fill method. Source must be of class symba_pl or its descendents!" + call util_exit(FAILURE) end select end associate return - end subroutine symba_util_copy_plplenc + end subroutine symba_util_fill_pl + + + module subroutine symba_util_fill_tp(self, inserts, lfill_list) + !! author: David A. Minton + !! + !! Insert new SyMBA test particle structure into an old one. + !! This is the inverse of a fill operation. + !! + implicit none + ! Arguments + class(symba_tp), intent(inout) :: self !! SyMBA test particle object + class(swiftest_body), intent(in) :: inserts !! Inserted object + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + + associate(keeps => self) + select type(inserts) + class is (symba_tp) + call util_fill(keeps%nplenc, inserts%nplenc, lfill_list) + call util_fill(keeps%levelg, inserts%levelg, lfill_list) + call util_fill(keeps%levelm, inserts%levelm, lfill_list) + + call util_fill_tp(keeps, inserts, lfill_list) ! Note: helio_tp does not have its own fill method, so we skip back to the base class + class default + write(*,*) "Invalid object passed to the fill method. Source must be of class symba_tp or its descendents!" + call util_exit(FAILURE) + end select + end associate + + return + end subroutine symba_util_fill_tp + + + module subroutine symba_util_peri_pl(self, system, param) + !! author: David A. Minton + !! + !! Determine system pericenter passages for planets in SyMBA + !! + !! Adapted from David E. Kaufmann's Swifter routine: symba_peri.f90 + !! Adapted from Hal Levison's Swift routine util_mass_peri.f + implicit none + ! Arguments + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + ! Internals + integer(I4B) :: i + real(DP) :: vdotr, e + + associate(pl => self, npl => self%nbody) + if (pl%lfirst) then + if (param%qmin_coord == "HELIO") then + do i = 1, npl + if (pl%status(i) == ACTIVE) then + vdotr = dot_product(pl%xh(:,i), pl%vh(:,i)) + if (vdotr > 0.0_DP) then + pl%isperi(i) = 1 + else + pl%isperi(i) = -1 + end if + end if + end do + else + do i = 1, npl + if (pl%status(i) == ACTIVE) then + vdotr = dot_product(pl%xb(:,i), pl%vb(:,i)) + if (vdotr > 0.0_DP) then + pl%isperi(i) = 1 + else + pl%isperi(i) = -1 + end if + end if + end do + end if + else + if (param%qmin_coord == "HELIO") then + do i = 1, npl + if (pl%status(i) == ACTIVE) then + vdotr = dot_product(pl%xh(:,i), pl%vh(:,i)) + if (pl%isperi(i) == -1) then + if (vdotr >= 0.0_DP) then + pl%isperi(i) = 0 + CALL orbel_xv2aeq(pl%mu(i), pl%xh(:,i), pl%vh(:,i), pl%atp(i), e, pl%peri(i)) + end if + else + if (vdotr > 0.0_DP) then + pl%isperi(i) = 1 + else + pl%isperi(i) = -1 + end if + end if + end if + end do + else + do i = 1, npl + if (pl%status(i) == ACTIVE) then + vdotr = dot_product(pl%xb(:,i), pl%vb(:,i)) + if (pl%isperi(i) == -1) then + if (vdotr >= 0.0_DP) then + pl%isperi(i) = 0 + CALL orbel_xv2aeq(system%Gmtot, pl%xb(:,i), pl%vb(:,i), pl%atp(i), e, pl%peri(i)) + end if + else + if (vdotr > 0.0_DP) then + pl%isperi(i) = 1 + else + pl%isperi(i) = -1 + end if + end if + end if + end do + end if + end if + end associate + + return + end subroutine symba_util_peri_pl + + + module subroutine symba_util_rearray_pl(self, system, param) + !! Author: the Purdue Swiftest Team - David A. Minton, Carlisle A. Wishard, Jennifer L.L. Pouplin, and Jacob R. Elliott + !! + !! Clean up the massive body structures to remove discarded bodies and add new bodies + implicit none + ! Arguments + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + class(symba_nbody_system), intent(inout) :: system !! Swiftest nbody system object + class(symba_parameters), intent(in) :: param !! Current run configuration parameters + ! Internals + class(symba_pl), allocatable :: pl_discards !! The discarded body list. + + associate(pl => self, mergeadd_list => system%mergeadd_list) + allocate(pl_discards, mold=pl) + ! Remove the discards + call pl%spill(pl_discards, lspill_list=(pl%ldiscard(:) .or. pl%status(:) == INACTIVE), ldestructive=.true.) + + ! Add in any new bodies + call pl%append(mergeadd_list) + + ! If there are still bodies in the system, sort by mass in descending order and re-index + if (pl%nbody > 0) then + call pl%sort("mass", ascending=.false.) + pl%lmtiny(:) = pl%Gmass(:) > param%MTINY + pl%nplm = count(pl%lmtiny(:)) + call pl%eucl_index() + end if + + ! Destroy the discarded body list, since we already have what we need in the mergesub_list + call pl_discards%setup(0,param) + deallocate(pl_discards) + end associate + + return + end subroutine symba_util_rearray_pl + - module subroutine symba_util_resize_pltpenc(self, nrequested) + module subroutine symba_util_resize_arr_info(arr, nnew) !! author: David A. Minton !! - !! Checks the current size of the encounter list against the required size and extends it by a factor of 2 more than requested if it is too small. - !! Polymorphic method works on both symba_pltpenc and symba_plplenc types + !! Resizes an array component of type character string. Array will only be resized if has previously been allocated. Passing nnew = 0 will deallocate. implicit none ! Arguments - class(symba_pltpenc), intent(inout) :: self !! SyMBA pl-tp encounter list - integer(I4B), intent(in) :: nrequested !! New size of list needed + type(symba_particle_info), dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size ! Internals - class(symba_pltpenc), allocatable :: enc_temp - integer(I4B) :: nold - logical :: lmalloc + type(symba_particle_info), dimension(:), allocatable :: tmp !! Temporary storage array in case the input array is already allocated + integer(I4B) :: nold !! Old size - lmalloc = allocated(self%status) - if (lmalloc) then - nold = size(self%status) + if (.not. allocated(arr) .or. nnew < 0) return + + nold = size(arr) + if (nnew == nold) return + + if (nnew == 0) then + deallocate(arr) + return + end if + + allocate(tmp(nnew)) + if (nnew > nold) then + tmp(1:nold) = arr(1:nold) else - nold = 0 + tmp(1:nnew) = arr(1:nnew) end if - if (nrequested > nold) then - if (lmalloc) allocate(enc_temp, source=self) - call self%setup(2 * nrequested) - if (lmalloc) then - call self%copy(enc_temp) - deallocate(enc_temp) - end if + call move_alloc(tmp, arr) + + return + end subroutine symba_util_resize_arr_info + + + module subroutine symba_util_resize_arr_kin(arr, nnew) + !! author: David A. Minton + !! + !! Resizes an array component of type character string. Array will only be resized if has previously been allocated. Passing nnew = 0 will deallocate. + implicit none + ! Arguments + type(symba_kinship), dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + ! Internals + type(symba_kinship), dimension(:), allocatable :: tmp !! Temporary storage array in case the input array is already allocated + integer(I4B) :: nold !! Old size + + if (.not. allocated(arr) .or. nnew < 0) return + + nold = size(arr) + if (nnew == nold) return + + if (nnew == 0) then + deallocate(arr) + return + end if + + allocate(tmp(nnew)) + if (nnew > nold) then + tmp(1:nold) = arr(1:nold) else - self%status(nrequested+1:nold) = INACTIVE + tmp(1:nnew) = arr(1:nnew) end if - self%nenc = nrequested + call move_alloc(tmp, arr) + + return + end subroutine symba_util_resize_arr_kin + + + module subroutine symba_util_resize_merger(self, nnew) + !! author: David A. Minton + !! + !! Checks the current size of a SyMBA merger list against the requested size and resizes it if it is too small. + implicit none + ! Arguments + class(symba_merger), intent(inout) :: self !! SyMBA massive body object + integer(I4B), intent(in) :: nnew !! New size neded + + call symba_util_resize_pl(self, nnew) + + call util_resize(self%ncomp, nnew) + + return + end subroutine symba_util_resize_merger + + + module subroutine symba_util_resize_pl(self, nnew) + !! author: David A. Minton + !! + !! Checks the current size of a SyMBA massive body object against the requested size and resizes it if it is too small. + implicit none + ! Arguments + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + integer(I4B), intent(in) :: nnew !! New size neded + + call util_resize_pl(self, nnew) + + call util_resize(self%lcollision, nnew) + call util_resize(self%lencounter, nnew) + call util_resize(self%lmtiny, nnew) + call util_resize(self%nplenc, nnew) + call util_resize(self%ntpenc, nnew) + call util_resize(self%levelg, nnew) + call util_resize(self%levelm, nnew) + call util_resize(self%isperi, nnew) + call util_resize(self%peri, nnew) + call util_resize(self%atp, nnew) + call util_resize(self%kin, nnew) + call util_resize(self%info, nnew) + + return + end subroutine symba_util_resize_pl + + + module subroutine symba_util_resize_tp(self, nnew) + !! author: David A. Minton + !! + !! Checks the current size of a test particle object against the requested size and resizes it if it is too small. + implicit none + ! Arguments + class(symba_tp), intent(inout) :: self !! SyMBA test particle object + integer(I4B), intent(in) :: nnew !! New size neded + + call util_resize_tp(self, nnew) + + call util_resize(self%nplenc, nnew) + call util_resize(self%levelg, nnew) + call util_resize(self%levelm, nnew) return - end subroutine symba_util_resize_pltpenc + end subroutine symba_util_resize_tp module subroutine symba_util_sort_pl(self, sortby, ascending) @@ -191,23 +646,25 @@ module subroutine symba_util_sort_rearrange_pl(self, ind) associate(pl => self, npl => self%nbody) call util_sort_rearrange_pl(pl,ind) allocate(pl_sorted, source=self) - pl%lcollision(1:npl) = pl_sorted%lcollision(ind(1:npl)) - pl%lencounter(1:npl) = pl_sorted%lencounter(ind(1:npl)) - pl%lmtiny(1:npl) = pl_sorted%lmtiny(ind(1:npl)) - pl%nplenc(1:npl) = pl_sorted%nplenc(ind(1:npl)) - pl%ntpenc(1:npl) = pl_sorted%ntpenc(ind(1:npl)) - pl%levelg(1:npl) = pl_sorted%levelg(ind(1:npl)) - pl%levelm(1:npl) = pl_sorted%levelm(ind(1:npl)) - pl%isperi(1:npl) = pl_sorted%isperi(ind(1:npl)) - pl%peri(1:npl) = pl_sorted%peri(ind(1:npl)) - pl%atp(1:npl) = pl_sorted%atp(ind(1:npl)) - pl%info(1:npl) = pl_sorted%info(ind(1:npl)) - pl%kin(1:npl) = pl_sorted%kin(ind(1:npl)) - do i = 1, npl - do j = 1, pl%kin(i)%nchild - pl%kin(i)%child(j) = ind(pl%kin(i)%child(j)) + if (allocated(pl%lcollision)) pl%lcollision(1:npl) = pl_sorted%lcollision(ind(1:npl)) + if (allocated(pl%lencounter)) pl%lencounter(1:npl) = pl_sorted%lencounter(ind(1:npl)) + if (allocated(pl%lmtiny)) pl%lmtiny(1:npl) = pl_sorted%lmtiny(ind(1:npl)) + if (allocated(pl%nplenc)) pl%nplenc(1:npl) = pl_sorted%nplenc(ind(1:npl)) + if (allocated(pl%ntpenc)) pl%ntpenc(1:npl) = pl_sorted%ntpenc(ind(1:npl)) + if (allocated(pl%levelg)) pl%levelg(1:npl) = pl_sorted%levelg(ind(1:npl)) + if (allocated(pl%levelm)) pl%levelm(1:npl) = pl_sorted%levelm(ind(1:npl)) + if (allocated(pl%isperi)) pl%isperi(1:npl) = pl_sorted%isperi(ind(1:npl)) + if (allocated(pl%peri)) pl%peri(1:npl) = pl_sorted%peri(ind(1:npl)) + if (allocated(pl%atp)) pl%atp(1:npl) = pl_sorted%atp(ind(1:npl)) + if (allocated(pl%info)) pl%info(1:npl) = pl_sorted%info(ind(1:npl)) + if (allocated(pl%kin)) then + pl%kin(1:npl) = pl_sorted%kin(ind(1:npl)) + do i = 1, npl + do j = 1, pl%kin(i)%nchild + pl%kin(i)%child(j) = ind(pl%kin(i)%child(j)) + end do end do - end do + end if deallocate(pl_sorted) end associate @@ -230,13 +687,175 @@ module subroutine symba_util_sort_rearrange_tp(self, ind) associate(tp => self, ntp => self%nbody) call util_sort_rearrange_tp(tp,ind) allocate(tp_sorted, source=self) - tp%nplenc(1:ntp) = tp_sorted%nplenc(ind(1:ntp)) - tp%levelg(1:ntp) = tp_sorted%levelg(ind(1:ntp)) - tp%levelm(1:ntp) = tp_sorted%levelm(ind(1:ntp)) + if (allocated(tp%nplenc)) tp%nplenc(1:ntp) = tp_sorted%nplenc(ind(1:ntp)) + if (allocated(tp%levelg)) tp%levelg(1:ntp) = tp_sorted%levelg(ind(1:ntp)) + if (allocated(tp%levelm)) tp%levelm(1:ntp) = tp_sorted%levelm(ind(1:ntp)) deallocate(tp_sorted) end associate return end subroutine symba_util_sort_rearrange_tp + + module subroutine symba_util_spill_arr_info(keeps, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Performs a spill operation on a single array of particle origin information types + !! This is the inverse of a spill operation + implicit none + ! Arguments + type(symba_particle_info), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + type(symba_particle_info), dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discardss + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + + if (.not.allocated(keeps) .or. count(lspill_list(:)) == 0) return + if (.not.allocated(discards)) allocate(discards(count(lspill_list(:)))) + + discards(:) = pack(keeps(:), lspill_list(:)) + if (ldestructive) then + if (count(.not.lspill_list(:)) > 0) then + keeps(:) = pack(keeps(:), .not. lspill_list(:)) + else + deallocate(keeps) + end if + end if + + return + end subroutine symba_util_spill_arr_info + + + module subroutine symba_util_spill_arr_kin(keeps, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Performs a spill operation on a single array of particle kinships + !! This is the inverse of a spill operation + implicit none + ! Arguments + type(symba_kinship), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + type(symba_kinship), dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discardss + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + + if (.not.allocated(keeps) .or. count(lspill_list(:)) == 0) return + if (.not.allocated(discards)) allocate(discards(count(lspill_list(:)))) + + discards(:) = pack(keeps(:), lspill_list(:)) + if (ldestructive) then + if (count(.not.lspill_list(:)) > 0) then + keeps(:) = pack(keeps(:), .not. lspill_list(:)) + else + deallocate(keeps) + end if + end if + + return + end subroutine symba_util_spill_arr_kin + + + module subroutine symba_util_spill_pl(self, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Move spilled (discarded) SyMBA massive body particle structure from active list to discard list + !! Adapted from David E. Kaufmann's Swifter routine whm_discard_spill.f90 + implicit none + ! Arguments + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + class(swiftest_body), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter body by removing the discard list + ! Internals + integer(I4B) :: i + + ! For each component, pack the discarded bodies into the discard object and do the inverse with the keeps + !> Spill all the common components + associate(keeps => self) + select type(discards) + class is (symba_pl) + call util_spill(keeps%lcollision, discards%lcollision, lspill_list, ldestructive) + call util_spill(keeps%lencounter, discards%lencounter, lspill_list, ldestructive) + call util_spill(keeps%lmtiny, discards%lmtiny, lspill_list, ldestructive) + call util_spill(keeps%nplenc, discards%nplenc, lspill_list, ldestructive) + call util_spill(keeps%ntpenc, discards%ntpenc, lspill_list, ldestructive) + call util_spill(keeps%levelg, discards%levelg, lspill_list, ldestructive) + call util_spill(keeps%levelm, discards%levelm, lspill_list, ldestructive) + call util_spill(keeps%isperi, discards%isperi, lspill_list, ldestructive) + call util_spill(keeps%peri, discards%peri, lspill_list, ldestructive) + call util_spill(keeps%atp, discards%atp, lspill_list, ldestructive) + call util_spill(keeps%info, discards%info, lspill_list, ldestructive) + call util_spill(keeps%kin, discards%kin, lspill_list, ldestructive) + + call util_spill_pl(keeps, discards, lspill_list, ldestructive) + class default + write(*,*) "Invalid object passed to the spill method. Source must be of class symba_pl or its descendents!" + call util_exit(FAILURE) + end select + end associate + + return + end subroutine symba_util_spill_pl + + + module subroutine symba_util_spill_pltpenc(self, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Move spilled (discarded) SyMBA encounter structure from active list to discard list + !! Note: Because the symba_plplenc currently does not contain any additional variable components, this method can recieve it as an input as well. + implicit none + ! Arguments + class(symba_pltpenc), intent(inout) :: self !! SyMBA pl-tp encounter list + class(swiftest_encounter), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter body by removing the discard list + ! Internals + integer(I4B) :: i + + associate(keeps => self) + select type(discards) + class is (symba_pltpenc) + call util_spill(keeps%level, discards%level, lspill_list, ldestructive) + call util_spill_encounter(keeps, discards, lspill_list, ldestructive) + class default + write(*,*) "Invalid object passed to the spill method. Source must be of class symba_pltpenc or its descendents!" + call util_exit(FAILURE) + end select + end associate + + return + end subroutine symba_util_spill_pltpenc + + + module subroutine symba_util_spill_tp(self, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Move spilled (discarded) SyMBA test particle structure from active list to discard list + !! Adapted from David E. Kaufmann's Swifter routine whm_discard_spill.f90 + implicit none + ! Arguments + class(symba_tp), intent(inout) :: self !! SyMBA test particle object + class(swiftest_body), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter body by removing the discard list + ! Internals + integer(I4B) :: i + + ! For each component, pack the discarded bodies into the discard object and do the inverse with the keeps + !> Spill all the common components + associate(keeps => self) + select type(discards) + class is (symba_tp) + call util_spill(keeps%nplenc, discards%nplenc, lspill_list, ldestructive) + call util_spill(keeps%levelg, discards%levelg, lspill_list, ldestructive) + call util_spill(keeps%levelm, discards%levelm, lspill_list, ldestructive) + + call util_spill_tp(keeps, discards, lspill_list, ldestructive) + class default + write(*,*) "Invalid object passed to the spill method. Source must be of class symba_tp or its descendents!" + call util_exit(FAILURE) + end select + end associate + + return + end subroutine symba_util_spill_tp + end submodule s_symba_util \ No newline at end of file diff --git a/src/util/util_append.f90 b/src/util/util_append.f90 new file mode 100644 index 000000000..0f7ac0bde --- /dev/null +++ b/src/util/util_append.f90 @@ -0,0 +1,307 @@ +submodule (swiftest_classes) s_util_append + use swiftest +contains + + module subroutine util_append_arr_char_string(arr, source, lsource_mask) + !! author: David A. Minton + !! + !! Append a single array of character string type onto another. If the destination array is not allocated, or is not big enough, this will allocate space for it. + implicit none + ! Arguments + character(len=STRMAX), dimension(:), allocatable, intent(inout) :: arr !! Destination array + character(len=STRMAX), dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + ! Internals + integer(I4B) :: narr, nsrc + + if (.not. allocated(source)) return + + if (present(lsource_mask)) then + nsrc = count(lsource_mask) + else + nsrc = size(source) + end if + + if (allocated(arr)) then + narr = size(arr) + else + allocate(arr(nsrc)) + narr = 0 + end if + + call util_resize(arr, narr + nsrc) + + if (present(lsource_mask)) then + arr(narr + 1:narr + nsrc) = pack(source(:), lsource_mask(:)) + else + arr(narr + 1:narr + nsrc) = source(:) + end if + + return + end subroutine util_append_arr_char_string + + + module subroutine util_append_arr_DP(arr, source, lsource_mask) + !! author: David A. Minton + !! + !! Append a single array of double precision type onto another. If the destination array is not allocated, or is not big enough, this will allocate space for it. + implicit none + ! Arguments + real(DP), dimension(:), allocatable, intent(inout) :: arr !! Destination array + real(DP), dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + ! Internals + integer(I4B) :: narr, nsrc + + if (.not. allocated(source)) return + + if (present(lsource_mask)) then + nsrc = count(lsource_mask) + else + nsrc = size(source) + end if + + if (allocated(arr)) then + narr = size(arr) + else + allocate(arr(nsrc)) + narr = 0 + end if + + call util_resize(arr, narr + nsrc) + + if (present(lsource_mask)) then + arr(narr + 1:narr + nsrc) = pack(source(:), lsource_mask(:)) + else + arr(narr + 1:narr + nsrc) = source(:) + end if + + return + end subroutine util_append_arr_DP + + + module subroutine util_append_arr_DPvec(arr, source, lsource_mask) + !! author: David A. Minton + !! + !! Append a single array of double precision vector type of size (NDIM, n) onto another. If the destination array is not allocated, or is not big enough, this will allocate space for it. + implicit none + ! Arguments + real(DP), dimension(:,:), allocatable, intent(inout) :: arr !! Destination array + real(DP), dimension(:,:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + ! Internals + integer(I4B) :: narr, nsrc + + if (.not. allocated(source)) return + + if (present(lsource_mask)) then + nsrc = count(lsource_mask) + else + nsrc = size(source, dim=2) + end if + + if (allocated(arr)) then + narr = size(arr, dim=2) + else + allocate(arr(NDIM, nsrc)) + narr = 0 + end if + + call util_resize(arr, narr + nsrc) + + if (present(lsource_mask)) then + arr(1, narr + 1:narr + nsrc) = pack(source(1,:), lsource_mask(:)) + arr(2, narr + 1:narr + nsrc) = pack(source(2,:), lsource_mask(:)) + arr(3, narr + 1:narr + nsrc) = pack(source(3,:), lsource_mask(:)) + else + arr(:, narr + 1:narr + nsrc) = source(:,:) + end if + + return + end subroutine util_append_arr_DPvec + + + module subroutine util_append_arr_I4B(arr, source, lsource_mask) + !! author: David A. Minton + !! + !! Append a single array of integer(I4B) onto another. If the destination array is not allocated, or is not big enough, this will allocate space for it. + implicit none + ! Arguments + integer(I4B), dimension(:), allocatable, intent(inout) :: arr !! Destination array + integer(I4B), dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + ! Internals + integer(I4B) :: narr, nsrc + + if (.not. allocated(source)) return + + if (present(lsource_mask)) then + nsrc = count(lsource_mask) + else + nsrc = size(source) + end if + + if (allocated(arr)) then + narr = size(arr) + else + allocate(arr(nsrc)) + narr = 0 + end if + + call util_resize(arr, narr + nsrc) + + if (present(lsource_mask)) then + arr(narr + 1:narr + nsrc) = pack(source(:), lsource_mask(:)) + else + arr(narr + 1:narr + nsrc) = source(:) + end if + + return + end subroutine util_append_arr_I4B + + + module subroutine util_append_arr_logical(arr, source, lsource_mask) + !! author: David A. Minton + !! + !! Append a single array of logical type onto another. If the destination array is not allocated, or is not big enough, this will allocate space for it. + implicit none + ! Arguments + logical, dimension(:), allocatable, intent(inout) :: arr !! Destination array + logical, dimension(:), allocatable, intent(in) :: source !! Array to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + ! Internals + integer(I4B) :: narr, nsrc + + if (.not. allocated(source)) return + + if (allocated(arr)) then + narr = size(arr) + else + allocate(arr(nsrc)) + narr = 0 + end if + + if (present(lsource_mask)) then + nsrc = count(lsource_mask) + else + nsrc = size(source) + end if + + call util_resize(arr, narr + nsrc) + + if (present(lsource_mask)) then + arr(narr + 1:narr + nsrc) = pack(source(:), lsource_mask(:)) + else + arr(narr + 1:narr + nsrc) = source(:) + end if + + return + end subroutine util_append_arr_logical + + + module subroutine util_append_body(self, source, lsource_mask) + !! author: David A. Minton + !! + !! Append components from one Swiftest body object to another. + !! This method will automatically resize the destination body if it is too small + implicit none + ! Arguments + class(swiftest_body), intent(inout) :: self !! Swiftest body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + + call util_append(self%name, source%name, lsource_mask) + call util_append(self%id, source%id, lsource_mask) + call util_append(self%status, source%status, lsource_mask) + call util_append(self%ldiscard, source%ldiscard, lsource_mask) + call util_append(self%lmask, source%lmask, lsource_mask) + call util_append(self%mu, source%mu, lsource_mask) + call util_append(self%xh, source%xh, lsource_mask) + call util_append(self%vh, source%vh, lsource_mask) + call util_append(self%xb, source%xb, lsource_mask) + call util_append(self%vb, source%vb, lsource_mask) + call util_append(self%ah, source%ah, lsource_mask) + call util_append(self%aobl, source%aobl, lsource_mask) + call util_append(self%atide, source%atide, lsource_mask) + call util_append(self%agr, source%agr, lsource_mask) + call util_append(self%ir3h, source%ir3h, lsource_mask) + call util_append(self%a, source%a, lsource_mask) + call util_append(self%e, source%e, lsource_mask) + call util_append(self%inc, source%inc, lsource_mask) + call util_append(self%capom, source%capom, lsource_mask) + call util_append(self%omega, source%omega, lsource_mask) + call util_append(self%capm, source%capm, lsource_mask) + + self%nbody = count(self%status(:) /= INACTIVE) + + return + end subroutine util_append_body + + + module subroutine util_append_pl(self, source, lsource_mask) + !! author: David A. Minton + !! + !! Append components from one Swiftest body object to another. + !! This method will automatically resize the destination body if it is too small + implicit none + ! Arguments + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + + + select type(source) + class is (swiftest_pl) + call util_append_body(self, source, lsource_mask) + + call util_append(self%mass, source%mass, lsource_mask) + call util_append(self%Gmass, source%Gmass, lsource_mask) + call util_append(self%rhill, source%rhill, lsource_mask) + call util_append(self%radius, source%radius, lsource_mask) + call util_append(self%xbeg, source%xbeg, lsource_mask) + call util_append(self%xend, source%xend, lsource_mask) + call util_append(self%vbeg, source%vbeg, lsource_mask) + call util_append(self%density, source%density, lsource_mask) + call util_append(self%Ip, source%Ip, lsource_mask) + call util_append(self%rot, source%rot, lsource_mask) + call util_append(self%k2, source%k2, lsource_mask) + call util_append(self%Q, source%Q, lsource_mask) + call util_append(self%tlag, source%tlag, lsource_mask) + + call self%eucl_index() + class default + write(*,*) "Invalid object passed to the append method. Source must be of class swiftest_pl or its descendents" + call util_exit(FAILURE) + end select + + return + end subroutine util_append_pl + + + module subroutine util_append_tp(self, source, lsource_mask) + !! author: David A. Minton + !! + !! Append components from one Swiftest body object to another. + !! This method will automatically resize the destination body if it is too small + implicit none + ! Arguments + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + + select type(source) + class is (swiftest_tp) + call util_append_body(self, source, lsource_mask) + + call util_append(self%isperi, source%isperi, lsource_mask) + call util_append(self%peri, source%peri, lsource_mask) + call util_append(self%atp, source%atp, lsource_mask) + class default + write(*,*) "Invalid object passed to the append method. Source must be of class swiftest_tp or its descendents" + call util_exit(FAILURE) + end select + + return + end subroutine util_append_tp + +end submodule s_util_append \ No newline at end of file diff --git a/src/util/util_coord.f90 b/src/util/util_coord.f90 index bdc772d21..c10dbace7 100644 --- a/src/util/util_coord.f90 +++ b/src/util/util_coord.f90 @@ -15,20 +15,21 @@ module subroutine util_coord_h2b_pl(self, cb) class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object ! Internals integer(I4B) :: i - real(DP) :: msys + real(DP) :: Gmtot real(DP), dimension(NDIM) :: xtmp, vtmp + if (self%nbody == 0) return associate(pl => self, npl => self%nbody) - msys = cb%Gmass + Gmtot = cb%Gmass xtmp(:) = 0.0_DP vtmp(:) = 0.0_DP do i = 1, npl - msys = msys + pl%Gmass(i) + Gmtot = Gmtot + pl%Gmass(i) xtmp(:) = xtmp(:) + pl%Gmass(i) * pl%xh(:,i) vtmp(:) = vtmp(:) + pl%Gmass(i) * pl%vh(:,i) end do - cb%xb(:) = -xtmp(:) / msys - cb%vb(:) = -vtmp(:) / msys + cb%xb(:) = -xtmp(:) / Gmtot + cb%vb(:) = -vtmp(:) / Gmtot do i = 1, npl pl%xb(:,i) = pl%xh(:,i) + cb%xb(:) pl%vb(:,i) = pl%vh(:,i) + cb%vb(:) @@ -51,10 +52,11 @@ module subroutine util_coord_h2b_tp(self, cb) class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object class(swiftest_cb), intent(in) :: cb !! Swiftest central body object + if (self%nbody == 0) return associate(ntp => self%nbody, xbcb => cb%xb, vbcb => cb%vb, status => self%status, & xb => self%xb, xh => self%xh, vb => self%vb, vh => self%vh) - where(status(1:ntp) == ACTIVE) + where(status(1:ntp) /= INACTIVE) xb(1, 1:ntp) = xh(1, 1:ntp) + xbcb(1) xb(2, 1:ntp) = xh(2, 1:ntp) + xbcb(2) xb(3, 1:ntp) = xh(3, 1:ntp) + xbcb(3) @@ -83,6 +85,8 @@ module subroutine util_coord_b2h_pl(self, cb) ! Internals integer(I4B) :: i + if (self%nbody == 0) return + associate(npl => self%nbody, xbcb => cb%xb, vbcb => cb%vb, xb => self%xb, xh => self%xh, & vb => self%vb, vh => self%vh) do i = 1, NDIM @@ -107,9 +111,11 @@ module subroutine util_coord_b2h_tp(self, cb) class(swiftest_tp), intent(inout) :: self !! Swiftest massive body object class(swiftest_cb), intent(in) :: cb !! Swiftest central body object + if (self%nbody == 0) return + associate(ntp => self%nbody, xbcb => cb%xb, vbcb => cb%vb, xb => self%xb, xh => self%xh, & vb => self%vb, vh => self%vh, status => self%status) - where(status(1:ntp) == ACTIVE) + where(status(1:ntp) /= INACTIVE) xh(1, 1:ntp) = xb(1, 1:ntp) - xbcb(1) xh(2, 1:ntp) = xb(2, 1:ntp) - xbcb(2) xh(3, 1:ntp) = xb(3, 1:ntp) - xbcb(3) diff --git a/src/util/util_copy.f90 b/src/util/util_copy.f90 new file mode 100644 index 000000000..f44777eec --- /dev/null +++ b/src/util/util_copy.f90 @@ -0,0 +1,29 @@ +submodule(swiftest_classes) s_util_copy + use swiftest +contains + +module subroutine util_copy_encounter(self, source) + !! author: David A. Minton + !! + !! Copies elements from the source encounter list into self. + implicit none + ! Arguments + class(swiftest_encounter), intent(inout) :: self !! Encounter list + class(swiftest_encounter), intent(in) :: source !! Source object to copy into + + associate(n => source%nenc) + self%nenc = n + self%lvdotr(1:n) = source%lvdotr(1:n) + self%status(1:n) = source%status(1:n) + self%index1(1:n) = source%index1(1:n) + self%index2(1:n) = source%index2(1:n) + self%x1(:,1:n) = source%x1(:,1:n) + self%x2(:,1:n) = source%x2(:,1:n) + self%v1(:,1:n) = source%v1(:,1:n) + self%v2(:,1:n) = source%v2(:,1:n) + end associate + + return +end subroutine util_copy_encounter + +end submodule s_util_copy diff --git a/src/util/util_exit.f90 b/src/util/util_exit.f90 index 6814b0029..e770c10f5 100644 --- a/src/util/util_exit.f90 +++ b/src/util/util_exit.f90 @@ -26,6 +26,7 @@ module subroutine util_exit(code) case default write(*, FAIL_MSG) VERSION_NUMBER write(*, BAR) + error stop end select stop diff --git a/src/util/util_fill.f90 b/src/util/util_fill.f90 new file mode 100644 index 000000000..4a5a70311 --- /dev/null +++ b/src/util/util_fill.f90 @@ -0,0 +1,219 @@ +submodule (swiftest_classes) s_util_fill + use swiftest +contains + + module subroutine util_fill_arr_char_string(keeps, inserts, lfill_list) + !! author: David A. Minton + !! + !! Performs a fill operation on a single array of type character strings + !! This is the inverse of a spill operation + implicit none + ! Arguments + character(len=STRMAX), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + character(len=STRMAX), dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + + if (.not.allocated(keeps) .or. .not.allocated(inserts)) return + + keeps(:) = unpack(keeps(:), .not.lfill_list(:), keeps(:)) + keeps(:) = unpack(inserts(:), lfill_list(:), keeps(:)) + + return + end subroutine util_fill_arr_char_string + + module subroutine util_fill_arr_DP(keeps, inserts, lfill_list) + !! author: David A. Minton + !! + !! Performs a fill operation on a single array of type DP + !! This is the inverse of a spill operation + implicit none + ! Arguments + real(DP), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + real(DP), dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + + if (.not.allocated(keeps) .or. .not.allocated(inserts)) return + + keeps(:) = unpack(keeps(:), .not.lfill_list(:), keeps(:)) + keeps(:) = unpack(inserts(:), lfill_list(:), keeps(:)) + + return + end subroutine util_fill_arr_DP + + module subroutine util_fill_arr_DPvec(keeps, inserts, lfill_list) + !! author: David A. Minton + !! + !! Performs a fill operation on a single array of DP vectors with shape (NDIM, n) + !! This is the inverse of a spill operation + implicit none + ! Arguments + real(DP), dimension(:,:), allocatable, intent(inout) :: keeps !! Array of values to keep + real(DP), dimension(:,:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + ! Internals + integer(I4B) :: i + + if (.not.allocated(keeps) .or. .not.allocated(inserts)) return + + do i = 1, NDIM + keeps(i,:) = unpack(keeps(i,:), .not.lfill_list(:), keeps(i,:)) + keeps(i,:) = unpack(inserts(i,:), lfill_list(:), keeps(i,:)) + end do + + return + end subroutine util_fill_arr_DPvec + + module subroutine util_fill_arr_I4B(keeps, inserts, lfill_list) + !! author: David A. Minton + !! + !! Performs a fill operation on a single array of type I4B + !! This is the inverse of a spill operation + implicit none + ! Arguments + integer(I4B), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + integer(I4B), dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + + if (.not.allocated(keeps) .or. .not.allocated(inserts)) return + + keeps(:) = unpack(keeps(:), .not.lfill_list(:), keeps(:)) + keeps(:) = unpack(inserts(:), lfill_list(:), keeps(:)) + + return + end subroutine util_fill_arr_I4B + + module subroutine util_fill_arr_logical(keeps, inserts, lfill_list) + !! author: David A. Minton + !! + !! Performs a fill operation on a single array of logicals + !! This is the inverse of a spill operation + implicit none + ! Arguments + logical, dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + logical, dimension(:), allocatable, intent(in) :: inserts !! Array of values to insert into keep + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + + if (.not.allocated(keeps) .or. .not.allocated(inserts)) return + + keeps(:) = unpack(keeps(:), .not.lfill_list(:), keeps(:)) + keeps(:) = unpack(inserts(:), lfill_list(:), keeps(:)) + + return + end subroutine util_fill_arr_logical + + + module subroutine util_fill_body(self, inserts, lfill_list) + !! author: David A. Minton + !! + !! Insert new Swiftest generic particle structure into an old one. + !! This is the inverse of a spill operation. + implicit none + ! Arguments + class(swiftest_body), intent(inout) :: self !! Swiftest generic body object + class(swiftest_body), intent(in) :: inserts !! Inserted object + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + ! internals + integer(I4B) :: i + + ! For each component, pack the discarded bodies into the discard object and do the inverse with the keeps + !> Fill all the common components + associate(keeps => self) + call util_fill(keeps%id, inserts%id, lfill_list) + call util_fill(keeps%name, inserts%name, lfill_list) + call util_fill(keeps%status, inserts%status, lfill_list) + call util_fill(keeps%ldiscard, inserts%ldiscard, lfill_list) + call util_fill(keeps%lmask, inserts%lmask, lfill_list) + call util_fill(keeps%mu, inserts%mu, lfill_list) + call util_fill(keeps%xh, inserts%xh, lfill_list) + call util_fill(keeps%vh, inserts%vh, lfill_list) + call util_fill(keeps%xb, inserts%xb, lfill_list) + call util_fill(keeps%vb, inserts%vb, lfill_list) + call util_fill(keeps%ah, inserts%ah, lfill_list) + call util_fill(keeps%aobl, inserts%aobl, lfill_list) + call util_fill(keeps%agr, inserts%agr, lfill_list) + call util_fill(keeps%atide, inserts%atide, lfill_list) + call util_fill(keeps%a, inserts%a, lfill_list) + call util_fill(keeps%e, inserts%e, lfill_list) + call util_fill(keeps%inc, inserts%inc, lfill_list) + call util_fill(keeps%capom, inserts%capom, lfill_list) + call util_fill(keeps%omega, inserts%omega, lfill_list) + call util_fill(keeps%capm, inserts%capm, lfill_list) + + ! This is the base class, so will be the last to be called in the cascade. + keeps%nbody = size(keeps%id(:)) + end associate + + return + end subroutine util_fill_body + + + module subroutine util_fill_pl(self, inserts, lfill_list) + !! author: David A. Minton + !! + !! Insert new Swiftest massive body structure into an old one. + !! This is the inverse of a spill operation. + implicit none + ! Arguments + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + class(swiftest_body), intent(in) :: inserts !! Swiftest body object to be inserted + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + ! Internals + integer(I4B) :: i + + associate(keeps => self) + + select type (inserts) ! The standard requires us to select the type of both arguments in order to access all the components + class is (swiftest_pl) + !> Fill components specific to the massive body class + call util_fill(keeps%mass, inserts%mass, lfill_list) + call util_fill(keeps%Gmass, inserts%Gmass, lfill_list) + call util_fill(keeps%rhill, inserts%rhill, lfill_list) + call util_fill(keeps%radius, inserts%radius, lfill_list) + call util_fill(keeps%density, inserts%density, lfill_list) + call util_fill(keeps%k2, inserts%k2, lfill_list) + call util_fill(keeps%Q, inserts%Q, lfill_list) + call util_fill(keeps%tlag, inserts%tlag, lfill_list) + call util_fill(keeps%xbeg, inserts%xbeg, lfill_list) + call util_fill(keeps%vbeg, inserts%vbeg, lfill_list) + call util_fill(keeps%Ip, inserts%Ip, lfill_list) + call util_fill(keeps%rot, inserts%rot, lfill_list) + + call util_fill_body(keeps, inserts, lfill_list) + class default + write(*,*) 'Error! fill method called for incompatible return type on swiftest_pl' + end select + end associate + + return + end subroutine util_fill_pl + + + module subroutine util_fill_tp(self, inserts, lfill_list) + !! author: David A. Minton + !! + !! Insert new Swiftest test particle structure into an old one. + !! This is the inverse of a fill operation. + implicit none + ! Arguments + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object + class(swiftest_body), intent(in) :: inserts !! Swiftest body object to be inserted + logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps + + associate(keeps => self) + select type(inserts) + class is (swiftest_tp) + !> Spill components specific to the test particle class + call util_fill(keeps%isperi, inserts%isperi, lfill_list) + call util_fill(keeps%peri, inserts%peri, lfill_list) + call util_fill(keeps%atp, inserts%atp, lfill_list) + + call util_fill_body(keeps, inserts, lfill_list) + class default + write(*,*) 'Error! fill method called for incompatible return type on swiftest_tp' + end select + end associate + + return + end subroutine util_fill_tp + +end submodule s_util_fill \ No newline at end of file diff --git a/src/util/util_get_energy_momentum.f90 b/src/util/util_get_energy_momentum.f90 new file mode 100644 index 000000000..69936e1b2 --- /dev/null +++ b/src/util/util_get_energy_momentum.f90 @@ -0,0 +1,121 @@ +submodule (swiftest_classes) s_util_get_energy_momentum + use swiftest +contains + module subroutine util_get_energy_momentum_system(self, param, ke_orbit, ke_spin, pe, Lorbit, Lspin) + !! author: David A. Minton + !! + !! Compute total system angular momentum vector and kinetic, potential and total system energy + !! + !! Adapted from David E. Kaufmann Swifter routine symba_energy_eucl.f90 + !! + !! Adapted from Martin Duncan's Swift routine anal_energy.f + implicit none + class(swiftest_nbody_system), intent(inout) :: self !! Swiftest nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(out) :: ke_orbit !! Orbital kinetic energy + real(DP), intent(out) :: ke_spin !! Spin kinetic energy + real(DP), intent(out) :: pe !! Potential energy + real(DP), dimension(:), intent(out) :: Lorbit !! Orbital angular momentum + real(DP), dimension(:), intent(out) :: Lspin !! Spin angular momentum + ! Internals + integer(I4B) :: i, j + integer(I8B) :: k + real(DP) :: rmag, v2, rot2, oblpot, hx, hy, hz, hsx, hsy, hsz + real(DP), dimension(self%pl%nbody) :: irh, kepl, kespinpl, pecb + real(DP), dimension(self%pl%nbody) :: Lplorbitx, Lplorbity, Lplorbitz + real(DP), dimension(self%pl%nbody) :: Lplspinx, Lplspiny, Lplspinz + real(DP), dimension(self%pl%nplpl) :: pepl + logical, dimension(self%pl%nplpl) :: lstatpl + logical, dimension(self%pl%nbody) :: lstatus + + Lorbit(:) = 0.0_DP + Lspin(:) = 0.0_DP + ke_orbit = 0.0_DP + ke_spin = 0.0_DP + associate(system => self, pl => self%pl, npl => self%pl%nbody, cb => self%cb) + kepl(:) = 0.0_DP + Lplorbitx(:) = 0.0_DP + Lplorbity(:) = 0.0_DP + Lplorbitz(:) = 0.0_DP + Lplspinx(:) = 0.0_DP + Lplspiny(:) = 0.0_DP + Lplspinz(:) = 0.0_DP + lstatus(1:npl) = pl%status(1:npl) /= INACTIVE + !!$omp simd private(v2, rot2, hx, hy, hz) + do i = 1, npl + v2 = dot_product(pl%vb(:,i), pl%vb(:,i)) + hx = pl%xb(2,i) * pl%vb(3,i) - pl%xb(3,i) * pl%vb(2,i) + hy = pl%xb(3,i) * pl%vb(1,i) - pl%xb(1,i) * pl%vb(3,i) + hz = pl%xb(1,i) * pl%vb(2,i) - pl%xb(2,i) * pl%vb(1,i) + + ! Angular momentum from orbit + Lplorbitx(i) = pl%mass(i) * hx + Lplorbity(i) = pl%mass(i) * hy + Lplorbitz(i) = pl%mass(i) * hz + + ! Kinetic energy from orbit and spin + kepl(i) = pl%mass(i) * v2 + end do + if (param%lrotation) then + do i = 1, npl + rot2 = dot_product(pl%rot(:,i), pl%rot(:,i)) + ! For simplicity, we always assume that the rotation pole is the 3rd principal axis + hsx = pl%Ip(3,i) * pl%radius(i)**2 * pl%rot(1,i) + hsy = pl%Ip(3,i) * pl%radius(i)**2 * pl%rot(2,i) + hsz = pl%Ip(3,i) * pl%radius(i)**2 * pl%rot(3,i) + + ! Angular momentum from spin + Lplspinx(i) = pl%mass(i) * hsx + Lplspiny(i) = pl%mass(i) * hsy + Lplspinz(i) = pl%mass(i) * hsz + kespinpl(i) = pl%mass(i) * pl%Ip(3, i) * pl%radius(i)**2 * rot2 + end do + else + kespinpl(:) = 0.0_DP + end if + + ! Do the central body potential energy component first + !$omp simd + do i = 1, npl + associate(px => pl%xh(1,i), py => pl%xh(2,i), pz => pl%xh(3,i)) + pecb(i) = -cb%mass * pl%mass(i) / sqrt(px**2 + py**2 + pz**2) + end associate + end do + + ! Do the potential energy between pairs of massive bodies + do k = 1, pl%nplpl + associate(ik => pl%k_plpl(1, k), jk => pl%k_plpl(2, k)) + pepl(k) = -pl%mass(ik) * pl%mass(jk) / norm2(pl%xb(:, jk) - pl%xb(:, ik)) + lstatpl(k) = (lstatus(ik) .and. lstatus(jk)) + end associate + end do + + ke_orbit = 0.5_DP * sum(kepl(1:npl), lstatus(:)) + if (param%lrotation) ke_spin = 0.5_DP * sum(kespinpl(1:npl), lstatus(:)) + + pe = sum(pepl(:), lstatpl(:)) + sum(pecb(2:npl), lstatus(2:npl)) + + ! Potential energy from the oblateness term + if (param%loblatecb) then + !$omp simd + do i = 1, npl + irh(i) = 1.0_DP / norm2(pl%xh(:,i)) + end do + call obl_pot(npl, cb%mass, pl%mass, cb%j2rp2, cb%j4rp4, pl%xh, irh, oblpot) + pe = pe + oblpot + end if + + Lorbit(1) = sum(Lplorbitx(1:npl), lstatus(1:npl)) + Lorbit(2) = sum(Lplorbity(1:npl), lstatus(1:npl)) + Lorbit(3) = sum(Lplorbitz(1:npl), lstatus(1:npl)) + + Lspin(1) = sum(Lplspinx(1:npl), lstatus(1:npl)) + Lspin(2) = sum(Lplspiny(1:npl), lstatus(1:npl)) + Lspin(3) = sum(Lplspinz(1:npl), lstatus(1:npl)) + + end associate + + return + end subroutine util_get_energy_momentum_system + +end submodule s_util_get_energy_momentum diff --git a/src/util/util_peri.f90 b/src/util/util_peri.f90 index 407ee5097..66f2254e1 100644 --- a/src/util/util_peri.f90 +++ b/src/util/util_peri.f90 @@ -45,7 +45,7 @@ module subroutine util_peri_tp(self, system, param) if (tp%isperi(i) == -1) then if (vdotr(i) >= 0.0_DP) then tp%isperi(i) = 0 - call orbel_xv2aeq(system%msys, tp%xb(:, i), tp%vb(:, i), tp%atp(i), e, tp%peri(i)) + call orbel_xv2aeq(system%Gmtot, tp%xb(:, i), tp%vb(:, i), tp%atp(i), e, tp%peri(i)) end if else if (vdotr(i) > 0.0_DP) then diff --git a/src/util/util_resize.f90 b/src/util/util_resize.f90 new file mode 100644 index 000000000..c6d5aa34f --- /dev/null +++ b/src/util/util_resize.f90 @@ -0,0 +1,297 @@ +submodule (swiftest_classes) s_util_resize + use swiftest +contains + module subroutine util_resize_arr_char_string(arr, nnew) + !! author: David A. Minton + !! + !! Resizes an array component of type character string. Array will only be resized if has previously been allocated. Passing nnew = 0 will deallocate. + implicit none + ! Arguments + character(len=STRMAX), dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + ! Internals + character(len=STRMAX), dimension(:), allocatable :: tmp !! Temporary storage array in case the input array is already allocated + integer(I4B) :: nold !! Old size + + if (.not. allocated(arr) .or. nnew < 0) return + + nold = size(arr) + if (nnew == nold) return + + if (nnew == 0) then + deallocate(arr) + return + end if + + allocate(tmp(nnew)) + if (nnew > nold) then + tmp(1:nold) = arr(1:nold) + else + tmp(1:nnew) = arr(1:nnew) + end if + call move_alloc(tmp, arr) + + return + end subroutine util_resize_arr_char_string + + + module subroutine util_resize_arr_DP(arr, nnew) + !! author: David A. Minton + !! + !! Resizes an array component of double precision type. Array will only be resized if has previously been allocated. Passing nnew = 0 will deallocate. + implicit none + ! Arguments + real(DP), dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + ! Internals + real(DP), dimension(:), allocatable :: tmp !! Temporary storage array in case the input array is already allocated + integer(I4B) :: nold !! Old size + + if (.not. allocated(arr) .or. nnew < 0) return + + nold = size(arr) + if (nnew == nold) return + + if (nnew == 0) then + deallocate(arr) + return + end if + + allocate(tmp(nnew)) + if (nnew > nold) then + tmp(1:nold) = arr(1:nold) + else + tmp(1:nnew) = arr(1:nnew) + end if + call move_alloc(tmp, arr) + + return + end subroutine util_resize_arr_DP + + + module subroutine util_resize_arr_DPvec(arr, nnew) + !! author: David A. Minton + !! + !! Resizes an array component of double precision vectors of size (NDIM, n). Array will only be resized if has previously been allocated. Passing nnew = 0 will deallocate. + implicit none + ! Arguments + real(DP), dimension(:,:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + ! Internals + real(DP), dimension(:,:), allocatable :: tmp !! Temporary storage array in case the input array is already allocated + integer(I4B) :: nold !! Old size + + if (.not. allocated(arr) .or. nnew < 0) return + + nold = size(arr, dim=2) + if (nnew == nold) return + + if (nnew == 0) then + deallocate(arr) + return + end if + + allocate(tmp(NDIM, nnew)) + if (nnew > nold) then + tmp(:, 1:nold) = arr(:, 1:nold) + else + tmp(:, 1:nnew) = arr(:, 1:nnew) + end if + call move_alloc(tmp, arr) + + return + end subroutine util_resize_arr_DPvec + + + module subroutine util_resize_arr_I4B(arr, nnew) + !! author: David A. Minton + !! + !! Resizes an array component of integer type. Array will only be resized if has previously been allocated. Passing nnew = 0 will deallocate. + implicit none + ! Arguments + integer(I4B), dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + ! Internals + integer(I4B), dimension(:), allocatable :: tmp !! Temporary storage array in case the input array is already allocated + integer(I4B) :: nold !! Old size + + if (.not. allocated(arr) .or. nnew < 0) return + + nold = size(arr) + if (nnew == nold) return + + if (nnew == 0) then + deallocate(arr) + return + end if + + allocate(tmp(nnew)) + if (nnew > nold) then + tmp(1:nold) = arr(1:nold) + else + tmp(1:nnew) = arr(1:nnew) + end if + call move_alloc(tmp, arr) + + return + end subroutine util_resize_arr_I4B + + + module subroutine util_resize_arr_logical(arr, nnew) + !! author: David A. Minton + !! + !! Resizes an array component of logical type. Array will only be resized if has previously been allocated. Passing nnew = 0 will deallocate. + implicit none + ! Arguments + logical, dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + ! Internals + logical, dimension(:), allocatable :: tmp !! Temporary storage array in case the input array is already allocated + integer(I4B) :: nold !! Old size + + if (.not. allocated(arr) .or. nnew < 0) return + + nold = size(arr) + if (nnew == nold) return + + if (nnew == 0) then + deallocate(arr) + return + end if + + allocate(tmp(nnew)) + if (nnew > nold) then + tmp(1:nold) = arr(1:nold) + else + tmp(1:nnew) = arr(1:nnew) + end if + call move_alloc(tmp, arr) + + return + end subroutine util_resize_arr_logical + + + module subroutine util_resize_body(self, nnew) + !! author: David A. Minton + !! + !! Checks the current size of a Swiftest body against the requested size and resizes it if it is too small. + implicit none + ! Arguments + class(swiftest_body), intent(inout) :: self !! Swiftest body object + integer(I4B), intent(in) :: nnew !! New size neded + + call util_resize(self%name, nnew) + call util_resize(self%id, nnew) + call util_resize(self%status, nnew) + call util_resize(self%ldiscard, nnew) + call util_resize(self%lmask, nnew) + call util_resize(self%mu, nnew) + call util_resize(self%xh, nnew) + call util_resize(self%vh, nnew) + call util_resize(self%xb, nnew) + call util_resize(self%vb, nnew) + call util_resize(self%ah, nnew) + call util_resize(self%aobl, nnew) + call util_resize(self%atide, nnew) + call util_resize(self%agr, nnew) + call util_resize(self%ir3h, nnew) + call util_resize(self%a, nnew) + call util_resize(self%e, nnew) + call util_resize(self%inc, nnew) + call util_resize(self%capom, nnew) + call util_resize(self%omega, nnew) + call util_resize(self%capm, nnew) + self%nbody = count(self%status(1:nnew) /= INACTIVE) + + return + end subroutine util_resize_body + + + module subroutine util_resize_encounter(self, nnew) + !! author: David A. Minton + !! + !! Checks the current size of the encounter list against the required size and extends it by a factor of 2 more than requested if it is too small. + !! Note: The reason to extend it by a factor of 2 is for performance. When there are many enounters per step, resizing every time you want to add an + !! encounter takes significant computational effort. Resizing by a factor of 2 is a tradeoff between performance (fewer resize calls) and memory managment + !! Memory usage grows by a factor of 2 each time it fills up, but no more. + implicit none + ! Arguments + class(swiftest_encounter), intent(inout) :: self !! Swiftest encounter list + integer(I4B), intent(in) :: nnew !! New size of list needed + ! Internals + class(swiftest_encounter), allocatable :: enc_temp + integer(I4B) :: nold + logical :: lmalloc + + lmalloc = allocated(self%status) + if (lmalloc) then + nold = size(self%status) + else + nold = 0 + end if + if (nnew > nold) then + if (lmalloc) allocate(enc_temp, source=self) + call self%setup(2 * nnew) + if (lmalloc) then + call self%copy(enc_temp) + deallocate(enc_temp) + end if + else + self%status(nnew+1:nold) = INACTIVE + end if + self%nenc = nnew + + return + end subroutine util_resize_encounter + + + module subroutine util_resize_pl(self, nnew) + !! author: David A. Minton + !! + !! Checks the current size of a Swiftest body against the requested size and resizes it if it is too small. + implicit none + ! Arguments + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + integer(I4B), intent(in) :: nnew !! New size neded + + call util_resize_body(self, nnew) + + call util_resize(self%mass, nnew) + call util_resize(self%Gmass, nnew) + call util_resize(self%rhill, nnew) + call util_resize(self%radius, nnew) + call util_resize(self%xbeg, nnew) + call util_resize(self%xend, nnew) + call util_resize(self%vbeg, nnew) + call util_resize(self%density, nnew) + call util_resize(self%Ip, nnew) + call util_resize(self%rot, nnew) + call util_resize(self%k2, nnew) + call util_resize(self%Q, nnew) + call util_resize(self%tlag, nnew) + call self%eucl_index() + + return + end subroutine util_resize_pl + + + module subroutine util_resize_tp(self, nnew) + !! author: David A. Minton + !! + !! Checks the current size of a Swiftest body against the requested size and resizes it if it is too small. + implicit none + ! Arguments + class(swiftest_tp), intent(inout) :: self !! Swiftest massive body object + integer(I4B), intent(in) :: nnew !! New size neded + + call util_resize_body(self, nnew) + + call util_resize(self%isperi, nnew) + call util_resize(self%peri, nnew) + call util_resize(self%atp, nnew) + + return + end subroutine util_resize_tp + + +end submodule s_util_resize \ No newline at end of file diff --git a/src/util/util_set.f90 b/src/util/util_set.f90 index c401cb0ce..86e021ab6 100644 --- a/src/util/util_set.f90 +++ b/src/util/util_set.f90 @@ -62,7 +62,7 @@ module subroutine util_set_msys(self) ! Arguments class(swiftest_nbody_system), intent(inout) :: self !! Swiftest nobdy system object - self%msys = self%cb%mass + sum(self%pl%mass(1:self%pl%nbody)) + self%Gmtot = self%cb%Gmass + sum(self%pl%Gmass(1:self%pl%nbody), self%pl%status(1:self%pl%nbody) /= INACTIVE) return end subroutine util_set_msys diff --git a/src/util/util_sort.f90 b/src/util/util_sort.f90 index 59f44c003..752e78ab7 100644 --- a/src/util/util_sort.f90 +++ b/src/util/util_sort.f90 @@ -161,28 +161,27 @@ module subroutine util_sort_rearrange_body(self, ind) associate(n => self%nbody) allocate(body_sorted, source=self) - self%id(1:n) = body_sorted%id(ind(1:n)) - self%name(1:n) = body_sorted%name(ind(1:n)) - self%status(1:n) = body_sorted%status(ind(1:n)) - self%ldiscard(1:n) = body_sorted%ldiscard(ind(1:n)) - self%xh(:,1:n) = body_sorted%xh(:,ind(1:n)) - self%vh(:,1:n) = body_sorted%vh(:,ind(1:n)) - self%xb(:,1:n) = body_sorted%xb(:,ind(1:n)) - self%vb(:,1:n) = body_sorted%vb(:,ind(1:n)) - self%ah(:,1:n) = body_sorted%ah(:,ind(1:n)) - self%ir3h(1:n) = body_sorted%ir3h(ind(1:n)) - self%mu(1:n) = body_sorted%mu(ind(1:n)) - self%lmask(1:n) = body_sorted%lmask(ind(1:n)) - - if (allocated(self%a)) self%a(1:n) = body_sorted%a(ind(1:n)) - if (allocated(self%e)) self%e(1:n) = body_sorted%e(ind(1:n)) - if (allocated(self%inc)) self%inc(1:n) = body_sorted%inc(ind(1:n)) - if (allocated(self%capom)) self%capom(1:n) = body_sorted%capom(ind(1:n)) - if (allocated(self%omega)) self%omega(1:n) = body_sorted%omega(ind(1:n)) - if (allocated(self%capm)) self%capm(1:n) = body_sorted%capm(ind(1:n)) - if (allocated(self%aobl)) self%aobl(:,1:n) = body_sorted%aobl(:,ind(1:n)) - if (allocated(self%atide)) self%atide(:,1:n) = body_sorted%atide(:,ind(1:n)) - if (allocated(self%agr)) self%agr(:,1:n) = body_sorted%agr(:,ind(1:n)) + if (allocated(self%id)) self%id(1:n) = body_sorted%id(ind(1:n)) + if (allocated(self%name)) self%name(1:n) = body_sorted%name(ind(1:n)) + if (allocated(self%status)) self%status(1:n) = body_sorted%status(ind(1:n)) + if (allocated(self%ldiscard)) self%ldiscard(1:n) = body_sorted%ldiscard(ind(1:n)) + if (allocated(self%xh)) self%xh(:,1:n) = body_sorted%xh(:,ind(1:n)) + if (allocated(self%vh)) self%vh(:,1:n) = body_sorted%vh(:,ind(1:n)) + if (allocated(self%xb)) self%xb(:,1:n) = body_sorted%xb(:,ind(1:n)) + if (allocated(self%vb)) self%vb(:,1:n) = body_sorted%vb(:,ind(1:n)) + if (allocated(self%ah)) self%ah(:,1:n) = body_sorted%ah(:,ind(1:n)) + if (allocated(self%ir3h)) self%ir3h(1:n) = body_sorted%ir3h(ind(1:n)) + if (allocated(self%mu)) self%mu(1:n) = body_sorted%mu(ind(1:n)) + if (allocated(self%lmask)) self%lmask(1:n) = body_sorted%lmask(ind(1:n)) + if (allocated(self%a)) self%a(1:n) = body_sorted%a(ind(1:n)) + if (allocated(self%e)) self%e(1:n) = body_sorted%e(ind(1:n)) + if (allocated(self%inc)) self%inc(1:n) = body_sorted%inc(ind(1:n)) + if (allocated(self%capom)) self%capom(1:n) = body_sorted%capom(ind(1:n)) + if (allocated(self%omega)) self%omega(1:n) = body_sorted%omega(ind(1:n)) + if (allocated(self%capm)) self%capm(1:n) = body_sorted%capm(ind(1:n)) + if (allocated(self%aobl)) self%aobl(:,1:n) = body_sorted%aobl(:,ind(1:n)) + if (allocated(self%atide)) self%atide(:,1:n) = body_sorted%atide(:,ind(1:n)) + if (allocated(self%agr)) self%agr(:,1:n) = body_sorted%agr(:,ind(1:n)) deallocate(body_sorted) end associate @@ -204,9 +203,9 @@ module subroutine util_sort_rearrange_pl(self, ind) associate(pl => self, npl => self%nbody) call util_sort_rearrange_body(pl,ind) allocate(pl_sorted, source=self) - pl%mass(1:npl) = pl_sorted%mass(ind(1:npl)) - pl%Gmass(1:npl) = pl_sorted%Gmass(ind(1:npl)) - pl%rhill(1:npl) = pl_sorted%rhill(ind(1:npl)) + if (allocated(pl%mass)) pl%mass(1:npl) = pl_sorted%mass(ind(1:npl)) + if (allocated(pl%Gmass)) pl%Gmass(1:npl) = pl_sorted%Gmass(ind(1:npl)) + if (allocated(pl%rhill)) pl%rhill(1:npl) = pl_sorted%rhill(ind(1:npl)) if (allocated(pl%xbeg)) pl%xbeg(:,1:npl) = pl_sorted%xbeg(:,ind(1:npl)) if (allocated(pl%xend)) pl%xend(:,1:npl) = pl_sorted%xend(:,ind(1:npl)) if (allocated(pl%vbeg)) pl%vbeg(:,1:npl) = pl_sorted%vbeg(:,ind(1:npl)) @@ -240,9 +239,9 @@ module subroutine util_sort_rearrange_tp(self, ind) associate(tp => self, ntp => self%nbody) call util_sort_rearrange_body(tp,ind) allocate(tp_sorted, source=self) - tp%isperi(1:ntp) = tp_sorted%isperi(ind(1:ntp)) - tp%peri(1:ntp) = tp_sorted%peri(ind(1:ntp)) - tp%atp(1:ntp) = tp_sorted%atp(ind(1:ntp)) + if (allocated(tp%isperi)) tp%isperi(1:ntp) = tp_sorted%isperi(ind(1:ntp)) + if (allocated(tp%peri)) tp%peri(1:ntp) = tp_sorted%peri(ind(1:ntp)) + if (allocated(tp%atp)) tp%atp(1:ntp) = tp_sorted%atp(ind(1:ntp)) deallocate(tp_sorted) end associate diff --git a/src/util/util_spill.f90 b/src/util/util_spill.f90 new file mode 100644 index 000000000..9acc6ae93 --- /dev/null +++ b/src/util/util_spill.f90 @@ -0,0 +1,300 @@ +submodule (swiftest_classes) s_util_spill + use swiftest +contains + + module subroutine util_spill_arr_char_string(keeps, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Performs a spill operation on a single array of type character strings + !! This is the inverse of a spill operation + implicit none + ! Arguments + character(len=STRMAX), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + character(len=STRMAX), dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + + if (.not.allocated(keeps) .or. count(lspill_list(:)) == 0) return + if (.not.allocated(discards)) allocate(discards(count(lspill_list(:)))) + + discards(:) = pack(keeps(:), lspill_list(:)) + if (ldestructive) then + if (count(.not.lspill_list(:)) > 0) then + keeps(:) = pack(keeps(:), .not. lspill_list(:)) + else + deallocate(keeps) + end if + end if + + return + end subroutine util_spill_arr_char_string + + module subroutine util_spill_arr_DP(keeps, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Performs a spill operation on a single array of type DP + !! This is the inverse of a spill operation + implicit none + ! Arguments + real(DP), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + real(DP), dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discardss + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + + if (.not.allocated(keeps) .or. count(lspill_list(:)) == 0) return + if (.not.allocated(discards)) allocate(discards(count(lspill_list(:)))) + + discards(:) = pack(keeps(:), lspill_list(:)) + if (ldestructive) then + if (count(.not.lspill_list(:)) > 0) then + keeps(:) = pack(keeps(:), .not. lspill_list(:)) + else + deallocate(keeps) + end if + end if + + return + end subroutine util_spill_arr_DP + + module subroutine util_spill_arr_DPvec(keeps, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Performs a spill operation on a single array of DP vectors with shape (NDIM, n) + !! This is the inverse of a spill operation + implicit none + ! Arguments + real(DP), dimension(:,:), allocatable, intent(inout) :: keeps !! Array of values to keep + real(DP), dimension(:,:), allocatable, intent(inout) :: discards !! Array discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + ! Internals + integer(I4B) :: i + + if (.not.allocated(keeps) .or. count(lspill_list(:)) == 0) return + if (.not.allocated(discards)) allocate(discards(NDIM, count(lspill_list(:)))) + + do i = 1, NDIM + discards(i,:) = pack(keeps(i,:), lspill_list(:)) + end do + if (ldestructive) then + if (count(.not.lspill_list(:)) > 0) then + do i = 1, NDIM + keeps(i,:) = pack(keeps(i,:), .not. lspill_list(:)) + end do + end if + end if + + return + end subroutine util_spill_arr_DPvec + + module subroutine util_spill_arr_I4B(keeps, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Performs a spill operation on a single array of type I4B + !! This is the inverse of a spill operation + implicit none + ! Arguments + integer(I4B), dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + integer(I4B), dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + + if (.not.allocated(keeps) .or. count(lspill_list(:)) == 0) return + if (.not.allocated(discards)) allocate(discards(count(lspill_list(:)))) + + discards(:) = pack(keeps(:), lspill_list(:)) + if (ldestructive) then + if (count(.not.lspill_list(:)) > 0) then + keeps(:) = pack(keeps(:), .not. lspill_list(:)) + else + deallocate(keeps) + end if + end if + + return + end subroutine util_spill_arr_I4B + + module subroutine util_spill_arr_logical(keeps, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Performs a spill operation on a single array of logicals + !! This is the inverse of a spill operation + implicit none + ! Arguments + logical, dimension(:), allocatable, intent(inout) :: keeps !! Array of values to keep + logical, dimension(:), allocatable, intent(inout) :: discards !! Array of discards + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or no + + if (.not.allocated(keeps) .or. count(lspill_list(:)) == 0) return + if (.not.allocated(discards)) allocate(discards(count(lspill_list(:)))) + + discards(:) = pack(keeps(:), lspill_list(:)) + if (ldestructive) then + if (count(.not.lspill_list(:)) > 0) then + keeps(:) = pack(keeps(:), .not. lspill_list(:)) + else + deallocate(keeps) + end if + end if + + return + end subroutine util_spill_arr_logical + + + module subroutine util_spill_body(self, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Move spilled (discarded) Swiftest generic particle structure from active list to discard list + !! Adapted from David E. Kaufmann's Swifter routine whm_discard_spill.f90 + implicit none + ! Arguments + class(swiftest_body), intent(inout) :: self !! Swiftest generic body object + class(swiftest_body), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter body by removing the discard list + ! Internals + integer(I4B) :: i + + ! For each component, pack the discarded bodies into the discard object and do the inverse with the keeps + !> Spill all the common components + associate(keeps => self) + call util_spill(keeps%id, discards%id, lspill_list, ldestructive) + call util_spill(keeps%name, discards%name, lspill_list, ldestructive) + call util_spill(keeps%status, discards%status, lspill_list, ldestructive) + call util_spill(keeps%lmask, discards%lmask, lspill_list, ldestructive) + call util_spill(keeps%ldiscard, discards%ldiscard, lspill_list, ldestructive) + call util_spill(keeps%mu, discards%mu, lspill_list, ldestructive) + call util_spill(keeps%xh, discards%xh, lspill_list, ldestructive) + call util_spill(keeps%vh, discards%vh, lspill_list, ldestructive) + call util_spill(keeps%xb, discards%xb, lspill_list, ldestructive) + call util_spill(keeps%vb, discards%vb, lspill_list, ldestructive) + call util_spill(keeps%ah, discards%ah, lspill_list, ldestructive) + call util_spill(keeps%aobl, discards%aobl, lspill_list, ldestructive) + call util_spill(keeps%agr, discards%agr, lspill_list, ldestructive) + call util_spill(keeps%atide, discards%atide, lspill_list, ldestructive) + call util_spill(keeps%a, discards%a, lspill_list, ldestructive) + call util_spill(keeps%e, discards%e, lspill_list, ldestructive) + call util_spill(keeps%inc, discards%inc, lspill_list, ldestructive) + call util_spill(keeps%capom, discards%capom, lspill_list, ldestructive) + call util_spill(keeps%omega, discards%omega, lspill_list, ldestructive) + call util_spill(keeps%capm, discards%capm, lspill_list, ldestructive) + + ! This is the base class, so will be the last to be called in the cascade. + ! Therefore we need to set the nbody values for both the keeps and discareds + discards%nbody = count(lspill_list(:)) + keeps%nbody = count(.not.lspill_list(:)) + if (keeps%nbody > size(keeps%status)) keeps%status(keeps%nbody+1:size(keeps%status)) = INACTIVE + + end associate + + return + end subroutine util_spill_body + + module subroutine util_spill_encounter(self, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Move spilled (discarded) Swiftest encounter structure from active list to discard list + implicit none + ! Arguments + class(swiftest_encounter), intent(inout) :: self !! Swiftest encounter list + class(swiftest_encounter), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter body by removing the discard list + ! Internals + integer(I4B) :: i + + associate(keeps => self) + + call util_spill(keeps%lvdotr, discards%lvdotr, lspill_list, ldestructive) + call util_spill(keeps%status, discards%status, lspill_list, ldestructive) + call util_spill(keeps%index1, discards%index1, lspill_list, ldestructive) + call util_spill(keeps%index2, discards%index2, lspill_list, ldestructive) + call util_spill(keeps%x1, discards%x1, lspill_list, ldestructive) + call util_spill(keeps%x2, discards%x2, lspill_list, ldestructive) + call util_spill(keeps%v1, discards%v1, lspill_list, ldestructive) + call util_spill(keeps%v2, discards%v2, lspill_list, ldestructive) + + ! This is the base class, so will be the last to be called in the cascade. + ! Therefore we need to set the nenc values for both the keeps and discareds + discards%nenc = count(lspill_list(:)) + keeps%nenc = count(.not.lspill_list(:)) + if (keeps%nenc > size(keeps%status)) keeps%status(keeps%nenc+1:size(keeps%status)) = INACTIVE + end associate + + return + end subroutine util_spill_encounter + + + module subroutine util_spill_pl(self, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Move spilled (discarded) Swiftest massive body structure from active list to discard list + !! Adapted from David E. Kaufmann's Swifter routine whm_discard_spill.f90 + implicit none + ! Arguments + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + class(swiftest_body), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter body by removing the discard list + ! Internals + integer(I4B) :: i + + associate(keeps => self) + + select type (discards) ! The standard requires us to select the type of both arguments in order to access all the components + class is (swiftest_pl) + !> Spill components specific to the massive body class + call util_spill(keeps%mass, discards%mass, lspill_list, ldestructive) + call util_spill(keeps%Gmass, discards%Gmass, lspill_list, ldestructive) + call util_spill(keeps%rhill, discards%rhill, lspill_list, ldestructive) + call util_spill(keeps%radius, discards%radius, lspill_list, ldestructive) + call util_spill(keeps%density, discards%density, lspill_list, ldestructive) + call util_spill(keeps%k2, discards%k2, lspill_list, ldestructive) + call util_spill(keeps%Q, discards%Q, lspill_list, ldestructive) + call util_spill(keeps%tlag, discards%tlag, lspill_list, ldestructive) + call util_spill(keeps%xbeg, discards%xbeg, lspill_list, ldestructive) + call util_spill(keeps%vbeg, discards%vbeg, lspill_list, ldestructive) + call util_spill(keeps%Ip, discards%Ip, lspill_list, ldestructive) + call util_spill(keeps%rot, discards%rot, lspill_list, ldestructive) + + call util_spill_body(keeps, discards, lspill_list, ldestructive) + class default + write(*,*) 'Error! spill method called for incompatible return type on swiftest_pl' + end select + end associate + + return + end subroutine util_spill_pl + + + module subroutine util_spill_tp(self, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Move spilled (discarded) Swiftest test particle structure from active list to discard list + !! Adapted from David E. Kaufmann's Swifter routine whm_discard_spill.f90 + implicit none + ! Arguments + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object + class(swiftest_body), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discardse + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter body by removing the discard list + + associate(keeps => self, ntp => self%nbody) + select type(discards) + class is (swiftest_tp) + !> Spill components specific to the test particle class + call util_spill(keeps%isperi, discards%isperi, lspill_list, ldestructive) + call util_spill(keeps%peri, discards%peri, lspill_list, ldestructive) + call util_spill(keeps%atp, discards%atp, lspill_list, ldestructive) + + call util_spill_body(keeps, discards, lspill_list, ldestructive) + class default + write(*,*) 'Error! spill method called for incompatible return type on swiftest_tp' + end select + end associate + + return + end subroutine util_spill_tp + +end submodule s_util_spill \ No newline at end of file diff --git a/src/util/util_spill_and_fill.f90 b/src/util/util_spill_and_fill.f90 deleted file mode 100644 index 8ea85f654..000000000 --- a/src/util/util_spill_and_fill.f90 +++ /dev/null @@ -1,506 +0,0 @@ -submodule (swiftest_classes) s_util_spill_and_fill - use swiftest -contains - - module subroutine util_spill_body(self, discards, lspill_list) - !! author: David A. Minton - !! - !! Move spilled (discarded) Swiftest generic particle structure from active list to discard list - !! Adapted from David E. Kaufmann's Swifter routine whm_discard_spill.f90 - implicit none - ! Arguments - class(swiftest_body), intent(inout) :: self !! Swiftest generic body object - class(swiftest_body), intent(inout) :: discards !! Discarded object - logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards - ! Internals - integer(I4B) :: i - - ! For each component, pack the discarded bodies into the discard object and do the inverse with the keeps - !> Spill all the common components - associate(keeps => self) - discards%id(:) = pack(keeps%id(:), lspill_list(:)) - discards%name(:) = pack(keeps%name(:), lspill_list(:)) - discards%status(:) = pack(keeps%status(:), lspill_list(:)) - discards%mu(:) = pack(keeps%mu(:), lspill_list(:)) - discards%lmask(:) = pack(keeps%lmask(:), lspill_list(:)) - do i = 1, NDIM - discards%xh(i, :) = pack(keeps%xh(i, :), lspill_list(:)) - discards%vh(i, :) = pack(keeps%vh(i, :), lspill_list(:)) - discards%xb(i, :) = pack(keeps%xb(i, :), lspill_list(:)) - discards%vb(i, :) = pack(keeps%vb(i, :), lspill_list(:)) - discards%ah(i, :) = pack(keeps%ah(i, :), lspill_list(:)) - end do - - if (allocated(keeps%a)) discards%a(:) = pack(keeps%a(:), lspill_list(:)) - if (allocated(keeps%e)) discards%e(:) = pack(keeps%e(:), lspill_list(:)) - if (allocated(keeps%capom)) discards%capom(:) = pack(keeps%capom(:), lspill_list(:)) - if (allocated(keeps%omega)) discards%omega(:) = pack(keeps%omega(:), lspill_list(:)) - if (allocated(keeps%capm)) discards%capm(:) = pack(keeps%capm(:), lspill_list(:)) - - - if (allocated(keeps%aobl)) then - do i = 1, NDIM - discards%aobl(i, :) = pack(keeps%aobl(i, :), lspill_list(:)) - end do - end if - if (allocated(keeps%agr)) then - do i = 1, NDIM - discards%agr(i, :) = pack(keeps%agr(i, :), lspill_list(:)) - end do - end if - if (allocated(keeps%atide)) then - do i = 1, NDIM - discards%atide(i, :) = pack(keeps%atide(i, :), lspill_list(:)) - end do - end if - - if (count(.not.lspill_list(:)) > 0) then - keeps%id(:) = pack(keeps%id(:), .not. lspill_list(:)) - keeps%name(:) = pack(keeps%name(:), .not. lspill_list(:)) - keeps%status(:) = pack(keeps%status(:), .not. lspill_list(:)) - keeps%mu(:) = pack(keeps%mu(:), .not. lspill_list(:)) - keeps%lmask(:) = pack(keeps%lmask(:), .not. lspill_list(:)) - - do i = 1, NDIM - keeps%xh(i, :) = pack(keeps%xh(i, :), .not. lspill_list(:)) - keeps%vh(i, :) = pack(keeps%vh(i, :), .not. lspill_list(:)) - keeps%xb(i, :) = pack(keeps%xb(i, :), .not. lspill_list(:)) - keeps%vb(i, :) = pack(keeps%vb(i, :), .not. lspill_list(:)) - keeps%ah(i, :) = pack(keeps%ah(i, :), .not. lspill_list(:)) - end do - - if (allocated(keeps%a)) keeps%a(:) = pack(keeps%a(:), .not. lspill_list(:)) - if (allocated(keeps%e)) keeps%e(:) = pack(keeps%e(:), .not. lspill_list(:)) - if (allocated(keeps%inc)) keeps%inc(:) = pack(keeps%inc(:), .not. lspill_list(:)) - if (allocated(keeps%capom)) keeps%capom(:) = pack(keeps%capom(:), .not. lspill_list(:)) - if (allocated(keeps%omega)) keeps%omega(:) = pack(keeps%omega(:), .not. lspill_list(:)) - if (allocated(keeps%capm)) keeps%capm(:) = pack(keeps%capm(:), .not. lspill_list(:)) - - if (allocated(keeps%aobl)) then - do i = 1, NDIM - keeps%aobl(i, :) = pack(keeps%aobl(i, :), .not. lspill_list(:)) - end do - end if - - if (allocated(keeps%agr)) then - do i = 1, NDIM - keeps%agr(i, :) = pack(keeps%agr(i, :), .not. lspill_list(:)) - end do - end if - - if (allocated(keeps%atide)) then - do i = 1, NDIM - keeps%atide(i, :) = pack(keeps%atide(i, :), .not. lspill_list(:)) - end do - end if - - end if - ! This is the base class, so will be the last to be called in the cascade. - ! Therefore we need to set the nbody values for both the keeps and discareds - discards%nbody = count(lspill_list(:)) - keeps%nbody = count(.not.lspill_list(:)) - if (allocated(keeps%ldiscard)) deallocate(keeps%ldiscard) - if (allocated(discards%ldiscard)) deallocate(discards%ldiscard) - allocate(keeps%ldiscard(keeps%nbody)) - allocate(discards%ldiscard(discards%nbody)) - keeps%ldiscard = .false. - discards%ldiscard = .true. - - end associate - - return - end subroutine util_spill_body - - - module subroutine util_fill_body(self, inserts, lfill_list) - !! author: David A. Minton - !! - !! Insert new Swiftest generic particle structure into an old one. - !! This is the inverse of a fill operation. - implicit none - ! Arguments - class(swiftest_body), intent(inout) :: self !! Swiftest generic body object - class(swiftest_body), intent(inout) :: inserts !! Insertted object - logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps - ! internals - integer(I4B) :: i - - ! For each component, pack the discarded bodies into the discard object and do the inverse with the keeps - !> Spill all the common components - associate(keeps => self) - keeps%id(:) = unpack(keeps%id(:), .not.lfill_list(:), keeps%id(:)) - keeps%id(:) = unpack(inserts%id(:), lfill_list(:), keeps%id(:)) - - keeps%name(:) = unpack(keeps%name(:), .not.lfill_list(:), keeps%name(:)) - keeps%name(:) = unpack(inserts%name(:), lfill_list(:), keeps%name(:)) - - keeps%status(:) = unpack(keeps%status(:), .not.lfill_list(:), keeps%status(:)) - keeps%status(:) = unpack(inserts%status(:), lfill_list(:), keeps%status(:)) - - keeps%ldiscard(:) = unpack(keeps%ldiscard(:), .not.lfill_list(:), keeps%ldiscard(:)) - keeps%ldiscard(:) = unpack(inserts%ldiscard(:), lfill_list(:), keeps%ldiscard(:)) - - keeps%mu(:) = unpack(keeps%mu(:), .not.lfill_list(:), keeps%mu(:)) - keeps%mu(:) = unpack(inserts%mu(:), lfill_list(:), keeps%mu(:)) - - keeps%lmask(:) = unpack(keeps%lmask(:), .not.lfill_list(:), keeps%ldiscard(:)) - keeps%lmask(:) = unpack(inserts%lmask(:), lfill_list(:), keeps%ldiscard(:)) - - do i = 1, NDIM - keeps%xh(i, :) = unpack(keeps%xh(i, :), .not.lfill_list(:), keeps%xh(i, :)) - keeps%xh(i, :) = unpack(inserts%xh(i, :), lfill_list(:), keeps%xh(i, :)) - - keeps%vh(i, :) = unpack(keeps%vh(i, :), .not.lfill_list(:), keeps%vh(i, :)) - keeps%vh(i, :) = unpack(inserts%vh(i, :), lfill_list(:), keeps%vh(i, :)) - - keeps%xb(i, :) = unpack(keeps%xb(i, :), .not.lfill_list(:), keeps%xb(i, :)) - keeps%xb(i, :) = unpack(inserts%xb(i, :), lfill_list(:), keeps%xb(i, :)) - - keeps%vb(i, :) = unpack(keeps%vb(i, :), .not.lfill_list(:), keeps%vb(i, :)) - keeps%vb(i, :) = unpack(inserts%vb(i, :), lfill_list(:), keeps%vb(i, :)) - - keeps%ah(i, :) = unpack(keeps%ah(i, :), .not.lfill_list(:), keeps%ah(i, :)) - keeps%ah(i, :) = unpack(inserts%ah(i, :), lfill_list(:), keeps%ah(i, :)) - end do - - if (allocated(keeps%aobl)) then - do i = 1, NDIM - keeps%aobl(i, :) = unpack(keeps%aobl(i, :), .not.lfill_list(:), keeps%aobl(i, :)) - keeps%aobl(i, :) = unpack(inserts%aobl(i, :), lfill_list(:), keeps%aobl(i, :)) - end do - end if - - if (allocated(keeps%agr)) then - do i = 1, NDIM - keeps%agr(i, :) = unpack(keeps%agr(i, :), .not.lfill_list(:), keeps%agr(i, :)) - keeps%agr(i, :) = unpack(inserts%agr(i, :), lfill_list(:), keeps%agr(i, :)) - end do - end if - - if (allocated(keeps%atide)) then - do i = 1, NDIM - keeps%atide(i, :) = unpack(keeps%atide(i, :), .not.lfill_list(:), keeps%atide(i, :)) - keeps%atide(i, :) = unpack(inserts%atide(i, :), lfill_list(:), keeps%atide(i, :)) - end do - end if - - if (allocated(keeps%a)) then - keeps%a(:) = unpack(keeps%a(:), .not.lfill_list(:), keeps%a(:)) - keeps%a(:) = unpack(inserts%a(:), lfill_list(:), keeps%a(:)) - end if - - if (allocated(keeps%e)) then - keeps%e(:) = unpack(keeps%e(:), .not.lfill_list(:), keeps%e(:)) - keeps%e(:) = unpack(inserts%e(:), lfill_list(:), keeps%e(:)) - end if - - if (allocated(keeps%inc)) then - keeps%inc(:) = unpack(keeps%inc(:), .not.lfill_list(:), keeps%inc(:)) - keeps%inc(:) = unpack(inserts%inc(:), lfill_list(:), keeps%inc(:)) - end if - - if (allocated(keeps%capom)) then - keeps%capom(:) = unpack(keeps%capom(:),.not.lfill_list(:), keeps%capom(:)) - keeps%capom(:) = unpack(inserts%capom(:),lfill_list(:), keeps%capom(:)) - end if - - if (allocated(keeps%omega)) then - keeps%omega(:) = unpack(keeps%omega(:),.not.lfill_list(:), keeps%omega(:)) - keeps%omega(:) = unpack(inserts%omega(:),lfill_list(:), keeps%omega(:)) - end if - - if (allocated(keeps%capm)) then - keeps%capm(:) = unpack(keeps%capm(:), .not.lfill_list(:), keeps%capm(:)) - keeps%capm(:) = unpack(inserts%capm(:), lfill_list(:), keeps%capm(:)) - end if - - ! This is the base class, so will be the last to be called in the cascade. - keeps%nbody = size(keeps%id(:)) - end associate - - return - end subroutine util_fill_body - - - module subroutine util_spill_pl(self, discards, lspill_list) - !! author: David A. Minton - !! - !! Move spilled (discarded) Swiftest massive body structure from active list to discard list - !! Adapted from David E. Kaufmann's Swifter routine whm_discard_spill.f90 - implicit none - ! Arguments - class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object - class(swiftest_body), intent(inout) :: discards !! Discarded object - logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discardse - ! Internals - integer(I4B) :: i - - associate(keeps => self) - - select type (discards) ! The standard requires us to select the type of both arguments in order to access all the components - class is (swiftest_pl) - !> Spill components specific to the massive body class - discards%mass(:) = pack(keeps%mass(:), lspill_list(:)) - discards%Gmass(:) = pack(keeps%Gmass(:), lspill_list(:)) - discards%rhill(:) = pack(keeps%rhill(:), lspill_list(:)) - - if (allocated(keeps%radius)) discards%radius(:) = pack(keeps%radius(:), lspill_list(:)) - if (allocated(keeps%density)) discards%density(:) = pack(keeps%density(:), lspill_list(:)) - if (allocated(keeps%k2)) discards%k2(:) = pack(keeps%k2(:), lspill_list(:)) - if (allocated(keeps%Q)) discards%Q(:) = pack(keeps%Q(:), lspill_list(:)) - if (allocated(keeps%tlag)) discards%tlag(:) = pack(keeps%tlag(:), lspill_list(:)) - - if (allocated(keeps%xbeg)) then - do i = 1, NDIM - discards%xbeg(i, :) = pack(keeps%xbeg(i, :), lspill_list(:)) - end do - end if - - if (allocated(keeps%xend)) then - do i = 1, NDIM - discards%xend(i, :) = pack(keeps%xend(i, :), lspill_list(:)) - end do - end if - - if (allocated(keeps%vbeg)) then - do i = 1, NDIM - discards%vbeg(i, :) = pack(keeps%vbeg(i, :), lspill_list(:)) - end do - end if - - if (allocated(keeps%Ip)) then - do i = 1, NDIM - discards%Ip(i, :) = pack(keeps%Ip(i, :), lspill_list(:)) - end do - end if - - if (allocated(keeps%rot)) then - do i = 1, NDIM - discards%rot(i, :) = pack(keeps%rot(i, :), lspill_list(:)) - end do - end if - - if (count(.not.lspill_list(:)) > 0) then - keeps%mass(:) = pack(keeps%mass(:), .not. lspill_list(:)) - keeps%Gmass(:) = pack(keeps%Gmass(:), .not. lspill_list(:)) - keeps%rhill(:) = pack(keeps%rhill(:), .not. lspill_list(:)) - if (allocated(keeps%radius)) keeps%radius(:) = pack(keeps%radius(:), .not. lspill_list(:)) - if (allocated(keeps%density)) keeps%density(:) = pack(keeps%density(:), .not. lspill_list(:)) - if (allocated(keeps%k2)) keeps%k2(:) = pack(keeps%k2(:), .not. lspill_list(:)) - if (allocated(keeps%Q)) keeps%Q(:) = pack(keeps%Q(:), .not. lspill_list(:)) - if (allocated(keeps%tlag)) keeps%tlag(:) = pack(keeps%tlag(:), .not. lspill_list(:)) - - if (allocated(keeps%xbeg)) then - do i = 1, NDIM - keeps%xbeg(i,:) = pack(keeps%xbeg(i,:), .not. lspill_list(:)) - end do - end if - - if (allocated(keeps%xend)) then - do i = 1, NDIM - keeps%xend(i,:) = pack(keeps%xend(i,:), .not. lspill_list(:)) - end do - end if - - if (allocated(keeps%vbeg)) then - do i = 1, NDIM - keeps%vbeg(i,:) = pack(keeps%vbeg(i,:), .not. lspill_list(:)) - end do - end if - - if (allocated(keeps%Ip)) then - do i = 1, NDIM - keeps%Ip(i,:) = pack(keeps%Ip(i,:), .not. lspill_list(:)) - end do - end if - - if (allocated(keeps%rot)) then - do i = 1, NDIM - keeps%rot(i,:) = pack(keeps%rot(i,:), .not. lspill_list(:)) - end do - end if - - end if - - call util_spill_body(keeps, discards, lspill_list) - class default - write(*,*) 'Error! spill method called for incompatible return type on swiftest_pl' - end select - end associate - - return - end subroutine util_spill_pl - - - module subroutine util_fill_pl(self, inserts, lfill_list) - !! author: David A. Minton - !! - !! Insert new Swiftest massive body structure into an old one. - !! This is the inverse of a fill operation. - implicit none - ! Arguments - class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object - class(swiftest_body), intent(inout) :: inserts !! Swiftest body object to be inserted - logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps - ! Internals - integer(I4B) :: i - - associate(keeps => self) - - select type (inserts) ! The standard requires us to select the type of both arguments in order to access all the components - class is (swiftest_pl) - !> Spill components specific to the massive body class - keeps%mass(:) = unpack(keeps%mass(:),.not.lfill_list(:), keeps%mass(:)) - keeps%mass(:) = unpack(inserts%mass(:),lfill_list(:), keeps%mass(:)) - - keeps%Gmass(:) = unpack(keeps%Gmass(:),.not.lfill_list(:), keeps%Gmass(:)) - keeps%Gmass(:) = unpack(inserts%Gmass(:),lfill_list(:), keeps%Gmass(:)) - - keeps%rhill(:) = unpack(keeps%rhill(:),.not.lfill_list(:), keeps%rhill(:)) - keeps%rhill(:) = unpack(inserts%rhill(:),lfill_list(:), keeps%rhill(:)) - - if (allocated(keeps%radius) .and. allocated(inserts%radius)) then - keeps%radius(:) = unpack(keeps%radius(:),.not.lfill_list(:), keeps%radius(:)) - keeps%radius(:) = unpack(inserts%radius(:),lfill_list(:), keeps%radius(:)) - end if - - if (allocated(keeps%density) .and. allocated(inserts%density)) then - keeps%density(:) = unpack(keeps%density(:),.not.lfill_list(:), keeps%density(:)) - keeps%density(:) = unpack(inserts%density(:),lfill_list(:), keeps%density(:)) - end if - - if (allocated(keeps%k2) .and. allocated(inserts%k2)) then - keeps%k2(:) = unpack(keeps%k2(:),.not.lfill_list(:), keeps%k2(:)) - keeps%k2(:) = unpack(inserts%k2(:),lfill_list(:), keeps%k2(:)) - end if - - if (allocated(keeps%Q) .and. allocated(inserts%Q)) then - keeps%Q(:) = unpack(keeps%Q(:),.not.lfill_list(:), keeps%Q(:)) - keeps%Q(:) = unpack(inserts%Q(:),lfill_list(:), keeps%Q(:)) - end if - - if (allocated(keeps%tlag) .and. allocated(inserts%tlag)) then - keeps%tlag(:) = unpack(keeps%tlag(:),.not.lfill_list(:), keeps%tlag(:)) - keeps%tlag(:) = unpack(inserts%tlag(:),lfill_list(:), keeps%tlag(:)) - end if - - if (allocated(keeps%xbeg) .and. allocated(inserts%xbeg)) then - do i = 1, NDIM - keeps%xbeg(i, :) = unpack(keeps%xbeg(i, :), .not.lfill_list(:), keeps%xbeg(i, :)) - keeps%xbeg(i, :) = unpack(inserts%xbeg(i, :), lfill_list(:), keeps%xbeg(i, :)) - end do - end if - - if (allocated(keeps%xend) .and. allocated(inserts%xend)) then - do i = 1, NDIM - keeps%xend(i, :) = unpack(keeps%xend(i, :), .not.lfill_list(:), keeps%xend(i, :)) - keeps%xend(i, :) = unpack(inserts%xend(i, :), lfill_list(:), keeps%xend(i, :)) - end do - end if - - if (allocated(keeps%vbeg) .and. allocated(inserts%vbeg)) then - do i = 1, NDIM - keeps%vbeg(i, :) = unpack(keeps%vbeg(i, :), .not.lfill_list(:), keeps%vbeg(i, :)) - keeps%vbeg(i, :) = unpack(inserts%vbeg(i, :), lfill_list(:), keeps%vbeg(i, :)) - end do - end if - - if (allocated(keeps%Ip) .and. allocated(inserts%Ip)) then - do i = 1, NDIM - keeps%Ip(i, :) = unpack(keeps%Ip(i, :), .not.lfill_list(:), keeps%Ip(i, :)) - keeps%Ip(i, :) = unpack(inserts%Ip(i, :), lfill_list(:), keeps%Ip(i, :)) - end do - end if - - if (allocated(keeps%rot) .and. allocated(inserts%rot)) then - do i = 1, NDIM - keeps%rot(i, :) = unpack(keeps%rot(i, :), .not.lfill_list(:), keeps%rot(i, :)) - keeps%rot(i, :) = unpack(inserts%rot(i, :), lfill_list(:), keeps%rot(i, :)) - end do - end if - - keeps%ldiscard(:) = unpack(inserts%ldiscard(:), lfill_list(:), keeps%ldiscard(:)) - - call util_fill_body(keeps, inserts, lfill_list) - class default - write(*,*) 'Error! fill method called for incompatible return type on swiftest_pl' - end select - end associate - - return - end subroutine util_fill_pl - - - module subroutine util_spill_tp(self, discards, lspill_list) - !! author: David A. Minton - !! - !! Move spilled (discarded) Swiftest test particle structure from active list to discard list - !! Adapted from David E. Kaufmann's Swifter routine whm_discard_spill.f90 - implicit none - ! Arguments - class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object - class(swiftest_body), intent(inout) :: discards !! Discarded object - logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discardse - - associate(keeps => self, ntp => self%nbody) - select type(discards) - class is (swiftest_tp) - !> Spill components specific to the test particle class - discards%isperi(:) = pack(keeps%isperi(:), lspill_list(:)) - discards%peri(:) = pack(keeps%peri(:), lspill_list(:)) - discards%atp(:) = pack(keeps%atp(:), lspill_list(:)) - if (count(.not.lspill_list(:)) > 0) then - keeps%atp(:) = pack(keeps%atp(:), .not. lspill_list(:)) - keeps%peri(:) = pack(keeps%peri(:), .not. lspill_list(:)) - keeps%isperi(:) = pack(keeps%isperi(:), .not. lspill_list(:)) - end if - call util_spill_body(keeps, discards, lspill_list) - class default - write(*,*) 'Error! spill method called for incompatible return type on swiftest_tp' - end select - end associate - - return - end subroutine util_spill_tp - - - module subroutine util_fill_tp(self, inserts, lfill_list) - !! author: David A. Minton - !! - !! Insert new Swiftest test particle structure into an old one. - !! This is the inverse of a fill operation. - implicit none - ! Arguments - class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object - class(swiftest_body), intent(inout) :: inserts !! Swiftest body object to be inserted - logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps - - associate(keeps => self) - select type(inserts) - class is (swiftest_tp) - !> Spill components specific to the test particle class - keeps%isperi(:) = unpack(keeps%isperi(:), .not.lfill_list(:), keeps%isperi(:)) - keeps%isperi(:) = unpack(inserts%isperi(:), lfill_list(:), keeps%isperi(:)) - - keeps%peri(:) = unpack(keeps%peri(:), .not.lfill_list(:), keeps%peri(:)) - keeps%peri(:) = unpack(inserts%peri(:), lfill_list(:), keeps%peri(:)) - - keeps%atp(:) = unpack(keeps%atp(:), .not.lfill_list(:), keeps%atp(:)) - keeps%atp(:) = unpack(inserts%atp(:), lfill_list(:), keeps%atp(:)) - - call util_fill_body(keeps, inserts, lfill_list) - class default - write(*,*) 'Error! fill method called for incompatible return type on swiftest_tp' - end select - end associate - - return - end subroutine util_fill_tp - -end submodule s_util_spill_and_fill - - - - - - diff --git a/src/whm/whm_setup.f90 b/src/whm/whm_setup.f90 index 0de03ec2c..cbf36cc90 100644 --- a/src/whm/whm_setup.f90 +++ b/src/whm/whm_setup.f90 @@ -18,6 +18,12 @@ module subroutine whm_setup_pl(self, n, param) call setup_pl(self, n, param) if (n <= 0) return + if (allocated(self%eta)) deallocate(self%eta) + if (allocated(self%muj)) deallocate(self%muj) + if (allocated(self%xj)) deallocate(self%xj) + if (allocated(self%vj)) deallocate(self%vj) + if (allocated(self%ir3j)) deallocate(self%ir3j) + allocate(self%eta(n)) allocate(self%muj(n)) allocate(self%xj(NDIM, n)) diff --git a/src/whm/whm_util.f90 b/src/whm/whm_util.f90 index 779480b3f..f3dc15d3e 100644 --- a/src/whm/whm_util.f90 +++ b/src/whm/whm_util.f90 @@ -2,48 +2,33 @@ use swiftest contains - module subroutine whm_util_spill_pl(self, discards, lspill_list) + module subroutine whm_util_append_pl(self, source, lsource_mask) !! author: David A. Minton !! - !! Move spilled (discarded) WHM test particle structure from active list to discard list - !! - !! Adapted from David E. Kaufmann's Swifter routine whm_discard_spill.f90 + !! Append components from one massive body object to another. + !! This method will automatically resize the destination body if it is too small implicit none - ! Arguments - class(whm_pl), intent(inout) :: self !! WHM massive body object - class(swiftest_body), intent(inout) :: discards !! Discarded object - logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards - ! Internals - integer(I4B) :: i - associate(keeps => self) - select type(discards) - class is (whm_pl) - discards%eta(:) = pack(keeps%eta(:), lspill_list(:)) - discards%muj(:) = pack(keeps%muj(:), lspill_list(:)) - discards%ir3j(:) = pack(keeps%ir3j(:), lspill_list(:)) - do i = 1, NDIM - discards%xj(i, :) = pack(keeps%xj(i, :), lspill_list(:)) - discards%vj(i, :) = pack(keeps%vj(i, :), lspill_list(:)) - end do - - if (count(.not.lspill_list(:)) > 0) then - keeps%eta(:) = pack(keeps%eta(:), .not. lspill_list(:)) - keeps%muj(:) = pack(keeps%muj(:), .not. lspill_list(:)) - keeps%ir3j(:) = pack(keeps%ir3j(:), .not. lspill_list(:)) - do i = 1, NDIM - keeps%xj(i, :) = pack(keeps%xj(i, :), .not. lspill_list(:)) - keeps%vj(i, :) = pack(keeps%vj(i, :), .not. lspill_list(:)) - end do - end if - call util_spill_pl(keeps, discards, lspill_list) - class default - write(*,*) 'Error! spill method called for incompatible return type on whm_pl' - end select - end associate + !! Arguments + class(whm_pl), intent(inout) :: self !! WHM massive body object + class(swiftest_body), intent(in) :: source !! Source object to append + logical, dimension(:), optional, intent(in) :: lsource_mask !! Logical mask indicating which elements to append to + + select type(source) + class is (whm_pl) + call util_append_pl(self, source, lsource_mask) + + call util_append(self%eta, source%eta, lsource_mask) + call util_append(self%muj, source%muj, lsource_mask) + call util_append(self%ir3j, source%ir3j, lsource_mask) + call util_append(self%xj, source%xj, lsource_mask) + call util_append(self%vj, source%vj, lsource_mask) + class default + write(*,*) "Invalid object passed to the append method. Source must be of class whm_pl or its descendents" + call util_exit(FAILURE) + end select return - end subroutine whm_util_spill_pl - + end subroutine whm_util_append_pl module subroutine whm_util_fill_pl(self, inserts, lfill_list) !! author: David A. Minton @@ -55,7 +40,7 @@ module subroutine whm_util_fill_pl(self, inserts, lfill_list) implicit none ! Arguments class(whm_pl), intent(inout) :: self !! WHM massive body object - class(swiftest_body), intent(inout) :: inserts !! inserted object + class(swiftest_body), intent(in) :: inserts !! inserted object logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps ! Internals integer(I4B) :: i @@ -63,26 +48,16 @@ module subroutine whm_util_fill_pl(self, inserts, lfill_list) associate(keeps => self) select type(inserts) class is (whm_pl) - keeps%eta(:) = unpack(keeps%eta(:), .not.lfill_list(:), keeps%eta(:)) - keeps%eta(:) = unpack(inserts%eta(:), lfill_list(:), keeps%eta(:)) - - keeps%muj(:) = unpack(keeps%muj(:), .not.lfill_list(:), keeps%muj(:)) - keeps%muj(:) = unpack(inserts%muj(:), lfill_list(:), keeps%muj(:)) - - keeps%ir3j(:) = unpack(keeps%ir3j(:), .not.lfill_list(:), keeps%ir3j(:)) - keeps%ir3j(:) = unpack(inserts%ir3j(:), lfill_list(:), keeps%ir3j(:)) - - - do i = 1, NDIM - keeps%xj(i, :) = unpack(keeps%xj(i, :), .not.lfill_list(:), keeps%xj(i, :)) - keeps%xj(i, :) = unpack(inserts%xj(i, :), lfill_list(:), keeps%xj(i, :)) - - keeps%vj(i, :) = unpack(keeps%vj(i, :), .not.lfill_list(:), keeps%vj(i, :)) - keeps%vj(i, :) = unpack(inserts%vj(i, :), lfill_list(:), keeps%vj(i, :)) - end do + call util_fill(keeps%eta, inserts%eta, lfill_list) + call util_fill(keeps%muj, inserts%muj, lfill_list) + call util_fill(keeps%ir3j, inserts%ir3j, lfill_list) + call util_fill(keeps%xj, inserts%xj, lfill_list) + call util_fill(keeps%vj, inserts%vj, lfill_list) + call util_fill_pl(keeps, inserts, lfill_list) class default - write(*,*) 'Error! fill method called for incompatible return type on whm_pl' + write(*,*) "Invalid object passed to the fill method. Inserts must be of class whm_pl or its descendents!" + call util_exit(FAILURE) end select end associate @@ -90,6 +65,27 @@ module subroutine whm_util_fill_pl(self, inserts, lfill_list) end subroutine whm_util_fill_pl + module subroutine whm_util_resize_pl(self, nnew) + !! author: David A. Minton + !! + !! Checks the current size of a massive body against the requested size and resizes it if it is too small. + implicit none + ! Arguments + class(whm_pl), intent(inout) :: self !! WHM massive body object + integer(I4B), intent(in) :: nnew !! New size neded + + call util_resize_pl(self, nnew) + + call util_resize(self%eta, nnew) + call util_resize(self%xj, nnew) + call util_resize(self%vj, nnew) + call util_resize(self%muj, nnew) + call util_resize(self%ir3j, nnew) + + return + end subroutine whm_util_resize_pl + + module subroutine whm_util_set_ir3j(self) !! author: David A. Minton !! @@ -176,15 +172,49 @@ module subroutine whm_util_sort_rearrange_pl(self, ind) associate(pl => self, npl => self%nbody) call util_sort_rearrange_pl(pl,ind) allocate(pl_sorted, source=self) - pl%eta(1:npl) = pl_sorted%eta(ind(1:npl)) - pl%xj(:,1:npl) = pl_sorted%xj(:,ind(1:npl)) - pl%vj(:,1:npl) = pl_sorted%vj(:,ind(1:npl)) - pl%muj(1:npl) = pl_sorted%muj(ind(1:npl)) - pl%ir3j(1:npl) = pl_sorted%ir3j(ind(1:npl)) + if (allocated(pl%eta)) pl%eta(1:npl) = pl_sorted%eta(ind(1:npl)) + if (allocated(pl%xj)) pl%xj(:,1:npl) = pl_sorted%xj(:,ind(1:npl)) + if (allocated(pl%vj)) pl%vj(:,1:npl) = pl_sorted%vj(:,ind(1:npl)) + if (allocated(pl%muj)) pl%muj(1:npl) = pl_sorted%muj(ind(1:npl)) + if (allocated(pl%ir3j)) pl%ir3j(1:npl) = pl_sorted%ir3j(ind(1:npl)) deallocate(pl_sorted) end associate return end subroutine whm_util_sort_rearrange_pl + + + module subroutine whm_util_spill_pl(self, discards, lspill_list, ldestructive) + !! author: David A. Minton + !! + !! Move spilled (discarded) WHM test particle structure from active list to discard list + !! + !! Adapted from David E. Kaufmann's Swifter routine whm_discard_spill.f90 + implicit none + ! Arguments + class(whm_pl), intent(inout) :: self !! WHM massive body object + class(swiftest_body), intent(inout) :: discards !! Discarded object + logical, dimension(:), intent(in) :: lspill_list !! Logical array of bodies to spill into the discards + logical, intent(in) :: ldestructive !! Logical flag indicating whether or not this operation should alter the keeps array or not + ! Internals + integer(I4B) :: i + associate(keeps => self) + select type(discards) + class is (whm_pl) + call util_spill(keeps%eta, discards%eta, lspill_list, ldestructive) + call util_spill(keeps%muj, discards%muj, lspill_list, ldestructive) + call util_spill(keeps%ir3j, discards%ir3j, lspill_list, ldestructive) + call util_spill(keeps%xj, discards%xj, lspill_list, ldestructive) + call util_spill(keeps%vj, discards%vj, lspill_list, ldestructive) + + call util_spill_pl(keeps, discards, lspill_list, ldestructive) + class default + write(*,*) "Invalid object passed to the spill method. Source must be of class whm_pl or its descendents!" + call util_exit(FAILURE) + end select + end associate + + return + end subroutine whm_util_spill_pl end submodule s_whm_util