diff --git a/Makefile b/Makefile index 0e2b361e6..94dfbceeb 100644 --- a/Makefile +++ b/Makefile @@ -45,13 +45,13 @@ #****************************************************************************** SWIFTEST_MODULES = swiftest_globals.f90 \ + lambda_function.f90\ swiftest_classes.f90 \ swiftest_operators.f90 \ whm_classes.f90 \ rmvs_classes.f90 \ helio_classes.f90 \ symba_classes.f90 \ - module_nrutil.f90 \ swiftest.f90 @@ -96,6 +96,11 @@ lib: ln -s $(SWIFTEST_HOME)/Makefile.Defines .; \ ln -s $(SWIFTEST_HOME)/Makefile .; \ make libdir + cd $(SWIFTEST_HOME)/src/fragmentation; \ + rm -f Makefile.Defines Makefile; \ + ln -s $(SWIFTEST_HOME)/Makefile.Defines .; \ + ln -s $(SWIFTEST_HOME)/Makefile .; \ + make libdir cd $(SWIFTEST_HOME)/src/gr; \ rm -f Makefile.Defines Makefile; \ ln -s $(SWIFTEST_HOME)/Makefile.Defines .; \ @@ -131,6 +136,11 @@ lib: ln -s $(SWIFTEST_HOME)/Makefile.Defines .; \ ln -s $(SWIFTEST_HOME)/Makefile .; \ make libdir + cd $(SWIFTEST_HOME)/src/tides; \ + rm -f Makefile.Defines Makefile; \ + ln -s $(SWIFTEST_HOME)/Makefile.Defines .; \ + ln -s $(SWIFTEST_HOME)/Makefile .; \ + make libdir cd $(SWIFTEST_HOME)/src/util; \ rm -f Makefile.Defines Makefile; \ ln -s $(SWIFTEST_HOME)/Makefile.Defines .; \ @@ -176,13 +186,6 @@ drivers: ln -s $(SWIFTEST_HOME)/Makefile .; \ make bin -#tools: -# cd $(SWIFTEST_HOME)/src/tool; \ -# rm -f Makefile.Defines Makefile; \ -# ln -s $(SWIFTEST_HOME)/Makefile.Defines .; \ -# ln -s $(SWIFTEST_HOME)/Makefile .; \ -# make bin - bin: *.f90 make $(basename $^) @@ -191,6 +194,7 @@ clean: cd $(SWIFTEST_HOME)/src/discard; rm -f Makefile.Defines Makefile *.gc* cd $(SWIFTEST_HOME)/src/drift; rm -f Makefile.Defines Makefile *.gc* cd $(SWIFTEST_HOME)/src/eucl; rm -f Makefile.Defines Makefile *.gc* + cd $(SWIFTEST_HOME)/src/fragmentation; rm -f Makefile.Defines Makefile *.gc* cd $(SWIFTEST_HOME)/src/gr; rm -f Makefile.Defines Makefile *.gc* cd $(SWIFTEST_HOME)/src/helio; rm -f Makefile.Defines Makefile *.gc* cd $(SWIFTEST_HOME)/src/io; rm -f Makefile.Defines Makefile *.gc* @@ -202,6 +206,7 @@ clean: cd $(SWIFTEST_HOME)/src/rmvs; rm -f Makefile.Defines Makefile *.gc* cd $(SWIFTEST_HOME)/src/setup; rm -f Makefile.Defines Makefile *.gc* cd $(SWIFTEST_HOME)/src/symba; rm -f Makefile.Defines Makefile *.gc* + cd $(SWIFTEST_HOME)/src/tides; rm -f Makefile.Defines Makefile *.gc* cd $(SWIFTEST_HOME)/src/user; rm -f Makefile.Defines Makefile *.gc* cd $(SWIFTEST_HOME)/src/util; rm -f Makefile.Defines Makefile *.gc* cd $(SWIFTEST_HOME)/src/whm; rm -f Makefile.Defines Makefile *.gc* diff --git a/Makefile.Defines b/Makefile.Defines index 07126f842..291f2c604 100644 --- a/Makefile.Defines +++ b/Makefile.Defines @@ -71,7 +71,7 @@ FORTRAN = ifort #AR = xiar #FORTRAN = gfortran -#FFLAGS = -ffree-line-length-none $(GDEBUG) #$(GMEM) +#FFLAGS = -ffree-line-length-none $(GDEBUG) $(GMEM) AR = ar # DO NOT include in CFLAGS the "-c" option to compile object only diff --git a/examples/helio_gr_test/cb.swiftest.in b/examples/helio_gr_test/cb.swiftest.in new file mode 100644 index 000000000..e4a010b1e --- /dev/null +++ b/examples/helio_gr_test/cb.swiftest.in @@ -0,0 +1,5 @@ +0 +39.476926408897626 +0.004650467260962157 +4.7535806948127355e-12 +-2.2473967953572827e-18 diff --git a/examples/helio_gr_test/init_cond.py b/examples/helio_gr_test/init_cond.py new file mode 100755 index 000000000..8d197c6f4 --- /dev/null +++ b/examples/helio_gr_test/init_cond.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +import swiftest + +sim = swiftest.Simulation() +sim.param['PL_IN'] = "pl.swiftest.in" +sim.param['TP_IN'] = "tp.swiftest.in" +sim.param['CB_IN'] = "cb.swiftest.in" +sim.param['BIN_OUT'] = "bin.swiftest.dat" +sim.param['ENC_OUT'] = "enc.swiftest.dat" + +sim.param['MU2KG'] = swiftest.MSun +sim.param['TU2S'] = swiftest.YR2S +sim.param['DU2M'] = swiftest.AU2M +sim.param['T0'] = 0.0 +sim.param['DT'] = 0.25 * swiftest.JD2S / swiftest.YR2S +sim.param['TSTOP'] = 1000.0 +sim.param['ISTEP_OUT'] = 1461 +sim.param['ISTEP_DUMP'] = 1461 +sim.param['CHK_QMIN_COORD'] = "HELIO" +sim.param['CHK_QMIN'] = swiftest.RSun / swiftest.AU2M +sim.param['CHK_QMIN_RANGE'] = f"{swiftest.RSun / swiftest.AU2M} 1000.0" +sim.param['CHK_RMIN'] = swiftest.RSun / swiftest.AU2M +sim.param['CHK_RMAX'] = 1000.0 +sim.param['CHK_EJECT'] = 1000.0 +sim.param['OUT_FORM'] = "EL" +sim.param['OUT_STAT'] = "UNKNOWN" +sim.param['GR'] = 'YES' + +bodyid = { + "Sun": 0, + "Mercury": 1, + "Venus": 2, + "Earth": 3, + "Mars": 4, + "Jupiter": 5, + "Saturn": 6, + "Uranus": 7, + "Neptune": 8, +} + +for name, id in bodyid.items(): + sim.add(name, idval=id) + +sim.save("param.swiftest.in") +sim.param['PL_IN'] = "pl.swifter.in" +sim.param['TP_IN'] = "tp.swifter.in" +sim.param['BIN_OUT'] = "bin.swifter.dat" +sim.param['ENC_OUT'] = "enc.swifter.dat" +sim.save("param.swifter.in", codename="Swifter") + + diff --git a/examples/helio_gr_test/param.swifter.in b/examples/helio_gr_test/param.swifter.in new file mode 100644 index 000000000..789250f41 --- /dev/null +++ b/examples/helio_gr_test/param.swifter.in @@ -0,0 +1,27 @@ +! VERSION Swifter parameter file converted from Swiftest +T0 0.0 +TSTOP 1000.0 +DT 0.0006844626967830253 +ISTEP_OUT 1461 +ISTEP_DUMP 1461 +OUT_FORM EL +OUT_TYPE REAL8 +OUT_STAT UNKNOWN +IN_TYPE ASCII +PL_IN pl.swifter.in +TP_IN tp.swifter.in +BIN_OUT bin.swifter.dat +ENC_OUT enc.swifter.dat +CHK_QMIN 0.004650467260962157 +CHK_RMIN 0.004650467260962157 +CHK_RMAX 1000.0 +CHK_EJECT 1000.0 +CHK_QMIN_COORD HELIO +CHK_QMIN_RANGE 0.004650467260962157 1000.0 +EXTRA_FORCE NO +BIG_DISCARD NO +CHK_CLOSE YES +C 63241.07708426628 +J2 4.7535806948127355e-12 +J4 -2.2473967953572827e-18 +RHILL_PRESENT YES diff --git a/examples/helio_gr_test/param.swiftest.in b/examples/helio_gr_test/param.swiftest.in new file mode 100644 index 000000000..ace6f3cad --- /dev/null +++ b/examples/helio_gr_test/param.swiftest.in @@ -0,0 +1,35 @@ +! VERSION Swiftest parameter input +T0 0.0 +TSTOP 1000.0 +DT 0.0006844626967830253 +ISTEP_OUT 1461 +ISTEP_DUMP 1461 +OUT_FORM EL +OUT_TYPE REAL8 +OUT_STAT UNKNOWN +IN_TYPE ASCII +PL_IN pl.swiftest.in +TP_IN tp.swiftest.in +CB_IN cb.swiftest.in +BIN_OUT bin.swiftest.dat +ENC_OUT enc.swiftest.dat +CHK_QMIN 0.004650467260962157 +CHK_RMIN 0.004650467260962157 +CHK_RMAX 1000.0 +CHK_EJECT 1000.0 +CHK_QMIN_COORD HELIO +CHK_QMIN_RANGE 0.004650467260962157 1000.0 +MU2KG 1.988409870698051e+30 +TU2S 31557600.0 +DU2M 149597870700.0 +EXTRA_FORCE NO +BIG_DISCARD NO +CHK_CLOSE YES +FRAGMENTATION NO +ROTATION NO +TIDES NO +ENERGY NO +GR YES +YARKOVSKY NO +YORP NO +MTINY 0.0 diff --git a/examples/helio_gr_test/pl.swifter.in b/examples/helio_gr_test/pl.swifter.in new file mode 100644 index 000000000..782e57140 --- /dev/null +++ b/examples/helio_gr_test/pl.swifter.in @@ -0,0 +1,36 @@ +9 +0 39.476926408897625196 +0.0 0.0 0.0 +0.0 0.0 0.0 +1 6.5537098095653139645e-06 0.0014751234419554511911 +1.6306381826061645943e-05 +0.13267502226188271353 0.2786606257975073886 0.010601098875389479426 +-11.331978934667442676 4.8184460126705647045 1.4332264599878684131 +2 9.663313399581537916e-05 0.00675908960945781479 +4.0453784346544178454e-05 +-0.69398700025820403425 -0.19235393648106968723 0.03740673057980103272 +1.9245789988923785786 -7.1528261190002948057 -0.20922405362759749996 +3 0.000120026935827952453094 0.010044837538502923644 +4.25875607065040958e-05 +0.49463573470256239073 -0.8874896493821613497 4.051630875713834232e-05 +5.386704768180099809 3.0357508899436080915 -0.00016218409216515533796 +4 1.2739802010675941456e-05 0.0072467236860282326973 +2.265740805092889601e-05 +-1.5655322071100350456 0.56626121192188216824 0.050269397991054412533 +-1.5477080637857006753 -4.370087697214287981 -0.05361768768801557225 +5 0.037692251088985676735 0.35527094075555771578 +0.00046732617030490929307 +4.0891378954287338487 -2.9329188614380639066 -0.07930573161132697946 +1.575024788882753283 2.3719591091996699917 -0.045089307261129988257 +6 0.011285899820091272997 0.43765464106459166412 +0.00038925687730393611812 +6.3349788609660162564 -7.674600716671800882 -0.11868650931385750502 +1.4598618704191345578 1.2948691245181617393 -0.080593167691228835176 +7 0.0017236589478267730203 0.46956055286931676728 +0.00016953449859497231466 +14.832516206189200858 13.032608531076540714 -0.14378102535616668622 +-0.9573374666934839659 1.014553546383260322 0.016118112341773867214 +8 0.0020336100526728302319 0.7813163071687303693 +0.000164587904124493665 +29.561664938083289655 -4.6012285192418387325 -0.586585578731106283 +0.17051705220469790965 1.1424784769020628332 -0.027423757798549895085 diff --git a/examples/helio_gr_test/pl.swiftest.in b/examples/helio_gr_test/pl.swiftest.in new file mode 100644 index 000000000..10d425453 --- /dev/null +++ b/examples/helio_gr_test/pl.swiftest.in @@ -0,0 +1,33 @@ +8 +1 6.5537098095653139645e-06 +1.6306381826061645943e-05 +0.13267502226188271353 0.2786606257975073886 0.010601098875389479426 +-11.331978934667442676 4.8184460126705647045 1.4332264599878684131 +2 9.663313399581537916e-05 +4.0453784346544178454e-05 +-0.69398700025820403425 -0.19235393648106968723 0.03740673057980103272 +1.9245789988923785786 -7.1528261190002948057 -0.20922405362759749996 +3 0.000120026935827952453094 +4.25875607065040958e-05 +0.49463573470256239073 -0.8874896493821613497 4.051630875713834232e-05 +5.386704768180099809 3.0357508899436080915 -0.00016218409216515533796 +4 1.2739802010675941456e-05 +2.265740805092889601e-05 +-1.5655322071100350456 0.56626121192188216824 0.050269397991054412533 +-1.5477080637857006753 -4.370087697214287981 -0.05361768768801557225 +5 0.037692251088985676735 +0.00046732617030490929307 +4.0891378954287338487 -2.9329188614380639066 -0.07930573161132697946 +1.575024788882753283 2.3719591091996699917 -0.045089307261129988257 +6 0.011285899820091272997 +0.00038925687730393611812 +6.3349788609660162564 -7.674600716671800882 -0.11868650931385750502 +1.4598618704191345578 1.2948691245181617393 -0.080593167691228835176 +7 0.0017236589478267730203 +0.00016953449859497231466 +14.832516206189200858 13.032608531076540714 -0.14378102535616668622 +-0.9573374666934839659 1.014553546383260322 0.016118112341773867214 +8 0.0020336100526728302319 +0.000164587904124493665 +29.561664938083289655 -4.6012285192418387325 -0.586585578731106283 +0.17051705220469790965 1.1424784769020628332 -0.027423757798549895085 diff --git a/examples/helio_gr_test/swiftest_relativity.ipynb b/examples/helio_gr_test/swiftest_relativity.ipynb new file mode 100644 index 000000000..03948cdf7 --- /dev/null +++ b/examples/helio_gr_test/swiftest_relativity.ipynb @@ -0,0 +1,192 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import swiftest\n", + "from astroquery.jplhorizons import Horizons" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading Swifter file param.swifter.in\n", + "Reading in time 1.000e+03\n", + "Creating Dataset\n", + "Successfully converted 1001 output frames.\n", + "Swifter simulation data stored as xarray DataSet .ds\n" + ] + } + ], + "source": [ + "swiftersim = swiftest.Simulation(param_file=\"param.swifter.in\", codename=\"Swifter\")\n", + "swiftersim.bin2xr()\n", + "swifterdat = swiftersim.ds" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading Swiftest file param.swiftest.in\n", + "Reading in time 1.000e+03\n", + "Creating Dataset\n", + "Successfully converted 1001 output frames.\n", + "Swiftest simulation data stored as xarray DataSet .ds\n" + ] + } + ], + "source": [ + "swiftestsim = swiftest.Simulation(param_file=\"param.swiftest.in\")\n", + "swiftestsim.bin2xr()\n", + "swiftestdat = swiftestsim.ds" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "swifterdat['varpi'] = swifterdat['omega'] + swifterdat['capom']\n", + "swiftestdat['varpi'] = swiftestdat['omega'] + swiftestdat['capom']" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "obj = Horizons(id='1', id_type='majorbody',location='@sun',\n", + " epochs={'start':'2021-01-28', 'stop':'3021-02-05',\n", + " 'step':'1y'})\n", + "el = obj.elements()\n", + "t = (el['datetime_jd']-el['datetime_jd'][0]) / 365.25\n", + "varpi_obs = el['w'] + el['Omega']" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "varpiswiftest = swiftestdat['varpi'].sel(id=1) * 180.0 / np.pi\n", + "varpiswifter = swifterdat['varpi'].sel(id=1) * 180.0 / np.pi\n", + "tsim = swiftestdat['time']" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "dvarpi_swiftest = np.diff(varpiswiftest) * 3600 * 100 \n", + "dvarpi_swifter = np.diff(varpiswifter) * 3600 * 100 \n", + "dvarpi_obs = np.diff(varpi_obs) / np.diff(t) * 3600 * 100 " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mean precession rate for Mercury long. peri. (arcsec/100 y)\n", + "JPL Horizons : 571.3210506300043\n", + "Swifter GR : 571.6183105524942\n", + "Swiftest GR : 571.6157754511288\n", + "Obs - Swifter : -0.2972599224899675\n", + "Obs - Swiftest : -0.2947248211246545\n", + "Swiftest - Swifter: -0.0025351013653107657\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "\n", + "ax.plot(t, varpi_obs, label=\"JPL Horizons\")\n", + "ax.plot(tsim, varpiswifter, label=\"Swifter GR\")\n", + "ax.plot(tsim, varpiswiftest, label=\"Swiftest GR\")\n", + "ax.set_xlabel('Time (y)')\n", + "ax.set_ylabel('Mercury $\\\\varpi$ (deg)')\n", + "ax.legend()\n", + "print('Mean precession rate for Mercury long. peri. (arcsec/100 y)')\n", + "print(f'JPL Horizons : {np.mean(dvarpi_obs)}')\n", + "print(f'Swifter GR : {np.mean(dvarpi_swifter)}')\n", + "print(f'Swiftest GR : {np.mean(dvarpi_swiftest)}')\n", + "print(f'Obs - Swifter : {np.mean(dvarpi_obs - dvarpi_swifter)}')\n", + "print(f'Obs - Swiftest : {np.mean(dvarpi_obs - dvarpi_swiftest)}')\n", + "print(f'Swiftest - Swifter: {np.mean(dvarpi_swiftest - dvarpi_swifter)}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "swiftestOOF", + "language": "python", + "name": "swiftestoof" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/helio_gr_test/tp.swifter.in b/examples/helio_gr_test/tp.swifter.in new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/examples/helio_gr_test/tp.swifter.in @@ -0,0 +1 @@ +0 diff --git a/examples/helio_gr_test/tp.swiftest.in b/examples/helio_gr_test/tp.swiftest.in new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/examples/helio_gr_test/tp.swiftest.in @@ -0,0 +1 @@ +0 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 52568fd84..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/rmvs_swifter_comparison/9pl_18tp_encounters/.idea/.gitignore b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/.idea/.gitignore similarity index 100% rename from examples/rmvs_swifter_comparison/9pl_18tp_encounters/.idea/.gitignore rename to examples/rmvs_swifter_comparison/8pl_16tp_encounters/.idea/.gitignore diff --git a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/cb.in b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/cb.in similarity index 100% rename from examples/rmvs_swifter_comparison/9pl_18tp_encounters/cb.in rename to examples/rmvs_swifter_comparison/8pl_16tp_encounters/cb.in diff --git a/examples/rmvs_swifter_comparison/8pl_16tp_encounters/cb.swiftest.in b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/cb.swiftest.in new file mode 100644 index 000000000..2e8d49f62 --- /dev/null +++ b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/cb.swiftest.in @@ -0,0 +1,5 @@ +0 +0.00029591220819207774 +0.004650467260962157 +0.0 +0.0 diff --git a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/init_cond.py b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/init_cond.py similarity index 98% rename from examples/rmvs_swifter_comparison/9pl_18tp_encounters/init_cond.py rename to examples/rmvs_swifter_comparison/8pl_16tp_encounters/init_cond.py index 321c79932..094b261f0 100755 --- a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/init_cond.py +++ b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/init_cond.py @@ -25,8 +25,8 @@ sim.param['T0'] = 0.0 sim.param['DT'] = 1.0 sim.param['TSTOP'] = 365.25e1 -sim.param['ISTEP_OUT'] = 11 -sim.param['ISTEP_DUMP'] = 1 +sim.param['ISTEP_OUT'] = 10 +sim.param['ISTEP_DUMP'] = 10 sim.param['CHK_QMIN_COORD'] = "HELIO" sim.param['CHK_QMIN'] = swiftest.RSun / swiftest.AU2M sim.param['CHK_QMIN_RANGE'] = f"{swiftest.RSun / swiftest.AU2M} 1000.0" diff --git a/examples/rmvs_swifter_comparison/8pl_16tp_encounters/param.swifter.in b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/param.swifter.in new file mode 100644 index 000000000..6a283276e --- /dev/null +++ b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/param.swifter.in @@ -0,0 +1,26 @@ +! VERSION Swifter parameter file converted from Swiftest +T0 0.0 +TSTOP 3652.5 +DT 1.0 +ISTEP_OUT 10 +ISTEP_DUMP 10 +OUT_FORM XV +OUT_TYPE REAL8 +OUT_STAT UNKNOWN +IN_TYPE ASCII +PL_IN pl.swifter.in +TP_IN tp.in +BIN_OUT bin.swifter.dat +ENC_OUT enc.swifter.dat +CHK_QMIN 0.004650467260962157 +CHK_RMIN 0.004650467260962157 +CHK_RMAX 1000.0 +CHK_EJECT 1000.0 +CHK_QMIN_COORD HELIO +CHK_QMIN_RANGE 0.004650467260962157 1000.0 +EXTRA_FORCE NO +BIG_DISCARD NO +CHK_CLOSE YES +RHILL_PRESENT YES +J2 0.0 +J4 0.0 diff --git a/examples/rmvs_swifter_comparison/8pl_16tp_encounters/param.swiftest.in b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/param.swiftest.in new file mode 100644 index 000000000..b08b66850 --- /dev/null +++ b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/param.swiftest.in @@ -0,0 +1,36 @@ +! VERSION Swiftest parameter input +T0 0.0 +TSTOP 3652.5 +DT 1.0 +ISTEP_OUT 10 +ISTEP_DUMP 10 +OUT_FORM XV +OUT_TYPE REAL8 +OUT_STAT UNKNOWN +IN_TYPE ASCII +PL_IN pl.swiftest.in +TP_IN tp.in +CB_IN cb.swiftest.in +BIN_OUT bin.swiftest.dat +ENC_OUT enc.swiftest.dat +CHK_QMIN 0.004650467260962157 +CHK_RMIN 0.004650467260962157 +CHK_RMAX 1000.0 +CHK_EJECT 1000.0 +CHK_QMIN_COORD HELIO +CHK_QMIN_RANGE 0.004650467260962157 1000.0 +MU2KG 1.988409870698051e+30 +TU2S 86400 +DU2M 149597870700.0 +EXTRA_FORCE NO +BIG_DISCARD NO +CHK_CLOSE YES +RHILL_PRESENT YES +FRAGMENTATION NO +ROTATION NO +TIDES NO +ENERGY NO +GR NO +YARKOVSKY NO +YORP NO +MTINY 0.0 diff --git a/examples/rmvs_swifter_comparison/8pl_16tp_encounters/pl.in b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/pl.in new file mode 100644 index 000000000..86a616119 --- /dev/null +++ b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/pl.in @@ -0,0 +1,33 @@ +8 +1 4.9125474498983623693e-11 0.0014751239400086721089 +1.6306381826061645943e-05 +-0.09861361766419070307 0.29750596935836171042 0.03335708456145129036 +-0.032353632540864457612 -0.0078122718370876240157 0.0023293874953380202045 +2 7.243452483873646905e-10 0.0067590794275223005208 +4.0453784346544178454e-05 +-0.6439817957564198947 -0.3248550380869373866 0.032702713983447248558 +0.008969709495375973937 -0.018153139924556138673 -0.0007667345025597138231 +3 8.9970113821660187435e-10 0.010044873080337524463 +4.25875607065040958e-05 +0.59421674333603324847 -0.82331253628773626296 3.7129329104855261984e-05 +0.013670550280388280365 0.010004295439859960809 -5.226292361234363611e-07 +4 9.549535102761465607e-11 0.0072467054748629370034 +2.265740805092889601e-05 +-1.592721551706784977 0.48166390206865000723 0.049163460846716633412 +-0.0035287723306552309585 -0.01219974682608557931 -0.00016910795626524249315 +5 2.825345908631354893e-07 0.35527074967975702942 +0.00046732617030490929307 +4.119089774477131094 -2.8872942462256898644 -0.080165336328135106125 +0.004245402942744468111 0.0065414198811065849687 -0.00012215100047356211078 +6 8.459715183006415395e-08 0.4376562090257202473 +0.00038925687730393611812 +6.3629100567525149756 -7.649727796147929304 -0.12023019299387090186 +0.0039834472120812329868 0.0035613826786502411278 -0.00022039988214595340028 +7 1.2920249163736673626e-08 0.4695793205674148502 +0.00016953449859497231466 +14.814154683311180349 13.052040295401360126 -0.14347198499748289868 +-0.002625101393275708784 0.0027742356008832688187 4.416821810149910185e-05 +8 1.5243589003230834323e-08 0.7813388398513013378 +0.000164587904124493665 +29.564924658285640646 -4.579331535234244299 -0.5871109926822926095 +0.00046449847307956888343 0.003128345390031967918 -7.5036135696161668576e-05 diff --git a/examples/rmvs_swifter_comparison/8pl_16tp_encounters/pl.swifter.in b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/pl.swifter.in new file mode 100644 index 000000000..595cdc169 --- /dev/null +++ b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/pl.swifter.in @@ -0,0 +1,36 @@ +9 +0 0.00029591220819207775568 +0.0 0.0 0.0 +0.0 0.0 0.0 +1 4.9125474498983623693e-11 0.0014751239400086721089 +1.6306381826061645943e-05 +-0.09861361766419070307 0.29750596935836171042 0.03335708456145129036 +-0.032353632540864457612 -0.0078122718370876240157 0.0023293874953380202045 +2 7.243452483873646905e-10 0.0067590794275223005208 +4.0453784346544178454e-05 +-0.6439817957564198947 -0.3248550380869373866 0.032702713983447248558 +0.008969709495375973937 -0.018153139924556138673 -0.0007667345025597138231 +3 8.9970113821660187435e-10 0.010044873080337524463 +4.25875607065040958e-05 +0.59421674333603324847 -0.82331253628773626296 3.7129329104855261984e-05 +0.013670550280388280365 0.010004295439859960809 -5.226292361234363611e-07 +4 9.549535102761465607e-11 0.0072467054748629370034 +2.265740805092889601e-05 +-1.592721551706784977 0.48166390206865000723 0.049163460846716633412 +-0.0035287723306552309585 -0.01219974682608557931 -0.00016910795626524249315 +5 2.825345908631354893e-07 0.35527074967975702942 +0.00046732617030490929307 +4.119089774477131094 -2.8872942462256898644 -0.080165336328135106125 +0.004245402942744468111 0.0065414198811065849687 -0.00012215100047356211078 +6 8.459715183006415395e-08 0.4376562090257202473 +0.00038925687730393611812 +6.3629100567525149756 -7.649727796147929304 -0.12023019299387090186 +0.0039834472120812329868 0.0035613826786502411278 -0.00022039988214595340028 +7 1.2920249163736673626e-08 0.4695793205674148502 +0.00016953449859497231466 +14.814154683311180349 13.052040295401360126 -0.14347198499748289868 +-0.002625101393275708784 0.0027742356008832688187 4.416821810149910185e-05 +8 1.5243589003230834323e-08 0.7813388398513013378 +0.000164587904124493665 +29.564924658285640646 -4.579331535234244299 -0.5871109926822926095 +0.00046449847307956888343 0.003128345390031967918 -7.5036135696161668576e-05 diff --git a/examples/rmvs_swifter_comparison/8pl_16tp_encounters/pl.swiftest.in b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/pl.swiftest.in new file mode 100644 index 000000000..86a616119 --- /dev/null +++ b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/pl.swiftest.in @@ -0,0 +1,33 @@ +8 +1 4.9125474498983623693e-11 0.0014751239400086721089 +1.6306381826061645943e-05 +-0.09861361766419070307 0.29750596935836171042 0.03335708456145129036 +-0.032353632540864457612 -0.0078122718370876240157 0.0023293874953380202045 +2 7.243452483873646905e-10 0.0067590794275223005208 +4.0453784346544178454e-05 +-0.6439817957564198947 -0.3248550380869373866 0.032702713983447248558 +0.008969709495375973937 -0.018153139924556138673 -0.0007667345025597138231 +3 8.9970113821660187435e-10 0.010044873080337524463 +4.25875607065040958e-05 +0.59421674333603324847 -0.82331253628773626296 3.7129329104855261984e-05 +0.013670550280388280365 0.010004295439859960809 -5.226292361234363611e-07 +4 9.549535102761465607e-11 0.0072467054748629370034 +2.265740805092889601e-05 +-1.592721551706784977 0.48166390206865000723 0.049163460846716633412 +-0.0035287723306552309585 -0.01219974682608557931 -0.00016910795626524249315 +5 2.825345908631354893e-07 0.35527074967975702942 +0.00046732617030490929307 +4.119089774477131094 -2.8872942462256898644 -0.080165336328135106125 +0.004245402942744468111 0.0065414198811065849687 -0.00012215100047356211078 +6 8.459715183006415395e-08 0.4376562090257202473 +0.00038925687730393611812 +6.3629100567525149756 -7.649727796147929304 -0.12023019299387090186 +0.0039834472120812329868 0.0035613826786502411278 -0.00022039988214595340028 +7 1.2920249163736673626e-08 0.4695793205674148502 +0.00016953449859497231466 +14.814154683311180349 13.052040295401360126 -0.14347198499748289868 +-0.002625101393275708784 0.0027742356008832688187 4.416821810149910185e-05 +8 1.5243589003230834323e-08 0.7813388398513013378 +0.000164587904124493665 +29.564924658285640646 -4.579331535234244299 -0.5871109926822926095 +0.00046449847307956888343 0.003128345390031967918 -7.5036135696161668576e-05 diff --git a/examples/rmvs_swifter_comparison/8pl_16tp_encounters/swiftest_rmvs_vs_swifter_rmvs.ipynb b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/swiftest_rmvs_vs_swifter_rmvs.ipynb new file mode 100644 index 000000000..124ae2910 --- /dev/null +++ b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/swiftest_rmvs_vs_swifter_rmvs.ipynb @@ -0,0 +1,642 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import swiftest\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading Swifter file param.swifter.in\n", + "Reading in time 3.650e+03\n", + "Creating Dataset\n", + "Successfully converted 366 output frames.\n", + "Swifter simulation data stored as xarray DataSet .ds\n" + ] + } + ], + "source": [ + "inparfile = 'param.swifter.in'\n", + "swiftersim = swiftest.Simulation(param_file=inparfile, codename=\"Swifter\")\n", + "swiftersim.bin2xr()\n", + "swifterdat = swiftersim.ds" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading Swiftest file param.swiftest.in\n", + "Reading in time 3.650e+03\n", + "Creating Dataset\n", + "Successfully converted 366 output frames.\n", + "Swiftest simulation data stored as xarray DataSet .ds\n" + ] + } + ], + "source": [ + "inparfile = 'param.swiftest.in'\n", + "swiftestsim = swiftest.Simulation(param_file=inparfile)\n", + "swiftestsim.bin2xr()\n", + "swiftestdat = swiftestsim.ds" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff = swiftestdat - swifterdat" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff = swiftdiff.rename({'time' : 'time (d)'})" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff['rmag'] = np.sqrt(swiftdiff['px']**2 + swiftdiff['py']**2 + swiftdiff['pz']**2)\n", + "swiftdiff['vmag'] = np.sqrt(swiftdiff['vx']**2 + swiftdiff['vy']**2 + swiftdiff['vz']**2)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "plidx = swiftdiff.id.values[swiftdiff.id.values < 10]\n", + "tpidx = swiftdiff.id.values[swiftdiff.id.values > 10]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "swiftdiff['rmag'].sel(id=plidx).plot.line(ax=ax, x=\"time (d)\")\n", + "ax.set_ylabel(\"$|\\mathbf{r}_{swiftest} - \\mathbf{r}_{swifter}|$\")\n", + "ax.set_title(\"Heliocentric position differences \\n Planets only\")\n", + "fig.savefig(\"rmvs_swifter_comparison-8pl_16tp-planets-rmag.png\", facecolor='white', transparent=False, dpi=300)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "swiftdiff['vmag'].sel(id=plidx).plot.line(ax=ax, x=\"time (d)\")\n", + "ax.set_ylabel(\"$|\\mathbf{v}_{swiftest} - \\mathbf{v}_{swifter}|$\")\n", + "ax.set_title(\"Heliocentric velocity differences \\n Planets only\")\n", + "fig.savefig(\"rmvs_swifter_comparison-8pl_16tp-planets-vmag.png\", facecolor='white', transparent=False, dpi=300)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "No handles with labels found to put in legend.\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "swiftdiff['rmag'].sel(id=tpidx).plot.line(ax=ax, x=\"time (d)\")\n", + "ax.set_ylabel(\"$|\\mathbf{r}_{swiftest} - \\mathbf{r}_{swifter}|$\")\n", + "ax.set_title(\"Heliocentric position differences \\n Test Particles only\")\n", + "legend = ax.legend()\n", + "legend.remove()\n", + "fig.savefig(\"rmvs_swifter_comparison-8pl_16tp-testparticles-rmag.png\", facecolor='white', transparent=False, dpi=300)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "No handles with labels found to put in legend.\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "swiftdiff['vmag'].sel(id=tpidx).plot.line(ax=ax, x=\"time (d)\")\n", + "ax.set_ylabel(\"$|\\mathbf{v}_{swiftest} - \\mathbf{v}_{swifter}|$\")\n", + "ax.set_title(\"Heliocentric velocity differences \\n Test Particles only\")\n", + "legend = ax.legend()\n", + "legend.remove()\n", + "fig.savefig(\"rmvs_swifter_comparison-8pl_16tp-testparticles-vmag.png\", facecolor='white', transparent=False, dpi=300)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff = swiftdiff.rename({'time (d)' :'time'})" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'px' ()>\n",
+       "array(0.)\n",
+       "Coordinates:\n",
+       "    id       int64 105\n",
+       "    time     float64 1.09e+03
" + ], + "text/plain": [ + "\n", + "array(0.)\n", + "Coordinates:\n", + " id int64 105\n", + " time float64 1.09e+03" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "swiftdiff['px'].sel(id=105).isel(time=109)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "swiftestOOF", + "language": "python", + "name": "swiftestoof" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/rmvs_swifter_comparison/8pl_16tp_encounters/tp.in b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/tp.in new file mode 100644 index 000000000..c8cc418b0 --- /dev/null +++ b/examples/rmvs_swifter_comparison/8pl_16tp_encounters/tp.in @@ -0,0 +1,49 @@ +16 +105 +0.59427697124197276235 -0.8232523083817967491 3.7129329104855261984e-05 +0.020564990514662154913 0.010004295439859960809 -5.226292361234363611e-07 +109 +4.119750673485228276 -2.8866333472175926822 -0.080165336328135106125 +0.041127620144391897894 0.0065414198811065849687 -0.00012215100047356211078 +101 +-0.09859055695785905182 0.2975290300646933339 0.03335708456145129036 +-0.029750083068855306956 -0.0078122718370876240157 0.0023293874953380202045 +102 +-0.09863667837052235432 0.29748290865203008693 0.03335708456145129036 +-0.034957182012873608268 -0.0078122718370876240157 0.0023293874953380202045 +103 +-0.6439245854659476631 -0.32479782779646521051 0.032702713983447248558 +0.0153169432007213678765 -0.018153139924556138673 -0.0007667345025597138231 +104 +-0.6440390060468921263 -0.32491224837740956266 0.032702713983447248558 +0.002622475790030579998 -0.018153139924556138673 -0.0007667345025597138231 +106 +0.5941565154300937346 -0.82337276419367577684 3.7129329104855261984e-05 +0.0067761100461144049487 0.010004295439859960809 -5.226292361234363611e-07 +107 +-1.5926895092930311026 0.48169594448240382611 0.049163460846716633412 +-0.00044929323243133797994 -0.01219974682608557931 -0.00016910795626524249315 +108 +-1.5927535941205388514 0.48163185965489618834 0.049163460846716633412 +-0.006608251428879123937 -0.01219974682608557931 -0.00016910795626524249315 +110 +4.118428875469033912 -2.8879551452337870465 -0.080165336328135106125 +-0.032636814258902961672 0.0065414198811065849687 -0.00012215100047356211078 +111 +6.3634605491076454697 -7.64917730379279881 -0.12023019299387090186 +0.026096616095614821179 0.0035613826786502411278 -0.00022039988214595340028 +112 +6.3623595643973844815 -7.650278288503059798 -0.12023019299387090186 +-0.01812972167145235694 0.0035613826786502411278 -0.00022039988214595340028 +113 +14.814394441298382787 13.052280053388562564 -0.14347198499748289868 +0.010469662145386185101 0.0027742356008832688187 4.416821810149910185e-05 +114 +14.813914925323977911 13.051800537414157688 -0.14347198499748289868 +-0.015719864931937603536 0.0027742356008832688187 4.416821810149910185e-05 +115 +29.565157420731857485 -4.579098772788029237 -0.5871109926822926095 +0.014900134286357700347 0.003128345390031967918 -7.5036135696161668576e-05 +116 +29.564691895839423808 -4.5795642976804593616 -0.5871109926822926095 +-0.0139711373401985618214 0.003128345390031967918 -7.5036135696161668576e-05 diff --git a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/pl.in b/examples/rmvs_swifter_comparison/9pl_18tp_encounters/pl.in deleted file mode 100644 index bd980fc4b..000000000 --- a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/pl.in +++ /dev/null @@ -1,33 +0,0 @@ -8 -1 4.9125474498983623693e-11 -1.6306381826061645943e-05 -0.33206272695596028566 0.07436707001147663254 -0.02438290851908785084 --0.0115920916602103591525 0.028710618792657981169 0.0034094833969203438596 -2 7.243452483873646905e-10 -4.0453784346544178454e-05 --0.7188115337296047125 -0.0118554711069603201795 0.041316403191083782287 -0.00021427347881133320621 -0.020313576971905909774 -0.00029114855617710840843 -3 8.9970113821660187435e-10 -4.25875607065040958e-05 -0.35677088372527121507 -0.95189300879814897627 4.4027442504036787155e-05 -0.015830039028334789986 0.0059737936889703449964 -3.3484113013969089573e-07 -4 9.549535102761465607e-11 -2.265740805092889601e-05 --1.5233712071242269115 0.6723825347339112968 0.051459143378398922164 --0.0051275613251079554117 -0.011607719813367209372 -0.000117479966462153095864 -5 2.825345908631354893e-07 -0.00046732617030490929307 -4.049944927347420176 -2.9910878677758190314 -0.078187280837353656526 -0.0043972077687938898594 0.006432188574295680597 -0.00012509257442073270106 -6 8.459715183006415395e-08 -0.00038925687730393611812 -6.298929503477405767 -7.706413024510769816 -0.11669919842191249504 -0.0040140666547768266703 0.0035242303011843410798 -0.00022097170940726839814 -7 1.2920249163736673626e-08 -0.00016953449859497231466 -14.856082147529010129 13.007589275314199284 -0.14417795763685259391 --0.0026158276515510360365 0.0027821364817078499815 4.40781085949555924e-05 -8 1.5243589003230834323e-08 -0.000164587904124493665 -29.55744967800954015 -4.629377558152945049 -0.58590957207831262377 -0.00046987400245862169295 0.0031274056019462009859 -7.51415892482447254e-05 diff --git a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/pl.swifter.in b/examples/rmvs_swifter_comparison/9pl_18tp_encounters/pl.swifter.in deleted file mode 100644 index 701e9a14f..000000000 --- a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/pl.swifter.in +++ /dev/null @@ -1,36 +0,0 @@ -9 -0 0.00029591220819207775568 -0.0 0.0 0.0 -0.0 0.0 0.0 -1 4.9125474498983623693e-11 0.0014751243077781048702 -1.6306381826061645943e-05 -0.33206272695596028566 0.07436707001147663254 -0.02438290851908785084 --0.0115920916602103591525 0.028710618792657981169 0.0034094833969203438596 -2 7.243452483873646905e-10 0.006759104275397271956 -4.0453784346544178454e-05 --0.7188115337296047125 -0.0118554711069603201795 0.041316403191083782287 -0.00021427347881133320621 -0.020313576971905909774 -0.00029114855617710840843 -3 8.9970113821660187435e-10 0.010044787321379672528 -4.25875607065040958e-05 -0.35677088372527121507 -0.95189300879814897627 4.4027442504036787155e-05 -0.015830039028334789986 0.0059737936889703449964 -3.3484113013969089573e-07 -4 9.549535102761465607e-11 0.007246743835971885302 -2.265740805092889601e-05 --1.5233712071242269115 0.6723825347339112968 0.051459143378398922164 --0.0051275613251079554117 -0.011607719813367209372 -0.000117479966462153095864 -5 2.825345908631354893e-07 0.35527126534549128905 -0.00046732617030490929307 -4.049944927347420176 -2.9910878677758190314 -0.078187280837353656526 -0.0043972077687938898594 0.006432188574295680597 -0.00012509257442073270106 -6 8.459715183006415395e-08 0.4376527512949726007 -0.00038925687730393611812 -6.298929503477405767 -7.706413024510769816 -0.11669919842191249504 -0.0040140666547768266703 0.0035242303011843410798 -0.00022097170940726839814 -7 1.2920249163736673626e-08 0.4695362423191493196 -0.00016953449859497231466 -14.856082147529010129 13.007589275314199284 -0.14417795763685259391 --0.0026158276515510360365 0.0027821364817078499815 4.40781085949555924e-05 -8 1.5243589003230834323e-08 0.7812870996943599397 -0.000164587904124493665 -29.55744967800954015 -4.629377558152945049 -0.58590957207831262377 -0.00046987400245862169295 0.0031274056019462009859 -7.51415892482447254e-05 diff --git a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/pl.swiftest.in b/examples/rmvs_swifter_comparison/9pl_18tp_encounters/pl.swiftest.in deleted file mode 100644 index bd980fc4b..000000000 --- a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/pl.swiftest.in +++ /dev/null @@ -1,33 +0,0 @@ -8 -1 4.9125474498983623693e-11 -1.6306381826061645943e-05 -0.33206272695596028566 0.07436707001147663254 -0.02438290851908785084 --0.0115920916602103591525 0.028710618792657981169 0.0034094833969203438596 -2 7.243452483873646905e-10 -4.0453784346544178454e-05 --0.7188115337296047125 -0.0118554711069603201795 0.041316403191083782287 -0.00021427347881133320621 -0.020313576971905909774 -0.00029114855617710840843 -3 8.9970113821660187435e-10 -4.25875607065040958e-05 -0.35677088372527121507 -0.95189300879814897627 4.4027442504036787155e-05 -0.015830039028334789986 0.0059737936889703449964 -3.3484113013969089573e-07 -4 9.549535102761465607e-11 -2.265740805092889601e-05 --1.5233712071242269115 0.6723825347339112968 0.051459143378398922164 --0.0051275613251079554117 -0.011607719813367209372 -0.000117479966462153095864 -5 2.825345908631354893e-07 -0.00046732617030490929307 -4.049944927347420176 -2.9910878677758190314 -0.078187280837353656526 -0.0043972077687938898594 0.006432188574295680597 -0.00012509257442073270106 -6 8.459715183006415395e-08 -0.00038925687730393611812 -6.298929503477405767 -7.706413024510769816 -0.11669919842191249504 -0.0040140666547768266703 0.0035242303011843410798 -0.00022097170940726839814 -7 1.2920249163736673626e-08 -0.00016953449859497231466 -14.856082147529010129 13.007589275314199284 -0.14417795763685259391 --0.0026158276515510360365 0.0027821364817078499815 4.40781085949555924e-05 -8 1.5243589003230834323e-08 -0.000164587904124493665 -29.55744967800954015 -4.629377558152945049 -0.58590957207831262377 -0.00046987400245862169295 0.0031274056019462009859 -7.51415892482447254e-05 diff --git a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/swiftest_rmvs_vs_swifter_rmvs.ipynb b/examples/rmvs_swifter_comparison/9pl_18tp_encounters/swiftest_rmvs_vs_swifter_rmvs.ipynb deleted file mode 100644 index c6bb630b2..000000000 --- a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/swiftest_rmvs_vs_swifter_rmvs.ipynb +++ /dev/null @@ -1,753 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import swiftest\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Reading Swifter file param.swifter.in\n", - "Reading in time 3.652e+03\n", - "Creating Dataset\n", - "Successfully converted 333 output frames.\n", - "Swifter simulation data stored as xarray DataSet .ds\n" - ] - } - ], - "source": [ - "inparfile = 'param.swifter.in'\n", - "swiftersim = swiftest.Simulation(param_file=inparfile, codename=\"Swifter\")\n", - "swiftersim.bin2xr()\n", - "swifterdat = swiftersim.ds" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Reading Swiftest file param.swiftest.in\n", - "Reading in time 3.652e+03\n", - "Creating Dataset\n", - "Successfully converted 333 output frames.\n", - "Swiftest simulation data stored as xarray DataSet .ds\n" - ] - } - ], - "source": [ - "inparfile = 'param.swiftest.in'\n", - "swiftestsim = swiftest.Simulation(param_file=inparfile)\n", - "swiftestsim.bin2xr()\n", - "swiftestdat = swiftestsim.ds" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "swiftdiff = swiftestdat - swifterdat" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "swiftdiff = swiftdiff.rename({'time' : 'time (d)'})" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "swiftdiff['rmag'] = np.sqrt(swiftdiff['px']**2 + swiftdiff['py']**2 + swiftdiff['pz']**2)\n", - "swiftdiff['vmag'] = np.sqrt(swiftdiff['vx']**2 + swiftdiff['vy']**2 + swiftdiff['vz']**2)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "plidx = swiftdiff.id.values[swiftdiff.id.values < 10]\n", - "tpidx = swiftdiff.id.values[swiftdiff.id.values > 10]" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "fig, ax = plt.subplots()\n", - "swiftdiff['rmag'].sel(id=plidx).plot.line(ax=ax, x=\"time (d)\")\n", - "ax.set_ylabel(\"$|\\mathbf{r}_{swiftest} - \\mathbf{r}_{swifter}|$\")\n", - "ax.set_title(\"Heliocentric position differences \\n Planets only\")\n", - "fig.savefig(\"rmvs_swifter_comparison-mars_ejecta-planets-rmag.png\", facecolor='white', transparent=False, dpi=300)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "fig, ax = plt.subplots()\n", - "swiftdiff['vmag'].sel(id=plidx).plot.line(ax=ax, x=\"time (d)\")\n", - "ax.set_ylabel(\"$|\\mathbf{v}_{swiftest} - \\mathbf{v}_{swifter}|$\")\n", - "ax.set_title(\"Heliocentric velocity differences \\n Planets only\")\n", - "fig.savefig(\"rmvs_swifter_comparison-mars_ejecta-planets-vmag.png\", facecolor='white', transparent=False, dpi=300)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "No handles with labels found to put in legend.\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "fig, ax = plt.subplots()\n", - "swiftdiff['rmag'].sel(id=tpidx).plot.line(ax=ax, x=\"time (d)\")\n", - "ax.set_ylabel(\"$|\\mathbf{r}_{swiftest} - \\mathbf{r}_{swifter}|$\")\n", - "ax.set_title(\"Heliocentric position differences \\n Test Particles only\")\n", - "legend = ax.legend()\n", - "legend.remove()\n", - "fig.savefig(\"rmvs_swifter_comparison-mars_ejecta-testparticles-rmag.png\", facecolor='white', transparent=False, dpi=300)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "No handles with labels found to put in legend.\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "fig, ax = plt.subplots()\n", - "swiftdiff['vmag'].sel(id=tpidx).plot.line(ax=ax, x=\"time (d)\")\n", - "ax.set_ylabel(\"$|\\mathbf{v}_{swiftest} - \\mathbf{v}_{swifter}|$\")\n", - "ax.set_title(\"Heliocentric velocity differences \\n Test Particles only\")\n", - "legend = ax.legend()\n", - "legend.remove()\n", - "fig.savefig(\"rmvs_swifter_comparison-mars_ejecta-testparticles-vmag.png\", facecolor='white', transparent=False, dpi=300)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray 'rmag' (time (d): 333)>\n",
-       "array([0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "...\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
-       "       0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 2.13180114e-12,\n",
-       "       6.30252092e-12, 1.12657932e-11, 1.70947866e-11, 2.35410127e-11,\n",
-       "       3.01486367e-11, 3.63634702e-11, 4.16224366e-11, 4.54289913e-11,\n",
-       "       4.74142910e-11, 4.73824194e-11, 4.53327404e-11, 4.14594589e-11,\n",
-       "       3.61300773e-11, 2.98446324e-11, 2.31845539e-11, 1.67548923e-11,\n",
-       "       1.11262399e-11, 6.78147816e-12, 4.07218435e-12, 3.25977426e-12,\n",
-       "       4.52137637e-12, 7.66342713e-12, 1.23344633e-11, 1.81013732e-11,\n",
-       "       2.44264806e-11, 3.07065663e-11, 3.63320360e-11, 4.07478190e-11,\n",
-       "       4.35128453e-11, 4.43475549e-11, 4.31649567e-11, 4.00801554e-11,\n",
-       "       3.53984592e-11, 2.95862328e-11, 2.32329074e-11, 1.70175537e-11,\n",
-       "       1.17040422e-11])\n",
-       "Coordinates:\n",
-       "    id        int64 2\n",
-       "  * time (d)  (time (d)) float64 0.0 11.0 22.0 ... 3.63e+03 3.641e+03 3.652e+03
" - ], - "text/plain": [ - "\n", - "array([0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - "...\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 2.13180114e-12,\n", - " 6.30252092e-12, 1.12657932e-11, 1.70947866e-11, 2.35410127e-11,\n", - " 3.01486367e-11, 3.63634702e-11, 4.16224366e-11, 4.54289913e-11,\n", - " 4.74142910e-11, 4.73824194e-11, 4.53327404e-11, 4.14594589e-11,\n", - " 3.61300773e-11, 2.98446324e-11, 2.31845539e-11, 1.67548923e-11,\n", - " 1.11262399e-11, 6.78147816e-12, 4.07218435e-12, 3.25977426e-12,\n", - " 4.52137637e-12, 7.66342713e-12, 1.23344633e-11, 1.81013732e-11,\n", - " 2.44264806e-11, 3.07065663e-11, 3.63320360e-11, 4.07478190e-11,\n", - " 4.35128453e-11, 4.43475549e-11, 4.31649567e-11, 4.00801554e-11,\n", - " 3.53984592e-11, 2.95862328e-11, 2.32329074e-11, 1.70175537e-11,\n", - " 1.17040422e-11])\n", - "Coordinates:\n", - " id int64 2\n", - " * time (d) (time (d)) float64 0.0 11.0 22.0 ... 3.63e+03 3.641e+03 3.652e+03" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "swiftdiff['rmag'].sel(id=2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "swiftestOOF", - "language": "python", - "name": "swiftestoof" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.10" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/tp.in b/examples/rmvs_swifter_comparison/9pl_18tp_encounters/tp.in deleted file mode 100644 index c7cf002d6..000000000 --- a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/tp.in +++ /dev/null @@ -1,49 +0,0 @@ -16 -101 -0.33208578766229190915 0.07439013071780828379 -0.02438290851908785084 --0.008988542188201206762 0.028710618792657981169 0.0034094833969203438596 -102 -0.33203966624962866216 0.07434400930514498129 -0.02438290851908785084 --0.014195641132219511543 0.028710618792657981169 0.0034094833969203438596 -103 --0.7187543234391324809 -0.011798260816488121555 0.041316403191083782287 -0.0065615071841567274707 -0.020313576971905909774 -0.00029114855617710840843 -104 --0.71886874402007694407 -0.011912681397432518804 0.041316403191083782287 --0.006132960226534060408 -0.020313576971905909774 -0.00029114855617710840843 -105 -0.35683111163121072895 -0.9518327808922094624 4.4027442504036787155e-05 -0.022724479262608666269 0.0059737936889703449964 -3.3484113013969089573e-07 -106 -0.3567106558193317012 -0.95195323670408849015 4.4027442504036787155e-05 -0.008935598794060913702 0.0059737936889703449964 -3.3484113013969089573e-07 -107 --1.5233391647104730371 0.6724145771476651712 0.051459143378398922164 --0.0020480822268840624331 -0.011607719813367209372 -0.000117479966462153095864 -108 --1.5234032495379807859 0.6723504923201574224 0.051459143378398922164 --0.008207040423331847523 -0.011607719813367209372 -0.000117479966462153095864 -109 -4.050605826355517358 -2.9904269687677218492 -0.078187280837353656526 -0.041279424970441319642 0.006432188574295680597 -0.00012509257442073270106 -110 -4.049284028339322994 -2.9917487667839162135 -0.078187280837353656526 --0.032485009432853539924 0.006432188574295680597 -0.00012509257442073270106 -111 -6.299479995832536261 -7.7058625321556393217 -0.11669919842191249504 -0.02612723553831041573 0.0035242303011843410798 -0.00022097170940726839814 -112 -6.2983790111222752728 -7.70696351686590031 -0.11669919842191249504 --0.01809910222875676239 0.0035242303011843410798 -0.00022097170940726839814 -113 -14.856321905516212567 13.007829033301401722 -0.14417795763685259391 -0.010478935887110856981 0.0027821364817078499815 4.40781085949555924e-05 -114 -14.855842389541807691 13.007349517326996846 -0.14417795763685259391 --0.015710591190212928187 0.0027821364817078499815 4.40781085949555924e-05 -115 -29.55768244045575699 -4.6291447957067299868 -0.58590957207831262377 -0.014905509815736753265 0.0031274056019462009859 -7.51415892482447254e-05 -116 -29.557216915563323312 -4.6296103205991601115 -0.58590957207831262377 --0.0139657618108195089035 0.0031274056019462009859 -7.51415892482447254e-05 diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/cb.swiftest.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/cb.swiftest.in new file mode 100644 index 000000000..d0ae0ed15 Binary files /dev/null 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 new file mode 100755 index 000000000..245f5fae0 --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1pl_encounter/init_cond.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +""" +For testing RMVS, the code generates clones of test particles based on one that is fated to impact Mercury. +To use the script, modify the variables just after the "if __name__ == '__main__':" line +""" +import numpy as np +import swiftest +from scipy.io import FortranFile +import sys + +swifter_input = "param.swifter.in" +swifter_pl = "pl.swifter.in" +swifter_tp = "tp.swifter.in" +swifter_bin = "bin.swifter.dat" +swifter_enc = "enc.swifter.dat" + +swiftest_input = "param.swiftest.in" +swiftest_pl = "pl.swiftest.in" +swiftest_tp = "tp.swiftest.in" +swiftest_cb = "cb.swiftest.in" +swiftest_bin = "bin.swiftest.dat" +swiftest_enc = "enc.swiftest.dat" + +MU2KG = swiftest.MSun +TU2S = swiftest.YR2S +DU2M = swiftest.AU2M + +GMSun = swiftest.GMSunSI * TU2S**2 / DU2M**3 + +# Simple initial conditions of a circular planet with one smaller massive body in a close encounter state +# Simulation start, stop, and output cadence times +t_0 = 0 # simulation start time +deltaT = 0.25 * swiftest.JD2S / TU2S # simulation step size +end_sim = 0.15 +t_print = deltaT #output interval to print results + +iout = int(np.ceil(t_print / deltaT)) +rmin = swiftest.RSun / swiftest.AU2M +rmax = 1000.0 + +npl = 2 +ntp = 0 +plid1 = 2 +plid2 = 100 + +radius1 = np.double(4.25875607065041e-05) +mass1 = np.double(0.00012002693582795244940133) +mass2 = mass1 / 100.0 +radius2 = radius1 * (mass2 / mass1)**(1.0/3.0) + +apl1 = np.longdouble(1.0) +apl2 = np.longdouble(1.01) +vpl1 = np.longdouble(2 * np.pi) +vpl2 = np.longdouble(2 * np.pi / np.sqrt(apl2)) + +p_pl1 = np.array([apl1, 0.0, 0.0], dtype=np.double) +v_pl1 = np.array([0.0, vpl1, 0.0], dtype=np.double) + +p_pl2 = np.array([apl2, 0.0, 0.0], dtype=np.double) +v_pl2 = np.array([0.0, vpl2, 0.0], dtype=np.double) + +Rhill1 = np.double(apl1 * 0.0100447248332378922085) +Rhill2 = Rhill1 * (mass2 / mass1)**(1.0 / 3.0) + +#Make Swifter files +plfile = open(swifter_pl, 'w') +print(npl+1, f'! Planet input file generated using init_cond.py',file=plfile) + +print(1,GMSun,file=plfile) +print('0.0 0.0 0.0',file=plfile) +print('0.0 0.0 0.0',file=plfile) + +print(plid1,"{:.23g}".format(mass1),Rhill1, file=plfile) +print(radius1, file=plfile) +print(*p_pl1, file=plfile) +print(*v_pl1, file=plfile) + +print(plid2,"{:.23g}".format(mass2),Rhill2, file=plfile) +print(radius2, file=plfile) +print(*p_pl2, file=plfile) +print(*v_pl2, file=plfile) + +plfile.close() + +tpfile = open(swifter_tp, 'w') +print(0,file=tpfile) +tpfile.close() + +sys.stdout = open(swifter_input, "w") +print(f'! Swifter input file generated using init_cond.py') +print(f'T0 {t_0} ') +print(f'TSTOP {end_sim}') +print(f'DT {deltaT}') +print(f'PL_IN {swifter_pl}') +print(f'TP_IN {swifter_tp}') +print(f'IN_TYPE ASCII') +print(f'ISTEP_OUT {iout:d}') +print(f'ISTEP_DUMP {iout:d}') +print(f'BIN_OUT {swifter_bin}') +print(f'OUT_TYPE REAL8') +print(f'OUT_FORM XV') +print(f'OUT_STAT UNKNOWN') +#print(f'J2 {swiftest.J2Sun}') +#print(f'J4 {swiftest.J4Sun}') +print(f'J2 0.0') +print(f'J4 0.0') +print(f'CHK_CLOSE yes') +print(f'CHK_RMIN {rmin}') +print(f'CHK_RMAX {rmax}') +print(f'CHK_EJECT {rmax}') +print(f'CHK_QMIN {rmin}') +print(f'CHK_QMIN_COORD HELIO') +print(f'CHK_QMIN_RANGE {rmin} {rmax}') +print(f'ENC_OUT {swifter_enc}') +print(f'EXTRA_FORCE no') +print(f'BIG_DISCARD no') +print(f'RHILL_PRESENT yes') +sys.stdout = sys.__stdout__ + +#Now make Swiftest files +cbfile = FortranFile(swiftest_cb, 'w') +Msun = np.double(1.0) +cbfile.write_record(0) +cbfile.write_record(np.double(GMSun)) +cbfile.write_record(np.double(rmin)) +#cbfile.write_record(np.double(swiftest.J2Sun)) +#cbfile.write_record(np.double(swiftest.J4Sun)) +cbfile.write_record(np.double(0.0)) +cbfile.write_record(np.double(0.0)) +cbfile.close() + +plfile = FortranFile(swiftest_pl, 'w') +plfile.write_record(npl) +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]])) +plfile.write_record(np.vstack([v_pl1[0],v_pl2[0]])) +plfile.write_record(np.vstack([v_pl1[1],v_pl2[1]])) +plfile.write_record(np.vstack([v_pl1[2],v_pl2[2]])) +plfile.write_record(np.array([mass1,mass2])) +plfile.write_record(np.array([Rhill1,Rhill2])) +plfile.write_record(np.array([radius1,radius2])) +plfile.close() +tpfile = FortranFile(swiftest_tp, 'w') +tpfile.write_record(ntp) + +tpfile.close() + +sys.stdout = open(swiftest_input, "w") +print(f'! Swiftest input file generated using init_cond.py') +print(f'T0 {t_0} ') +print(f'TSTOP {end_sim}') +print(f'DT {deltaT}') +print(f'CB_IN {swiftest_cb}') +print(f'PL_IN {swiftest_pl}') +print(f'TP_IN {swiftest_tp}') +print(f'IN_TYPE REAL8') +print(f'ISTEP_OUT {iout:d}') +print(f'ISTEP_DUMP {iout:d}') +print(f'BIN_OUT {swiftest_bin}') +print(f'OUT_TYPE REAL8') +print(f'OUT_FORM XV') +print(f'OUT_STAT REPLACE') +print(f'CHK_CLOSE yes') +print(f'CHK_RMIN {rmin}') +print(f'CHK_RMAX {rmax}') +print(f'CHK_EJECT {rmax}') +print(f'CHK_QMIN {rmin}') +print(f'CHK_QMIN_COORD HELIO') +print(f'CHK_QMIN_RANGE {rmin} {rmax}') +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'ENERGY yes') +print(f'GR no') +print(f'MU2KG {MU2KG}') +print(f'DU2M {DU2M}') +print(f'TU2S {TU2S}') +print(f'RHILL_PRESENT yes') +print(f'MTINY 1e-12') + + + + diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/param.swifter.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/param.swifter.in new file mode 100644 index 000000000..853815639 --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1pl_encounter/param.swifter.in @@ -0,0 +1,26 @@ +! Swifter input file generated using init_cond.py +T0 0 +TSTOP 0.15 +DT 0.0006844626967830253 +PL_IN pl.swifter.in +TP_IN tp.swifter.in +IN_TYPE ASCII +ISTEP_OUT 1 +ISTEP_DUMP 1 +BIN_OUT bin.swifter.dat +OUT_TYPE REAL8 +OUT_FORM XV +OUT_STAT UNKNOWN +J2 0.0 +J4 0.0 +CHK_CLOSE yes +CHK_RMIN 0.004650467260962157 +CHK_RMAX 1000.0 +CHK_EJECT 1000.0 +CHK_QMIN 0.004650467260962157 +CHK_QMIN_COORD HELIO +CHK_QMIN_RANGE 0.004650467260962157 1000.0 +ENC_OUT enc.swifter.dat +EXTRA_FORCE no +BIG_DISCARD no +RHILL_PRESENT yes diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/param.swiftest.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/param.swiftest.in new file mode 100644 index 000000000..3050dea4a --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1pl_encounter/param.swiftest.in @@ -0,0 +1,33 @@ +! Swiftest input file generated using init_cond.py +T0 0 +TSTOP 0.15 +DT 0.0006844626967830253 +CB_IN cb.swiftest.in +PL_IN pl.swiftest.in +TP_IN tp.swiftest.in +IN_TYPE REAL8 +ISTEP_OUT 1 +ISTEP_DUMP 1 +BIN_OUT bin.swiftest.dat +OUT_TYPE REAL8 +OUT_FORM XV +OUT_STAT REPLACE +CHK_CLOSE yes +CHK_RMIN 0.004650467260962157 +CHK_RMAX 1000.0 +CHK_EJECT 1000.0 +CHK_QMIN 0.004650467260962157 +CHK_QMIN_COORD HELIO +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 +ENERGY yes +GR no +MU2KG 1.988409870698051e+30 +DU2M 149597870700.0 +TU2S 31557600.0 +RHILL_PRESENT yes +MTINY 1e-12 diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swifter.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swifter.in new file mode 100644 index 000000000..9f0548fc1 --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swifter.in @@ -0,0 +1,12 @@ +3 ! Planet input file generated using init_cond.py +1 39.476926408897625196 +0.0 0.0 0.0 +0.0 0.0 0.0 +2 0.00012002693582795244940133 0.010044724833237892 +4.25875607065041e-05 +1.0 0.0 0.0 +0.0 6.283185307179586 0.0 +100 1.2002693582795244601319e-06 0.002164070363255244 +9.17521181499312e-06 +1.01 0.0 0.0 +0.0 6.252003053624663 0.0 diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swiftest.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/pl.swiftest.in new file mode 100644 index 000000000..1bda0535d Binary files /dev/null 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 new file mode 100644 index 000000000..3a80eebd1 --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1pl_encounter/swiftest_vs_swifter.ipynb @@ -0,0 +1,1644 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import swiftest\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading Swifter file param.swifter.in\n", + "Reading in time 1.506e-01\n", + "Creating Dataset\n", + "Successfully converted 221 output frames.\n", + "Swifter simulation data stored as xarray DataSet .ds\n" + ] + } + ], + "source": [ + "swiftersim = swiftest.Simulation(param_file=\"param.swifter.in\", codename=\"Swifter\")\n", + "swiftersim.bin2xr()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading Swiftest file param.swiftest.in\n", + "Reading in time 1.506e-01\n", + "Creating Dataset\n", + "Successfully converted 221 output frames.\n", + "Swiftest simulation data stored as xarray DataSet .ds\n" + ] + } + ], + "source": [ + "swiftestsim = swiftest.Simulation(param_file=\"param.swiftest.in\")\n", + "swiftestsim.bin2xr()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff = swiftestsim.ds - swiftersim.ds" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff = swiftdiff.rename({'time' : 'time (y)'})\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[,\n", + " ]" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "swiftdiff['vx'].plot.line(x=\"time (y)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'vx' (time (y): 221)>\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 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. ,\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 2.0\n", + " * time (y) (time (y)) float64 0.0 0.0006845 0.001369 ... 0.1492 0.1499 0.1506" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "swiftestOOF", + "language": "python", + "name": "swiftestoof" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/tp.swifter.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/tp.swifter.in new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1pl_encounter/tp.swifter.in @@ -0,0 +1 @@ +0 diff --git a/examples/symba_swifter_comparison/1pl_1pl_encounter/tp.swiftest.in b/examples/symba_swifter_comparison/1pl_1pl_encounter/tp.swiftest.in new file mode 100644 index 000000000..64bf92f74 Binary files /dev/null and b/examples/symba_swifter_comparison/1pl_1pl_encounter/tp.swiftest.in differ diff --git a/examples/symba_swifter_comparison/1pl_1tp_encounter/.idea/.gitignore b/examples/symba_swifter_comparison/1pl_1tp_encounter/.idea/.gitignore new file mode 100644 index 000000000..26d33521a --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1tp_encounter/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/examples/symba_swifter_comparison/1pl_1tp_encounter/cb.swiftest.in b/examples/symba_swifter_comparison/1pl_1tp_encounter/cb.swiftest.in new file mode 100644 index 000000000..d0ae0ed15 Binary files /dev/null and b/examples/symba_swifter_comparison/1pl_1tp_encounter/cb.swiftest.in differ diff --git a/examples/symba_swifter_comparison/1pl_1tp_encounter/init_cond.py b/examples/symba_swifter_comparison/1pl_1tp_encounter/init_cond.py new file mode 100755 index 000000000..338b5d5a8 --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1tp_encounter/init_cond.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +""" +For testing RMVS, the code generates clones of test particles based on one that is fated to impact Mercury. +To use the script, modify the variables just after the "if __name__ == '__main__':" line +""" +import numpy as np +import swiftest +from scipy.io import FortranFile +import sys + +swifter_input = "param.swifter.in" +swifter_pl = "pl.swifter.in" +swifter_tp = "tp.swifter.in" +swifter_bin = "bin.swifter.dat" +swifter_enc = "enc.swifter.dat" + +swiftest_input = "param.swiftest.in" +swiftest_pl = "pl.swiftest.in" +swiftest_tp = "tp.swiftest.in" +swiftest_cb = "cb.swiftest.in" +swiftest_bin = "bin.swiftest.dat" +swiftest_enc = "enc.swiftest.dat" + +MU2KG = swiftest.MSun +TU2S = swiftest.YR2S +DU2M = swiftest.AU2M + +J2 = 0.0 #swiftest.J2Sun +J4 = 0.0 #swiftest.J4Sun + +GMSun = swiftest.GMSunSI * TU2S**2 / DU2M**3 + +# Simple initial conditions of a circular planet with one test particle in a close encounter state +# Simulation start, stop, and output cadence times +t_0 = 0 # simulation start time +deltaT = 0.25 * swiftest.JD2S / TU2S # simulation step size +end_sim = 0.15 +t_print = deltaT #output interval to print results + +iout = int(np.ceil(t_print / deltaT)) +rmin = swiftest.RSun / swiftest.AU2M +rmax = 1000.0 + +npl = 1 +plid = 2 +tpid = 100 + +radius = np.double(4.25875607065041e-05) +mass = np.double(0.00012002693582795244940133) +apl = np.longdouble(1.0) +atp = np.longdouble(1.01) +vpl = np.longdouble(2 * np.pi) +vtp = np.longdouble(2 * np.pi / np.sqrt(atp)) + +p_pl = np.array([apl, 0.0, 0.0], dtype=np.double) +v_pl = np.array([0.0, vpl, 0.0], dtype=np.double) + +p_tp = np.array([atp, 0.0, 0.0], dtype=np.double) +v_tp = np.array([0.0, vtp, 0.0], dtype=np.double) + +Rhill = np.double(apl * 0.0100447248332378922085) + +#Make Swifter files +plfile = open(swifter_pl, 'w') +print(npl+1, f'! Planet input file generated using init_cond.py',file=plfile) +print(1,GMSun,file=plfile) +print('0.0 0.0 0.0',file=plfile) +print('0.0 0.0 0.0',file=plfile) +print(plid,"{:.23g}".format(mass),Rhill, file=plfile) +print(radius, file=plfile) +print(*p_pl, file=plfile) +print(*v_pl, file=plfile) +plfile.close() + +tpfile = open(swifter_tp, 'w') +print(1,file=tpfile) +print(tpid, file=tpfile) +print(*p_tp, file=tpfile) +print(*v_tp, file=tpfile) +tpfile.close() + +sys.stdout = open(swifter_input, "w") +print(f'! Swifter input file generated using init_cond.py') +print(f'T0 {t_0} ') +print(f'TSTOP {end_sim}') +print(f'DT {deltaT}') +print(f'PL_IN {swifter_pl}') +print(f'TP_IN {swifter_tp}') +print(f'IN_TYPE ASCII') +print(f'ISTEP_OUT {iout:d}') +print(f'ISTEP_DUMP {iout:d}') +print(f'BIN_OUT {swifter_bin}') +print(f'OUT_TYPE REAL8') +print(f'OUT_FORM XV') +print(f'OUT_STAT UNKNOWN') +print(f'J2 {J2}') +print(f'J4 {J4}') +print(f'CHK_CLOSE yes') +print(f'CHK_RMIN {rmin}') +print(f'CHK_RMAX {rmax}') +print(f'CHK_EJECT {rmax}') +print(f'CHK_QMIN {rmin}') +print(f'CHK_QMIN_COORD HELIO') +print(f'CHK_QMIN_RANGE {rmin} {rmax}') +print(f'ENC_OUT {swifter_enc}') +print(f'EXTRA_FORCE no') +print(f'BIG_DISCARD no') +print(f'RHILL_PRESENT yes') +sys.stdout = sys.__stdout__ + +#Now make Swiftest files +cbfile = FortranFile(swiftest_cb, 'w') +Msun = np.double(1.0) +cbfile.write_record(0) +cbfile.write_record(np.double(GMSun)) +cbfile.write_record(np.double(rmin)) +cbfile.write_record(np.double(J2)) +cbfile.write_record(np.double(J4)) +cbfile.close() + +plfile = FortranFile(swiftest_pl, 'w') +plfile.write_record(npl) + +plfile.write_record(plid) +plfile.write_record(p_pl[0]) +plfile.write_record(p_pl[1]) +plfile.write_record(p_pl[2]) +plfile.write_record(v_pl[0]) +plfile.write_record(v_pl[1]) +plfile.write_record(v_pl[2]) +plfile.write_record(mass) +plfile.write_record(Rhill) +plfile.write_record(radius) +plfile.close() +tpfile = FortranFile(swiftest_tp, 'w') +ntp = 1 +tpfile.write_record(ntp) +tpfile.write_record(tpid) +tpfile.write_record(p_tp[0]) +tpfile.write_record(p_tp[1]) +tpfile.write_record(p_tp[2]) +tpfile.write_record(v_tp[0]) +tpfile.write_record(v_tp[1]) +tpfile.write_record(v_tp[2]) + +tpfile.close() + +sys.stdout = open(swiftest_input, "w") +print(f'! Swiftest input file generated using init_cond.py') +print(f'T0 {t_0} ') +print(f'TSTOP {end_sim}') +print(f'DT {deltaT}') +print(f'CB_IN {swiftest_cb}') +print(f'PL_IN {swiftest_pl}') +print(f'TP_IN {swiftest_tp}') +print(f'IN_TYPE REAL8') +print(f'ISTEP_OUT {iout:d}') +print(f'ISTEP_DUMP {iout:d}') +print(f'BIN_OUT {swiftest_bin}') +print(f'OUT_TYPE REAL8') +print(f'OUT_FORM XV') +print(f'OUT_STAT REPLACE') +print(f'CHK_CLOSE yes') +print(f'CHK_RMIN {rmin}') +print(f'CHK_RMAX {rmax}') +print(f'CHK_EJECT {rmax}') +print(f'CHK_QMIN {rmin}') +print(f'CHK_QMIN_COORD HELIO') +print(f'CHK_QMIN_RANGE {rmin} {rmax}') +print(f'ENC_OUT {swiftest_enc}') +print(f'EXTRA_FORCE no') +print(f'BIG_DISCARD no') +print(f'ROTATION no') +print(f'GR no') +print(f'MU2KG {MU2KG}') +print(f'DU2M {DU2M}') +print(f'TU2S {TU2S}') +print(f'RHILL_PRESENT yes') +print(f'MTINY 1e-12') + + + + diff --git a/examples/symba_swifter_comparison/1pl_1tp_encounter/param.swifter.in b/examples/symba_swifter_comparison/1pl_1tp_encounter/param.swifter.in new file mode 100644 index 000000000..853815639 --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1tp_encounter/param.swifter.in @@ -0,0 +1,26 @@ +! Swifter input file generated using init_cond.py +T0 0 +TSTOP 0.15 +DT 0.0006844626967830253 +PL_IN pl.swifter.in +TP_IN tp.swifter.in +IN_TYPE ASCII +ISTEP_OUT 1 +ISTEP_DUMP 1 +BIN_OUT bin.swifter.dat +OUT_TYPE REAL8 +OUT_FORM XV +OUT_STAT UNKNOWN +J2 0.0 +J4 0.0 +CHK_CLOSE yes +CHK_RMIN 0.004650467260962157 +CHK_RMAX 1000.0 +CHK_EJECT 1000.0 +CHK_QMIN 0.004650467260962157 +CHK_QMIN_COORD HELIO +CHK_QMIN_RANGE 0.004650467260962157 1000.0 +ENC_OUT enc.swifter.dat +EXTRA_FORCE no +BIG_DISCARD no +RHILL_PRESENT yes diff --git a/examples/symba_swifter_comparison/1pl_1tp_encounter/param.swiftest.in b/examples/symba_swifter_comparison/1pl_1tp_encounter/param.swiftest.in new file mode 100644 index 000000000..a7f91ba33 --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1tp_encounter/param.swiftest.in @@ -0,0 +1,31 @@ +! Swiftest input file generated using init_cond.py +T0 0 +TSTOP 0.15 +DT 0.0006844626967830253 +CB_IN cb.swiftest.in +PL_IN pl.swiftest.in +TP_IN tp.swiftest.in +IN_TYPE REAL8 +ISTEP_OUT 1 +ISTEP_DUMP 1 +BIN_OUT bin.swiftest.dat +OUT_TYPE REAL8 +OUT_FORM XV +OUT_STAT REPLACE +CHK_CLOSE yes +CHK_RMIN 0.004650467260962157 +CHK_RMAX 1000.0 +CHK_EJECT 1000.0 +CHK_QMIN 0.004650467260962157 +CHK_QMIN_COORD HELIO +CHK_QMIN_RANGE 0.004650467260962157 1000.0 +ENC_OUT enc.swiftest.dat +EXTRA_FORCE no +BIG_DISCARD no +ROTATION no +GR no +MU2KG 1.988409870698051e+30 +DU2M 149597870700.0 +TU2S 31557600.0 +RHILL_PRESENT yes +MTINY 1e-12 diff --git a/examples/symba_swifter_comparison/1pl_1tp_encounter/pl.swifter.in b/examples/symba_swifter_comparison/1pl_1tp_encounter/pl.swifter.in new file mode 100644 index 000000000..17d461561 --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1tp_encounter/pl.swifter.in @@ -0,0 +1,8 @@ +2 ! Planet input file generated using init_cond.py +1 39.476926408897625196 +0.0 0.0 0.0 +0.0 0.0 0.0 +2 0.00012002693582795244940133 0.010044724833237892 +4.25875607065041e-05 +1.0 0.0 0.0 +0.0 6.283185307179586 0.0 diff --git a/examples/symba_swifter_comparison/1pl_1tp_encounter/pl.swiftest.in b/examples/symba_swifter_comparison/1pl_1tp_encounter/pl.swiftest.in new file mode 100644 index 000000000..c94c6ae61 Binary files /dev/null and b/examples/symba_swifter_comparison/1pl_1tp_encounter/pl.swiftest.in differ diff --git a/examples/symba_swifter_comparison/1pl_1tp_encounter/swiftest_vs_swifter.ipynb b/examples/symba_swifter_comparison/1pl_1tp_encounter/swiftest_vs_swifter.ipynb new file mode 100644 index 000000000..02d6b0bef --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1tp_encounter/swiftest_vs_swifter.ipynb @@ -0,0 +1,593 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import swiftest\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading Swifter file param.swifter.in\n", + "Reading in time 1.355e-01\n", + "Creating Dataset\n", + "Successfully converted 199 output frames.\n", + "Swifter simulation data stored as xarray DataSet .ds\n" + ] + } + ], + "source": [ + "swiftersim = swiftest.Simulation(param_file=\"param.swifter.in\", codename=\"Swifter\")\n", + "swiftersim.bin2xr()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading Swiftest file param.swiftest.in\n", + "Reading in time 1.506e-01\n", + "Creating Dataset\n", + "Successfully converted 221 output frames.\n", + "Swiftest simulation data stored as xarray DataSet .ds\n" + ] + } + ], + "source": [ + "swiftestsim = swiftest.Simulation(param_file=\"param.swiftest.in\")\n", + "swiftestsim.bin2xr()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff = swiftestsim.ds - swiftersim.ds" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff = swiftdiff.rename({'time' : 'time (y)'})\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[,\n", + " ]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "swiftdiff['vx'].plot.line(x=\"time (y)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'vx' (time (y): 199)>\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.,  0., nan])\n",
+       "Coordinates:\n",
+       "    id        float64 100.0\n",
+       "  * time (y)  (time (y)) float64 0.0 0.0006845 0.001369 ... 0.1342 0.1348 0.1355
" + ], + "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., 0., nan])\n", + "Coordinates:\n", + " id float64 100.0\n", + " * time (y) (time (y)) float64 0.0 0.0006845 0.001369 ... 0.1342 0.1348 0.1355" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "swiftdiff['vx'].sel(id=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "swiftestOOF", + "language": "python", + "name": "swiftestoof" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/symba_swifter_comparison/1pl_1tp_encounter/tp.swifter.in b/examples/symba_swifter_comparison/1pl_1tp_encounter/tp.swifter.in new file mode 100644 index 000000000..9c026369e --- /dev/null +++ b/examples/symba_swifter_comparison/1pl_1tp_encounter/tp.swifter.in @@ -0,0 +1,4 @@ +1 +100 +1.01 0.0 0.0 +0.0 6.252003053624663 0.0 diff --git a/examples/symba_swifter_comparison/1pl_1tp_encounter/tp.swiftest.in b/examples/symba_swifter_comparison/1pl_1tp_encounter/tp.swiftest.in new file mode 100644 index 000000000..e1506974a Binary files /dev/null and b/examples/symba_swifter_comparison/1pl_1tp_encounter/tp.swiftest.in differ diff --git a/examples/symba_swifter_comparison/8pl_16tp_encounters/.idea/.gitignore b/examples/symba_swifter_comparison/8pl_16tp_encounters/.idea/.gitignore new file mode 100644 index 000000000..26d33521a --- /dev/null +++ b/examples/symba_swifter_comparison/8pl_16tp_encounters/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/cb.swiftest.in b/examples/symba_swifter_comparison/8pl_16tp_encounters/cb.in similarity index 100% rename from examples/rmvs_swifter_comparison/9pl_18tp_encounters/cb.swiftest.in rename to examples/symba_swifter_comparison/8pl_16tp_encounters/cb.in diff --git a/examples/symba_swifter_comparison/8pl_16tp_encounters/cb.swiftest.in b/examples/symba_swifter_comparison/8pl_16tp_encounters/cb.swiftest.in new file mode 100644 index 000000000..2e8d49f62 --- /dev/null +++ b/examples/symba_swifter_comparison/8pl_16tp_encounters/cb.swiftest.in @@ -0,0 +1,5 @@ +0 +0.00029591220819207774 +0.004650467260962157 +0.0 +0.0 diff --git a/examples/symba_swifter_comparison/8pl_16tp_encounters/init_cond.py b/examples/symba_swifter_comparison/8pl_16tp_encounters/init_cond.py new file mode 100755 index 000000000..18ef4ce48 --- /dev/null +++ b/examples/symba_swifter_comparison/8pl_16tp_encounters/init_cond.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +import numpy as np +import swiftest +import swiftest.io as swio +import astropy.constants as const +import sys +import xarray as xr + +# Both codes use the same tp input file +tpin = "tp.in" + +swifter_input = "param.swifter.in" +swifter_pl = "pl.swifter.in" +swifter_bin = "bin.swifter.dat" +swifter_enc = "enc.swifter.dat" + +swiftest_input = "param.swiftest.in" +swiftest_pl = "pl.swiftest.in" +swiftest_cb = "cb.swiftest.in" +swiftest_bin = "bin.swiftest.dat" +swiftest_enc = "enc.swiftest.dat" + +sim = swiftest.Simulation() + +sim.param['T0'] = 0.0 +sim.param['DT'] = 1.0 +sim.param['TSTOP'] = 365.25e1 +sim.param['ISTEP_OUT'] = 11 +sim.param['ISTEP_DUMP'] = 1 +sim.param['CHK_QMIN_COORD'] = "HELIO" +sim.param['CHK_QMIN'] = swiftest.RSun / swiftest.AU2M +sim.param['CHK_QMIN_RANGE'] = f"{swiftest.RSun / swiftest.AU2M} 1000.0" +sim.param['CHK_RMIN'] = swiftest.RSun / swiftest.AU2M +sim.param['CHK_RMAX'] = 1000.0 +sim.param['CHK_EJECT'] = 1000.0 +sim.param['OUT_FORM'] = "XV" +sim.param['OUT_STAT'] = "UNKNOWN" +sim.param['GR'] = 'NO' +sim.param['CHK_CLOSE'] = 'YES' +sim.param['RHILL_PRESENT'] = 'YES' +sim.param['MTINY'] = 1.0e-12 + +sim.param['MU2KG'] = swiftest.MSun +sim.param['TU2S'] = swiftest.JD2S +sim.param['DU2M'] = swiftest.AU2M + +bodyid = { + "Sun": 0, + "Mercury": 1, + "Venus": 2, + "Earth": 3, + "Mars": 4, + "Jupiter": 5, + "Saturn": 6, + "Uranus": 7, + "Neptune": 8, +} + +for name, id in bodyid.items(): + sim.add(name, idval=id) + +ds = sim.ds +cb = ds.sel(id=0) +pl = ds.where(ds.id > 0, drop=True) +npl = pl.id.size + +ntp = 16 +dims = ['time', 'id', 'vec'] +tp = [] +t = np.array([0.0]) +clab, plab, tlab = swio.make_swiftest_labels(sim.param) + +# For each planet, we will initialize a pair of test particles. One on its way in, and one on its way out. We will also initialize two additional particles that don't encounter anything +tpnames = np.arange(101, 101 + ntp) +tpxv1 = np.empty((6)) +tpxv2 = np.empty((6)) + +p1 = [] +p2 = [] +p3 = [] +p4 = [] +p5 = [] +p6 = [] + +for i in pl.id: + pli = pl.sel(id=i) + rstart = 2 * np.double(pli['Radius']) # Start the test particles at a multiple of the planet radius away + vstart = 1.5 * np.sqrt(2 * np.double(pli['Mass']) / rstart) # Start the test particle velocities at a multiple of the escape speed + xvstart = np.array([rstart / np.sqrt(2.0), rstart / np.sqrt(2.0), 0.0, vstart, 0.0, 0.0]) + # The positions and velocities of each pair of test particles will be in reference to a planet + plvec = np.array([np.double(pli['px']), + np.double(pli['py']), + np.double(pli['pz']), + np.double(pli['vx']), + np.double(pli['vy']), + np.double(pli['vz'])]) + tpxv1 = plvec + xvstart + tpxv2 = plvec - xvstart + p1.append(tpxv1[0]) + p1.append(tpxv2[0]) + p2.append(tpxv1[1]) + p2.append(tpxv2[1]) + p3.append(tpxv1[2]) + p3.append(tpxv2[2]) + p4.append(tpxv1[3]) + p4.append(tpxv2[3]) + p5.append(tpxv1[4]) + p5.append(tpxv2[4]) + p6.append(tpxv1[5]) + p6.append(tpxv2[5]) + +tvec = np.vstack([p1, p2, p3, p4, p5, p6]) +tpframe = np.expand_dims(tvec.T, axis=0) +tpxr = xr.DataArray(tpframe, dims = dims, coords = {'time' : t, 'id' : tpnames, 'vec' : tlab}) + +tp = [tpxr] +tpda = xr.concat(tp,dim='time') +tpds = tpda.to_dataset(dim = 'vec') + +sim.ds = xr.combine_by_coords([sim.ds, tpds]) +swio.swiftest_xr2infile(sim.ds, sim.param) + +sim.param['PL_IN'] = swiftest_pl +sim.param['TP_IN'] = tpin +sim.param['CB_IN'] = swiftest_cb +sim.param['BIN_OUT'] = swiftest_bin +sim.param['ENC_OUT'] = swiftest_enc +sim.save(swiftest_input) + +sim.param['PL_IN'] = swifter_pl +sim.param['TP_IN'] = tpin +sim.param['BIN_OUT'] = swifter_bin +sim.param['ENC_OUT'] = swifter_enc +sim.save(swifter_input, codename="Swifter") diff --git a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/param.swifter.in b/examples/symba_swifter_comparison/8pl_16tp_encounters/param.swifter.in similarity index 88% rename from examples/rmvs_swifter_comparison/9pl_18tp_encounters/param.swifter.in rename to examples/symba_swifter_comparison/8pl_16tp_encounters/param.swifter.in index aa33eeaa4..f9305cfa2 100644 --- a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/param.swifter.in +++ b/examples/symba_swifter_comparison/8pl_16tp_encounters/param.swifter.in @@ -21,6 +21,6 @@ CHK_QMIN_RANGE 0.004650467260962157 1000.0 EXTRA_FORCE NO BIG_DISCARD NO CHK_CLOSE YES -J2 4.7535806948127355e-12 -J4 -2.2473967953572827e-18 RHILL_PRESENT YES +J2 0.0 +J4 0.0 diff --git a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/param.swiftest.in b/examples/symba_swifter_comparison/8pl_16tp_encounters/param.swiftest.in similarity index 95% rename from examples/rmvs_swifter_comparison/9pl_18tp_encounters/param.swiftest.in rename to examples/symba_swifter_comparison/8pl_16tp_encounters/param.swiftest.in index 6504c9637..e9ed6376c 100644 --- a/examples/rmvs_swifter_comparison/9pl_18tp_encounters/param.swiftest.in +++ b/examples/symba_swifter_comparison/8pl_16tp_encounters/param.swiftest.in @@ -25,6 +25,7 @@ DU2M 149597870700.0 EXTRA_FORCE NO BIG_DISCARD NO CHK_CLOSE YES +RHILL_PRESENT YES FRAGMENTATION NO ROTATION NO TIDES NO @@ -32,4 +33,4 @@ ENERGY NO GR NO YARKOVSKY NO YORP NO -MTINY 0.0 +MTINY 1e-12 diff --git a/examples/symba_swifter_comparison/8pl_16tp_encounters/pl.in b/examples/symba_swifter_comparison/8pl_16tp_encounters/pl.in new file mode 100644 index 000000000..86a616119 --- /dev/null +++ b/examples/symba_swifter_comparison/8pl_16tp_encounters/pl.in @@ -0,0 +1,33 @@ +8 +1 4.9125474498983623693e-11 0.0014751239400086721089 +1.6306381826061645943e-05 +-0.09861361766419070307 0.29750596935836171042 0.03335708456145129036 +-0.032353632540864457612 -0.0078122718370876240157 0.0023293874953380202045 +2 7.243452483873646905e-10 0.0067590794275223005208 +4.0453784346544178454e-05 +-0.6439817957564198947 -0.3248550380869373866 0.032702713983447248558 +0.008969709495375973937 -0.018153139924556138673 -0.0007667345025597138231 +3 8.9970113821660187435e-10 0.010044873080337524463 +4.25875607065040958e-05 +0.59421674333603324847 -0.82331253628773626296 3.7129329104855261984e-05 +0.013670550280388280365 0.010004295439859960809 -5.226292361234363611e-07 +4 9.549535102761465607e-11 0.0072467054748629370034 +2.265740805092889601e-05 +-1.592721551706784977 0.48166390206865000723 0.049163460846716633412 +-0.0035287723306552309585 -0.01219974682608557931 -0.00016910795626524249315 +5 2.825345908631354893e-07 0.35527074967975702942 +0.00046732617030490929307 +4.119089774477131094 -2.8872942462256898644 -0.080165336328135106125 +0.004245402942744468111 0.0065414198811065849687 -0.00012215100047356211078 +6 8.459715183006415395e-08 0.4376562090257202473 +0.00038925687730393611812 +6.3629100567525149756 -7.649727796147929304 -0.12023019299387090186 +0.0039834472120812329868 0.0035613826786502411278 -0.00022039988214595340028 +7 1.2920249163736673626e-08 0.4695793205674148502 +0.00016953449859497231466 +14.814154683311180349 13.052040295401360126 -0.14347198499748289868 +-0.002625101393275708784 0.0027742356008832688187 4.416821810149910185e-05 +8 1.5243589003230834323e-08 0.7813388398513013378 +0.000164587904124493665 +29.564924658285640646 -4.579331535234244299 -0.5871109926822926095 +0.00046449847307956888343 0.003128345390031967918 -7.5036135696161668576e-05 diff --git a/examples/symba_swifter_comparison/8pl_16tp_encounters/pl.swifter.in b/examples/symba_swifter_comparison/8pl_16tp_encounters/pl.swifter.in new file mode 100644 index 000000000..595cdc169 --- /dev/null +++ b/examples/symba_swifter_comparison/8pl_16tp_encounters/pl.swifter.in @@ -0,0 +1,36 @@ +9 +0 0.00029591220819207775568 +0.0 0.0 0.0 +0.0 0.0 0.0 +1 4.9125474498983623693e-11 0.0014751239400086721089 +1.6306381826061645943e-05 +-0.09861361766419070307 0.29750596935836171042 0.03335708456145129036 +-0.032353632540864457612 -0.0078122718370876240157 0.0023293874953380202045 +2 7.243452483873646905e-10 0.0067590794275223005208 +4.0453784346544178454e-05 +-0.6439817957564198947 -0.3248550380869373866 0.032702713983447248558 +0.008969709495375973937 -0.018153139924556138673 -0.0007667345025597138231 +3 8.9970113821660187435e-10 0.010044873080337524463 +4.25875607065040958e-05 +0.59421674333603324847 -0.82331253628773626296 3.7129329104855261984e-05 +0.013670550280388280365 0.010004295439859960809 -5.226292361234363611e-07 +4 9.549535102761465607e-11 0.0072467054748629370034 +2.265740805092889601e-05 +-1.592721551706784977 0.48166390206865000723 0.049163460846716633412 +-0.0035287723306552309585 -0.01219974682608557931 -0.00016910795626524249315 +5 2.825345908631354893e-07 0.35527074967975702942 +0.00046732617030490929307 +4.119089774477131094 -2.8872942462256898644 -0.080165336328135106125 +0.004245402942744468111 0.0065414198811065849687 -0.00012215100047356211078 +6 8.459715183006415395e-08 0.4376562090257202473 +0.00038925687730393611812 +6.3629100567525149756 -7.649727796147929304 -0.12023019299387090186 +0.0039834472120812329868 0.0035613826786502411278 -0.00022039988214595340028 +7 1.2920249163736673626e-08 0.4695793205674148502 +0.00016953449859497231466 +14.814154683311180349 13.052040295401360126 -0.14347198499748289868 +-0.002625101393275708784 0.0027742356008832688187 4.416821810149910185e-05 +8 1.5243589003230834323e-08 0.7813388398513013378 +0.000164587904124493665 +29.564924658285640646 -4.579331535234244299 -0.5871109926822926095 +0.00046449847307956888343 0.003128345390031967918 -7.5036135696161668576e-05 diff --git a/examples/symba_swifter_comparison/8pl_16tp_encounters/pl.swiftest.in b/examples/symba_swifter_comparison/8pl_16tp_encounters/pl.swiftest.in new file mode 100644 index 000000000..86a616119 --- /dev/null +++ b/examples/symba_swifter_comparison/8pl_16tp_encounters/pl.swiftest.in @@ -0,0 +1,33 @@ +8 +1 4.9125474498983623693e-11 0.0014751239400086721089 +1.6306381826061645943e-05 +-0.09861361766419070307 0.29750596935836171042 0.03335708456145129036 +-0.032353632540864457612 -0.0078122718370876240157 0.0023293874953380202045 +2 7.243452483873646905e-10 0.0067590794275223005208 +4.0453784346544178454e-05 +-0.6439817957564198947 -0.3248550380869373866 0.032702713983447248558 +0.008969709495375973937 -0.018153139924556138673 -0.0007667345025597138231 +3 8.9970113821660187435e-10 0.010044873080337524463 +4.25875607065040958e-05 +0.59421674333603324847 -0.82331253628773626296 3.7129329104855261984e-05 +0.013670550280388280365 0.010004295439859960809 -5.226292361234363611e-07 +4 9.549535102761465607e-11 0.0072467054748629370034 +2.265740805092889601e-05 +-1.592721551706784977 0.48166390206865000723 0.049163460846716633412 +-0.0035287723306552309585 -0.01219974682608557931 -0.00016910795626524249315 +5 2.825345908631354893e-07 0.35527074967975702942 +0.00046732617030490929307 +4.119089774477131094 -2.8872942462256898644 -0.080165336328135106125 +0.004245402942744468111 0.0065414198811065849687 -0.00012215100047356211078 +6 8.459715183006415395e-08 0.4376562090257202473 +0.00038925687730393611812 +6.3629100567525149756 -7.649727796147929304 -0.12023019299387090186 +0.0039834472120812329868 0.0035613826786502411278 -0.00022039988214595340028 +7 1.2920249163736673626e-08 0.4695793205674148502 +0.00016953449859497231466 +14.814154683311180349 13.052040295401360126 -0.14347198499748289868 +-0.002625101393275708784 0.0027742356008832688187 4.416821810149910185e-05 +8 1.5243589003230834323e-08 0.7813388398513013378 +0.000164587904124493665 +29.564924658285640646 -4.579331535234244299 -0.5871109926822926095 +0.00046449847307956888343 0.003128345390031967918 -7.5036135696161668576e-05 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 new file mode 100644 index 000000000..c3c42dd4f --- /dev/null +++ b/examples/symba_swifter_comparison/8pl_16tp_encounters/swiftest_symba_vs_swifter_symba.ipynb @@ -0,0 +1,643 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import swiftest\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading Swifter file param.swifter.in\n", + "Reading in time 3.652e+03\n", + "Creating Dataset\n", + "Successfully converted 333 output frames.\n", + "Swifter simulation data stored as xarray DataSet .ds\n" + ] + } + ], + "source": [ + "inparfile = 'param.swifter.in'\n", + "swiftersim = swiftest.Simulation(param_file=inparfile, codename=\"Swifter\")\n", + "swiftersim.bin2xr()\n", + "swifterdat = swiftersim.ds" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading Swiftest file param.swiftest.in\n", + "Reading in time 3.652e+03\n", + "Creating Dataset\n", + "Successfully converted 333 output frames.\n", + "Swiftest simulation data stored as xarray DataSet .ds\n" + ] + } + ], + "source": [ + "inparfile = 'param.swiftest.in'\n", + "swiftestsim = swiftest.Simulation(param_file=inparfile)\n", + "swiftestsim.bin2xr()\n", + "swiftestdat = swiftestsim.ds" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff = swiftestdat - swifterdat" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff = swiftdiff.rename({'time' : 'time (d)'})" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff['rmag'] = np.sqrt(swiftdiff['px']**2 + swiftdiff['py']**2 + swiftdiff['pz']**2)\n", + "swiftdiff['vmag'] = np.sqrt(swiftdiff['vx']**2 + swiftdiff['vy']**2 + swiftdiff['vz']**2)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "plidx = swiftdiff.id.values[swiftdiff.id.values < 10]\n", + "tpidx = swiftdiff.id.values[swiftdiff.id.values > 10]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAElCAYAAADnZln1AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAABHBUlEQVR4nO29eZxcZZX//z619JqNJQFCCGEnwEgIAVQWWQYEZVQ2HQRHHEbU0Rn9KjKoM4rOzwEdRXDUcRAFFYY4LoyAwKCAsrhg2ASEKEuUsKUJhCTdndR2fn/ceyu3qu9aXVt3nffrVa9UV93l1O3OuZ/6POc5j6gqhmEYxvQn0+kADMMwjPZgCd8wDKNHsIRvGIbRI1jCNwzD6BEs4RuGYfQIlvANwzB6BEv4PYaIXCAiV7nPF4rIRhHJdjquKETkcBFZ2ek4ID6Wdl5TEfm5iPyd+/wMEbnF996hIvJHN5a3iMh2InKHiGwQkS+2OjajO7GEP8UQkVUi8pd1r50lInelPZaq/llVZ6hquXkRpkNEVER2j9pGVe9U1b3aFVMU9bHU/z46dU1V9WpVPc730meAr7ix/C9wDvAiMEtVP9LO2IzuwRK+0dWISK7TMUxRdgYeqfv599rATEv7HUwfLOFPQ0Rkvoj8UERGROQpEfnHkO0WuQo759vvOhF5SUQeF5F3+7bNisjHReQJ1xa4V0R2ct/bW0R+6u63UkTe6tvvShH5qoj8xN3vNyKym/veHe5mD7rWw9tE5EgRWS0i/yQizwNXeK/5jrmTiPzI/XxrReQrIZ/vAhH5gYh8zz33fSKyv+/9xa4tsk5EHhGRN/nee4OI/N7d7xkROdd9vRqLiHwXWAhc78Z/XspreoGI/I+IfMc9zyMisizi93qsiDwmIq+4n1l871W/5YnIE8CuvriuAd4JnOf+/JcikhGR893f51o3jq3r/i7OFpE/A7e5r/+tiDwqIi+LyP+JyM6+86uIvNe1kV52f+f++N7t7rvBva5Lfdcn8G9VRA4WkRUisl5EXhCRi8OujZEQVbXHFHoAq4C/rHvtLOAu93kGuBf4JNCH8x//SeD17vsXAFe5zxcBCuTcn38BfA0YAJYAI8Ax7nsfBR4C9sJJNPsD2wDDwNPAu4AcsBTHOtjX3e9K4CXgYPf9q4HlvtgV2N3385FACfgc0A8Muq+tdt/PAg8CX3LPPQAcFnKtLgCKwKlAHjgXeMp9ngceBz7uXqejgQ3AXu6+zwGHu8+3Apb64lsd9vtIeU0vADYBb3A/14XAr0M+y7bAet9n+X/udfq7+r+BkLiuBP4/388fAn4NLHCv838B19R9hu+413gQeIt7vRa7v8d/Bn5Z93u8AZiDcxMcAY533zsNeAY4COdvZ3ecbxxxf6u/At7hPp8BvLrT//+m+qPjAdgj5S/M+Y+8EVjne4yxJeEfAvy5bp+PAVe4zy8gIOEDOwFlYKZvvwuBK93nK4E3B8TzNuDOutf+C/iU+/xK4HLfe28AHvP9HJTwC8BA3Wtewn+Nm0xyCa7VBfgSqJtgngMOdx/PAxnf+9cAF7jP/wy8B8fzJigW3+8jMOEnuKYXAD/zvbcPMB7yWf6m7rMIsJrGE/6juDce9+cdcG6OOd9n2NX3/k3A2XXXcgzY2fd7PMz3/v8A57vP/w/4YMBnivtbvQP4NLBtp//fTZeHWTpTk7eo6hzvAfy9772dgfmuTbFORNbhqNjtYo45H3hJVTf4XvsTsKP7fCfgiYD9dgYOqTvfGcD2vm2e9z0fw1FrUYyo6qaQ93YC/qSqpZhjeDztPVHVCk6SnO8+nnZf8/B/3lNwbk5/EpFfiMhrEp7PT9w1hYnXZkCCPfP5dZ9F/T83wM7Atb7f2aM4Nyf/38nTddtf6tv+JZybTtRn8X7PUX87UX+rZwN7Ao+JyG9F5MTUn9KowQZjph9PA0+p6h4p93sW2FpEZvoS1EKcr+LecXcDHg443y9U9dhGAw4gamDxaWChiOQSJv2dvCciksGxMJ713hORjC/pLwT+AKCqvwXeLCJ54AM4irV6rISxxl3TNDxX91kkJJ6kPA38rareXf+GiCxyn2rd9p9V1asbPNduIa+H/q2q6h+B093f28nAD0RkG1UdbSAGAxu0nY7cA6x3Bz0HxRls3U9EDoraSVWfBn4JXCgiAyLyKhyF5f0Hvxz4VxHZQxxeJSLb4Pi2e4rIO0Qk7z4OEpHFCeN9Ace7TfP5ngMuEpFhN9ZDI7Y/UEROdlXzh4DNON71b4BRnIHMvIgcCfwVsFxE+sSpa5+tqkUc7zyszDI0/gTXNA0/Afb1fZZ/pPZbVFq+DnzWG3gVkbki8uaY7T8mIvu6288WkdMSnuty4FwROdD929ndPW/k36qInCkic90b8jr3WB0rIZ4OWMKfZqhT//1XOAOET+EMoF4OzE6w++k4/u2zwLU4PvxP3fcuxlG5t+AkwG8Cg65yPQ74a3e/59ky4JqEC4Bvu1/p3xq3se/z7Y7js6/GGUcI48fu+y8D7wBOVtWiqhaANwEn4FyjrwF/o6qPufu9A1glIuuB9wJnhhz/QuCf3fjPDXg/6pomRlVfxBn8vAhYC+wBTFDnKbgUuA64RUQ24NwED4k4/7U4v9fl7jV5GOfaJYn9+8Bngf/GGRj/X2DrBH+rxwOPiMhGN96/jrD6jASIOzhiGNMOEbkAZ0A4LFkbRk9hCt8wDKNHsIRvGIbRI5ilYxiG0SOYwjcMw+gRLOEb0wYJ6CQ6XZC6Hj2G0QiW8I0phZv0RsVpAvaMiFwsbe7nLwlaOhtGN2IJ35iK7K+qM4BjgLcD747Z3jAMLOEbUxh3ktSdwH7177mtdX/lToh6TkS+IiJ9vvfj2vkGtgKW4JbO24rIDe65XhKRO912ABMQkde6fWFecf99re+9n4vIv4rI3eK0Eb5FRLYNOMZpInJv3WsfEZH/TXcFjV7DEr4xZRGRfXC6Xt4f8HYZp4XwtjgdNo+htskcwIk4LXv3B94KvN497ltwmnidDMzFualcA6CqR7j77q/OalLfAz6CM+N3Lk7jr48T0GNHnH7zPwG+jNNa+mLgJ26LCo+347SanofTMjho9u51wC517SvOBL4bsK1hVOn6hC8i3xKRNSJS37SrkWMtcVXfIyLyOxF5m++9XcRZnOOP4iyY0Rd1LKOj3CciLwPX40zFv6J+A1W9V1V/raolVV2F07L5dXWbXaSq61T1z8DtOFP8wWmLfKGqPuo2aPs3YIn4Fvyoo4jTXnhnt23DnRpc7/xG4I+q+l03rmuAx3DaC3hcoap/UNVxnFYWS+oPoqqbge/htntw+9sswulrZBihdH3Cx+njfXyTjjWG0y9lX/eYl4jIHPe9zwFfcjv3vYzT5MroTpaq6laqupuq/nNdi2MARGRP12Z53u398m84at9PWDvfJK2A/fw7zuIgt4jIkyJyfsh283HaI/uJa5cc1kr628DbXRvqHcD/uDcCwwil6xO+qt6B8x+uiojsJiI3i7PM3p0isnfCY/3BbbmKqj4LrAHmuv9pjgZ+4G76bZwVfoypy3/iqOc9VHUWjs0i0btUeRp4j3/NAVUdVNVfBm2sqhtU9SOquiuOWv+wiBwTsOmzODcTPw21S1bVX+MsFHM4jg1kdo4RS9cn/BAuA/5BVQ/E8Ti/lvYAInIwjkf6BI6fus7XX3014WrOmBrMxOnqudEVBO9LsW9cK+CalsgicqLb8lfY0ko5qI3vjTitpN8uIjnXUtyHxq2Y7wBfAUqqeleDxzB6iCk3iUNEZgCvBb7vK6rod987GfhMwG7PqOrrfcfYAUcRvVNVK/7qDB/Wc2Jqcy6OMDgPZ1D3ezjf4mJR1Wvdv7Plrm//CvBT4PvuJhfgtHQeBM7BEQdfwRm0fRn4mqr+POC4a8VZtelSnG8gjwMnuq2PG+G7wL+6D8OIZUr00hFnBZ4bVHU/EZkFrFTVHRo81izg5ziDct93XxOcdVK3V9WSOMvZXeC/SRhGt+HecNbgjGn8sdPxGN3PlLN0VHU98JT3FVsc9k+yr1t5cy3wHS/Zu8dUnCqNU92X3omzcIZhdDPvA35ryd5IStcrfBG5BjgSp8LiBeBTwG04X4l3APLAclUNsnLqj3UmTgnfI76Xz1LVB0RkV2A5sDWOBXCmVT0Y3YqIrMIZhH6LqgbNQzCMCXR9wjcMwzCaw5SzdAzDMIzG6OoqnW233VYXLVrU6TAMwzCmDPfee++Lqjo36L2uTviLFi1ixYoVnQ7DMAxjyiAi9bO5q5ilYxiG0SNYwjcMw+gRLOEbhmH0CF3t4QdRLBZZvXo1mzZt6nQooQwMDLBgwQLy+XynQzEMw6gy5RL+6tWrmTlzJosWLSK4BU5nUVXWrl3L6tWr2WWXXTodjmEYRpUpZ+ls2rSJbbbZpiuTPYCIsM0223T1NxDDMHqTKZfwga5N9h7dHp9hGL3JlLN0DMMwpgIbChtY/thyNpc3s3DWQvaYswcbixs5aPuDOhZTTyb81772tfzylxMXLzrrrLM48cQTOfXUUwP2MgzDSM5dz9zFl+//8oTXH3rnQx2IxmFKWjqTJSjZG4ZhNJNNJWcc75o3XkMu0x3auicT/owZzrrQqsoHPvAB9tlnH974xjeyZs2aDkdmGMZ0oVgpArDd0HYcv+h4APoyfZ0MqTcTvse1117LypUreeihh/jGN75hyt8wjKbhJfy+bB//8up/4aTdT6KkJTrZkr6nE/4dd9zB6aefTjabZf78+Rx9dKIlTw3DMGIplAsA5DN5hvJD7DxrZypaYVO5cyXbPZ3wwUooDcNoDZ7Cz2edGffD+WEARoujHYuppxP+EUccwfLlyymXyzz33HPcfvvtnQ7JMIxpgqfwc+IM2HoJf6w41rGYumPouEOcdNJJ3HbbbfzFX/wFe+65J6973es6HZJhGNOEYqVIX6av6iIM5YeAzir8nkz4GzduBBw75ytf+UqHozEMYzpSKBeqdg70mKUjInuJyAO+x3oR+VC7zm8YhtFOPIXvMZxzLZ1SD1g6qroSWAIgIlngGeDadp3fMAyjnRQrRfKZiQp/Y2Fjp0Lq2KDtMcATqhq69qJhGMZUJtTSKfWApVPHXwPXBL0hIueIyAoRWTEyMtLmsAzDMJpDmMLvZJVO2xO+iPQBbwK+H/S+ql6mqstUddncuXPbG5xhGEaTKJQL9GW3ePjdUKXTCYV/AnCfqr7QgXMbhmG0hfpB24xkGMwN9lzCP50QO2eq8Ld/+7fMmzeP/fbbr9OhGIbRpRTLxRoPHxxbp2cSvogMAccCP2rneZvNWWedxc0339zpMAzD6GLqFT44Cb9nZtqq6hiwTTvP2QqOOOIIVq1a1ekwDMPoAGvG1rD8seWUtFR97Ygdj2DZ9stqtiuUCwzmB2teG8oNdbRKZ0rPtP309Y/w+2fXN/WY+8yfxaf+at+mHtMwjOnDDU/ewDce+gb92X7ASey/X/t7Lt/+8prtwhS+tVYwDMOYIqwZW8Nwfphfv/3XALzr5ndRqpQmbFeoFGrKMsFJ+GvGOrfQ0pRO+KbEDcNoN2vG1jBvaF7156xkKVQKE7Yrlos1ZZnglGb2zKCtYRjGVGdkbIR5g1sSfkYylCvlCduFKXxL+FOM008/nde85jWsXLmSBQsW8M1vfrPTIRmG0SZGxkeYO7RlUmg2k6WsExN+qVKaoPCHc8O90TxtOnHNNVN6GoFhGA2iqqwZW1Ob8CVLRSsTti2UgxX+eGmcUqVELtP+9GsK3zAMIyHrNq+jWClOtHQCFH6xMnHilddeoVMq3xK+YRhGQrwKG7/Cz2VyEzx8VQ1V+NC5BmqW8A3DMBIyMu508N1uaLvqa0EKv6QlFA2sw4fONVCzhG8YhpGQlze9DMBWA1tVX8tIZoKHXywXAQJ76YAlfMMwjK5nvDQObEnc4Aza1iv8YsVJ+KbwDcMwpiie9z6Y29IjJzLh15dlmoc/tXj66ac56qijWLx4Mfvuuy+XXnppp0MyDKNNeNU1A9mB6mvZTJZKpdbSKZSdmbcTBm1znV3m0OrwU5LL5fjiF7/I0qVL2bBhAwceeCDHHnss++yzT6dDMwyjxYyXxhnMDZLNZKuvBQ3aego/rCzTLJ0pwg477MDSpUsBmDlzJosXL+aZZ57pcFSGYbSDseJYjZ0DwZZOqMLvsIc/tRX+TefD8w8195jb/wWccFGiTVetWsX999/PIYcc0twYDMPoSjyF7yfNoG1/tp+sZM3Dn2ps3LiRU045hUsuuYRZs2Z1OhzDMNrAWGmiwg8qy6wq/DpLR0Q62jFzaiv8hEq82RSLRU455RTOOOMMTj755I7EYBhG+xkrjlV9eI+gXjphCh862zGz3WvazhGRH4jIYyLyqIi8pp3nbwaqytlnn83ixYv58Ic/3OlwDMNoI4GWTiY7YQGUsIlX4FTq9ETCBy4FblbVvYH9gUfbfP5Jc/fdd/Pd736X2267jSVLlrBkyRJuvPHGTodlGEYbGCuNMZSbugq/bZaOiMwCjgDOAlDVAjBxmZgu57DDDkNVOx2GYRgdIMjSCSrL9FbACmqBPJTv3ELm7VT4uwIjwBUicr+IXC4iw/Ubicg5IrJCRFaMjIy0MTzDMIxowqp0gBqV7w3a1s+0BUfh90KVTg5YCvynqh4AjALn12+kqpep6jJVXTZ37tz6tw3DMDpGoKXjTsLyt0iuTrzKBHj4PTJouxpYraq/cX/+Ac4NwDAMo+upaIVNpU2BZZlAja0TpfCHcp0ry2xbwlfV54GnRWQv96VjgN+36/yGYRiTYVNpE4oGlmVCraUTN2g7VhzryFhgu+vw/wG4WkT6gCeBd7X5/IZhGA3hNU6rt3Q8hV/SLaWZkWWZ+WFKWqJQKdCf7W9VuIG0NeGr6gPAsnae0zAMoxl4vfDrLR2vEsffMTNO4YPTT6fdCd9aK6Rk06ZNHHzwwey///7su+++fOpTn+p0SIZhtAGvsiaoLBPqPPyIssxONlCb2q0VOkB/fz+33XYbM2bMoFgscthhh3HCCSfw6le/utOhGYbRQsIUfqCHXy6Sz+QRkQnH6eQiKKbwUyIizJgxA3B66hSLxcBfqmEY04u4hF+v8INKMqGzPfGntML/3D2f47GXHmvqMffeem/+6eB/itymXC5z4IEH8vjjj/P+97/f2iMbRg8QVlsfZOkUy8XAkkzorKVjCr8BstksDzzwAKtXr+aee+7h4Ycf7nRIhmG0GK9BWn3C9yZe1Q/ahin8Ti5zOKUVfpwSbzVz5szhyCOP5Oabb2a//fbraCyGYbQWT+HXD8QGKvxKAoVfMIXf9YyMjLBu3ToAxsfH+dnPfsbee+/d2aAMw2g5YQo/J84NoH6mrXn404DnnnuOd77znZTLZSqVCm9961s58cQTOx2WYRgtJo3CL5QLgZOuwKfwzdLpfl71qldx//33dzoMwzDqUFW+/uDXGRl3uuzO6Z/D+5e8v+qxh7F2fC2XP3Q5m8uba14/aqejOHzB4dWfQz38kNYKYQo/l8nRn+3vSFmmJXzDMKYFa8bW8LUHv8ZwfpgMGTYUN3Diriey65xdI/e7+9m7uerRq9iqf6uqWn+l8ApPvvJkTcIPU/hB3TILlULgLFuPwdxgtcyznVjCNwxjWuB1qPz4IR9nODfMh37+oeqM1yg2FjYCcO2br2WbwW0A+MCtH2DN2Jqa7aoKP5usLDPM0gGn5YJ3A2knNmhrGMa0wN+/xku2XhOzKLymaJ63Du6qVHWDqlWFL3UKP8TSiVL4+Ww+UWzNxhK+YRjTAk/N5zP5qn+eROGPFkfJSramkVnQIiWpFH4lWuHnM/lEsTUbS/iGYUwL/C2JvRr4JLbJaHGUofxQTYuU4dxwVflXjx+i8D1Pv8bDjyjL9GI0hW8YhtEggQq/nEzh++0ccBT+eGm8JomXKiVymdyE3llpJ16BYzuZwp9ClMtlDjjgAKvBN4wuoerhZ/tSKfyx4li13YGHNznKr/K9Dpj1hC1iHqnwM/mODNrGVumIyMKEx1qnqusnGc+U4dJLL2Xx4sWsX98zH9kwuhpPzfsVfhLbJEzhe+/N7JsJOCtaBfW3D1X4EYO2fdm+jlg6Scoyvw0oENUDWIErge9EHUhEVgEbgDJQUtUpufrV6tWr+clPfsInPvEJLr744k6HYxgGdQo/k8LDL41OWNQkqKNlqMIPqMOPK8vMZ/LdWYevqkfVvyYi27uLkjfCUar6YoP71vD8v/0bmx9tbnvk/sV7s/3HPx65zYc+9CE+//nPs2HDhqae2zCMxqkO2mby1WSbxMMfK44xb3BezWtBCT9M4TdalpkktmbTqIf/N02NYgpxww03MG/ePA488MBOh2IYhg9vELQv05e6LLNe4XsLlSdR+GFLHAbdHDy61sMP4c0iMgb8VFVXpthPgVtERIH/UtXL6jcQkXOAcwAWLowePohT4q3g7rvv5rrrruPGG29k06ZNrF+/njPPPJOrrrqq7bEYhrEFf1lmmolXUR6+v99NqVIKTPhemaan8EuVEhWtRFfpZPumlMI/GXgcOElELk+x36GquhQ4AXi/iBxRv4GqXqaqy1R12dy5cxsMr3VceOGFrF69mlWrVrF8+XKOPvpoS/aG0QX4yzI9OyVO4auqU6UTNmjr62hZrBQjB21LWqpu58URxpRS+Kr6AnCz+0iz37Puv2tE5FrgYOCORmIwDMPwU6PwvSqdmKRaqBQoaWlCwg/qWR+m8Os9fE+5x9XhT5leOiLyVRG50n1+XMJ9hkVkpvccOA6Y0msDHnnkkdxwww2dDsMwDGo9/GwmS1aysZaOl9A9z94jsEonTOFnXA/frdJJpPA7NNO2UQ+/ALzgPj8auCXBPtsB17qz1HLAf6tqqm8IhmEYYdQn2r5svIr2lhmsV/gD2QEykknk4XsK3xu09RJ5N860bTThjwGzRSQPJJqYpapPAvs3eD7DMIxIiuUiWclW6+JzmVzswKjn0dcnfBGZ0EAtTOHXWzqJFf5U8fCBl4Bx4KvA3c0LxzAMYwtXP3o1h+94OAtnxevK+v41UT65qnLpfZfyh5f/ADChLBMmdswsVUoM5gYnbFdfllmd8Rsz8aqilWp/nnaRysMXkTkicgVwivvSd4ApOVvWMIzuZrw0zkX3XMT1T16faPtCubb2PWpy0wtjL/DNh7/J7178HbvP2Z3d5+w+YZuB7EDNsodJFb5XrZOXiAVQUvT6aSapbi2quk5ELgIWAS8CrwJ+1IK4DMPocTx1Xd+XPoz62a1RCt/z2c876DzetNubArfJSnZij/uI1gpev3wv8XvKPwh/N8+gbw2topHvEmcDT6nq/wH3NjkewzAMYEuiT7rYd6FcqLFRomrd/atjhZHJZALbI9dTr/CTJPw0vX6aSSMJ/2XgvSKyF/Ag8ICq3t/csLqbRYsWMXPmTLLZLLlcjhUrVnQ6JMOYdkxa4Ud0pPRP0gojK9kJ/XGStFbw/o1U+ClmAjeT1AlfVS8UkVuBPwBLgCOAnkr4ALfffjvbbrttp8MwjGmLl+g3Fjcm2r4+IUctI+ifpBVGRjITLJ1AhZ+pVfiq6rzuKv8gkk4MazapE76IfAbIAg/gqPufNzkmwzCMqpWTxtLxV+lEDdomUfg5ydUk/Ng6/Eqtwq9fGctPmm6ezaQRhf9JEdkOOAA4RUR2U9V3Nz+0eO78nz/w4tPJ7v5J2XanGRz+1j0jtxERjjvuOESE97znPZxzzjlNjcEwjMYsnXqFH9Zz3t87P4ykCr/e0vGUfpTCn0oePsB7cLpd9uRM2bvvvpv58+ezZs0ajj32WPbee2+OOGJCHzjDMCaBNykqacKvH7Tty/bxyuZXQreFaIWfkUyNhx+m8KHW7/f+jVT4Kdo3N5NGE/63gPe5PXGuVtUHmhdScuKUeKuYP38+APPmzeOkk07innvusYRvGE2maumUklk6xUqxpsQxUZVOhMLPZrK1q1iFKHxwEr5Xf+8p/UiF79Xht3nQttH2yP+Ic7PIAV9uXjjdz+joaHWlq9HRUW655Rb222+/DkdlGNOPtJZOvYefpA4/qizTX4evqo7CDxnkzWayVCq1g7aJ6vCniMJ/AtgD+LGq/r8mxtP1vPDCC5x00kkAlEol3v72t3P88cd3OCrDmH54iX5zeXOiFgQTPPyIjpRJ+t34bRpPvXuLndTj9/uTlGV6NyZvsla7aDThPwI8DZwtIv+uqgc1MaauZtddd+XBBx/sdBiGMe3xK/vR4iiz+2dHbl9fhx9Vlpmk340/iceVcfpvDmnKMru+SsdlT2AEuAxnIpZhGEZT8ZdjjhXH4hN+uThh0DbM0kk08SqTXOH77Z80ZZntrtJp1MPfG2ey1bm4688ahmE0E//ygkl8/EKlMHHiVYiCTtKzPivZquUSp/D93waSlGV2SuE3mvDnAP8EnAdsalo0hmEYLjWWTik+4U9ojzxJhe8vy/QSf1SVzlTopdNowv8MzoDtSqASt7FhGEZaxopjzOqbBSRU+OWJCr9UKdXU0nukHbSN295fwpmoW2aHZtomSvgikhWR50Tk7wBUdbWq/sx9fn4rAzQMozcZLY4yb2gekKy9QrE8UeFDsIquXx0riGxmiy8fp/CDLJ1uVPiJBm1VtSwiDwO7TfaEIpIFVgDPqOqJkz2eYRjdw/rCer644os1CXrJvCWcsfiM1MfaWNzIXjP24vF1j3PFw1dw1zN3cd5B5wWuTlXRCiUtTVD44CT3/mx/zfb19k8QWckmXpg8IxluePIG3rbX21J1y/zCii9wzMJjWDBzQWQszSKNpTMEnCciK0TkOvfx4wbO+UHg0Qb26xrWrVvHqaeeyt57783ixYv51a9+1emQDKMreHjkYX70xx/x4MiDPPbSY9z97N18/cGvN3SsQrnAwlkLOWj7gxgZH+GHf/whD734UOC2m0rOUKI/sXvPN5UnDjPWr44VRFBtfViVzlE7HQXAjU/dmFjhL5m7BIC7n2nfKrFpEv5rAAGWAif6HokRkQXAG4HL0+zXbXzwgx/k+OOP57HHHuPBBx9k8eLFnQ7JMLoCLzF+4XVf4PqTrue0PU9LPFM26FgD2QG+9fpvcclRlwDhXr7XfsG/GLn3PGif+pr9IPwevqf0w24SHz3oo8zpn0NFK4mqdESELx/tNCnwN2hrNWnq8HdpwvkuwansmRm2gYicg1vquXBh/MLF7Wb9+vXccccdXHnllQD09fXR1xf9h2MYvUJ9shvOD1OsFCfUyCehXCmTyWSqx4HwhO+97k/4nvUTtE99o7Ug/LX1Xh1+lGrPSAZVTaTw/e93ZcJX1T9N5kQiciKwRlXvFZEjI85zGc6ELpYtW6ZRx7z9ystY86cnJxPWBObtvCtHnRU+teDJJ59k7ty5vOtd7+LBBx/kwAMP5NJLL2V4eDh0H8PoFeonHfkT9ZzsnFTHqmil5sYB4YO3XlL3+/uTVfiBtfURg7wZyVChksjDh4lLI7aDRssyG+FQ4E0isgpYDhwtIle18fxNoVQqcd999/G+972P+++/n+HhYS666KJOh2UYXUF9W4GhnKuyE9TR11PWcjVpxh0nSOEP58JvEmHLFfrxN0SrVumEePgAGRyFn6R5mnd86FKFP1lU9WPAxwBchX+uqp45mWNGKfFWsWDBAhYsWMAhhxwCwKmnnmoJ3zBcohR+GipaQdFqgh3MDZKRTLiH7yZ1L8nHnbu+hDOIoEHbKIUvIlQ0vcL3t2BuNakVvoj8VSsCmSpsv/327LTTTqxcuRKAW2+9lX322afDURlGdxDk4UPyZQo96pOmiDCUG4q1dAI9/IBvBfVtGILwL3HoJeWogVjvBqHEN0/ztofuV/ifBa6fzEnddXB/PpljdJL/+I//4IwzzqBQKLDrrrtyxRVXdDokw+gK6gcsJ6PwoVZRD+WHwgdtS+EefqilEzNoG6jwYxK+qlZvDlHN0/zHaqeH30jCj/4UPcCSJUtYsWJFp8MwjK6jXplHVcpEHqcy0RYZzg/HWjoz8jOqr1V9/5AqHf/qWEH4e+lUFX6CQdskZZng3BDq181tNY0M2kZWzhiG0bt4dsZkFX6Qoh7ODccO2voVfjaTZTA3GF6lE+Ph5zI5ZyxBNbHCr2iFitteTBJo44xkutvDNwzDCKNemVcrZRKuS+sRpJKH88ORHr43sOtnKBdsAxXKhURlmeDcfLw6/MjJVEhNHX7UtwEP/+SudmAJ3zCMphE2aNuowvcn8EgPvzhaM2DrEXaTKFVK8WWZPo/dK8+MtXR8M23jqnS8bbrd0nmh6VEYhjEt8OwML9nls3nymXzjg7Z1Cj/Kww9L+IFVOglm2voVflwvHW/7mrLMBOk1J7nuVviqemwrAjEMY+rjKeGkg61heBOd/Io60tIpjVYHaf2EfStIMvHK65tTrpSr8XitHoJoSOFnMm1dyNwsHcMwmka9wofoRB16nACF30xLp1ApJJp4BbUKP3bQ1q3SESS2LNM7Xlcr/F5n5cqVLFmypPqYNWsWl1xySafDMoyuoKpuSea9hxHk4Q/nhilUCoGLhoRaOrngbxfFcrzC987tV+1RLZX9g7ZxJZn+c3T7xCtE5MOqerH7fC93qcOeYK+99uKBBx4AoFwus+OOO3LSSSd1NijD6BKqVTqZ2kSdtpdOkC3in0g1u392zfajxdHAhVHCbjZJFH619YFusXQSlWVqJZGd4x2vawdtRWSOiFwBnCYify8ihwE9u8Thrbfeym677cbOO+/c6VAMoysIaiswnB/m0bWP8sHbPsgdq+9IdJyg/vNhFT///eh/8+cNfw63dOpKQlU1WZVOZkuvmyT9cRpN+PWWzvce+x6fuOsTifZPSyqFr6rrgHeJyBuB54HjgB+1IK5ErLv+CQrPNra4Qhh984eZ81fJVnJcvnw5p59+elPPbxhTmWrzNN+ko2N2PoaR8RHueuYuspksRyw4IvFx6ssyYWLCv/KRKwE4bMfDJhynP9vP5vLmmtc2FDcABN4g/NSUZSawdPxVOkkTfpCl89CLD/Gb536TaP+0NOrhvw6nPPPVQE9W7RQKBa677jpOO+20TodiGKkpVUrcsuqWps/yDJp0dNqep/HDN/2QvbfeO7GXHzRo67VCGC+N12w7VhrjbXu9jWMWHjPhONlMtjpb1mNkbASA7Ya2i4yhZuJVUksH51xJPfxcJletbPJ/nribUaM02h55DvBPOKtXnd20aNIGkVCJt4KbbrqJpUuXst120X80htGN3P707XzkFx/hffu/j79f8vdNO27QoK1HmsHbQIXvll3WJ/ywCh1/HIpWv3W8MOZMJZo7NDcyBr/CT9Qe2R20LWs5UYUOOJ/Pm8XrEfV5JkujCv8zwI/dwdr21RR1Eddcc43ZOcaUpVh2Kl2u+n1z1yCK8rrT1OMHefiepeMvsyyUC5QqpfCEH9CC2FP48wbnRcbgJfySlhK3R/bsnzRVOvUeftgAdDNoKOGr6mpV/Zn7vOcGbcfGxvjpT3/KySef3OlQDKMhNhY3Ao6f/dzG55p23KjVntLU4wfdODxLxz8IG9QH34+nyGssnXEn4W87tG1kDF6lUaXiKHxBEg/aJlX4QVU6o8XRmoVcmklDCV9EvioiV7rPj2tqRFOAoaEh1q5dy+zZs+M3NowuxEv4AC+Ov9i043qJMSjhDeWGas4bRVBZZpClE5fwPRvHn1TXjK1hZt/M2PbI/rLMspZjm6F5K16lUfjeGIOfseIYM/pmhOwxORq1dAqAt3r40U2KxTCMNrGxsCXxvlJ4pWnHjRqw9BS+X22HEdS7ZjDvKvxiCoUfsMjIyNhIrJ1Tv2+5Uo5N4lnJVmfapqrDrxs4D2sT0QwaTfhjwGwRyQMLk+wgIgMico+IPCgij4jIpxs8t2EYk2RDYUP1+frN65t23KgBy+H8MCUtUagUEh0H4i0d73mYBeKfLeuxZnxN7IAt1E280lKiBU28QdvJlGV246DtS8ATwFeBuxPusxk4WlX3B5YAx4vIqxs8v2EYk2BjcWM1ga4vNC/hR9kZaVa/CmpHnM/k6cv01Vg63jeVsEHOwIQ/toZ5Q/EK3z/gW9FKrKWTIVMtAW104lXcIPRkaXSm7SnuS98BliXZVx2875F592GrZxlGB9hY2MgOwzsA8Mrm5lk6UXZGmt74YdU+g/nBWkunFG3pBFXpvLL5Feb0z4mNwW/plCqlyNbI3rnSTrzKZmoHbYNW7momqWfaishFwCLgReBVpJhpKyJZ4F5gd+CrqjphOpmInAOcA7BwYSK3yDCMlGwobmCrga0YHB1susKPS/hJKnXC+s8P5YZqFL53rLiE76noilYYL40nSqieoi9VSomSeCODthnJVEtkIX5MYrI0YumcDeyqqveq6hWqen3SHVW1rKpLgAXAwSKyX8A2l6nqMlVdNnduvM9mGEZ6NhY2MjM/k1l9s9qn8HNNUPi5wVRlmfUJf1NpE0CiQVH/vuVKfJVOhkxDg7Z+S6cbE/7LwHtF5BIReZeIHJD2AG5Pnp8Dxzdw/o7zpS99iX333Zf99tuP008/nU2bNnU6JMNIxcbiRmb0zWB2/+ymKvwoJZzKww9ZRGQoNxSY8MMUe32VjrdvkoRfX5YZZ+lkM9lqe+RGu2XGDUJPlkZWvLoQeDdwAfAUEN8JCRCRuSIyx30+CPwl8Fja83eaZ555hi9/+cusWLGChx9+mHK5zPLlyzsdlmGkYkNhAzPyM9qr8D0PP0Gr5LAFRwbzg4wXay2dvkxfaOfLeoXv7euVeEbhKXrPl4+tw2eLpdNowu8qDx9ARD4DZIEHgAdU9ecJd90B+Lbr42eA/1HVG9KevxsolUqMj4+Tz+cZGxtj/vz5nQ7JMBKjqowWR5nZN5PZ/bP50/o/Ne3YUf51Kg+/Ety7Zig3xJqxNdWf40oY6wdtG1L4lXKiOvxG2iPXl2W22tJJnfBV9ZMi8kmcpH2KiOymqu9OsN/vgNT2TxQ33XQTzz//fDMPyfbbb88JJ5wQ+v6OO+7Iueeey8KFCxkcHOS4447juON6brKxMYUZL41T1jLD+WFm9c1q+qBtVB0+pLN06pNs/aDtaCm674yXeL3JXt6+cbNs/ftWZ9omqcMnXR1+NpOt6ZYZNwg9WRqtw/8WsBjYBvha88Lpfl5++WV+/OMf89RTT/Hss88yOjrKVVc1twGVYbQSb9KVp/CbOfEqicJvallmcZQZ+fA2BBMUvrtvoiqd+pm2cYO2kqFcKVMhRWuFEEunaxS+yz/itFfIAZeS0MdvNlFKvFX87Gc/Y5dddsGrIDr55JP55S9/yZlnntn2WAyjEbx+Np6Hv6m8iYvuuYjzD57YB3G8NM6n7v4U64vhN4W37fk2jlp4FBDt4ecyOfqz/YksnSiFXzPTNmQtWw9v/3qF38igbZLWCopSqSRvntZuS6dRhf8EMIDTIrkjyb5TLFy4kF//+teMjTk9QW699VYWL17c6bAMIzHVhN83g9fOfy0AVz96deBiKE++8iQ3rbqJ1RtWs37z+gmPFc+v4CdP/aS6fZydUV9WGUZ1wZE6VT2YG2S8NF5N4HGthL3EW+/hJ7J03G6Z5UrC1greoG1Khe8vy9xU3kQuk4tcWWsyNHrUR4CngbNF5N9V9aAmxtTVHHLIIZx66qksXbqUXC7HAQccwDnnnNPpsAwjMX4Vue+2+3LusnP5woovMFYaY2bfzJptvUlBHzv4Yxy646ETjnXKdafUTByKW+1pIDdQrYWPIrQsMz9EWcsUKgX6s/2MFkdZMHNB6HEmlGU2YOmUtUylkqC1gr89Mo0p/EK5QF8menH1ydBowt8Npx7/MvffnuLTn/40n/609X4zpib1A4P++vgJCb/iJPOwssd8Jl/TDC1utaeB7MCENWaDCCvLHMgOAE55pWcPJanSqZZlNjBoWy3LTFClo6qJ/H6P+iUOi5Ui+Wz04uqTodGE/7Sq3iYiOwBrYrc2DKNrqCp8d3KP92+Qt14oO8m8LxusOvuyfdWbAjhLCbZS4XtxeDeZuFbC9Qnfs3S8G0cU3kSrai+dGJtFRJw1bUnePK1+icNWK/xGPfzjRWQB8HXgS02MxzCMFlM/uSeqeiaJwvdbOuVKtMLvz/azqRyf8IOWOPTHUSgXUNXECt9fpTOQHUikwL19S5qsl47XLbOs5cA1fcPO4ffwi5Vi6LVuBo0m/DlsWcQ8/vtZk0mygEIn6fb4jN6mOn2/3tIJmAHrKfwwmyGfzVe3geiyTEiu8MPKMv0Kf7w0jqKpLZ2ks1irM20rlWTtkcXXHjnT2EzbYrkY+m2qGTRjEfOJQ/stZGBggLVr13ZtUlVV1q5dy8BA/FdGw+gEo8VRspKlP9sPJFP4YTZDX6bW0qkQPct0IDuQSOGHlWV6ybBYLiYqYQyydJL49/59y1pO3x45YWrNZmpXvCpWii2r0IGEHr7bDmE18C+qermqrnZ/bvsi5gsWLGD16tWMjIy087SpGBgYYMGC8MoBw+gkXimjZ71EtTyIVfgBg7ZRyW7SCt+98RTKhUR9Z+qrdMZL44kTfn0dfqyl4w7aplrTtn4BlEqhpQo/UcJX1bKIPIxTndNR8vk8u+yyS6fDMIwpS33/mUkp/GzfhLLMKDsjsYcfUqXj3XgKlcKWxU8iOktOUPjFseSWTt1M26SDtpPppVMst9bDT/PdYQg4T0SOBZ51X1NVfXPzwzIMo1WMFcdqkqRX5dLooG0ahT+YG2RzKUFZZsVR1PUDwH6F7yXxtM3TGrF0EpVlkr55WlcqfJfXuP8udR9gSxQaxpSjXuEP5gbJSCY44bvqPaos05sVC9GtFSC5wg87TtXDrxSr9fzDfelaK2w9sHXs+WFLhVDS9siZTAMJ313iUFUREYqVYmRvoMmSJuGbj2IY04D6DpMiMqFHjYen3iMVfn2VTkRi9Dx8L8GFEaao/YO23o0jytKpb60Q107ZT7Uss1JK1h65AYXvt5yyknWqdPo7qPBFxFtYNlDN+95fp6rNa7tnGEZLGCuOMXewdvnQofxQpKUT5l/ns/naKh2tRFazDOYGUbTaGiGM0ISf2VKWmaRKp6rw3fS1fvN6ZvXNCt0+aN+KVhL10qnOtE3THtl3jizZrphp+22cZB/VHEKBK4HvNCEmwzBaSJDKHc4PByb8QrlAPpMPVeOewvcUexJLB5y1ZaMSflilS3XQtpws4VcVvruIyYbiBmb1J0v4IoIgTi+dBHX4/kHbNFU64Nzg8uSr17tVxCZ8VT2qZWc3DKPtjBYntiMYzg0HlmUWK9ETgfoyfdVFP3KSi034Azlnfsqm0iZm988O3a5cKQdW+9QrfEEiB2H9CtpbB2B2X/h5J+yfyW7ph59ixauk7ZH9CR+cz9WNM20Nw5iCeO0IZvTVDgzGKfww/Iob4tsjez1s4hqoxXn4nsL3zycIouqRU6mu7JVU4YM7EzZhe2SvOinJzaE+Pi/hd+tM29SIyE4icruIPCoij4jIB9t1bsMwHAqVAiUtTbBBhvJDga0VipViZDMv7z3Px0/SHhmoWaYwiDBbxD9oO1YaixywBV8S1nJ1sfY0Ct+rk0/aWgGc3jtpqnSAasfMbirLnCwl4COqep+IzATuFZGfqurv2xiDYfQ01dmp9ZZOPsTSKUcPInrq30v4ce2RPd8+icIPLMuss3TiJlF5tlClMgmFr47/n6S1AjjXIu2grdcxs1QpTQ9LR1WfU9X73OcbgEeBHdt1fsMwYLQQPNAZaunEeMp+xQ3xzdM8vz2uvULYcbxqoUK5wObS5uo3hjA8hV9j6SSs0vHOV6qUHEsnwaAtbJk0loT6mcCtHrTtiIcvIouAA4DfBLx3joisEJEV3dwvxzCmItV2BAkTfpzCryZgt14/bsCyWqUTM/mqVAm2RUSEvkwfhUqBosa3IfAP2lYtnYjB4nq85nBJkri/br+RssxyxZnR28qyzLYnfBGZAfwQ+FBQ3b6qXqaqy1R1mbdQuGEYzSGs4dhwfphipVgziQpcTznKw0+p8P1VOlFUtBJa++/170myKEnV0tHGFH4+m2dzeTOKxls6bPHwE5dlZrZU6cT1LWoGbU34IpLHSfZXq+qP2nluwzAIrV0Pa6AWV5ZZXZDEp/CbVaUTdpy+bB+FciFZwseX8DevZzA3mGpQNJ/JV29OSQdtGyrLrJRj+xY1g3ZW6QjwTeBRVb24Xec1DGML1fVs66pbwhqoxXVv9Pe2gQRlmSmqdMKO4zVsK1aKiQdSy1rmlcIrE9bsjaMv27cl4Seow/dopCwzbjnJZtBOhX8o8A7gaBF5wH28oY3nN4yepxGFn6RKx0tWSWfaxir8iFp2v8KP87s9Va6qrN+8PpV/D87n825OcUncr+ob8fDbofDbVpapqncR3Z7BMIwWE+XhAxMaqMVVjbSqSieqO6U3kJpkFSphS/O0VwqvpPLvwVX45XSWTv3zKGo8/JjOpM3AZtoaRg8RVaUDIR5+xCBifR1+nML3to9T+FE3jjQevl9Br9u0jq36t4rcPijepArfvw5A2rLMaefhG4bRecaKY/Rn+yckyupC5nUJv1AuJLN0Eg7aigj92f4J1UD1RI0FeAunJ0n4ns1S0Qovb36ZrQZSJvxsPrGHP1lLp9qKusPdMg3DmCaE9YMPW9c2VuG7yclv6cQlu75sX8O9dIAtdfgJFvyuzmStlFi3eR1z+udEbl9PPpOv3gRjxwt88aZW+FqmXC5Xz9kqTOEbRg8R1CkTtlTtBA7aRnn4vlYHEF+lA87AbaJB2zAP31eHH5ccvVhe3vwyFa0kXu2qeq5MX/WaRLVz9p8LklfpeGMQ/kFb8/ANw2gKY8WxSIUfZOlEtkfOpmueBiSydKK+KXgKP1EdvnuMteNrARqydLxOlt4cgjAasXS8iWGlSql6TUzhG4bRFEZLwZZOPpt37ItSOoVfX5YZ1zwNklk6UYO2fg8/aWuFtZsaS/h+OytOeTdUpRNQljltZtoahtFZotZ0re+Yqaqxg7ZeEvQWMleao/Cj2hN4VTpJPHzv5uMp/LSWjv+GEteoTXxV541MvPLGQaZVLx3DMDpHmKUDExuolbWMoukUfqX1Cj+NpVNV+J6lk7Is06/q03j4knDKkX/FK1P4hmE0lSiFX7+QeZKp/kF1+EkU/mR76RTLRUoab+l469K+OP4i0ICH7zt+XML3f+64SVr1+9SUZZqHbxhGM4haNKR+XdskE4FEhFwmt6W1AsnKMmPr8COqdPKZfHVGcJzCB0d5K8pwfjh1BYzfXolL+P5vNkkVvjdoW660x9KxOnzD6BEqWnGWBYywdLye8arKx+78GBBvMXitDlQ1UR1+f6afzZVahf/b53/Lf9z/H9WFQJ7Z+Ax7bLVH8Pl8llDShF/Wcmo7B2o/e+xiK5Moyyxr2RS+YRjNw2sRELYO7EBuoNo3ZnN5M3c+cycAB+9wcORxvaoZRYH4CpX+3MRB2188/Qt+N/I7hnJDDOWGWDpvKSfsckLw/j6lHddLB7Yk3/qF25OQRuH7WyskuRH5tytpqS29dEzhG0aPsLGwEZjYOM3DU+qwZSLVR5d9lF1m7xJ53OqqUG69eiZGRwZ5+KOlUeb0z+Gy4y6L/Rz+hJjE/vCslkYGQ/37pLF0ktoy1TGQcrF6zW3ilWEYkyascZpHPpuvqsw0ajOfyVOsFKt2TNyAZVBZZtRgcj3+CVBJlLSn8JOqbj+NDtomvbn4J65Z8zTDMJpGdfGTsITvLiwCyQZsPbyqGS/hN9JLJ6pctJ7+XDpLp6rwG1DO3j65TC7xIuaQPGn7F2Uvlp0FXZJO2moES/iG0SOELX7i4a+eSaPwc5kchUphS8JvwNLZWNwYajUF7e8/dxye8m4k4XvHj1P3UHujS2rp1Cv8VlbogCV8w+gZwhY/8fCsGSBVxUhftq/G0kmi8EuVEuVKufpaKoXvS75J4vPiacQq8RJyooTvS6dJz+WfuBa32EwzaOeatt8SkTUi8nC7zmkYxhaqCj+kSsezZmDLpKskijOfcap0kiZ8L3l6NxUvtrC46vF7+GkSfiODtt7x4xqnQa2lk/TbhBdTseIM2k6bhA9cCRzfxvMZhuEjiYdf0lLq9VU9hV+t0kma8Mu1CT+xpZNLZ+lUFX4DdomXuJMk8BpLJ2HizmayZCRT9fBbWaEDbUz4qnoH8FK7zmcYRi1xVTp+PzlJWwWPfCZfM2gbN+nIO6bfxx8rjTEjn6xOPq2HPxlLp6rwYyZd+c8D6cYL+jKOxTXdFH4iROQcEVkhIitGRkY6HY5hTBtGi6MIUl1IvB5/TXgqhe82M/MSflzzNC9hewm/XCkzXhpvyMNPM2jbcg+/AYXvbes1g5s2Cj8pqnqZqi5T1WVz587tdDiGMW3wBkbDErJ/fdo0nRu9+v20Ct/7FuH1xUlq6aStw696+A0kU++apE34acYLvOs3rQZtDcPoLHE+uX992jSNvDyFmnjQNlOr8OPKReupmWnb4iqdNAnf3zAtzXiBd/2sLNMwjKYRN5vVvz5tdZp/EoXvlnM2OmgbN5hcj99PT6LwvUQ8mYlXaT38NDcXb9B7Wil8EbkG+BWwl4isFpGz23VuwzDc5Q0jSh8Dp/knUJzehC3VZM3T6gdt0yr8tB6+981jMgo/yc2iprVCipuLV9ZarBRbuvgJtLF5mqqe3q5zGYYxkbjJTTWNvFIsqN2owq8mfLd6aCjXGg/fi2tSCj9lHX6jCn92/+zUMabBLB3D6BHiPPwghZ8kSXoTtjyFn3bQNq3C9yf5vMQnVm+93VZ7+JOp0vEqo1qt8C3hG0aPEOfh+xt5TUbhpy3LTOvhp21D7MU1mYlXaQdtky5xCFuuX7FSnD4evmEYnSXO0vFP80+j8PPZPGUt8w+3/QMQr/C95Hn+nefz9IanY3v8RJHI0nF79kymtYJ/dm8YjXa59BaQKZatSscwjCaRtCzTSz6QfOIVOMsSDmQH2H/u/pHbzxuax8HbO6toPTTyUGpLx0+S9sglbdzS6cv28f4l7+e4nY+L3bbRhO8tINOTM20Nw2g+3opKkVU6dQo/aW92f5K68PALmTsUPWEym8ny2cM+CzgDtqPFUTKSSTQwWk8qhd/gLNb37v/e0PV1/TSs8N06/EK50HszbQ3DaD5JVLR/pm2hXEjd0x2S2zJeHGPFMWdh9Vz4DOAoUnn4rVbPjSp8d9DbBm0Nw2gKcY3TwFelU043gOjfLqkt45VgjhZHU3XKrCeJpeMp/Nb7440r/GKlaB6+YRjNIcnAaLUOP6Wf7Ff4SXvaZzNZBnOD1YTfiH/vHSeOyXj4aWjkGwpsWfKxpCVT+IZhTJ4kpY/+Qds0fnIjCh8clT9aHE212tVkaLk/3qClk8vkqjdkU/iGYUyaNB5+2ppwf5JKY80M54cZK45NytJJQzd7+OOlcaANMbb06IZhdAVVSyeifYF/Bmya1ZcaVfjD+WFGS6NsLG5MvPjJZGi5XTKJKp2g563AEr5h9ABewp/RF55Y68syG/Hwk5RJegzlzdKB2rhaHWPbmqcZhtE5vEVGogZV/eurpinLbFSVDueHGRkbYbQ0mrhxmse3j/82j697PNU+LR+0pbFB23YqfEv4htEDJJ3N6q2vmqYmvFGrZDg/zKriqoaqdJZut5Sl2y1NtU/LB0QnMdO2+twmXhmGMVlGi6PkM/nYpFed9ZmiLLPRRDqcH2bd5nWUKqW2WDrdOmjrv37m4RuGMWmSqmhvfdU0g7YNK/zcMOsL64HGGqelpVs9fH+SN4VvGMakSTowWrO+agMzbdPgj6enFb4vrjSD3o1gCd8weoCkte7e6ktpFtRu1NLxx9OOhN/qZNrooG1Nlc50mmkrIseLyEoReVxEzm/nuQ2jl4lbz9bDW181zYLaTVH4CVsydDNNqcOfLjNtRSQLfBU4AdgHOF1E9mnX+Q2jl0lq6fgVfmIPv0Hf2R9POzz8VtOUOvwWK3zx1qFsNSLyGuACVX29+/PHAFT1wrB9li1bpitWrEh9rm+c93k2tfa6GYZhtIyhgnD25z/a0L4icq+qLgt6r52Wzo7A076fV7uv1SAi54jIChFZMTIy0rbgDMMwpjvtnHgVNKIx4euFql4GXAaOwm/kRO/+/HmN7GYYhjGtaafCXw3s5Pt5AfBsG89vGIbR07Qz4f8W2ENEdhGRPuCvgevaeH7DMIyepm2WjqqWROQDwP8BWeBbqvpIu85vGIbR67S1eZqq3gjc2M5zGoZhGA4209YwDKNHsIRvGIbRI1jCNwzD6BEs4RuGYfQIbWut0AgiMgL8qcHdtwVebGI4rWAqxAgWZzOZCjGCxdlM2h3jzqo6N+iNrk74k0FEVoT1k+gWpkKMYHE2k6kQI1iczaSbYjRLxzAMo0ewhG8YhtEjTOeEf1mnA0jAVIgRLM5mMhViBIuzmXRNjNPWwzcMwzBqmc4K3zAMw/BhCd8wDKNHmHYJv9sWSheRVSLykIg8ICIr3Ne2FpGfisgf3X+38m3/MTf2lSLy+hbG9S0RWSMiD/teSx2XiBzofr7HReTLIhK00E0zY7xARJ5xr+cDIvKGDse4k4jcLiKPisgjIvJB9/Vuu5ZhcXbb9RwQkXtE5EE3zk+7r3fN9YyIsauuZSCqOm0eOG2XnwB2BfqAB4F9OhzTKmDbutc+D5zvPj8f+Jz7fB835n5gF/ezZFsU1xHAUuDhycQF3AO8BmdFs5uAE1oc4wXAuQHbdirGHYCl7vOZwB/cWLrtWobF2W3XU4AZ7vM88Bvg1d10PSNi7KprGfSYbgr/YOBxVX1SVQvAcuDNHY4piDcD33affxt4i+/15aq6WVWfAh7H+UxNR1XvAF6aTFwisgMwS1V/pc5f73d8+7QqxjA6FeNzqnqf+3wD8CjOWs3ddi3D4gyjU3Gqqm50f8y7D6WLrmdEjGF05FoGMd0SfqKF0tuMAreIyL0ico772naq+hw4/xGBee7rnY4/bVw7us/rX281HxCR37mWj/fVvuMxisgi4AAcxde117IuTuiy6ykiWRF5AFgD/FRVu+56hsQIXXYt65luCT/RQult5lBVXQqcALxfRI6I2LYb44fwuDoR738CuwFLgOeAL7qvdzRGEZkB/BD4kKquj9o0JJ5Oxdl111NVy6q6BGfd64NFZL+IzTsSZ0iMXXct65luCb/rFkpX1Wfdf9cA1+JYNC+4X+dw/13jbt7p+NPGtdp9Xv96y1DVF9z/bBXgG2yxvDoWo4jkcZLo1ar6I/flrruWQXF24/X0UNV1wM+B4+nC61kfYzdfS4/plvC7aqF0ERkWkZnec+A44GE3pne6m70T+LH7/Drgr0WkX0R2AfbAGdRpF6nicr9abxCRV7vVBX/j26cleP/pXU7CuZ4di9E95jeBR1X1Yt9bXXUtw+Lswus5V0TmuM8Hgb8EHqOLrmdYjN12LQNp5YhwJx7AG3AqEJ4APtHhWHbFGZ1/EHjEiwfYBrgV+KP779a+fT7hxr6SFo7YA9fgfO0s4iiNsxuJC1iG84f9BPAV3NnbLYzxu8BDwO9w/iPt0OEYD8P5Gv474AH38YYuvJZhcXbb9XwVcL8bz8PAJxv9P9OqOCNi7KprGfSw1gqGYRg9wnSzdAzDMIwQLOEbhmH0CJbwDcMwegRL+IZhGD2CJXzDMIwewRK+0ROIyBwR+Xvfz/NF5ActOtdbROSTIe9tdP+dKyI3t+L8hhGGJXyjV5gDVBO+qj6rqqe26FznAV+L2kBVR4DnROTQFsVgGBOwhG/0ChcBu7l9yv9dRBaJ22dfRM4Skf8VketF5CkR+YCIfFhE7heRX4vI1u52u4nIzW4jvDtFZO/6k4jInsBmVX3R/XkXEfmViPxWRP61bvP/Bc5o6ac2DB+W8I1e4XzgCVVdoqofDXh/P+DtOP1PPguMqeoBwK9wpryDsxj1P6jqgcC5BKv4Q4H7fD9fCvynqh4EPF+37Qrg8AY/j2GkJtfpAAyjS7hdnT7xG0TkFeB69/WHgFe5XSZfC3zftyhRf8BxdgBGfD8fCpziPv8u8Dnfe2uA+c0J3zDisYRvGA6bfc8rvp8rOP9PMsA6dVriRjEOzK57Lax/yYC7vWG0BbN0jF5hA87Sfg2hTu/4p0TkNHC6T4rI/gGbPgrs7vv5bpyurTDRr9+TLR0VDaPlWMI3egJVXQvcLSIPi8i/N3iYM4CzRcTrfhq0fOYdwAGyxff5IM7CN79lovI/CvhJg7EYRmqsW6ZhNBkRuRS4XlV/FrPdHcCbVfXl9kRm9Dqm8A2j+fwbMBS1gYjMBS62ZG+0E1P4hmEYPYIpfMMwjB7BEr5hGEaPYAnfMAyjR7CEbxiG0SNYwjcMw+gR/n91QLDU7CZfmAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "swiftdiff['rmag'].sel(id=plidx).plot.line(ax=ax, x=\"time (d)\")\n", + "ax.set_ylabel(\"$|\\mathbf{r}_{swiftest} - \\mathbf{r}_{swifter}|$\")\n", + "ax.set_title(\"Heliocentric position differences \\n Planets only\")\n", + "fig.savefig(\"symba_swifter_comparison-8pl-16tp-planets-rmag.png\", facecolor='white', transparent=False, dpi=300)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "swiftdiff['vmag'].sel(id=plidx).plot.line(ax=ax, x=\"time (d)\")\n", + "ax.set_ylabel(\"$|\\mathbf{v}_{swiftest} - \\mathbf{v}_{swifter}|$\")\n", + "ax.set_title(\"Heliocentric velocity differences \\n Planets only\")\n", + "fig.savefig(\"symba_swifter_comparison-8pl-16tp-planets-vmag.png\", facecolor='white', transparent=False, dpi=300)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "No handles with labels found to put in legend.\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "swiftdiff['rmag'].sel(id=tpidx).plot.line(ax=ax, x=\"time (d)\")\n", + "ax.set_ylabel(\"$|\\mathbf{r}_{swiftest} - \\mathbf{r}_{swifter}|$\")\n", + "ax.set_title(\"Heliocentric position differences \\n Test Particles only\")\n", + "legend = ax.legend()\n", + "legend.remove()\n", + "fig.savefig(\"symba_swifter_comparison-8pl-16tp-testparticles-rmag.png\", facecolor='white', transparent=False, dpi=300)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "No handles with labels found to put in legend.\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "swiftdiff['vmag'].sel(id=tpidx).plot.line(ax=ax, x=\"time (d)\")\n", + "ax.set_ylabel(\"$|\\mathbf{v}_{swiftest} - \\mathbf{v}_{swifter}|$\")\n", + "ax.set_title(\"Heliocentric velocity differences \\n Test Particles only\")\n", + "legend = ax.legend()\n", + "legend.remove()\n", + "fig.savefig(\"symba_swifter_comparison-8pl-16tp-testparticles-vmag.png\", facecolor='white', transparent=False, dpi=300)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "swiftdiff = swiftdiff.rename({'time (d)' : 'time'})" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'px' (id: 16)>\n",
+       "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
" + ], + "text/plain": [ + "\n", + "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" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "swiftdiff['px'].sel(id=tpidx).isel(time=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "swiftestOOF", + "language": "python", + "name": "swiftestoof" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/symba_swifter_comparison/8pl_16tp_encounters/tp.in b/examples/symba_swifter_comparison/8pl_16tp_encounters/tp.in new file mode 100644 index 000000000..ae7796698 --- /dev/null +++ b/examples/symba_swifter_comparison/8pl_16tp_encounters/tp.in @@ -0,0 +1,49 @@ +16 +101 +-0.09859055695785905182 0.2975290300646933339 0.03335708456145129036 +-0.029750083068855306956 -0.0078122718370876240157 0.0023293874953380202045 +102 +-0.09863667837052235432 0.29748290865203008693 0.03335708456145129036 +-0.034957182012873608268 -0.0078122718370876240157 0.0023293874953380202045 +103 +-0.6439245854659476631 -0.32479782779646521051 0.032702713983447248558 +0.0153169432007213678765 -0.018153139924556138673 -0.0007667345025597138231 +104 +-0.6440390060468921263 -0.32491224837740956266 0.032702713983447248558 +0.002622475790030579998 -0.018153139924556138673 -0.0007667345025597138231 +105 +0.59427697124197276235 -0.8232523083817967491 3.7129329104855261984e-05 +0.020564990514662154913 0.010004295439859960809 -5.226292361234363611e-07 +106 +0.5941565154300937346 -0.82337276419367577684 3.7129329104855261984e-05 +0.0067761100461144049487 0.010004295439859960809 -5.226292361234363611e-07 +107 +-1.5926895092930311026 0.48169594448240382611 0.049163460846716633412 +-0.00044929323243133797994 -0.01219974682608557931 -0.00016910795626524249315 +108 +-1.5927535941205388514 0.48163185965489618834 0.049163460846716633412 +-0.006608251428879123937 -0.01219974682608557931 -0.00016910795626524249315 +109 +4.119750673485228276 -2.8866333472175926822 -0.080165336328135106125 +0.041127620144391897894 0.0065414198811065849687 -0.00012215100047356211078 +110 +4.118428875469033912 -2.8879551452337870465 -0.080165336328135106125 +-0.032636814258902961672 0.0065414198811065849687 -0.00012215100047356211078 +111 +6.3634605491076454697 -7.64917730379279881 -0.12023019299387090186 +0.026096616095614821179 0.0035613826786502411278 -0.00022039988214595340028 +112 +6.3623595643973844815 -7.650278288503059798 -0.12023019299387090186 +-0.01812972167145235694 0.0035613826786502411278 -0.00022039988214595340028 +113 +14.814394441298382787 13.052280053388562564 -0.14347198499748289868 +0.010469662145386185101 0.0027742356008832688187 4.416821810149910185e-05 +114 +14.813914925323977911 13.051800537414157688 -0.14347198499748289868 +-0.015719864931937603536 0.0027742356008832688187 4.416821810149910185e-05 +115 +29.565157420731857485 -4.579098772788029237 -0.5871109926822926095 +0.014900134286357700347 0.003128345390031967918 -7.5036135696161668576e-05 +116 +29.564691895839423808 -4.5795642976804593616 -0.5871109926822926095 +-0.0139711373401985618214 0.003128345390031967918 -7.5036135696161668576e-05 diff --git a/examples/whm_gr_test/init_cond.py b/examples/whm_gr_test/init_cond.py index 57b0fb534..8d197c6f4 100755 --- a/examples/whm_gr_test/init_cond.py +++ b/examples/whm_gr_test/init_cond.py @@ -13,7 +13,7 @@ sim.param['DU2M'] = swiftest.AU2M sim.param['T0'] = 0.0 sim.param['DT'] = 0.25 * swiftest.JD2S / swiftest.YR2S -sim.param['TSTOP'] = 100.0 +sim.param['TSTOP'] = 1000.0 sim.param['ISTEP_OUT'] = 1461 sim.param['ISTEP_DUMP'] = 1461 sim.param['CHK_QMIN_COORD'] = "HELIO" diff --git a/examples/whm_gr_test/param.swifter.in b/examples/whm_gr_test/param.swifter.in index 6addd694c..789250f41 100644 --- a/examples/whm_gr_test/param.swifter.in +++ b/examples/whm_gr_test/param.swifter.in @@ -1,6 +1,6 @@ ! VERSION Swifter parameter file converted from Swiftest T0 0.0 -TSTOP 100.0 +TSTOP 1000.0 DT 0.0006844626967830253 ISTEP_OUT 1461 ISTEP_DUMP 1461 diff --git a/examples/whm_gr_test/param.swiftest.in b/examples/whm_gr_test/param.swiftest.in index c9b7462f0..ace6f3cad 100644 --- a/examples/whm_gr_test/param.swiftest.in +++ b/examples/whm_gr_test/param.swiftest.in @@ -1,6 +1,6 @@ ! VERSION Swiftest parameter input T0 0.0 -TSTOP 100.0 +TSTOP 1000.0 DT 0.0006844626967830253 ISTEP_OUT 1461 ISTEP_DUMP 1461 diff --git a/examples/whm_gr_test/pl.swifter.in b/examples/whm_gr_test/pl.swifter.in index e0ef4e881..782e57140 100644 --- a/examples/whm_gr_test/pl.swifter.in +++ b/examples/whm_gr_test/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.0014751243077781048702 +1 6.5537098095653139645e-06 0.0014751234419554511911 1.6306381826061645943e-05 -0.33206272695596028566 0.07436707001147663254 -0.02438290851908785084 --4.2340114788918336805 10.486553514018327622 1.2453138107251555947 -2 9.663313399581537916e-05 0.006759104275397271956 +0.13267502226188271353 0.2786606257975073886 0.010601098875389479426 +-11.331978934667442676 4.8184460126705647045 1.4332264599878684131 +2 9.663313399581537916e-05 0.00675908960945781479 4.0453784346544178454e-05 --0.7188115337296047125 -0.0118554711069603201795 0.041316403191083782287 -0.07826338813583945357 -7.419533988988633545 -0.10634201014368884618 -3 0.000120026935827952453094 0.010044787321379672528 +-0.69398700025820403425 -0.19235393648106968723 0.03740673057980103272 +1.9245789988923785786 -7.1528261190002948057 -0.20922405362759749996 +3 0.000120026935827952453094 0.010044837538502923644 4.25875607065040958e-05 -0.35677088372527121507 -0.95189300879814897627 4.4027442504036787155e-05 -5.7819217550992820422 2.18192814489641851 -0.00012230072278352209966 -4 1.2739802010675941456e-05 0.007246743835971885302 +0.49463573470256239073 -0.8874896493821613497 4.051630875713834232e-05 +5.386704768180099809 3.0357508899436080915 -0.00016218409216515533796 +4 1.2739802010675941456e-05 0.0072467236860282326973 2.265740805092889601e-05 --1.5233712071242269115 0.6723825347339112968 0.051459143378398922164 --1.8728417739956807141 -4.239719661832373223 -0.042909557750301418264 -5 0.037692251088985676735 0.35527126534549128905 +-1.5655322071100350456 0.56626121192188216824 0.050269397991054412533 +-1.5477080637857006753 -4.370087697214287981 -0.05361768768801557225 +5 0.037692251088985676735 0.35527094075555771578 0.00046732617030490929307 -4.049944927347420176 -2.9910878677758190314 -0.078187280837353656526 -1.6060801375519682711 2.349356876761497338 -0.045690062807172619064 -6 0.011285899820091272997 0.4376527512949726007 +4.0891378954287338487 -2.9329188614380639066 -0.07930573161132697946 +1.575024788882753283 2.3719591091996699917 -0.045089307261129988257 +6 0.011285899820091272997 0.43765464106459166412 0.00038925687730393611812 -6.298929503477405767 -7.706413024510769816 -0.11669919842191249504 -1.4661378456572359413 1.2872251175075805794 -0.08070991686100478242 -7 0.0017236589478267730203 0.4695362423191493196 +6.3349788609660162564 -7.674600716671800882 -0.11868650931385750502 +1.4598618704191345578 1.2948691245181617393 -0.080593167691228835176 +7 0.0017236589478267730203 0.46956055286931676728 0.00016953449859497231466 -14.856082147529010129 13.007589275314199284 -0.14417795763685259391 --0.9554310497290159123 1.0161753499437922057 0.016099529164307530124 -8 0.0020336100526728302319 0.7812870996943599397 +14.832516206189200858 13.032608531076540714 -0.14378102535616668622 +-0.9573374666934839659 1.014553546383260322 0.016118112341773867214 +8 0.0020336100526728302319 0.7813163071687303693 0.000164587904124493665 -29.55744967800954015 -4.629377558152945049 -0.58590957207831262377 -0.17162147939801157335 1.1422848961108499101 -0.027445465472921385952 +29.561664938083289655 -4.6012285192418387325 -0.586585578731106283 +0.17051705220469790965 1.1424784769020628332 -0.027423757798549895085 diff --git a/examples/whm_gr_test/pl.swiftest.in b/examples/whm_gr_test/pl.swiftest.in index 9d49cc3da..10d425453 100644 --- a/examples/whm_gr_test/pl.swiftest.in +++ b/examples/whm_gr_test/pl.swiftest.in @@ -1,33 +1,33 @@ 8 1 6.5537098095653139645e-06 1.6306381826061645943e-05 -0.33206272695596028566 0.07436707001147663254 -0.02438290851908785084 --4.2340114788918336805 10.486553514018327622 1.2453138107251555947 +0.13267502226188271353 0.2786606257975073886 0.010601098875389479426 +-11.331978934667442676 4.8184460126705647045 1.4332264599878684131 2 9.663313399581537916e-05 4.0453784346544178454e-05 --0.7188115337296047125 -0.0118554711069603201795 0.041316403191083782287 -0.07826338813583945357 -7.419533988988633545 -0.10634201014368884618 +-0.69398700025820403425 -0.19235393648106968723 0.03740673057980103272 +1.9245789988923785786 -7.1528261190002948057 -0.20922405362759749996 3 0.000120026935827952453094 4.25875607065040958e-05 -0.35677088372527121507 -0.95189300879814897627 4.4027442504036787155e-05 -5.7819217550992820422 2.18192814489641851 -0.00012230072278352209966 +0.49463573470256239073 -0.8874896493821613497 4.051630875713834232e-05 +5.386704768180099809 3.0357508899436080915 -0.00016218409216515533796 4 1.2739802010675941456e-05 2.265740805092889601e-05 --1.5233712071242269115 0.6723825347339112968 0.051459143378398922164 --1.8728417739956807141 -4.239719661832373223 -0.042909557750301418264 +-1.5655322071100350456 0.56626121192188216824 0.050269397991054412533 +-1.5477080637857006753 -4.370087697214287981 -0.05361768768801557225 5 0.037692251088985676735 0.00046732617030490929307 -4.049944927347420176 -2.9910878677758190314 -0.078187280837353656526 -1.6060801375519682711 2.349356876761497338 -0.045690062807172619064 +4.0891378954287338487 -2.9329188614380639066 -0.07930573161132697946 +1.575024788882753283 2.3719591091996699917 -0.045089307261129988257 6 0.011285899820091272997 0.00038925687730393611812 -6.298929503477405767 -7.706413024510769816 -0.11669919842191249504 -1.4661378456572359413 1.2872251175075805794 -0.08070991686100478242 +6.3349788609660162564 -7.674600716671800882 -0.11868650931385750502 +1.4598618704191345578 1.2948691245181617393 -0.080593167691228835176 7 0.0017236589478267730203 0.00016953449859497231466 -14.856082147529010129 13.007589275314199284 -0.14417795763685259391 --0.9554310497290159123 1.0161753499437922057 0.016099529164307530124 +14.832516206189200858 13.032608531076540714 -0.14378102535616668622 +-0.9573374666934839659 1.014553546383260322 0.016118112341773867214 8 0.0020336100526728302319 0.000164587904124493665 -29.55744967800954015 -4.629377558152945049 -0.58590957207831262377 -0.17162147939801157335 1.1422848961108499101 -0.027445465472921385952 +29.561664938083289655 -4.6012285192418387325 -0.586585578731106283 +0.17051705220469790965 1.1424784769020628332 -0.027423757798549895085 diff --git a/examples/whm_gr_test/swiftest_relativity.ipynb b/examples/whm_gr_test/swiftest_relativity.ipynb index 0d8111fea..69bacdf51 100644 --- a/examples/whm_gr_test/swiftest_relativity.ipynb +++ b/examples/whm_gr_test/swiftest_relativity.ipynb @@ -22,9 +22,9 @@ "output_type": "stream", "text": [ "Reading Swifter file param.swifter.in\n", - "Reading in time 1.000e+02\n", + "Reading in time 1.000e+03\n", "Creating Dataset\n", - "Successfully converted 101 output frames.\n", + "Successfully converted 1001 output frames.\n", "Swifter simulation data stored as xarray DataSet .ds\n" ] } @@ -45,9 +45,9 @@ "output_type": "stream", "text": [ "Reading Swiftest file param.swiftest.in\n", - "Reading in time 1.000e+02\n", + "Reading in time 1.000e+03\n", "Creating Dataset\n", - "Successfully converted 101 output frames.\n", + "Successfully converted 1001 output frames.\n", "Swiftest simulation data stored as xarray DataSet .ds\n" ] } @@ -75,7 +75,7 @@ "outputs": [], "source": [ "obj = Horizons(id='1', id_type='majorbody',location='@sun',\n", - " epochs={'start':'2021-01-28', 'stop':'2121-02-05',\n", + " epochs={'start':'2021-01-28', 'stop':'3021-02-05',\n", " 'step':'1y'})\n", "el = obj.elements()\n", "t = (el['datetime_jd']-el['datetime_jd'][0]) / 365.25\n", @@ -114,17 +114,17 @@ "output_type": "stream", "text": [ "Mean precession rate for Mercury long. peri. (arcsec/100 y)\n", - "JPL Horizons : 573.8351991142854\n", - "Swifter GR : 579.5897815748845\n", - "Swiftest GR : 579.5897815748845\n", - "Obs - Swifter : -5.754582460598964\n", - "Obs - Swiftest : -5.754582460598964\n", - "Swiftest - Swifter: 0.0\n" + "JPL Horizons : 571.3210506300043\n", + "Swifter GR : 571.6183105524942\n", + "Swiftest GR : 571.61831053222\n", + "Obs - Swifter : -0.2972599224899675\n", + "Obs - Swiftest : -0.29725990221562437\n", + "Swiftest - Swifter: -2.0274342205084395e-08\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYwAAAEGCAYAAAB2EqL0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAA4zklEQVR4nO3deZxN9f/A8df7zm4XWiRbi2JiMLasocUQkUKUJSEtaNW3UvmmVCpaJHtFKHvKksj2k2VQCIksg6+yxixmue/fH/fSGIN7mZkzy/v5eNyHez73fM55f64xb+fzOefzEVXFGGOMuRiX0wEYY4zJGSxhGGOM8YklDGOMMT6xhGGMMcYnljCMMcb4JNDpADJT8eLFtWzZsk6HYYwxOUZ0dPQhVS2R3me5OmGULVuWtWvXOh2GMcbkGCKy+3yfWZeUMcYYn1jCMMYY4xNLGMYYY3ySq8cw0pOUlERMTAwJCQlOh5LnhIaGUqpUKYKCgpwOxRhzCfJcwoiJiaFgwYKULVsWEXE6nDxDVTl8+DAxMTGUK1fO6XCMMZcgz3VJJSQkUKxYMUsWWUxEKFasmF3ZGZOD5bmEAViycIh978bkbHkyYRhjTG419cdPeGviI7hTUjL82JYwHFCgQAF27dpFWFgYERERVKxYkV69euF2u9m1axfh4eEXrP/aa68xZMiQs8rKli3LoUOH/IojKiqKY8eO+Ru+MSYbSjx1gj7jGvB6zAgWx6/i+MkjGX6OPDfonZ1cf/31bNiwgeTkZBo3bszMmTOpVq1app9XVVFVvv/++0w/lzEm801ZMIRvd03il5BEap4sStd6L1G0cLqze1wWu8LIBgIDA7ntttv4448/MuR477//PuHh4YSHhzN06FAAdu3axS233ELv3r2pVq0ae/fuPXNVMmLECCIiIoiIiKBcuXLcfvvtAEyaNIlbb72V8PBwXnjhhTPHL1CgAC+99BJVqlShdu3aHDx4EIBvvvmG8PBwqlSpQoMGDTKkLcaY8/v9z9X0HlOPNw58zi8hiTRJLMnwnoupV+WuTDlfnr7CeP3bzfy2/58MPWbFkoV49Z5KftWJi4vjxx9/ZODAgT7X+eCDD5gwYcKZ7f379wMQHR3NuHHjWLVqFapKrVq1aNiwIUWLFmXbtm2MGzeO4cOHn3WsXr160atXL5KSkmjcuDFPP/00+/fv54UXXiA6OpqiRYty5513MnPmTO69915iY2OpXbs2gwYN4vnnn2fUqFG8/PLLDBw4kPnz53PttddaV5cxmcidksyS9bN5c8MADgZC5LGreKLpM1S75W7ElXnXAXaF4aAdO3YQERFB3bp1ad68Oc2aNfO5br9+/diwYcOZV8mSJQFYvnw5rVu3Jn/+/BQoUIA2bdqwbNkyAMqUKUPt2rXPe8w+ffrQuHFj7rnnHtasWUOjRo0oUaIEgYGBdOzYkaVLlwIQHBxMixYtAKhevTq7du0CoG7dunTp0oVRo0aRkgkDbsYYWPnrAjqNrcFTm1/lf0FCj4JteLPLHKpXisrUZAF5/ArD3yuBjHZ6DCMjqep5P8ufP/95Pxs/fjy7d+/m448/vuhxgoKCztwiGxAQQHJyMgAjRoxg1apVfPfdd0RERLBhwwaKFSt2Kc0wxqSRnJTA7GXjeXXvJxAMNWMLc3vZunSK8r1n4nLZFUYu06BBA2bOnElcXByxsbHMmDGD+vXrX7BOdHQ0Q4YMYcKECbi8/0OpVasWS5Ys4dChQ6SkpDBp0iQaNmx4wePs2LGDWrVqMXDgQIoXL87evXszrF3G5FWqyvYdC6k3oTqv7v2EQFUecN3DGx1/oFPU21kaS5ZdYYhIBWBKqqLywABgMTACKADsAjqq6jkDCyJyNzAMCABGq+rgzI45MyQnJxMSEnLBfbZt20apUqXObH/wwQfcf//9Ph2/WrVqdOnShZo1awLQvXt3qlateqbbKD0ff/wxR44cOTPYHRkZyejRo3nrrbe4/fbbUVWioqJo1arVBc/93HPPsX37dlSVJk2aUKVKFZ9iNsac39OjmrMwZC+4XFSJDeOBit1o2aiXI7HIhboeMu2kIgHAPqAWMBV4VlWXiEg3oJyqvpLO/r8DdwAxwBqgg6r+dqHzREZGatoFlLZs2cItt9ySYW3x1y+//MKjjz7K6tWrHYvBSU5//8bkFJ/O6MHsw/9HTJAQqMp9AbV5qdOoTJ8xQUSiVTUyvc+cGsNoAuxQ1d3eK4+l3vIfgPnAK2n2rwn8oao7AURkMtAKuGDCyG5GjBjBhx9+eOZWV2OMSWvnnrV8OO8VfgyJgSCh5vGreKfrTIoVLOB0aI4ljPbAJO/7TUBLYBZwP3BdOvtfC6TuEI/Bc3VyDhHpAfQAKF26dAaFmzFO375qjDFpJSenMO67Vxl9ZAZxIS4CVHnvlte4rWprwoIDnA4PcGDQW0SC8SSIb7xF3YDHRSQaKAgkplctnbJ0+9JUdaSqRqpqZIkSGf+kozHGZLSt2+fz3PgoPjw2iziXizvjazOl/jCa1GqbbZIFOHOF0QxYp6oHAVR1K3AngIjcBDRPp04MZ195lAL2Z3KcxhiTqZJT3Gza9j1dVvcnJUi4NknpeN0T3FO/G0XyBTsd3jmcSBgd+Lc7ChG5UlX/EhEX8DKeO6bSWgPcKCLl8AyWtwcezIpgjTEmo6nbTVzcXwz46jGWBG0nAGgYX4m+LV6h3HXOPh92IVmaMEQkH547nXqmKu4gIo97308Hxnn3LYnn9tkoVU0WkSfwDIgHAGNVdXMWhm6MMRlmzJzeDDu6AkLghkQXjYo+wJOdX8Llyt5rxmTpGIaqxqlqMVU9nqpsmKre5H31V+99vqq6X1WjUu33vXef61V1UFbGndEGDRpEpUqVqFy5MhEREaxatcqnegMGDGDhwoUALFu2jEqVKhEREcHKlSszZObZgwcP8uCDD1K+fHmqV69OnTp1mDFjBgA//fQThQsXpmrVqtx88808++yzl30+Y/Kaw0f28PzIrow+vIxQt9IypRoTHlpJn7YvZ/tkAXl8ahAnrFy5kjlz5rBu3TpCQkI4dOgQiYnpjfOfK/XkhBMnTuTZZ5+la9eujB8/nrVr1xIVFXWB2mdLTk4mMPDfv35V5d5776Vz58589dVXAOzevZvZs2ef2ad+/frMmTOH+Ph4qlatSuvWralbt67P5zQmr3KnpND/i2b8xD7iQ1wUT4aXK79Fkxr3OB2aXyxhZLEDBw5QvHjxM097Fy9eHIDVq1czePBgpk+fzqxZs2jfvj3Hjx/H7XZTsWJFdu7cSZcuXWjRogXHjh3j66+/Zv78+SxYsIAVK1YQHx/P8uXLefHFF2nRogVPPvkkGzduJDk5mddee41WrVoxfvx4vvvuOxISEoiNjWXRokVn4lq0aBHBwcFn3fZbpkwZnnzyyXPacHrhp3379mXyt2VMzvfjyo+ZvWkmi4IPEqhCx6D7eL7Ty7gCct6v35wXcUaa2x/+tzFjj3n1rdDs/LOW3HnnnQwcOJCbbrqJpk2b0q5dOxo2bEi1atVYv3494OluCg8PZ82aNSQnJ1Or1tmPnHTv3p3ly5fTokUL2rZte+YK4/TEgf/5z39o3LgxY8eO5dixY9SsWZOmTZsCniucX3/9lSuuuOKsY27evNnnxZuOHj3K9u3bbc0LYy5g/5GjfDnvZSYkLYVgCD8VyGcdV1Aofz6nQ7tkNvlgFitQoADR0dGMHDmSEiVK0K5dO8aPH09gYCA33HADW7ZsYfXq1Tz99NMsXbqUZcuWXXTywLQWLFjA4MGDiYiIoFGjRiQkJLBnzx4A7rjjjnOSRXoef/xxqlSpQo0aNc6ULVu2jMqVK3P11VfTokULrr76av8ab0we4E5JZueu5Tz7dVMmJC2lcIqbZ4u354uu/5ejkwXk9SuMC1wJZKaAgAAaNWpEo0aNuPXWW/n888/p0qUL9evXZ+7cuQQFBdG0aVO6dOlCSkrKOet3X4yqMm3aNCpUqHBW+apVq847xXmlSpWYNm3ame1PPvmEQ4cOERn575Qyp8cwfv/9d+rVq0fr1q2JiIjwKzZjcjN1u3nmyyYslCMQBk0TbqR7sw+oVLqM06FlCLvCyGLbtm1j+/btZ7Y3bNhAmTKeH6YGDRowdOhQ6tSpQ4kSJTh8+DBbt26lUqUL35ddsGBBTpw4cWb7rrvu4qOPPjqzpsXprq4Lady4MQkJCXz66adnyuLi4tLd96abbuLFF1/k7bezdmplY7KzFeum8fCIWiyUI1SKD+SZYg/wQc/puSZZgCWMLHfy5Ek6d+5MxYoVqVy5Mr/99huvvfYa4FmD4uDBg2fGBipXrkzlypUvOjvl7bffzm+//UZERARTpkzhlVdeISkpicqVKxMeHs4rr6Sdy/FcIsLMmTNZsmQJ5cqVo2bNmnTu3Pm8SaFXr14sXbqUP//8078vwJhc5tg/f/Py563o+8ur/B4WR1RKGUZ1XkWXFhf/d5fTODK9eVbJjtOb53X2/ZvcZPGq8by7cQh7g4QqJ8PoVucdGldr5HRYlyU7Tm9ujDE5VmxCIh/PfJgJpzZDkPBwcH26dxlK0fzZb/6njGQJwxhjfKRuNwv+7zPe2fYxfwW6CI8LpmfkczSq0d7p0LKEJQxjjPFB0qlYek9ows+BsRDoop2rHvdEvUaVMlc5HVqWsYRhjDEX8eX3g5mwfwL7g4TacUW499auNK/XzemwspwlDGOMOY/ftv/IKz89ze/BbiQQmp4qzaudZ2TLtSqygiUMY4xJI/HUST6e+RKzYheSFADVj1zHM63e49by2Xetiqxgz2E4IDtNb37s2DGGDx9+3s9tynOT14yfM4D2X9zGuIRFHAlw0alIU8b2mZvnkwVYwshyqac3//XXX1m4cCHXXXfdxSvimd789CSCp6c337BhA9u2bcuUhHF6yvMGDRqwc+dOoqOjmTx5MjExMWf2qV+/PuvXr2f9+vXMmTOHFStWXFIcxjhtw+ZpDJncnfcOz2B7sFLnn5JMr/cBvdsMyxFrVWSFLEsYIlJBRDakev0jIn1FJEJEfvaWrRWRmuep309ENovIJhGZJCKhWRV7RkpvevOSJUuyevVq2rRpA8CsWbMICwsjMTGRhIQEypcvD0CXLl2YOnUqo0eP5uuvv2bgwIF06NCBAQMGMGXKlDNPesfGxtKtWzdq1KhB1apVmTVrFuCZkbZmzZpERERQuXJltm/fTv/+/dmxYwcRERE899xzZ8VqU56bvCApKZnvl39B99Wv8vmpVbhU6X/dIP7beRY3Xt/U6fCylSwbw1DVbUAEgIgE4FmbewYwCnhdVeeKSBTwDtAodV0RuRZ4CqioqvEi8jWedb3HX05Mb69+m61Htl7OIc5x8xU380LNF877uZPTm48YMYI+ffrQsWNHEhMTSUlJYfDgwWzatIkNGzacE6tNeW5yuxP/7OPtqc8yK2ATuIS6J6/h3ir3cXe9lk6Hli05NejdBNihqrtFRIFC3vLCwP7z1AkEwkQkCch3gf2ytdPTmy9btozFixfTrl07Bg8eTJcuXdKd3jwlJeWSpjefPXv2mVluT09vXqdOHQYNGkRMTAxt2rThxhtv9Ou4jz/+OMuXLyc4OJg1a9YA/055vm3bNvr3729Tnpsc44vv+vDuoUUQ4Nl+KLgufXsOJzjQeurPx6mE0R6Y5H3fF5gvIkPwdJHdlnZnVd3n/XwPEA8sUNUF6R1YRHoAPQBKly59wSAudCWQmZya3vyWW26hVq1afPfdd9x1112MHj36THdXemzKc5Mbxcf/w7Oft2Fp2EEKpLipfqoMTzb/iAqlr3c6tGwvy1OpiAQDLYFvvEWPAf1U9TqgHzAmnTpFgVZAOaAkkF9EOqV3fFUdqaqRqhpZokSJzGjCZXFyevOdO3dSvnx5nnrqKVq2bMmvv/56Tt3UbMpzk9uMmd2Pe766jaVhBymY4ubFG/ry8WPfW7LwkRPXXs2Adap60LvdGZjuff8NkN6gd1PgT1X9W1WTvPufcyWSEzg5vfmUKVMIDw8nIiKCrVu38vDDD1OsWDHq1q1LeHj4OYPeNuW5yS1W/zqTPiPvYujRhRwMFOr+cyNLO2+kZYNHnQ4tR8ny6c1FZDIwX1XHebe3AI+p6k8i0gR4R1Wrp6lTCxgL1MDTJTUeWKuqH13oXDa9efZj37/JSkkpbqYvHsXHez7kWICLK5PdvNNgJhXLlCcsOMDp8LKlbDO9uYjkA+4AeqYqfhQYJiKBQALe8QcRKQmMVtUoVV0lIlOBdUAysB4YmZWxG2NylkUrhzF74zx+DIkh0CV0CGrHE217Uyj/xde0N+nL0oShqnFAsTRly4Hq6ey7H4hKtf0q8Gpmx2iMydlS3Mr85cN44c8xEAIVEwJpdcNLtG3Yxu6Aukx5ci4pVb3ouIDJeLl5dUfjvOSkBI6dOMjLkx9hVb7/EQS0C25E77ZvUjB/oYvWNxeX5xJGaGgohw8fplixYpY0spCqcvjwYUJDc+QD+iabU7ebx75oyM+BcZAfqiXko3P1ATSObOF0aLlKnksYpUqVIiYmhr///tvpUPKc0NBQSpUq5XQYJpf5bsnbfLN1OtGhcVyTpEQVbkPfzgOdDitXynMJIygoiHLlyjkdhjHmMh06foiPZj7BdDZDKNQ6VYAPH15EvtAwp0PLtfJcwjDG5GzqdrNi3VhGRH/KL6GJ3BTvome1d7mz5p1Oh5brWcIwxuQYCYmJvPJVFPPkIIRCq5TKPNVxDFcWtLGxrGAJwxiTI2zfvZZX5j3K5tBkwuODuO/GzrRt0sfpsPIUSxjGmGzt2PF9vDG9M/NdBwkLdtNWqtHnodEUyR/idGh5jiUMY0y2NfunoYz7fSx/BrupeqIgLcOfpG2jB50OK8+yhGGMyXb+3LeNMQufYxZ/Qgh0DIrghce/tGenHGYJwxiTbSSeOsHo757h0xMrAah8Ioznmo0l4vpwhyMzYAnDGJNNHIuL57lJd/BzYCwA3fO1pGmT56hUqoizgZkzLGEYYxx1/NguvlkykjGHZ3Ey0EWd2GJ0qtGNBjUedjo0k4YlDGOMY37/4wc6LutHgksoDLR2V+WxDiO4pmg+p0Mz6bCEYYzJcslJCbzweRsWBe4hAKh5rBQd6z5B48jmTodmLsAShjEmS42e9QRjDy/mRJALEDqF1eSZrmOcDsv4IMsShohUAKakKioPDAB+AkYAoXhW0+utqqvTqV8EGA2EAwp0U9WVmRu1MSajrFo/genrJ/N9wG4IcFHreEne6z6HwvmCnA7N+CjLEoaqbgMiAEQkANgHzABGAa+r6lwRiQLeARqlc4hhwDxVbSsiwYB1chqTA7hTUhg/902G/z2FUwGCqPJOhZepW/U+CoZasshJnOqSagLsUNXdIqLA6eWwCgP70+4sIoWABkAXAFVNBBKzJlRjzKXauGUm32+YxYTEteASmifVol3t5lQNb+10aOYS+J0wRCQ/kKCqKZdx3vbAJO/7vsB8ERkCuIDb0tm/PPA3ME5EqgDRQB9VjU0nvh5AD4DSpUtfRojGmEuVkJTCop9H8MLOEQAUSXHzZJnnadWwEyGBAQ5HZy7VRVdEFxGXiDwoIt+JyF/AVuCAiGwWkXdF5EZ/TujtTmoJfOMtegzop6rXAf2A9Ea/AoFqwKeqWhWIBfqnd3xVHamqkaoaWaJECX9CM8ZkkP7jmp9JFo3jbuC9Wp/xQJPOlixyOF+uMBYDC4EXgU2q6gYQkSuA24HBIjJDVSf4eM5mwDpVPejd7gycnqP4GzwD22nFADGqusq7PZXzJAxjjHNGz36CLw4t5miIi5JJSrcyvWl3R2+nwzIZxJeE0VRVk9IWquoRYBowTUT8GbnqwL/dUeAZs2iI526pxsD2dM71PxHZKyIVvIPnTYDf/DinMSYTbfp9MR8sHMDafEe5QqHBybK83ulLihcu4nRoJgNdNGGklywuZR8AEckH3AH0TFX8KDBMRAKBBLzjDyJSEhitqlHe/Z4EJnq7tHYCXX05pzEm87hTUnhnSg/mJPzM8fwuyiYKgxqOoPJN9ZwOzWQCnwe9ReTpdIqPA9GqusGXY6hqHFAsTdlyoHo6++4HolJtbwAifY3XGJN51O3m4xmd2XT4T/4v5DihItwnD/Dao684HZrJRP7cJRXpfX3r3W4OrAF6icg3qvpORgdnjMl+4hNT+OL7Vxl5cgOEQKWEQF5vuZCbrr7C6dBMJvMnYRQDqqnqSQAReRXP4HMDPLe5WsIwJhdLSoojeuNXjF09npVhxymW7KbHdU9y/+2dCQoKczo8kwX8SRilOfthuSSgjKrGi8ipjA3LGJPdPD+xGQvlCIRBg/ir6d74XareFOF0WCYL+ZMwvgJ+FpFZ3u17gEneB/nsjiVjcqm5y97nq82T2BCWQIUEF81KPsgj97zgdFjGAT4nDFX9r4h8D9QDBOilqmu9H3fMjOCMMc6JS4jjrSkdmKU70DDhruRreenBrylasNDFK5tcyZ+7pAS4BSisqgNFpLSI1ExvZlljTM7lTknmh//7kAm/fcWG0FNExOane4NRNLy1stOhGYf50yU1HHDjebhuIHACz4N7NTIhLmOMA47GxvL65Hv4MfBvCIVWlOWVnjNtSg8D+JcwaqlqNRFZD6CqR70P0RljcoEt25fy8uIn+T3ETeSJIrSv1p07az+EuC465ZzJI/xJGEnedSwUQERK4LniMMbkYCdjj9Nv0l38HBQLIdAhoB6PdHqPq4rYkjPmbP4kjA/xLHh0lYgMAtoCL2dKVMaYLPH5nJeY+L9ZHAgSIk8Wpun1beh4d3qTOhjj311SE0UkGs/EfwD3quqWzAnLGJOZtu5YwXuLn+HnoFiuEKVDQDWe7Tme4EDrfjLnd9GEcZ45pACaiUgzVX0/g2MyxmSS2Ni/GfjNQ3wv+wgMVKodvYqnWk2gerlrnA7N5AC+XGEU9P5ZAc8dUbO92/cASzMjKGNMxpu7YiKTfvuA9cGeiRl6X3EPbe9/naL57d4V4xtfpjd/HUBEFuCZS+qEd/s1/l01zxiTTW3aMocZa79kWspmUoKF2/4pw6tt36TkNfZchfHP5cwllQiUzdBojDEZxu1WfvtzI91+foF4lwtE6F24Pa1a9qVk0fxOh2dyIH8SxpfAahGZgefW2tbA55kSlTHmsqjbzfNj72F+0B5wuaj7T1nuq/Egd9Tu4HRoJgfz5y6pQSIyF6jvLeqqqut9rS8iFYApqYrKAwPwLM06AggFkoHe55tuxPscyFpgn6q28PXcxuQlc356mxd3T4AguDpJiSpYlz4PjcDlEqdDMzmcL3dJiaoqgKquA9ZdaJ/z8a7FHeHdPwDYh+e5jlHA66o6V0Si8Kyr0eg8h+kDbAFs9jNj0jh2/AADv36EJYF7wCU0iC3Hi+0nUuqKghevbIwPfLnperGIPCkipVMXikiwiDQWkc+Bzn6etwmwQ1V34+neOp0ACgP706sgIqXwrPI32s9zGZPrfTS1Fy2nNeWH4L0kCzxTrDWf9J5tycJkKF+6pO4GuuFZ+6IccAwIw5NsFgAf+LqmdyrtgUne932B+SIyxHvM285TZyjwPP/e5psuEekB9AAoXbr0hXY1Jsdb++sUZkZPYZZrOwS4aHQykvce/ZjgYBvUNhnPl9tqE/DMVDtcRIKA4kC8qh67lBN6JyxsCbzoLXoM6Keq00TkAWAM0DRNnRbAX6oaLSKNLhLvSGAkQGRk5AW7yYzJqU7GJzJiVn8mJiwg2SXkc7sZUe9LbilbheAgm1nWZA5/7pJCVZOAA5d5zmbAOlU96N3ujGdsAjzPdaTX5VQXaOkd4wgFConIBFXtdJmxGJPj7N27guELhjEncAuBwP005t6691P5xmpOh2ZyOb8SRgbpwL/dUeAZs2iI526pxsD2tBVU9UW8VyTeK4xnLVmYvOavEwn8sPxNBv81AwKhVJLyQtUhNKx2F571zYzJXFmaMEQkH3AH0DNV8aPAMBEJBBLwjj+ISElgtKpGZWWMxmRH+w5s5NlZndkUlkTBFDeNtCa9Wr5B6SuvdTo0k4f4s0TrI6o65nJOpqpxQLE0ZcuB6unsux84J1mo6k94rkaMyRP+M74l38qfBIcoNeIL0q3WS9Srao8hmaznzxXGeyLSEc/DdauBSaq6OXPCMsaM/fYJlu5fTXRoPMWT3TQJuZeXurxh3U/GMf4kjMPAG0AwngfwvhaRD1X1s8wIzJi86siJWEbOfoaJySsgFKqcCuazTovIn6+w06GZPM6fhHFcVRd5388TkWHAKsAShjEZwJ2SzPwV7zJu6xS2hKRQ+pTSu+pnNK9R1+nQjAEuYdBbRF7A8yxGYeBEhkdkTB6U4laGfP0gExK3QAjcx630emA0V9u62iYbuZS7pKbhmdqjFfBmxoZjTN6zIvpLPl0zlF/CErk5PoCHKvWlZf0uTodlzDn8SRhFReQ6Vf0D+ENERgHrge8yJzRjcjd1u3n5y3uYzR6CQ5QW7nI8ef8XlCxW1OnQjEmXPwmjEPCTiBwCfgOKACmZEZQxud3sxe/yze9T2BB6iuonCtHxtve5o1otp8My5oL8SRi3A5uAWnjW91bs6sIYvxw4/BeDprVjScghCIWmySV4t9cPBAba/E8m+/NnAaVfvW9Xel/GGB+p283/rRvHO+uHsjMEIo8Xp1fjV6hZsRHi8mWVAWOc58RcUsbkKX/sj+GNOa2JDkmAYOgSUo8u3YZSrECI06EZ4xdLGMZkom8Wfcqbez4hOUSoGVeIJuVa8ODdL168ojHZkD9zST0BTFTVo5kYjzG5wh+7V/HEwkfYFyiEKDwU1pBH231AwdAgp0Mz5pL5c4VxNbBGRNYBY4H5F1vH25i8Rt1unht3N/MDD+AKgJpHS/NQo5dpFFHH6dCMuWz+DHq/LCKvAHcCXYGPReRrYIyq7sisAI3JKT6fM4DvD8zmt2DP3ebd8temTxdbgt7kHv6uuKci8j/gf3hmrS0KTBWRH1T1+cwI0JjsbvuORYxcOph5rgMQDDWOF+f9bvMoYoPaJpfxZwzjKTzLqR7Cs4zqc6qaJCIuPKvkXTBhiEgFYEqqovLAADxrW4zAs/RqMtBbVVenqXsd8AWebjE3MFJVh/kauzGZwe1WZi79ks92vMP+QCFQlffD36Rqxbspki/Y6fCMyXA+JQzxTMBfBWijqrtTf6aqbhG56GouqroNz7ToiEgAsA+YAYwCXlfVud41u98BGqWpngw8o6rrRKQgEO29qvnNl/iNyWjbdy7mq+Wjmaq/QqBwe2wlHm3Sk1sr3O50aMZkGp8ShrcrqmraZJHq8y1+nrcJsENVd4uI4pl2BDwz4O5P5/gHgAPe9ydEZAtwLZ4pSozJMqrKpq3f8fCq/iSLEOZ281iJNrS+72XrgjK5nj9jGCtFpIaqrsmA87YHJnnf9wXmi8gQwAXcdqGKIlIWqIpnLQ5jssyphOP0/6ITC0N2gQi3Ha/EU60GU6lMWadDMyZL+DuXVC8R2QXEAoLn4qOyPycUkWCgJXD66aXHgH6qOk1EHgDGAE3PU7cAnunV+6rqP+fZpwfQA6B06dL+hGbMeQ39pjtj4lZBCFyXqNxZuD59Hh5uy6WaPEV8fZRCRMqkV36+bqoLHKcV8Liq3undPg4U8XZ7CZ6V/QqlUy8ImIPn+Y/3fTlXZGSkrl271p/wjDnL5m1zGbX0fZYGHSBQldpxdXmr+3Dyh9gkCSZ3EpFoVY1M7zN/fuo7n6d8oJ/xdODf7ijwjFk0xHO3VGM8d1ydxZtIxgBbfE0WxlyOxKREnh/XipVBe4gLdhHqhveq/Jf61do4HZoxjvEnYcSmeh8KtAD8GuwWkXzAHUDPVMWPAsNEJBBIwNudJCIlgdGqGgXUBR4CNorIBm+9/6jq9/6c3xhfzPnpdeb9sYglIUcAF22lNc+1e4p8+Yo7HZoxjvK5S+qciiIhwGxVvStjQ8o41iVl/LHv6Akmz3+V8ad+AOCmUy4+abeCqwrlt7EKk2dkVJdUWvnwPHxnTI6mbjcx+1bx4ndP8UtYAoVT3HS84mEebPoohQsUcDo8Y7INf5703ohnlT2AAKAE/o9fGJPt/OfLKOawD8Kg4anidK3/OtVvaeB0WMZkO/5cYaR+mjsZOKiqyRkcjzFZZunqzxi3bhxrw2KpEB9AoxKtePzh16z7yZjz8Ge2Wr9unzUmu0pISuHjac8wOX4hp8KE+olFeavjbAoXLOJ0aMZka/50SX0O9FHVY97tosB7qtotk2IzJsN9t+RNJmydyqbQJMonurjvhrd4uElzp8MyJkfwp0uq8ulkAaCqR0WkasaHZEzGi09M4Z0p7Zjq3gah0FKu44VOUyiUv6DToRmTY/iTMFwiUvT0Eq0icoWf9Y3Jcu6UZL5d8l/G7pjOzmCIOJmPzrX+S9PIO50OzZgcx59f+O8B/yciU/HcLfUAMChTojImA7hTUnj2izv5wfU3YYFuWlOJbg+MpGyJIk6HZkyO5M96GIuBtXim7xA8a2PY9OImW5q5eBjTtk1gQ1gCtU4W5b7q/6FZ7budDsuYHM2f9TBmqmp1bA0Kk43t2L2OAfO782tIEoRBk5TiDH50AaHBQU6HZkyO50+X1M8ZuB6GMRkq8dQJRs7px+gTPxMcpFQ7dAPPtR1OeJlrnQ7NmFwjy9fDMCajfbtsAlN+G8ovoadAhK6Fm9Cz81BcLnsAz5iM5E/CaJZpURhzCQ4e/JVFG75hyP4ZJIYKtU9czcN1ulG/egenQzMmV/InYewBOgLlVXWgiJQGrgbsCXCT5fbu28j9CzoQ63IRArxwZWda3PekrattTCbyJ2EMB9x47pIaCJzAs1xqjUyIy5h0qdvNaxPuZ7r+Di4XDU9UoXWdzjSpfofToRmT6/mTMGqpajURWQ9nnvQOzqS4jDnHzEVv8d6uCRwLcFE82U2LApE80/lzp8MyJs/wJ2EkiUgA3inORaQEnisOn4hIBWBKqqLywAA8S7OOwLOKXzLQW1VXp1P/bmAYnqnVR6vqYD9iNznY0SM7eWP64ywIioEAFzX/Kc4z902jYskrnA7NmDzFn4TxITADuEpEBgFtgVd8rayq24AIAG/i2ec93ijgdVWdKyJRwDtAo9R1vft/gmd51xhgjYjMtgcHc7/PZg1k2qGvORDkuePpP1d3oEPn/zgclTF5kz/Tm08UkWigibeolapuvcTzNgF2qOpuEVGgkLe8MLA/nf1rAn+o6k4AEZkMtMIeIsy1Vq2fyITokfwUdAQChTrHbuadLh9SpPA1TodmTJ510YQhIrPTFnn/vEtEUNWWl3De9sAk7/u+wHwRGQK4gNvS2f9aYG+q7Rig1nni7QH0AChduvQlhGaclJTiZtnG1by44U3iglxckexmQOW3ubVCU4oUCnU6PGPyNF+uMOrg+WU9CVjFvwnjkngHylsCL3qLHgP6qeo0EXkAGAM0TVstnUNpOmWo6khgJEBkZGS6+5jsx52SzJLVQ/n+t0XMC9wLLhctkpvTtemD3FTGng01JjvwJWFcjWfsoAPwIPAdMElVN1/iOZsB61T1oHe7M9DH+/4bYHQ6dWKA61JtlyL9riuTQ02Y9xLvHvoeAuHmhACaXtWC7i3/S4A9rW1MtnHRhKGqKcA8YJ6IhOBJHD+JyEBV/egSztmBf7ujwPOLvyGeu6UaA9vTqbMGuFFEyuEZLG+PJ3mZHG7PnpW8Orcvv4TEUsitNKQdfdr346rCtrCRMdmNr9ObhwDN8fyyL4vnjqnp/p5MRPLhuVrpmar4UWCYiAQCCXjHH0SkJJ7bZ6NUNVlEngDm47mtduxlXOGYbEBV6T2qActDjkEoVEgIoGvFx2lev4fToRljzkNUL9zN713LOxyYC0xW1U1ZEVhGiIyM1LVr1zodhklj8vxn+GHPClYHx1IoxU0TVzNe7/wunmVXjDFOEpFoVY1M7zNfrjAewjM77U3AU6n+UZ+erbbQ+Soak9rhEyf4eHpfprIagqF8Inz54DIK5bcH8IzJCXwZw3BlRSAm91K3m1Xrx/JR9HB+DUmiWLKbJ258l/vq3Y247MfLmJzCnye9jfFbQlIK707pwNcpWyAE7koqR797R3Ft8aucDs0Y4ydLGCbT7Ni1gtfm9WVDWALhccHcXeZhHrr7KVvYyJgcyhKGyXDJyUkMnNiBb3UrwSHKvVTg2YfGUbhAYadDM8ZcBksYJkMtXPkpQzcNZ3cw3Bobxh03PEPXu9o7HZYxJgNYwjAZIjY+ng+mPskU9yoIhlaU47+PzbRBbWNyEUsY5rIkJyXw2bc9+OyfdagIN8cF0LfBOOpWqup0aMaYDGYJw1yypKRknp8QxULX3yDCw8GNaHP3QK6/qqjToRljMoElDOM3dbuZt2IoH24dS0ywUPtkMR6+7QXqV23mdGjGmExkCcP4ZXfMBnrN60RMkFAwQGkj1/PCI1PJFxrkdGjGmExmCcP4JCH+KG98/RDf6S5cAVDzSBW63P0y9W+52enQjDFZxBKGuahZS0YxcdsnbAlJ8YxVhFbnua7jnQ7LGJPFLGGY8/pjx0Km/DyCGYlbORUi1PznSt7pMo1iBYs4HZoxxgGWMEy6lm34gZfX9eVIgIsAgcHXP8ftNR8kX7D9yBiTV9m/fnOWhPijTFzwFkOPzYUAF/X/qULH+h2pG2F3QBmT12VZwhCRCsCUVEXlgQFAHaCCt6wIcExVI9Kp3w/oDiiwEeiqqgmZGHKe8/OGr3l8/UASXYKo8kSR+nR/aLhNFmiMAbIwYajqNiACQEQC8KzNPUNVh57eR0TeA46nrSsi1wJPARVVNV5Evsazrvf4TA88D0hJTqTfuCgWBx8El1D/RDnaNvgvjStXcTo0Y0w24lSXVBNgh6ruPl0gnqX8HgAan6dOIBAmIklAPmB/pkeZB4ye/QaT/57MwWChZJLS7qoounV+x+mwjDHZkFMJoz0wKU1ZfeCgqm5Pu7Oq7hORIcAeIB5YoKoL0juwiPQAegCULl06Q4POTVatn8CHa97n15AkCBRqHLuGwd1mc2XBUKdDM8ZkU1k+laiIBAMtgW/SfNSBc5PI6TpFgVZAOaAkkF9EOqW3r6qOVNVIVY0sUaJExgWei3wy/XWeW/8Wv4Ykkd/t5tNbBzG891xLFsaYC3LiCqMZsE5VD54uEJFAoA1Q/Tx1mgJ/qurf3v2nA7cBEzI51lxD3W7GzOnB+r+2sTToGAS4uCepMS91eJn8+S2xGmMuzomEkd6VRFNgq6rGnKfOHqC2iOTD0yXVBFibeSHmLrGnkpn10wcMO7oKguCGU0LvygNoXPM+AuwOKGOMj7I0YXh/4d8B9Ezz0TljGiJSEhitqlGqukpEpgLrgGRgPTAyC0LO0dTtZs/eFbw271nWhsZRJMXNA0V60K5lJ64sWszp8IwxOYyoqtMxZJrIyEhduzbvXogMmvgAk5O3AFArviD33dqDZnW7OBuUMSZbE5FoVY1M7zN70jsXWvLzUL7aNIXogBPgEjoE3Mnzj75LYIAtl2qMuXSWMHKR5BQ3gyf2YmbK/3EqSKh4KpC3mn1J+etudTo0Y0wuYAkjl1i+5lO+2PAFK4NPUjoJ7ik1kJ7N7sXzPKQxxlw+Sxg53LHYUwyb2oOprINguO1UYYZ1XkBoSD6nQzPG5DKWMHKwP/5cwSsLnmRTaBKV4oK4/5ZnaF2/Pa6AAKdDM8bkQpYwcqC4U6cY8vXDTE/ZjIRAO1dVenX8lOKFCjgdmjEmF7OEkcN8v3QoQ38fzYEgITwuhDvLPknXqC5Oh2WMyQMsYeQQfx89yPvTezMvYBvJQcL9ATfwUo+pBARa95MxJmtYwsjmkpLiePub9kxJ+hMC4daTofRsOIaG4bZWhTEma1nCyMb2/PU3H8xpx8KAvwHonq8e7Vt/wFWFbFZZY0zWs4SRDSXEH2XGkvcYemAGcQEuap8swdPNBnNL+ZpOh2aMycMsYWQzx48f4IGpd7A/UCio8FjBBjx0/7sUzGfPVRhjnGUJI5tISU7klYn3Mk/3kBQoND5ZjVb1+tK4SlWnQzPGGMASRrawZO1k3l//JjuDlYJupUVgOQY+/rnTYRljzFksYTho3761DFvwH36Q/SQHC9WPFeP5tjOpeG0Rp0MzxphzWMJwyKQfPuWL3Z8QEySA8EThpjzS6T2bgtwYk21lWcIQkQrAlFRF5YEBQB2ggresCHBMVSPSqV8EGA2EAwp0U9WVmRdx5tj55yKWbv6BDw59iztIaHDiFp5u0Y/ry9ZxOjRjjLmgLEsYqroNiAAQkQBgHzBDVYee3kdE3gOOn+cQw4B5qtpWRIKBHHXbUEJSCnN+XsTQ7X05HuAiROH165+gXmQ3CocFOR2eMcZclFNdUk2AHaq6+3SBeBZueABonHZnESkENAC6AKhqIpCYJZFmgPi4I7wx5RFmu/6AABdN4+pwe3hTmtd/wOnQjDHGZ04ljPbApDRl9YGDqro9nf3LA38D40SkChAN9FHV2LQ7ikgPoAdA6dKlMzRof6kqUxYOZ9D+EeCC8onQrlRzHmw22NG4jDHmUmT5CKu3O6kl8E2ajzpwbhI5LRCoBnyqqlWBWKB/ejuq6khVjVTVyBIlSmRQ1P7btGU2zcfc6kkWQP0TNzKk5TJLFsaYHMuJK4xmwDpVPXi6QEQCgTZA9fPUiQFiVHWVd3sq50kYTlNVBn7xKPNTVnIiyEX5ROh5yxNE1evpdGjGGHNZnEgY6V1JNAW2qmpMehVU9X8isldEKngHz5sAv2VynH77fM5j/HBgFb8EJ0GAizvjIxnSc6ytq22MyRWyNGGISD7gDiDtf7fPGdMQkZLAaFWN8hY9CUz0dmntBLpmcrg+S05xM2r2YIb/sxyCoXSi8uUDP3BF4WucDs0YYzJMliYMVY0DiqVT3iWdsv1AVKrtDUBkJoZ3SdasH8Nn0aNYFRRLqFt5pHg/ut7ZnpCQ/E6HZowxGcqe9L5Eqsr4Oc/y/pEFEAT146+kd9PBhN9Qw+nQjDEmU1jCuAQbt8zmg6VvsjH4JCXcSpsrHqVHx6cIDrRpPYwxuZclDD8NGP8AM2QLQSFKjaRCPN5wCJVvus3psIwxJtNZwvDR8jWf8uEvn7ElKIWKsUG0qPA6DzW5x+mwjDEmy1jCuIhjJ0/xxlcdmB+yHYKg5qn8fNj1R/KH2aC2MSZvsYRxASvWjmTYuuFsCUmhyskw7o94nZZ17kJcNlZhjMl7LGGk4+jJBN6b2p5ZsgNCoK3cTL+uEyiUL8Tp0IwxxjGWMNI4GptIqynVORroIiIulKblutP5bpvWwxhjLGGkUSA4mUi5hlJh1/JUx9EEBtpaFcYYA5YwzhEUlI/3uy10OgxjjMl2bPTWGGOMTyxhGGOM8YklDGOMMT6xhGGMMcYnljCMMcb4xBKGMcYYn1jCMMYY4xNLGMYYY3wiqup0DJlGRP4Gdl9i9eLAoQwMJyewNud+ea29YG32VxlVLZHeB7k6YVwOEVmrqtluDfHMZG3O/fJae8HanJGsS8oYY4xPLGEYY4zxiSWM8xvpdAAOsDbnfnmtvWBtzjA2hmGMMcYndoVhjDHGJ5YwjDHG+MQSRhoicreIbBORP0Skv9PxZBQRuU5EFovIFhHZLCJ9vOVXiMgPIrLd+2fRVHVe9H4P20TkLueiv3QiEiAi60Vkjnc7V7cXQESKiMhUEdnq/fuuk5vbLSL9vD/Tm0RkkoiE5sb2ishYEflLRDalKvO7nSJSXUQ2ej/7UETE5yBU1V7eFxAA7ADKA8HAL0BFp+PKoLZdA1Tzvi8I/A5UBN4B+nvL+wNve99X9LY/BCjn/V4CnG7HJbT7aeArYI53O1e319uWz4Hu3vfBQJHc2m7gWuBPIMy7/TXQJTe2F2gAVAM2pSrzu53AaqAOIMBcoJmvMdgVxtlqAn+o6k5VTQQmA60cjilDqOoBVV3nfX8C2ILnH1srPL9g8P55r/d9K2Cyqp5S1T+BP/B8PzmGiJQCmgOjUxXn2vYCiEghPL9YxgCoaqKqHiN3tzsQCBORQCAfsJ9c2F5VXQocSVPsVztF5BqgkKquVE/2+CJVnYuyhHG2a4G9qbZjvGW5ioiUBaoCq4CrVPUAeJIKcKV3t9zwXQwFngfcqcpyc3vBc3X8NzDO2xU3WkTyk0vbrar7gCHAHuAAcFxVF5BL25sOf9t5rfd92nKfWMI4W3p9ebnqvmMRKQBMA/qq6j8X2jWdshzzXYhIC+AvVY32tUo6ZTmmvakE4um2+FRVqwKxeLoqzidHt9vbZ98KT7dLSSC/iHS6UJV0ynJMe/1wvnZeVvstYZwtBrgu1XYpPJe3uYKIBOFJFhNVdbq3+KD3MhXvn395y3P6d1EXaCkiu/B0LTYWkQnk3vaeFgPEqOoq7/ZUPAkkt7a7KfCnqv6tqknAdOA2cm970/K3nTHe92nLfWIJ42xrgBtFpJyIBAPtgdkOx5QhvHdCjAG2qOr7qT6aDXT2vu8MzEpV3l5EQkSkHHAjnsGyHEFVX1TVUqpaFs/f4yJV7UQube9pqvo/YK+IVPAWNQF+I/e2ew9QW0TyeX/Gm+AZn8ut7U3Lr3Z6u61OiEht7/f1cKo6F+f0yH92ewFReO4g2gG85HQ8GdiuenguPX8FNnhfUUAx4Edgu/fPK1LVecn7PWzDjzspstsLaMS/d0nlhfZGAGu9f9czgaK5ud3A68BWYBPwJZ47g3Jde4FJeMZpkvBcKTxyKe0EIr3f1Q7gY7wzfvjysqlBjDHG+MS6pIwxxvjEEoYxxhifWMIwxhjjE0sYxhhjfGIJwxhjjE8sYRhzESJSTEQ2eF//E5F93vcnRWR4Jp2zr4g8fJF9JovIjZlxfmPSY7fVGuMHEXkNOKmqQzLxHIHAOjyzCydfYL+GQCdVfTSzYjEmNbvCMOYSiUijVOtsvCYin4vIAhHZJSJtROQd77oD87zTspxei2CJiESLyPzT0zqk0RhYp6rJInK9iKxLdc4bReT0/FjLgKbeBGNMprOEYUzGuR7PdOqtgAnAYlW9FYgHmnuTxkdAW1WtDowFBqVznLpANICq7gCOi0iE97OuwHjvZ24801ZXyaT2GHMW+5+JMRlnrqomichGPItxzfOWbwTKAhWAcOAH7yJnAXimekjrGjzzIZ02GugqIk8D7Th7/Ya/8MzS6uusvMZcMksYxmScU+D5n7+IJOm/A4RuPP/WBNisqnUucpx4IDTV9jTgVWAREK2qh1N9Furd35hMZ11SxmSdbUAJEakDnunmRaRSOvttAW44vaGqCcB84FNgXJp9bwI2Z064xpzNEoYxWUQ9y/62Bd4WkV/wzBh8Wzq7zsWzzGpqE/HMNrzgdIGIXAXEq3fFNWMym91Wa0w2JCIzgOdVdbt3+1mgsKq+kmqffsA/qjrGoTBNHmNjGMZkT/3xDH5v9yaP6/HcbpvaMTzrPxiTJewKwxhjjE9sDMMYY4xPLGEYY4zxiSUMY4wxPrGEYYwxxieWMIwxxvjk/wGdr7WyKk0w+gAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] @@ -153,6 +153,13 @@ "print(f'Swiftest - Swifter: {np.mean(dvarpi_swiftest - dvarpi_swifter)}')" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, diff --git a/examples/whm_swifter_comparison/cb.swiftest.in b/examples/whm_swifter_comparison/cb.swiftest.in index 058975b81..e4a010b1e 100644 --- a/examples/whm_swifter_comparison/cb.swiftest.in +++ b/examples/whm_swifter_comparison/cb.swiftest.in @@ -1,3 +1,4 @@ +0 39.476926408897626 0.004650467260962157 4.7535806948127355e-12 diff --git a/examples/whm_swifter_comparison/init_cond.py b/examples/whm_swifter_comparison/init_cond.py index aac82eed9..cc86e7635 100755 --- a/examples/whm_swifter_comparison/init_cond.py +++ b/examples/whm_swifter_comparison/init_cond.py @@ -1,10 +1,5 @@ #!/usr/bin/env python3 import swiftest -import numpy as np -import sys -from astroquery.jplhorizons import Horizons -import astropy.constants as const -from scipy.io import FortranFile sim = swiftest.Simulation() diff --git a/examples/whm_swifter_comparison/param.swifter.in b/examples/whm_swifter_comparison/param.swifter.in index 5cf0cb8b9..417c3ab04 100644 --- a/examples/whm_swifter_comparison/param.swifter.in +++ b/examples/whm_swifter_comparison/param.swifter.in @@ -21,6 +21,6 @@ CHK_QMIN_RANGE 0.004650467260962157 1000.0 EXTRA_FORCE NO BIG_DISCARD NO CHK_CLOSE YES +RHILL_PRESENT YES J2 4.7535806948127355e-12 J4 -2.2473967953572827e-18 -RHILL_PRESENT YES diff --git a/examples/whm_swifter_comparison/param.swiftest.in b/examples/whm_swifter_comparison/param.swiftest.in index 73818e198..13fdad2ec 100644 --- a/examples/whm_swifter_comparison/param.swiftest.in +++ b/examples/whm_swifter_comparison/param.swiftest.in @@ -25,6 +25,7 @@ DU2M 149597870700.0 EXTRA_FORCE NO BIG_DISCARD NO CHK_CLOSE YES +RHILL_PRESENT YES FRAGMENTATION NO ROTATION NO TIDES NO diff --git a/examples/whm_swifter_comparison/pl.swifter.in b/examples/whm_swifter_comparison/pl.swifter.in index 7412144e0..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.0014751253039664285066 +1 6.5537098095653139645e-06 0.0014751242768086609319 1.6306381826061645943e-05 -0.36019833403308620934 -0.07157757063116521046 -0.038889932331457412185 -0.012062683987023428416 10.539199589223686515 0.86012493216791845955 -2 9.663313399581537916e-05 0.006759112363391176217 +-0.21794225400065470044 0.24570059548519398995 0.040069659678364698274 +-9.768342370075118952 -6.4098488749322373205 0.37225116289830816995 +2 9.663313399581537916e-05 0.0067590742435367571566 4.0453784346544178454e-05 --0.71276554591539231787 0.0894943770131735733 0.042358444034962597358 --0.96047232050779632014 -7.363179644470093107 -0.045627977257453471387 -3 0.000120026935827952453094 0.010044757472678654026 +-0.60413504586259936247 -0.39527613440541492507 0.029436881824798030033 +3.992938767473374092 -6.2169034295501688922 -0.3157349287333398891 +3 0.000120026935827952453094 0.010044891628501106769 4.25875607065040958e-05 -0.27645830888837641393 -0.97837771886398083865 4.5542715832163949185e-05 -5.9448497026859876977 1.6852493323830119935 -9.895818943662129852e-05 -4 1.2739802010675941456e-05 0.007246754169100752911 +0.6475137988388671717 -0.78146344078682306034 3.4954277703126252982e-05 +4.7364737841481480227 3.9858178826605781494 -0.000206181980282845843 +4 1.2739802010675941456e-05 0.0072466933032545104062 2.265740805092889601e-05 --1.4965217056830220077 0.729867855162097956 0.052005223740499352536 --2.049353987860530548 -4.1577626275819368415 -0.03686191825212072444 -5 0.037692251088985676735 0.3552713962079929143 +-1.6060166552595489531 0.43262604649099911658 0.048461907252935247647 +-1.1388942318608360441 -4.4988235352611598648 -0.066344559364066134143 +5 0.037692251088985676735 0.3552707368190505097 0.00046732617030490929307 -4.027841704615886087 -3.0231618001306270749 -0.077559557972985263 -1.6231826570873460391 2.3366237981055781438 -0.046019759896080974796 -6 0.011285899820091272997 0.43765160695836118215 +4.1359946230316175786 -2.8610749953481979801 -0.08065244615734604161 +1.536603427793050461 2.399023353553466048 -0.044342472584791124157 +6 0.011285899820091272997 0.4376572328164372643 0.00038925687730393611812 -6.2788354074558432316 -7.724005035333701308 -0.11559390097316769863 -1.4696075442034620881 1.282966226939726742 -0.08077393754283409384 -7 0.0017236589478267730203 0.4695227539643713788 +6.3788284394924916754 -7.635463758938534795 -0.121111501730720202974 +1.4521392831727842248 1.3041738917825064364 -0.08044788317293871613 +7 0.0017236589478267730203 0.46959013246222981483 0.00016953449859497231466 -14.869154031353570389 12.9936724365634095335 -0.1443982771709022006 --0.95437109658589562686 1.0170745961532793757 0.016089151184688745742 -8 0.0020336100526728302319 0.78127049251990261927 +14.803649648126269156 13.063133279359290029 -0.14329526741228329478 +-0.9596636872292902537 1.0125665712568530355 0.016140607193432704789 +8 0.0020336100526728302319 0.78135207839715916734 0.000164587904124493665 -29.55509611047864027 -4.6450138458072487424 -0.585533781429422695 -0.17223467348300621534 1.1421766618084267115 -0.027457548207218328868 +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 e144eae8f..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 +1 6.5537098095653139645e-06 0.0014751242768086609319 1.6306381826061645943e-05 -0.36019833403308620934 -0.07157757063116521046 -0.038889932331457412185 -0.012062683987023428416 10.539199589223686515 0.86012493216791845955 -2 9.663313399581537916e-05 +-0.21794225400065470044 0.24570059548519398995 0.040069659678364698274 +-9.768342370075118952 -6.4098488749322373205 0.37225116289830816995 +2 9.663313399581537916e-05 0.0067590742435367571566 4.0453784346544178454e-05 --0.71276554591539231787 0.0894943770131735733 0.042358444034962597358 --0.96047232050779632014 -7.363179644470093107 -0.045627977257453471387 -3 0.000120026935827952453094 +-0.60413504586259936247 -0.39527613440541492507 0.029436881824798030033 +3.992938767473374092 -6.2169034295501688922 -0.3157349287333398891 +3 0.000120026935827952453094 0.010044891628501106769 4.25875607065040958e-05 -0.27645830888837641393 -0.97837771886398083865 4.5542715832163949185e-05 -5.9448497026859876977 1.6852493323830119935 -9.895818943662129852e-05 -4 1.2739802010675941456e-05 +0.6475137988388671717 -0.78146344078682306034 3.4954277703126252982e-05 +4.7364737841481480227 3.9858178826605781494 -0.000206181980282845843 +4 1.2739802010675941456e-05 0.0072466933032545104062 2.265740805092889601e-05 --1.4965217056830220077 0.729867855162097956 0.052005223740499352536 --2.049353987860530548 -4.1577626275819368415 -0.03686191825212072444 -5 0.037692251088985676735 +-1.6060166552595489531 0.43262604649099911658 0.048461907252935247647 +-1.1388942318608360441 -4.4988235352611598648 -0.066344559364066134143 +5 0.037692251088985676735 0.3552707368190505097 0.00046732617030490929307 -4.027841704615886087 -3.0231618001306270749 -0.077559557972985263 -1.6231826570873460391 2.3366237981055781438 -0.046019759896080974796 -6 0.011285899820091272997 +4.1359946230316175786 -2.8610749953481979801 -0.08065244615734604161 +1.536603427793050461 2.399023353553466048 -0.044342472584791124157 +6 0.011285899820091272997 0.4376572328164372643 0.00038925687730393611812 -6.2788354074558432316 -7.724005035333701308 -0.11559390097316769863 -1.4696075442034620881 1.282966226939726742 -0.08077393754283409384 -7 0.0017236589478267730203 +6.3788284394924916754 -7.635463758938534795 -0.121111501730720202974 +1.4521392831727842248 1.3041738917825064364 -0.08044788317293871613 +7 0.0017236589478267730203 0.46959013246222981483 0.00016953449859497231466 -14.869154031353570389 12.9936724365634095335 -0.1443982771709022006 --0.95437109658589562686 1.0170745961532793757 0.016089151184688745742 -8 0.0020336100526728302319 +14.803649648126269156 13.063133279359290029 -0.14329526741228329478 +-0.9596636872292902537 1.0125665712568530355 0.016140607193432704789 +8 0.0020336100526728302319 0.78135207839715916734 0.000164587904124493665 -29.55509611047864027 -4.6450138458072487424 -0.585533781429422695 -0.17223467348300621534 1.1421766618084267115 -0.027457548207218328868 +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 82bd3d63f..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" ] } diff --git a/examples/whm_swifter_comparison/tp.swifter.in b/examples/whm_swifter_comparison/tp.swifter.in index a9fa06f46..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.3071617894844269614 1.6438449758645010679 -0.37312906258436789875 --2.256588666826445461 2.8302735208962828827 0.50519430783805206563 +2.1437140623725170485 1.8307543455088179929 -0.33710883085786358393 +-2.5169991736250634084 2.6269266483088493027 0.54674712095669365287 102 -3.011471099377928784 -1.1061264985150089935 0.5067865823770466571 -0.69505215270382913404 2.5183263418638507098 -1.8031340524448678953 +3.0507953356624089025 -0.9309107058567914761 0.38209550228666327998 +0.45214249601424874418 2.5995875558304815747 -1.8388641770977671949 103 --0.51350730399144917104 -3.139963346661017951 0.7339670445581878422 -3.0598116277417892524 -0.1107568728194456082 -0.09922455469700767241 +-0.30288545144121659103 -3.139125526168093927 0.7252151132548391166 +3.0919425994019995516 0.13633790246363267858 -0.15665049243950410883 104 --2.070517783632789044 -0.7764919020604850175 0.27514297675486260042 -1.7817875607764876778 -3.94088558602991294 -0.09896621676031464546 +-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 a9fa06f46..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.3071617894844269614 1.6438449758645010679 -0.37312906258436789875 --2.256588666826445461 2.8302735208962828827 0.50519430783805206563 +2.1437140623725170485 1.8307543455088179929 -0.33710883085786358393 +-2.5169991736250634084 2.6269266483088493027 0.54674712095669365287 102 -3.011471099377928784 -1.1061264985150089935 0.5067865823770466571 -0.69505215270382913404 2.5183263418638507098 -1.8031340524448678953 +3.0507953356624089025 -0.9309107058567914761 0.38209550228666327998 +0.45214249601424874418 2.5995875558304815747 -1.8388641770977671949 103 --0.51350730399144917104 -3.139963346661017951 0.7339670445581878422 -3.0598116277417892524 -0.1107568728194456082 -0.09922455469700767241 +-0.30288545144121659103 -3.139125526168093927 0.7252151132548391166 +3.0919425994019995516 0.13633790246363267858 -0.15665049243950410883 104 --2.070517783632789044 -0.7764919020604850175 0.27514297675486260042 -1.7817875607764876778 -3.94088558602991294 -0.09896621676031464546 +-1.9314729940131600827 -1.0389307897540689396 0.26607157142831372454 +2.2775049779995786108 -3.7157836040053666307 -0.16601542341215017115 diff --git a/python/swiftest/swiftest/init_cond.py b/python/swiftest/swiftest/init_cond.py index 6e5048c0f..f9a7378c0 100644 --- a/python/swiftest/swiftest/init_cond.py +++ b/python/swiftest/swiftest/init_cond.py @@ -121,7 +121,6 @@ def solar_system_horizons(plname, idval, param, ephemerides_start_date, ds): tlab.append('vx') tlab.append('vy') tlab.append('vz') - plab.append('Rhill') dims = ['time', 'id', 'vec'] t = np.array([0.0]) @@ -193,11 +192,14 @@ def solar_system_horizons(plname, idval, param, ephemerides_start_date, ds): p11.append(pldata[key].vectors()['vy'][0] * VCONV) p12.append(pldata[key].vectors()['vz'][0] * VCONV) if ispl: - Rhill.append(pldata[key].elements()['a'][0] * (3 * MSun_over_Mpl[key]) ** (-THIRDLONG)) Rpl.append(planetradius[key] * DCONV) GMpl.append(GMcb[0] / MSun_over_Mpl[key]) # Generate planet value vectors - pvec = np.vstack([p1, p2, p3, p4, p5, p6, GMpl, Rpl, p7, p8, p9, p10, p11, p12, Rhill]) + if (param['RHILL_PRESENT'] == 'YES'): + Rhill.append(pldata[key].elements()['a'][0] * DCONV * (3 * MSun_over_Mpl[key]) ** (-THIRDLONG)) + pvec = np.vstack([p1, p2, p3, p4, p5, p6, GMpl, Rpl, Rhill, p7, p8, p9, p10, p11, p12]) + else: + pvec = np.vstack([p1, p2, p3, p4, p5, p6, GMpl, Rpl, p7, p8, p9, p10, p11, p12]) else: pvec = np.vstack([p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12]) plab = tlab.copy() diff --git a/python/swiftest/swiftest/io.py b/python/swiftest/swiftest/io.py index caf1c0e39..ce8b800ce 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): """ @@ -71,6 +71,7 @@ def read_swiftest_param(param_file_name, param): param['EXTRA_FORCE'] = param['EXTRA_FORCE'].upper() param['BIG_DISCARD'] = param['BIG_DISCARD'].upper() param['CHK_CLOSE'] = param['CHK_CLOSE'].upper() + param['RHILL_PRESENT'] = param['RHILL_PRESENT'].upper() param['FRAGMENTATION'] = param['FRAGMENTATION'].upper() param['ROTATION'] = param['ROTATION'].upper() param['TIDES'] = param['TIDES'].upper() @@ -278,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', @@ -401,6 +403,8 @@ def make_swiftest_labels(param): plab = tlab.copy() plab.append('Mass') plab.append('Radius') + if param['RHILL_PRESENT'] == 'YES': + plab.append('Rhill') clab = ['Mass', 'Radius', 'J_2', 'J_4'] if param['ROTATION'] == 'YES': clab.append('Ip_x') @@ -492,6 +496,8 @@ def swiftest_stream(f, param): p6 = f.read_reals(np.float64) Mpl = f.read_reals(np.float64) Rpl = f.read_reals(np.float64) + if param['RHILL_PRESENT'] == 'YES': + Rhill = f.read_reals(np.float64) if param['ROTATION'] == 'YES': Ipplx = f.read_reals(np.float64) Ipply = f.read_reals(np.float64) @@ -524,6 +530,9 @@ def swiftest_stream(f, param): tvec = np.empty((6, 0)) tpid = np.empty(0) cvec = np.array([Mcb, Rcb, J2cb, J4cb]) + if param['RHILL_PRESENT'] == 'YES': + if npl > 0: + pvec = np.vstack([pvec, Rhill]) if param['ROTATION'] == 'YES': cvec = np.vstack([cvec, Ipcbx, Ipcby, Ipcbz, rotcbx, rotcby, rotcbz]) if npl > 0: @@ -671,7 +680,10 @@ def swiftest_xr2infile(ds, param, framenum=-1): print(pl.id.count().values, file=plfile) for i in pl.id: pli = pl.sel(id=i) - print(i.values, pli['Mass'].values, file=plfile) + if param['RHILL_PRESENT'] == 'YES': + print(i.values, pli['Mass'].values, pli['Rhill'].values, file=plfile) + else: + print(i.values, pli['Mass'].values, file=plfile) print(pli['Radius'].values, file=plfile) print(pli['px'].values, pli['py'].values, pli['pz'].values, file=plfile) print(pli['vx'].values, pli['vy'].values, pli['vz'].values, file=plfile) @@ -690,36 +702,55 @@ def swiftest_xr2infile(ds, param, framenum=-1): # Now make Swiftest files cbfile = FortranFile(param['CB_IN'], 'w') cbfile.write_record(cbid) - MSun = np.double(1.0) cbfile.write_record(np.double(GMSun)) - cbfile.write_record(np.double(rmin)) + cbfile.write_record(np.double(RSun)) cbfile.write_record(np.double(J2)) cbfile.write_record(np.double(J4)) cbfile.close() plfile = FortranFile(param['PL_IN'], 'w') - plfile.write_record(npl) + npl = pl.id.count().values + plid = pl.id.values + px = pl['px'].values + py = pl['py'].values + pz = pl['pz'].values + vx = pl['vx'].values + vy = pl['vy'].values + vz = pl['vz'].values + mass = pl['Mass'].values + radius = pl['Radius'].values + plfile.write_record(npl) plfile.write_record(plid) - plfile.write_record(p_pl[0]) - plfile.write_record(p_pl[1]) - plfile.write_record(p_pl[2]) - plfile.write_record(v_pl[0]) - plfile.write_record(v_pl[1]) - plfile.write_record(v_pl[2]) + plfile.write_record(px) + plfile.write_record(py) + plfile.write_record(pz) + plfile.write_record(vx) + plfile.write_record(vy) + plfile.write_record(vz) plfile.write_record(mass) + if param['RHILL_PRESENT'] == 'YES': + rhill = pl['Rhill'].values + plfile.write_record(rhill) plfile.write_record(radius) plfile.close() tpfile = FortranFile(param['TP_IN'], 'w') - ntp = 1 + ntp = tp.id.count().values + tpid = tp.id.values + px = tp['px'].values + py = tp['py'].values + pz = tp['pz'].values + vx = tp['vx'].values + vy = tp['vy'].values + vz = tp['vz'].values tpfile.write_record(ntp) tpfile.write_record(tpid) - tpfile.write_record(p_tp[0]) - tpfile.write_record(p_tp[1]) - tpfile.write_record(p_tp[2]) - tpfile.write_record(v_tp[0]) - tpfile.write_record(v_tp[1]) - tpfile.write_record(v_tp[2]) + tpfile.write_record(px) + tpfile.write_record(py) + tpfile.write_record(pz) + tpfile.write_record(vx) + tpfile.write_record(vy) + tpfile.write_record(vz) else: print(f"{param['IN_TYPE']} is an unknown file type") @@ -860,7 +891,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)> ") @@ -1022,9 +1053,13 @@ def swifter2swiftest(swifter_param, plname="", tpname="", cbname="", conversion_ for n in range(1, npl): # Loop over planets line = plold.readline() i_list = [i for i in line.split(" ") if i.strip()] - name = int(i_list[0]) + idnum = int(i_list[0]) GMpl = real2float(i_list[1]) - print(name, GMpl, file=plnew) + if swifter_param['RHILL_PRESENT'] == 'YES': + Rhill = real2float(i_list[2]) + print(idnum, GMpl, Rhill, file=plnew) + else: + print(idnum, GMpl, file=plnew) if swifter_param['CHK_CLOSE'] == 'YES': line = plold.readline() i_list = [i for i in line.split(" ") if i.strip()] @@ -1195,6 +1230,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 @@ -1238,7 +1280,6 @@ def swiftest2swifter_param(swiftest_param, J2=0.0, J4=0.0): tmp = swifter_param.pop(key, None) swifter_param['J2'] = J2 swifter_param['J4'] = J4 - swifter_param['RHILL_PRESENT'] = "YES" swifter_param['CHK_CLOSE'] = "YES" if swifter_param['OUT_STAT'] == "REPLACE": swifter_param['OUT_STAT'] = "UNKNOWN" diff --git a/python/swiftest/swiftest/simulation_class.py b/python/swiftest/swiftest/simulation_class.py index e20ef05f7..670b72a60 100644 --- a/python/swiftest/swiftest/simulation_class.py +++ b/python/swiftest/swiftest/simulation_class.py @@ -39,6 +39,7 @@ def __init__(self, codename="Swiftest", param_file=""): 'EXTRA_FORCE': "NO", 'BIG_DISCARD': "NO", 'CHK_CLOSE': "YES", + 'RHILL_PRESENT': "YES", 'FRAGMENTATION': "NO", 'ROTATION': "NO", 'TIDES': "NO", diff --git a/python/swiftest/tests/bin2xr/swiftest/bin2xr_swiftest.py b/python/swiftest/tests/bin2xr/swiftest/bin2xr_swiftest.py index 9a377876a..0a430c654 100644 --- a/python/swiftest/tests/bin2xr/swiftest/bin2xr_swiftest.py +++ b/python/swiftest/tests/bin2xr/swiftest/bin2xr_swiftest.py @@ -1,5 +1,4 @@ import swiftest -import swiftest.io as swio -param_file = "param.swiftest.in" sim = swiftest.Simulation(param_file="param.swiftest.in") -ds = swio.swiftest2xr(sim.param) \ No newline at end of file +sim.bin2xr() +sim.ds \ No newline at end of file diff --git a/python/swiftest/tests/bin2xr/swiftest/param.swiftest.in b/python/swiftest/tests/bin2xr/swiftest/param.swiftest.in index 6a9e599f9..a7f91ba33 100644 --- a/python/swiftest/tests/bin2xr/swiftest/param.swiftest.in +++ b/python/swiftest/tests/bin2xr/swiftest/param.swiftest.in @@ -1,31 +1,31 @@ -! VERSION Swiftest parameter file converted from Swift -BIG_DISCARD NO -BIN_OUT bin.dat -CB_IN cb.swiftest.in -CHK_CLOSE YES -CHK_QMIN 0.00468 -CHK_QMIN_COORD HELIO -CHK_QMIN_RANGE 4.68e-03 100.0 -CHK_RMAX 100.0 -CHK_RMIN 0.00468 -DT 5.0 -DU2M 149597870700.0 -ENC_OUT enc.dat -ENERGY NO -EXTRA_FORCE NO -FRAGMENTATION NO -GR NO -ISTEP_DUMP 7305000 -ISTEP_OUT 7305000 -MU2KG 1.988409870698051e+30 -OUT_FORM EL -OUT_STAT UNKNOWN -PL_IN pl.swiftest.in -ROTATION NO -T0 0.0 -TIDES NO -TP_IN tp.swiftest.in -TSTOP 365250000000.0 -TU2S 86400 -YARKOVSKY NO -YORP NO +! Swiftest input file generated using init_cond.py +T0 0 +TSTOP 0.15 +DT 0.0006844626967830253 +CB_IN cb.swiftest.in +PL_IN pl.swiftest.in +TP_IN tp.swiftest.in +IN_TYPE REAL8 +ISTEP_OUT 1 +ISTEP_DUMP 1 +BIN_OUT bin.swiftest.dat +OUT_TYPE REAL8 +OUT_FORM XV +OUT_STAT REPLACE +CHK_CLOSE yes +CHK_RMIN 0.004650467260962157 +CHK_RMAX 1000.0 +CHK_EJECT 1000.0 +CHK_QMIN 0.004650467260962157 +CHK_QMIN_COORD HELIO +CHK_QMIN_RANGE 0.004650467260962157 1000.0 +ENC_OUT enc.swiftest.dat +EXTRA_FORCE no +BIG_DISCARD no +ROTATION no +GR no +MU2KG 1.988409870698051e+30 +DU2M 149597870700.0 +TU2S 31557600.0 +RHILL_PRESENT yes +MTINY 1e-12 diff --git a/python/swiftest/tests/bin2xr/swiftest/pl.swiftest.in b/python/swiftest/tests/bin2xr/swiftest/pl.swiftest.in index b7814ac98..d8da7a92a 100644 Binary files a/python/swiftest/tests/bin2xr/swiftest/pl.swiftest.in and b/python/swiftest/tests/bin2xr/swiftest/pl.swiftest.in differ diff --git a/python/swiftest/tests/bin2xr/swiftest/tp.swiftest.in b/python/swiftest/tests/bin2xr/swiftest/tp.swiftest.in index b3b8f423c..64bf92f74 100644 Binary files a/python/swiftest/tests/bin2xr/swiftest/tp.swiftest.in and b/python/swiftest/tests/bin2xr/swiftest/tp.swiftest.in differ diff --git a/src/discard/discard.f90 b/src/discard/discard.f90 index f82826fac..be377e49e 100644 --- a/src/discard/discard.f90 +++ b/src/discard/discard.f90 @@ -1,23 +1,32 @@ submodule (swiftest_classes) s_discard use swiftest contains + module subroutine discard_system(self, param) !! author: David A. Minton !! !! Calls the discard methods for each body class and then the write method if any discards were detected !! 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 associate(system => self, tp => self%tp, pl => self%pl) - call tp%discard(system, param) + lany_discards = .false. call pl%discard(system, param) - if (any(tp%ldiscard(:) .or. any(pl%ldiscard(:)))) call system%write_discard(param) + call tp%discard(system, param) + if (tp%nbody > 0) lany_discards = lany_discards .or. any(tp%ldiscard(:)) + if (pl%nbody > 0) lany_discards = lany_discards .or. any(pl%ldiscard(:)) + if (lany_discards) call system%write_discard(param) end associate + return end subroutine discard_system + module subroutine discard_pl(self, system, param) !! author: David A. Minton !! @@ -27,11 +36,15 @@ 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 end subroutine discard_pl + module subroutine discard_tp(self, system, param) !! author: David A. Minton !! @@ -43,26 +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 @@ -79,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 @@ -90,18 +105,21 @@ subroutine discard_sun_tp(tp, system, param) tp%status(i) = DISCARDED_RMAX write(*, *) "Particle ", tp%id(i), " too far from sun at t = ", t tp%ldiscard(i) = .true. + tp%lmask(i) = .false. else if ((param%rmin >= 0.0_DP) .and. (rh2 < rmin2)) then tp%status(i) = DISCARDED_RMIN write(*, *) "Particle ", tp%id(i), " too close to sun at t = ", t tp%ldiscard(i) = .true. + tp%lmask(i) = .false. 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 tp%ldiscard(i) = .true. + tp%lmask(i) = .false. end if end if end if @@ -109,7 +127,8 @@ 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) !! author: David A. Minton @@ -153,10 +172,11 @@ subroutine discard_peri_tp(tp, system, param) end if end do end associate + return - end subroutine discard_peri_tp + subroutine discard_pl_tp(tp, system, param) !! author: David A. Minton !! @@ -184,6 +204,7 @@ subroutine discard_pl_tp(tp, system, param) call discard_pl_close(dx(:), dv(:), dt, radius**2, isp, r2min) if (isp /= 0) then tp%status(i) = DISCARDED_PLR + tp%lmask(i) = .false. pl%ldiscard(j) = .true. write(*, *) "Particle ", tp%id(i), " too close to massive body ", pl%id(j), " at t = ", t tp%ldiscard(i) = .true. @@ -192,11 +213,11 @@ subroutine discard_pl_tp(tp, system, param) end do end if end do - end associate + return - end subroutine discard_pl_tp + subroutine discard_pl_close(dx, dv, dt, r2crit, iflag, r2min) !! author: David A. Minton diff --git a/src/drift/drift.f90 b/src/drift/drift.f90 index 8bba1a273..79744c0f3 100644 --- a/src/drift/drift.f90 +++ b/src/drift/drift.f90 @@ -9,6 +9,82 @@ integer(I2B), parameter :: NLAG2 = 40 contains + + module subroutine drift_body(self, system, param, dt) + !! author: David A. Minton + !! + !! Loop bodies and call Danby drift routine on the heliocentric position and velocities. + !! + !! Adapted from Hal Levison's Swift routine drift_tp.f + !! Adapted from David E. Kaufmann's Swifter routine whm_drift_tp.f90 + implicit none + ! Arguments + class(swiftest_body), intent(inout) :: self !! Swiftest 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) :: dt !! Stepsize + ! Internals + integer(I4B) :: i + integer(I4B), dimension(:), allocatable :: iflag + + associate(n => self%nbody) + allocate(iflag(n)) + iflag(:) = 0 + call drift_all(self%mu, self%xh, self%vh, self%nbody, param, dt, self%lmask, iflag) + if (any(iflag(1:n) /= 0)) then + where(iflag(1:n) /= 0) self%status(1:n) = DISCARDED_DRIFTERR + do i = 1, n + if (iflag(i) /= 0) write(*, *) " Body ", self%id(i), " lost due to error in Danby drift" + end do + end if + end associate + + return + end subroutine drift_body + + + module pure subroutine drift_all(mu, x, v, n, param, dt, mask, iflag) + !! author: David A. Minton + !! + !! Loop bodies and call Danby drift routine on all bodies for the given position and velocity vector. + !! + !! Adapted from Hal Levison's Swift routine drift_tp.f + !! Adapted from David E. Kaufmann's Swifter routine whm_drift_tp.f9 + implicit none + ! Arguments + real(DP), dimension(:), intent(in) :: mu !! Vector of gravitational constants + real(DP), dimension(:,:), intent(inout) :: x, v !! Position and velocity vectors + integer(I4B), intent(in) :: n !! number of bodies + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(in) :: dt !! Stepsize + logical, dimension(:), intent(in) :: mask !! Logical mask of size self%nbody that determines which bodies to drift. + integer(I4B), dimension(:), intent(out) :: iflag !! Vector of error flags. 0 means no problem + ! Internals + integer(I4B) :: i + real(DP) :: energy, vmag2, rmag !! Variables used in GR calculation + real(DP), dimension(:), allocatable :: dtp + + if (n == 0) return + + allocate(dtp(n)) + if (param%lgr) then + do concurrent(i = 1:n, mask(i)) + rmag = norm2(x(:, i)) + vmag2 = dot_product(v(:, i), v(:, i)) + energy = 0.5_DP * vmag2 - mu(i) / rmag + dtp(i) = dt * (1.0_DP + 3 * param%inv_c2 * energy) + end do + else + where(mask(1:n)) dtp(1:n) = dt + end if + do concurrent(i = 1:n, mask(i)) + call drift_one(mu(i), x(1,i), x(2,i), x(3,i), v(1,i), v(2,i), v(3,i), dtp(i), iflag(i)) + end do + + return + end subroutine drift_all + + module pure elemental subroutine drift_one(mu, px, py, pz, vx, vy, vz, dt, iflag) !! author: The Purdue Swiftest Team - David A. Minton, Carlisle A. Wishard, Jennifer L.L. Pouplin, and Jacob R. Elliott !! @@ -44,6 +120,7 @@ module pure elemental subroutine drift_one(mu, px, py, pz, vx, vy, vz, dt, iflag return end subroutine drift_one + pure subroutine drift_dan(mu, x0, v0, dt0, iflag) !! author: David A. Minton !! @@ -110,9 +187,11 @@ pure subroutine drift_dan(mu, x0, v0, dt0, iflag) x0(:) = x(:) v0(:) = v(:) end if + return end subroutine drift_dan + pure subroutine drift_kepmd(dm, es, ec, x, s, c) !! author: David A. Minton !! @@ -151,6 +230,7 @@ pure subroutine drift_kepmd(dm, es, ec, x, s, c) return end subroutine drift_kepmd + pure subroutine drift_kepu(dt,r0,mu,alpha,u,fp,c1,c2,c3,iflag) !! author: David A. Minton !! @@ -178,6 +258,7 @@ pure subroutine drift_kepu(dt,r0,mu,alpha,u,fp,c1,c2,c3,iflag) return end subroutine drift_kepu + pure subroutine drift_kepu_fchk(dt, r0, mu, alpha, u, s, f) !! author: David A. Minton !! @@ -200,6 +281,7 @@ pure subroutine drift_kepu_fchk(dt, r0, mu, alpha, u, s, f) return end subroutine drift_kepu_fchk + pure subroutine drift_kepu_guess(dt, r0, mu, alpha, u, s) !! author: David A. Minton !! @@ -237,6 +319,7 @@ pure subroutine drift_kepu_guess(dt, r0, mu, alpha, u, s) return end subroutine drift_kepu_guess + pure subroutine drift_kepu_lag(s, dt, r0, mu, alpha, u, fp, c1, c2, c3, iflag) !! author: David A. Minton !! @@ -281,6 +364,7 @@ pure subroutine drift_kepu_lag(s, dt, r0, mu, alpha, u, fp, c1, c2, c3, iflag) return end subroutine drift_kepu_lag + pure subroutine drift_kepu_new(s, dt, r0, mu, alpha, u, fp, c1, c2, c3, iflag) !! author: David A. Minton !! @@ -322,6 +406,7 @@ pure subroutine drift_kepu_new(s, dt, r0, mu, alpha, u, fp, c1, c2, c3, iflag) return end subroutine drift_kepu_new + pure subroutine drift_kepu_p3solve(dt, r0, mu, alpha, u, s, iflag) !! author: David A. Minton !! @@ -364,6 +449,7 @@ pure subroutine drift_kepu_p3solve(dt, r0, mu, alpha, u, s, iflag) return end subroutine drift_kepu_p3solve + pure subroutine drift_kepu_stumpff(x, c0, c1, c2, c3) !! author: David A. Minton @@ -406,4 +492,5 @@ pure subroutine drift_kepu_stumpff(x, c0, c1, c2, c3) return end subroutine drift_kepu_stumpff + end submodule drift_implementation diff --git a/src/eucl/eucl.f90 b/src/eucl/eucl.f90 index 519a38d76..af1646e4c 100644 --- a/src/eucl/eucl.f90 +++ b/src/eucl/eucl.f90 @@ -1,6 +1,7 @@ submodule (swiftest_classes) s_eucl use swiftest contains + module subroutine eucl_dist_index_plpl(self) !! author: Jacob R. Elliott and David A. Minton !! @@ -14,64 +15,24 @@ module subroutine eucl_dist_index_plpl(self) ! Arguments class(swiftest_pl), intent(inout) :: self !! Swiftest massive body objec ! Internals - integer(I4B) :: i, j, k, kp, p - - associate(npl => self%nbody, num_comparisons => self%num_comparisons, k_eucl => self%k_eucl) - num_comparisons = (npl * (npl - 1) / 2) ! number of entries in a strict lower triangle, nplm x npl, minus first column - if (allocated(self%k_eucl)) deallocate(self%k_eucl) ! Reset the index array if it's been set previously - if (allocated(self%irij3)) deallocate(self%irij3) - allocate(self%k_eucl(2, num_comparisons)) - allocate(self%irij3(num_comparisons)) - !do concurrent(k = 1:num_comparisons) !shared(num_comparisons, k_eucl, npl) local(kp, i, j, p) - do k = 1, num_comparisons - kp = npl * (npl - 1) / 2 - k - p = floor((sqrt(1._DP + 8 * kp) - 1._DP) / 2._DP) - i = k - (npl - 1) * (npl - 2) / 2 + p * (p + 1) / 2 + 1 - j = npl - 1 - p - k_eucl(1, k) = i - k_eucl(2, k) = j - end do - end associate - return - - end subroutine eucl_dist_index_plpl - - module subroutine eucl_dist_index_pltp(self, pl) - !! author: Jacob R. Elliott and David A. Minton - !! - !! Turns i,j indices into k index for use in the Euclidean distance matrix - !! - !! Reference: - !! - !! Mélodie Angeletti, Jean-Marie Bonny, Jonas Koko. Parallel Euclidean distance matrix computation on big datasets *. - !! 2019. hal-0204751 implicit none - class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object - class(swiftest_pl), intent(inout) :: pl !! Swiftest massive body object - end subroutine eucl_dist_index_pltp - - module subroutine eucl_irij3_plpl(self) - !! author: Jacob R. Elliott and David A. Minton - !! - !! Efficient parallel loop-blocking algrorithm for evaluating the Euclidean distance matrix for planet-planet - implicit none - ! Arguments - class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object - ! Internals - integer(I4B) :: k, i, j - real(DP), dimension(NDIM) :: dx - real(DP) :: rji2 + integer(I8B) :: i, j, counter, npl - associate(k_eucl => self%k_eucl, xh => self%xh, irij3 => self%irij3, nk => self%num_comparisons) - do k = 1, nk - i = k_eucl(1, k) - j = k_eucl(2, k) - dx(:) = xh(:, j) - xh(:, i) - rji2 = dot_product(dx(:), dx(:)) - irij3(k) = 1.0_DP / (rji2 * sqrt(rji2)) + npl = int(self%nbody, kind=I8B) + associate(nplpl => self%nplpl) + nplpl = (npl * (npl - 1) / 2) ! number of entries in a strict lower triangle, nplm x npl, minus first column + if (allocated(self%k_plpl)) deallocate(self%k_plpl) ! Reset the index array if it's been set previously + allocate(self%k_plpl(2, nplpl)) + do i = 1, npl + counter = (i - 1_I8B) * npl - i * (i - 1_I8B) / 2_I8B + 1_I8B + do j = i + 1_I8B, npl + self%k_plpl(1, counter) = i + self%k_plpl(2, counter) = j + counter = counter + 1_I8B + end do end do end associate return - end subroutine eucl_irij3_plpl + end subroutine eucl_dist_index_plpl end submodule s_eucl diff --git a/src/fragmentation/fragmentation.f90 b/src/fragmentation/fragmentation.f90 new file mode 100644 index 000000000..460060183 --- /dev/null +++ b/src/fragmentation/fragmentation.f90 @@ -0,0 +1,1114 @@ +submodule(swiftest_classes) s_fragmentation + use swiftest +contains + + module subroutine fragmentation_initialize(system, param, family, x, v, L_spin, Ip, mass, radius, & + nfrag, Ip_frag, m_frag, rad_frag, xb_frag, vb_frag, rot_frag, Qloss, lfailure) + !! Author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! Initialize the position and velocity of fragments to conserve energy and momentum. + use, intrinsic :: ieee_exceptions + implicit none + ! Arguments + class(swiftest_nbody_system), intent(inout) :: system + class(swiftest_parameters), intent(in) :: param + integer(I4B), dimension(:), intent(in) :: family + real(DP), dimension(:,:), intent(inout) :: x, v, L_spin, Ip + real(DP), dimension(:), intent(inout) :: mass, radius + integer(I4B), intent(inout) :: nfrag + real(DP), dimension(:), allocatable, intent(inout) :: m_frag, rad_frag + real(DP), dimension(:,:), allocatable, intent(inout) :: Ip_frag + real(DP), dimension(:,:), allocatable, intent(inout) :: xb_frag, vb_frag, rot_frag + logical, intent(out) :: lfailure ! Answers the question: Should this have been a merger instead? + real(DP), intent(inout) :: Qloss + ! Internals + real(DP) :: mscale, rscale, vscale, tscale, Lscale, Escale ! Scale factors that reduce quantities to O(~1) in the collisional system + real(DP) :: mtot + real(DP), dimension(NDIM) :: xcom, vcom + integer(I4B) :: ii + logical, dimension(:), allocatable :: lexclude + real(DP), dimension(NDIM, 2) :: rot, L_orb + real(DP), dimension(:,:), allocatable :: x_frag, v_frag, v_r_unit, v_t_unit, v_h_unit + real(DP), dimension(:), allocatable :: rmag, rotmag, v_r_mag, v_t_mag + real(DP), dimension(NDIM) :: Ltot_before + real(DP), dimension(NDIM) :: Ltot_after + real(DP) :: Etot_before, ke_orbit_before, ke_spin_before, pe_before, Lmag_before + real(DP) :: Etot_after, ke_orbit_after, ke_spin_after, pe_after, Lmag_after, dEtot, dLmag + real(DP), dimension(NDIM) :: L_frag_tot, L_frag_orb + real(DP) :: ke_frag_budget, ke_frag_orbit, ke_radial, ke_frag_spin, ke_avg_deficit, ke_avg_deficit_old + real(DP), dimension(NDIM) :: x_col_unit, y_col_unit, z_col_unit + character(len=*), parameter :: fmtlabel = "(A14,10(ES11.4,1X,:))" + integer(I4B) :: try, subtry + integer(I4B), parameter :: NFRAG_MIN = 7 !! The minimum allowable number of fragments (set to 6 because that's how many unknowns are needed in the tangential velocity calculation) + real(DP) :: r_max_start, r_max_start_old, r_max, f_spin + real(DP), parameter :: Ltol = 10 * epsilon(1.0_DP) + real(DP), parameter :: Etol = 1e-10_DP + integer(I4B), parameter :: MAXTRY = 3000 + integer(I4B), parameter :: TANTRY = 3 + logical, dimension(size(IEEE_ALL)) :: fpe_halting_modes, fpe_quiet_modes + + if (nfrag < NFRAG_MIN) then + write(*,*) "symba_frag_pos needs at least ",NFRAG_MIN," fragments, but only ",nfrag," were given." + lfailure = .true. + return + end if + + call ieee_get_halting_mode(IEEE_ALL,fpe_halting_modes) ! Save the current halting modes so we can turn them off temporarily + fpe_quiet_modes(:) = .false. + call ieee_set_halting_mode(IEEE_ALL,fpe_quiet_modes) + + f_spin = 0.05_DP + mscale = 1.0_DP + rscale = 1.0_DP + vscale = 1.0_DP + tscale = 1.0_DP + Lscale = 1.0_DP + Escale = 1.0_DP + + associate(npl => system%pl%nbody, status => system%pl%status) + allocate(lexclude(npl)) + where (status(1:npl) == INACTIVE) ! Safety check in case one of the included bodies has been previously deactivated + lexclude(1:npl) = .true. + elsewhere + lexclude(1:npl) = .false. + end where + end associate + + allocate(x_frag, source=xb_frag) + allocate(v_frag, source=vb_frag) + + call set_scale_factors() + call define_coordinate_system() + call calculate_system_energy(linclude_fragments=.false.) + + r_max_start = norm2(x(:,2) - x(:,1)) + try = 1 + lfailure = .false. + ke_avg_deficit = 0.0_DP + do while (try < MAXTRY) + lfailure = .false. + ke_avg_deficit_old = ke_avg_deficit + ke_avg_deficit = 0.0_DP + subtry = 1 + do + call set_fragment_position_vectors() + call set_fragment_tan_vel(lfailure) + ke_avg_deficit = ke_avg_deficit - ke_radial + subtry = subtry + 1 + if (.not.lfailure .or. subtry == TANTRY) exit + !write(*,*) 'Trying new arrangement' + end do + ke_avg_deficit = ke_avg_deficit / subtry + if (lfailure) write(*,*) 'Failed to find tangential velocities' + + if (.not.lfailure) then + call set_fragment_radial_velocities(lfailure) + if (lfailure) write(*,*) 'Failed to find radial velocities' + if (.not.lfailure) then + call calculate_system_energy(linclude_fragments=.true.) + !write(*,*) 'Qloss : ',Qloss + !write(*,*) '-dEtot: ',-dEtot + !write(*,*) 'delta : ',abs((dEtot + Qloss)) + if ((abs(dEtot + Qloss) > Etol) .or. (dEtot > 0.0_DP)) then + write(*,*) 'Failed due to high energy error: ',dEtot, abs(dEtot + Qloss) / Etol + lfailure = .true. + else if (abs(dLmag) / Lmag_before > Ltol) then + write(*,*) 'Failed due to high angular momentum error: ', dLmag / Lmag_before + lfailure = .true. + end if + end if + end if + + if (.not.lfailure) exit + call restructure_failed_fragments() + try = try + 1 + end do + write(*, "(' -------------------------------------------------------------------------------------')") + write(*, "(' Final diagnostic')") + write(*, "(' -------------------------------------------------------------------------------------')") + if (lfailure) then + write(*,*) "symba_frag_pos failed after: ",try," tries" + do ii = 1, nfrag + vb_frag(:, ii) = vcom(:) + end do + else + write(*,*) "symba_frag_pos succeeded after: ",try," tries" + write(*, "(' dL_tot should be very small' )") + write(*,fmtlabel) ' dL_tot |', dLmag / Lmag_before + write(*, "(' dE_tot should be negative and equal to Qloss' )") + write(*,fmtlabel) ' dE_tot |', dEtot / abs(Etot_before) + write(*,fmtlabel) ' Qloss |', -Qloss / abs(Etot_before) + write(*,fmtlabel) ' dE - Qloss |', (Etot_after - Etot_before + Qloss) / abs(Etot_before) + end if + write(*, "(' -------------------------------------------------------------------------------------')") + + call restore_scale_factors() + call ieee_set_halting_mode(IEEE_ALL,fpe_halting_modes) ! Save the current halting modes so we can turn them off temporarily + + return + + contains + + ! Because of the complexity of this procedure, we have chosen to break it up into a series of nested subroutines. + + subroutine set_scale_factors() + !! author: David A. Minton + !! + !! Scales dimenional quantities to ~O(1) with respect to the collisional system. This scaling makes it easier for the non-linear minimization + !! to converge on a solution + implicit none + integer(I4B) :: i + + ! Find the center of mass of the collisional system + mtot = sum(mass(:)) + xcom(:) = (mass(1) * x(:,1) + mass(2) * x(:,2)) / mtot + vcom(:) = (mass(1) * v(:,1) + mass(2) * v(:,2)) / mtot + + ! Set scale factors + !! Because of the implied G, mass is actually G*mass with units of distance**3 / time**2 + Escale = 0.5_DP * (mass(1) * dot_product(v(:,1), v(:,1)) + mass(2) * dot_product(v(:,2), v(:,2))) + rscale = sum(radius(:)) + mscale = sqrt(Escale * rscale) + vscale = sqrt(Escale / mscale) + tscale = rscale / vscale + Lscale = mscale * rscale * vscale + + xcom(:) = xcom(:) / rscale + vcom(:) = vcom(:) / vscale + + mtot = mtot / mscale + mass = mass / mscale + radius = radius / rscale + x = x / rscale + v = v / vscale + L_spin = L_spin / Lscale + do i = 1, 2 + rot(:,i) = L_spin(:,i) / (mass(i) * radius(i)**2 * Ip(3, i)) + end do + + m_frag = m_frag / mscale + rad_frag = rad_frag / rscale + Qloss = Qloss / Escale + + return + end subroutine set_scale_factors + + subroutine restore_scale_factors() + !! author: David A. Minton + !! + !! Restores dimenional quantities back to the system units + implicit none + integer(I4B) :: i + + call ieee_set_halting_mode(IEEE_ALL,.false.) + ! Restore scale factors + xcom(:) = xcom(:) * rscale + vcom(:) = vcom(:) * vscale + + mtot = mtot * mscale + mass = mass * mscale + radius = radius * rscale + x = x * rscale + v = v * vscale + L_spin = L_spin * Lscale + do i = 1, 2 + rot(:,i) = L_spin(:,i) * (mass(i) * radius(i)**2 * Ip(3, i)) + end do + + m_frag = m_frag * mscale + rad_frag = rad_frag * rscale + rot_frag = rot_frag / tscale + x_frag = x_frag * rscale + v_frag = v_frag * vscale + Qloss = Qloss * Escale + + do i = 1, nfrag + xb_frag(:, i) = x_frag(:, i) + xcom(:) + vb_frag(:, i) = v_frag(:, i) + vcom(:) + end do + + Etot_before = Etot_before * Escale + pe_before = pe_before * Escale + ke_spin_before = ke_spin_before * Escale + ke_orbit_before = ke_orbit_before * Escale + Ltot_before = Ltot_before * Lscale + Lmag_before = Lmag_before * Lscale + Etot_after = Etot_after * Escale + pe_after = pe_after * Escale + ke_spin_after = ke_spin_after * Escale + ke_orbit_after = ke_orbit_after * Escale + Ltot_after = Ltot_after * Lscale + Lmag_after = Lmag_after * Lscale + + mscale = 1.0_DP + rscale = 1.0_DP + vscale = 1.0_DP + tscale = 1.0_DP + Lscale = 1.0_DP + Escale = 1.0_DP + + return + end subroutine restore_scale_factors + + subroutine define_coordinate_system() + !! author: David A. Minton + !! + !! Defines the collisional coordinate system, including the unit vectors of both the system and individual fragments. + implicit none + integer(I4B) :: i + real(DP), dimension(NDIM) :: x_cross_v, xc, vc, delta_r, delta_v + real(DP) :: r_col_norm, v_col_norm + + allocate(rmag(nfrag)) + allocate(rotmag(nfrag)) + allocate(v_r_mag(nfrag)) + allocate(v_t_mag(nfrag)) + allocate(v_r_unit(NDIM,nfrag)) + allocate(v_t_unit(NDIM,nfrag)) + allocate(v_h_unit(NDIM,nfrag)) + + rmag(:) = 0.0_DP + rotmag(:) = 0.0_DP + v_r_mag(:) = 0.0_DP + v_t_mag(:) = 0.0_DP + v_r_unit(:,:) = 0.0_DP + v_t_unit(:,:) = 0.0_DP + v_h_unit(:,:) = 0.0_DP + + L_orb(:, :) = 0.0_DP + ! Compute orbital angular momentum of pre-impact system + do i = 1, 2 + xc(:) = x(:, i) - xcom(:) + vc(:) = v(:, i) - vcom(:) + x_cross_v(:) = xc(:) .cross. vc(:) + L_orb(:, i) = mass(i) * x_cross_v(:) + end do + + ! Compute orbital angular momentum of pre-impact system. This will be the normal vector to the collision fragment plane + L_frag_tot(:) = L_spin(:, 1) + L_spin(:, 2) + L_orb(:, 1) + L_orb(:, 2) + + delta_v(:) = v(:, 2) - v(:, 1) + v_col_norm = norm2(delta_v(:)) + delta_r(:) = x(:, 2) - x(:, 1) + r_col_norm = norm2(delta_r(:)) + + ! We will initialize fragments on a plane defined by the pre-impact system, with the z-axis aligned with the angular momentum vector + ! and the y-axis aligned with the pre-impact distance vector. + y_col_unit(:) = delta_r(:) / r_col_norm + z_col_unit(:) = L_frag_tot(:) / norm2(L_frag_tot) + ! The cross product of the y- by z-axis will give us the x-axis + x_col_unit(:) = y_col_unit(:) .cross. z_col_unit(:) + + return + end subroutine define_coordinate_system + + subroutine calculate_system_energy(linclude_fragments) + !! Author: David A. Minton + !! + !! Calculates total system energy, including all bodies in the pl list that do not have a corresponding value of the lexclude array that is true + !! and optionally including fragments. + implicit none + ! Arguments + logical, intent(in) :: linclude_fragments + ! Internals + integer(I4B) :: i, npl_new, nplm + logical, dimension(:), allocatable :: ltmp + logical :: lk_plpl + class(swiftest_nbody_system), allocatable :: tmpsys + + ! Because we're making a copy of symba_pl with the excludes/fragments appended, we need to deallocate the + ! big k_plpl array and recreate it when we're done, otherwise we run the risk of blowing up the memory by + ! allocating two of these ginormous arrays simulteouously. This is not particularly efficient, but as this + ! subroutine should be called relatively infrequently, it shouldn't matter too much. + !if (allocated(pl%k_plpl)) deallocate(pl%k_plpl) + + ! Build the internal planet list out of the non-excluded bodies and optionally with fragments appended. This + ! will get passed to the energy calculation subroutine so that energy is computed exactly the same way is it + ! is in the main program. + associate(pl => system%pl, npl => system%pl%nbody, cb => system%cb) + lk_plpl = allocated(pl%k_plpl) + if (lk_plpl) deallocate(pl%k_plpl) + if (linclude_fragments) then ! Temporarily expand the planet list to feed it into symba_energy + lexclude(family(:)) = .true. + npl_new = npl + nfrag + else + npl_new = npl + end if + call setup_construct_system(tmpsys, param) + deallocate(tmpsys%cb) + allocate(tmpsys%cb, source=cb) + allocate(ltmp(npl)) + ltmp(:) = .true. + call tmpsys%pl%setup(npl_new, param) + call tmpsys%pl%fill(pl, ltmp) + deallocate(ltmp) + + if (linclude_fragments) then ! Append the fragments if they are included + ! Energy calculation requires the fragments to be in the system barcyentric frame, s + tmpsys%pl%mass(npl+1:npl_new) = m_frag(1:nfrag) + tmpsys%pl%radius(npl+1:npl_new) = rad_frag(1:nfrag) + tmpsys%pl%xb(:,npl+1:npl_new) = xb_frag(:,1:nfrag) + tmpsys%pl%vb(:,npl+1:npl_new) = vb_frag(:,1:nfrag) + tmpsys%pl%status(npl+1:npl_new) = COLLISION + if (param%lrotation) then + tmpsys%pl%Ip(:,npl+1:npl_new) = Ip_frag(:,1:nfrag) + tmpsys%pl%rot(:,npl+1:npl_new) = rot_frag(:,1:nfrag) + end if + call tmpsys%pl%b2h(tmpsys%cb) + allocate(ltmp(npl_new)) + ltmp(1:npl) = lexclude(1:npl) + ltmp(npl+1:npl_new) = .false. + call move_alloc(ltmp, lexclude) + end if + + where (lexclude(1:npl_new)) + tmpsys%pl%status(1:npl_new) = INACTIVE + end where + + select type(plwksp => tmpsys%pl) + class is (symba_pl) + select type(param) + class is (symba_parameters) + plwksp%nplm = count(plwksp%Gmass > param%mtiny / mscale) + end select + end select + call tmpsys%pl%eucl_index() + call tmpsys%get_energy_and_momentum(param) + + ! Restore the big array + deallocate(tmpsys%pl%k_plpl) + select type(pl) + class is (symba_pl) + select type(param) + class is (symba_parameters) + nplm = count(pl%mass > param%mtiny) + end select + end select + if (lk_plpl) call pl%eucl_index() + + ! Calculate the current fragment energy and momentum balances + if (linclude_fragments) then + Ltot_after(:) = tmpsys%Lorbit(:) + tmpsys%Lspin(:) + Lmag_after = norm2(Ltot_after(:)) + ke_orbit_after = tmpsys%ke_orbit + ke_spin_after = tmpsys%ke_spin + pe_after = tmpsys%pe + Etot_after = ke_orbit_after + ke_spin_after + pe_after + dEtot = Etot_after - Etot_before + dLmag = norm2(Ltot_after(:) - Ltot_before(:)) + else + Ltot_before(:) = tmpsys%Lorbit(:) + tmpsys%Lspin(:) + Lmag_before = norm2(Ltot_before(:)) + ke_orbit_before = tmpsys%ke_orbit + ke_spin_before = tmpsys%ke_spin + pe_before = tmpsys%pe + Etot_before = ke_orbit_before + ke_spin_before + pe_before + end if + end associate + return + end subroutine calculate_system_energy + + subroutine shift_vector_to_origin(m_frag, vec_frag) + !! Author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! Adjusts the position or velocity of the fragments as needed to align them with the origin + implicit none + ! Arguments + real(DP), dimension(:), intent(in) :: m_frag !! Fragment masses + real(DP), dimension(:,:), intent(inout) :: vec_frag !! Fragment positions or velocities in the center of mass frame + + ! Internals + real(DP), dimension(NDIM) :: mvec_frag, COM_offset + integer(I4B) :: i + + mvec_frag(:) = 0.0_DP + + do i = 1, nfrag + mvec_frag = mvec_frag(:) + vec_frag(:,i) * m_frag(i) + end do + COM_offset(:) = -mvec_frag(:) / mtot + do i = 1, nfrag + vec_frag(:, i) = vec_frag(:, i) + COM_offset(:) + end do + + return + end subroutine shift_vector_to_origin + + subroutine set_fragment_position_vectors() + !! Author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! Initializes the orbits of the fragments around the center of mass. The fragments are initially placed on a plane defined by the + !! pre-impact angular momentum. They are distributed on an ellipse surrounding the center of mass. + !! The initial positions do not conserve energy or momentum, so these need to be adjusted later. + + implicit none + real(DP) :: dis, rad + real(DP), dimension(NDIM) :: L_sigma + logical, dimension(:), allocatable :: loverlap + integer(I4B) :: i, j + + allocate(loverlap(nfrag)) + + ! Place the fragments into a region that is big enough that we should usually not have overlapping bodies + ! An overlapping bodies will collide in the next time step, so it's not a major problem if they do (it just slows the run down) + r_max = r_max_start + rad = sum(radius(:)) + + ! We will treat the first two fragments of the list as special cases. They get initialized the maximum distances apart along the original impactor distance vector. + ! This is done because in a regular disruption, the first body is the largest, the second the second largest, and the rest are smaller equal-mass fragments. + + call random_number(x_frag(:,3:nfrag)) + loverlap(:) = .true. + do while (any(loverlap(3:nfrag))) + x_frag(:, 1) = x(:, 1) - xcom(:) + x_frag(:, 2) = x(:, 2) - xcom(:) + r_max = r_max + 0.1_DP * rad + do i = 3, nfrag + if (loverlap(i)) then + call random_number(x_frag(:,i)) + x_frag(:, i) = 2 * (x_frag(:, i) - 0.5_DP) * r_max + end if + end do + loverlap(:) = .false. + do j = 1, nfrag + do i = j + 1, nfrag + dis = norm2(x_frag(:,j) - x_frag(:,i)) + loverlap(i) = loverlap(i) .or. (dis <= (rad_frag(i) + rad_frag(j))) + end do + end do + end do + call shift_vector_to_origin(m_frag, x_frag) + + do i = 1, nfrag + rmag(i) = norm2(x_frag(:, i)) + v_r_unit(:, i) = x_frag(:, i) / rmag(i) + call random_number(L_sigma(:)) ! Randomize the tangential velocity direction. This helps to ensure that the tangential velocity doesn't completely line up with the angular momentum vector, + ! otherwise we can get an ill-conditioned system + v_h_unit(:, i) = z_col_unit(:) + 2e-1_DP * (L_sigma(:) - 0.5_DP) + v_h_unit(:, i) = v_h_unit(:, i) / norm2(v_h_unit(:, i)) + v_t_unit(:, i) = v_h_unit(:, i) .cross. v_r_unit(:, i) + xb_frag(:,i) = x_frag(:,i) + xcom(:) + end do + + return + end subroutine set_fragment_position_vectors + + subroutine set_fragment_tan_vel(lerr) + !! Author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! Adjusts the tangential velocities and spins of a collection of fragments such that they conserve angular momentum without blowing the fragment kinetic energy budget. + !! This procedure works in several stages, with a goal to solve the angular and linear momentum constraints on the fragments, while still leaving a positive balance of + !! our fragment kinetic energy (ke_frag_budget) that we can put into the radial velocity distribution. + !! + !! The first thing we'll try to do is solve for the tangential velocities of the first 6 fragments, using angular and linear momentum as constraints and an initial + !! tangential velocity distribution for the remaining bodies (if there are any) that distributes their angular momentum equally between them. + !! If that doesn't work and we blow our kinetic energy budget, we will attempt to find a tangential velocity distribution that minimizes the kinetic energy while + !! conserving momentum. + !! + !! A failure will trigger a restructuring of the fragments so we will try new values of the radial position distribution. + implicit none + ! Arguments + logical, intent(out) :: lerr + ! Internals + integer(I4B) :: i + real(DP), parameter :: TOL = 1e-4_DP + real(DP), dimension(:), allocatable :: v_t_initial + type(lambda_obj) :: spinfunc + type(lambda_obj_err) :: objective_function + real(DP), dimension(NDIM) :: L_frag_spin, L_remainder, Li, rot_L, rot_ke + + ! Initialize the fragments with 0 velocity and spin so we can divide up the balance between the tangential, radial, and spin components while conserving momentum + lerr = .false. + vb_frag(:,:) = 0.0_DP + rot_frag(:,:) = 0.0_DP + v_t_mag(:) = 0.0_DP + v_r_mag(:) = 0.0_DP + + call calculate_system_energy(linclude_fragments=.true.) + ke_frag_budget = -dEtot - Qloss + !write(*,*) '***************************************************' + !write(*,*) 'Original dis : ',norm2(x(:,2) - x(:,1)) + !write(*,*) 'r_max : ',r_max + !write(*,*) 'f_spin : ',f_spin + !write(*,*) '***************************************************' + !write(*,*) 'Energy balance so far: ' + !write(*,*) 'ke_frag_budget : ',ke_frag_budget + !write(*,*) 'ke_orbit_before: ',ke_orbit_before + !write(*,*) 'ke_orbit_after : ',ke_orbit_after + !write(*,*) 'ke_spin_before : ',ke_spin_before + !write(*,*) 'ke_spin_after : ',ke_spin_after + !write(*,*) 'pe_before : ',pe_before + !write(*,*) 'pe_after : ',pe_after + !write(*,*) 'Qloss : ',Qloss + !write(*,*) '***************************************************' + if (ke_frag_budget < 0.0_DP) then + write(*,*) 'Negative ke_frag_budget: ',ke_frag_budget + r_max_start = r_max_start / 2 + lerr = .true. + return + end if + + allocate(v_t_initial, mold=v_t_mag) + + L_frag_spin(:) = 0.0_DP + ke_frag_spin = 0.0_DP + ! Start the first two bodies with the same rotation as the original two impactors, then distribute the remaining angular momentum among the rest + do i = 1, 2 + rot_frag(:, i) = rot(:, i) + L_frag_spin(:) = L_frag_spin(:) + m_frag(i) * rad_frag(i)**2 * Ip_frag(3, i) * rot_frag(:, i) + end do + L_frag_orb(:) = L_frag_tot(:) - L_frag_spin(:) + L_frag_spin(:) = 0.0_DP + do i = 1, nfrag + ! Convert a fraction (f_spin) of either the remaining angular momentum or kinetic energy budget into spin, whichever gives the smaller rotation so as not to blow any budgets + rot_ke(:) = sqrt(2 * f_spin * ke_frag_budget / (nfrag * m_frag(i) * rad_frag(i)**2 * Ip_frag(3, i))) * L_frag_orb(:) / norm2(L_frag_orb(:)) + rot_L(:) = f_spin * L_frag_orb(:) / (nfrag * m_frag(i) * rad_frag(i)**2 * Ip_frag(3, i)) + if (norm2(rot_ke) < norm2(rot_L)) then + rot_frag(:,i) = rot_frag(:, i) + rot_ke(:) + else + rot_frag(:, i) = rot_frag(:, i) + rot_L(:) + end if + L_frag_spin(:) = L_frag_spin(:) + m_frag(i) * rad_frag(i)**2 * Ip_frag(3, i) * rot_frag(:, i) + ke_frag_spin = ke_frag_spin + m_frag(i) * Ip_frag(3, i) * rad_frag(i)**2 * dot_product(rot_frag(:, i), rot_frag(:, i)) + end do + ke_frag_spin = 0.5_DP * ke_frag_spin + ! Convert a fraction of the pre-impact angular momentum into fragment spin angular momentum + L_frag_orb(:) = L_frag_tot(:) - L_frag_spin(:) + L_remainder(:) = L_frag_orb(:) + ! Next we will solve for the tangential component of the velocities that both conserves linear momentum and uses the remaining angular momentum not used in spin. + ! This will be done using a linear solver that solves for the tangential velocities of the first 6 fragments, constrained by the linear and angular momentum vectors, + ! which is embedded in a non-linear minimizer that will adjust the tangential velocities of the remaining i>6 fragments to minimize kinetic energy for a given momentum solution + ! The initial conditions fed to the minimizer for the fragments will be the remaining angular momentum distributed between the fragments. + do i = 1, nfrag + v_t_initial(i) = norm2(L_remainder(:)) / ((nfrag - i + 1) * m_frag(i) * norm2(x_frag(:,i))) + Li(:) = m_frag(i) * x_frag(:,i) .cross. v_t_initial(i) * v_t_unit(:, i) + L_remainder(:) = L_remainder(:) - Li(:) + end do + + ! Find the local kinetic energy minimum for the system that conserves linear and angular momentum + objective_function = lambda_obj(tangential_objective_function, lerr) + v_t_mag(7:nfrag) = util_minimize_bfgs(objective_function, nfrag-6, v_t_initial(7:nfrag), TOL, lerr) + ! Now that the KE-minimized values of the i>6 fragments are found, calculate the momentum-conserving solution for tangential velociteis + v_t_initial(7:nfrag) = v_t_mag(7:nfrag) + v_t_mag(1:nfrag) = solve_fragment_tan_vel(v_t_mag_input=v_t_initial(7:nfrag), lerr=lerr) + + ! Perform one final shift of the radial velocity vectors to align with the center of mass of the collisional system (the origin) + vb_frag(:,1:nfrag) = vmag_to_vb(v_r_mag(1:nfrag), v_r_unit(:,1:nfrag), v_t_mag(1:nfrag), v_t_unit(:,1:nfrag), m_frag(1:nfrag), vcom(:)) + ! Now do a kinetic energy budget check to make sure we are still within the budget. + ke_frag_orbit = 0.0_DP + do i = 1, nfrag + v_frag(:, i) = vb_frag(:, i) - vcom(:) + ke_frag_orbit = ke_frag_orbit + m_frag(i) * dot_product(vb_frag(:, i), vb_frag(:, i)) + end do + ke_frag_orbit = 0.5_DP * ke_frag_orbit + ke_radial = ke_frag_budget - ke_frag_orbit - ke_frag_spin + + ! If we are over the energy budget, flag this as a failure so we can try again + lerr = (ke_radial < 0.0_DP) + !write(*,*) 'Tangential' + !write(*,*) 'ke_frag_budget: ',ke_frag_budget + !write(*,*) 'ke_frag_orbit : ',ke_frag_orbit + !write(*,*) 'ke_frag_spin : ',ke_frag_spin + !write(*,*) 'ke_radial : ',ke_radial + + return + + end subroutine set_fragment_tan_vel + + function tangential_objective_function(v_t_mag_input, lerr) result(fval) + !! Author: David A. Minton + !! + !! Objective function for evaluating how close our fragment velocities get to minimizing KE error from our required value + implicit none + ! Arguments + real(DP), dimension(:), intent(in) :: v_t_mag_input !! Unknown tangential component of velocity vector set previously by angular momentum constraint + logical, intent(out) :: lerr !! Error flag + ! Result + real(DP) :: fval + ! Internals + integer(I4B) :: i + real(DP), dimension(:,:), allocatable :: v_shift + real(DP), dimension(:), allocatable :: v_t_new + real(DP) :: keo + + lerr = .false. + + allocate(v_shift(NDIM, nfrag)) + allocate(v_t_new(nfrag)) + + v_t_new(:) = solve_fragment_tan_vel(v_t_mag_input=v_t_mag_input(:), lerr=lerr) + v_shift(:,:) = vmag_to_vb(v_r_mag, v_r_unit, v_t_new, v_t_unit, m_frag, vcom) + + keo = 0.0_DP + do i = 1, nfrag + keo = keo + m_frag(i) * dot_product(v_shift(:, i), v_shift(:, i)) + end do + keo = 0.5_DP * keo + fval = keo + lerr = .false. + return + end function tangential_objective_function + + function solve_fragment_tan_vel(lerr, v_t_mag_input) result(v_t_mag_output) + !! Author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! Adjusts the positions, velocities, and spins of a collection of fragments such that they conserve angular momentum + implicit none + ! Arguments + logical, intent(out) :: lerr !! Error flag + real(DP), dimension(:), optional, intent(in) :: v_t_mag_input !! Unknown tangential velocities for fragments 7:nfrag + ! Internals + integer(I4B) :: i + ! Result + real(DP), dimension(:), allocatable :: v_t_mag_output + + real(DP), dimension(2 * NDIM, 2 * NDIM) :: A ! LHS of linear equation used to solve for momentum constraint in Gauss elimination code + real(DP), dimension(2 * NDIM) :: b ! RHS of linear equation used to solve for momentum constraint in Gauss elimination code + real(DP), dimension(NDIM) :: L_lin_others, L_orb_others, L, vtmp + + v_frag(:,:) = 0.0_DP + lerr = .false. + + ! We have 6 constraint equations (2 vector constraints in 3 dimensions each) + ! The first 3 are that the linear momentum of the fragments is zero with respect to the collisional barycenter + ! The second 3 are that the sum of the angular momentum of the fragments is conserved from the pre-impact state + L_lin_others(:) = 0.0_DP + L_orb_others(:) = 0.0_DP + do i = 1, nfrag + if (i <= 2 * NDIM) then ! The tangential velocities of the first set of bodies will be the unknowns we will solve for to satisfy the constraints + A(1:3, i) = m_frag(i) * v_t_unit(:, i) + L(:) = v_r_unit(:, i) .cross. v_t_unit(:, i) + A(4:6, i) = m_frag(i) * rmag(i) * L(:) + else if (present(v_t_mag_input)) then + vtmp(:) = v_t_mag_input(i - 6) * v_t_unit(:, i) + L_lin_others(:) = L_lin_others(:) + m_frag(i) * vtmp(:) + L(:) = x_frag(:, i) .cross. vtmp(:) + L_orb_others(:) = L_orb_others(:) + m_frag(i) * L(:) + end if + end do + b(1:3) = -L_lin_others(:) + b(4:6) = L_frag_orb(:) - L_orb_others(:) + allocate(v_t_mag_output(nfrag)) + v_t_mag_output(1:6) = util_solve_linear_system(A, b, 6, lerr) + if (present(v_t_mag_input)) v_t_mag_output(7:nfrag) = v_t_mag_input(:) + + return + end function solve_fragment_tan_vel + + subroutine set_fragment_radial_velocities(lerr) + !! Author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! + !! Adjust the fragment velocities to set the fragment orbital kinetic energy. This will minimize the difference between the fragment kinetic energy and the energy budget + implicit none + ! Arguments + logical, intent(out) :: lerr + ! Internals + real(DP), parameter :: TOL = 1e-10_DP + integer(I4B) :: i, j + real(DP), dimension(:), allocatable :: v_r_initial, v_r_sigma + real(DP), dimension(:,:), allocatable :: v_r + type(lambda_obj) :: objective_function + + ! Set the "target" ke_orbit_after (the value of the orbital kinetic energy that the fragments ought to have) + + allocate(v_r_initial, source=v_r_mag) + ! Initialize radial velocity magnitudes with a random value that is approximately 10% of that found by distributing the kinetic energy equally + allocate(v_r_sigma, source=v_r_mag) + call random_number(v_r_sigma(1:nfrag)) + v_r_sigma(1:nfrag) = sqrt(1.0_DP + 2 * (v_r_sigma(1:nfrag) - 0.5_DP) * 1e-4_DP) + v_r_initial(1:nfrag) = v_r_sigma(1:nfrag) * sqrt(abs(ke_radial) / (2 * m_frag(1:nfrag) * nfrag)) + + ! Initialize the lambda function using a structure constructor that calls the init method + ! Minimize the ke objective function using the BFGS optimizer + objective_function = lambda_obj(radial_objective_function) + v_r_mag = util_minimize_bfgs(objective_function, nfrag, v_r_initial, TOL, lerr) + ! Shift the radial velocity vectors to align with the center of mass of the collisional system (the origin) + vb_frag(:,1:nfrag) = vmag_to_vb(v_r_mag(1:nfrag), v_r_unit(:,1:nfrag), v_t_mag(1:nfrag), v_t_unit(:,1:nfrag), m_frag(1:nfrag), vcom(:)) + do i = 1, nfrag + v_frag(:, i) = vb_frag(:, i) - vcom(:) + end do + ke_frag_orbit = 0.0_DP + do i = 1, nfrag + ke_frag_orbit = ke_frag_orbit + m_frag(i) * dot_product(vb_frag(:, i), vb_frag(:, i)) + end do + ke_frag_orbit = 0.5_DP * ke_frag_orbit + !write(*,*) 'Radial' + !write(*,*) 'Failure? ',lerr + !write(*,*) 'ke_frag_budget: ',ke_frag_budget + !write(*,*) 'ke_frag_orbit : ',ke_frag_orbit + !write(*,*) 'ke_frag_spin : ',ke_frag_spin + !write(*,*) 'ke_remainder : ',ke_frag_budget - (ke_frag_orbit + ke_frag_spin) + lerr = .false. + + return + end subroutine set_fragment_radial_velocities + + function radial_objective_function(v_r_mag_input) result(fval) + !! Author: David A. Minton + !! + !! Objective function for evaluating how close our fragment velocities get to minimizing KE error from our required value + implicit none + ! Arguments + real(DP), dimension(:), intent(in) :: v_r_mag_input !! Unknown radial component of fragment velocity vector + ! Result + real(DP) :: fval !! The objective function result, which is the square of the difference between the calculated fragment kinetic energy and our target + !! Minimizing this brings us closer to our objective + ! Internals + integer(I4B) :: i + real(DP), dimension(:,:), allocatable :: v_shift + + allocate(v_shift, mold=vb_frag) + v_shift(:,:) = vmag_to_vb(v_r_mag_input, v_r_unit, v_t_mag, v_t_unit, m_frag, vcom) + fval = 2 * ke_frag_budget + do i = 1, nfrag + fval = fval - m_frag(i) * (Ip_frag(3, i) * rad_frag(i)**2 * dot_product(rot_frag(:, i), rot_frag(:, i)) + dot_product(v_shift(:, i), v_shift(:, i))) + end do + ! The following ensures that fval = 0 is a local minimum, which is what the BFGS method is searching for + fval = (fval / (2 * ke_radial))**2 + + return + + end function radial_objective_function + + function vmag_to_vb(v_r_mag, v_r_unit, v_t_mag, v_t_unit, m_frag, vcom) result(vb) + !! Author: David A. Minton + !! + !! Converts radial and tangential velocity magnitudes into barycentric velocity + implicit none + ! Arguments + real(DP), dimension(:), intent(in) :: v_r_mag !! Unknown radial component of fragment velocity vector + real(DP), dimension(:), intent(in) :: v_t_mag !! Tangential component of velocity vector set previously by angular momentum constraint + real(DP), dimension(:,:), intent(in) :: v_r_unit, v_t_unit !! Radial and tangential unit vectors for each fragment + real(DP), dimension(:), intent(in) :: m_frag !! Fragment masses + real(DP), dimension(:), intent(in) :: vcom !! Barycentric velocity of collisional system center of mass + ! Result + real(DP), dimension(:,:), allocatable :: vb + ! Internals + integer(I4B) :: i + + allocate(vb, mold=v_r_unit) + ! Make sure the velocity magnitude stays positive + do i = 1, nfrag + vb(:,i) = abs(v_r_mag(i)) * v_r_unit(:, i) + end do + ! In order to keep satisfying the kinetic energy constraint, we must shift the origin of the radial component of the velocities to the center of mass + call shift_vector_to_origin(m_frag, vb) + + do i = 1, nfrag + vb(:, i) = vb(:, i) + v_t_mag(i) * v_t_unit(:, i) + vcom(:) + end do + + end function vmag_to_vb + + subroutine restructure_failed_fragments() + !! Author: David A. Minton + !! + !! We failed to find a set of positions and velocities that satisfy all the constraints, and so we will alter the fragments and try again. + implicit none + integer(I4B) :: i + real(DP), dimension(:), allocatable :: m_frag_new, rad_frag_new + real(DP), dimension(:,:), allocatable :: xb_frag_new, vb_frag_new, Ip_frag_new, rot_frag_new + real(DP) :: delta_r, delta_r_max + real(DP), parameter :: ke_avg_deficit_target = 0.0_DP + + ! Introduce a bit of noise in the radius determination so we don't just flip flop between similar failed positions + call random_number(delta_r_max) + delta_r_max = sum(radius(:)) * (1.0_DP + 2e-1_DP * (delta_r_max - 0.5_DP)) + if (try > 2) then + ! Linearly interpolate the last two failed solution ke deficits to find a new distance value to try + delta_r = (r_max_start - r_max_start_old) * (ke_avg_deficit_target - ke_avg_deficit_old) / (ke_avg_deficit - ke_avg_deficit_old) + if (abs(delta_r) > delta_r_max) delta_r = sign(delta_r_max, delta_r) + else + delta_r = delta_r_max + end if + r_max_start_old = r_max_start + r_max_start = r_max_start + delta_r ! The larger lever arm can help if the problem is in the angular momentum step + if (f_spin > epsilon(1.0_DP)) then + f_spin = f_spin / 2 + else + f_spin = 0.0_DP + end if + end subroutine restructure_failed_fragments + + + end subroutine fragmentation_initialize + + + + module subroutine fragmentation_regime(Mcb, m1, m2, rad1, rad2, xh1, xh2, vb1, vb2, den1, den2, regime, Mlr, Mslr, mtiny, Qloss) + !! Author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! Determine the collisional regime of two colliding bodies. + !! Current version requires all values to be converted to SI units prior to calling the function + !! References: + !! Kokubo, E., Genda, H., 2010. Formation of Terrestrial Planets from Protoplanets Under a Realistic Accretion + !! Condition. ApJL 714, L21. https://doi.org/10.1088/2041-8205/714/1/L21 + !! Leinhardt, Z.M., Stewart, S.T., 2012. Collisions between Gravity-dominated Bodies. I. Outcome Regimes and Scaling + !! Laws 745, 79. https://doi.org/10.1088/0004-637X/745/1/79 + !! Mustill, A.J., Davies, M.B., Johansen, A., 2018. The dynamical evolution of transiting planetary systems including + !! a realistic collision prescription. Mon Not R Astron Soc 478, 2896–2908. https://doi.org/10.1093/mnras/sty1273 + !! Rufu, R., Aharonson, O., 2019. Impact Dynamics of Moons Within a Planetary Potential. J. Geophys. Res. Planets 124, + !! 1008–1019. https://doi.org/10.1029/2018JE005798 + !! Stewart, S.T., Leinhardt, Z.M., 2012. Collisions between Gravity-dominated Bodies. II. The Diversity of Impact + !! Outcomes during the End Stage of Planet Formation. ApJ 751, 32. https://doi.org/10.1088/0004-637X/751/1/32 + !! + implicit none + ! Arguments + integer(I4B), intent(out) :: regime + real(DP), intent(out) :: Mlr, Mslr + real(DP), intent(in) :: Mcb, m1, m2, rad1, rad2, den1, den2, mtiny + real(DP), dimension(:), intent(in) :: xh1, xh2, vb1, vb2 + real(DP), intent(out) :: Qloss !! The residual energy after the collision + ! Constants + integer(I4B), parameter :: N1 = 1 !number of objects with mass equal to the largest remnant from LS12 + integer(I4B), parameter :: N2 = 2 !number of objects with mass larger than second largest remnant from LS12 + real(DP), parameter :: DENSITY1 = 1000.0_DP !standard density parameter from LS12 [kg/m3] + real(DP), parameter :: MU_BAR = 0.37_DP !0.385#0.37#0.3333# 3.978 # 1/3 material parameter for hydrodynamic planet-size bodies (LS12) + real(DP), parameter :: BETA = 2.85_DP !slope of sfd for remnants from LS12 2.85 + real(DP), parameter :: C1 = 2.43_DP !! Kokubo & Genda (2010) eq. (3) + real(DP), parameter :: C2 = -0.0408_DP !! Kokubo & Genda (2010) eq. (3) + real(DP), parameter :: C3 = 1.86_DP !! Kokubo & Genda (2010) eq. (3) + real(DP), parameter :: C4 = 1.08_DP !! Kokubo & Genda (2010) eq. (3) + real(DP), parameter :: CRUFU = 2.0_DP - 3 * MU_BAR ! central potential variable from Rufu and Aharonson (2019) + real(DP), parameter :: SUPERCAT_QRATIO = 1.8_DP ! See Section 4.1 of LS12 + ! Internals + real(DP) :: a1, alpha, aint, b, bcrit, c_star, egy, zeta, l, lint, mu, phi, theta + real(DP) :: Qr, Qrd_pstar, Qr_erosion, Qr_supercat + real(DP) :: Vhr, Verosion, Vescp, Vhill, Vimp, Vsupercat + real(DP) :: Mint, Mtot + real(DP) :: Rp, rhill + real(DP) :: Mresidual + real(DP) :: U_binding + + Vimp = norm2(vb2(:) - vb1(:)) + b = calc_b(xh2, vb2, xh1, vb1) + l = (rad1 + rad2) * (1 - b) + egy = 0.5_DP * dot_product(vb1, vb1) - GC * Mcb / norm2(xh1) + a1 = - GC * Mcb / 2.0_DP / egy + Mtot = m1 + m2 + mu = (m1 * m2) / Mtot + if (l < 2 * rad2) then + !calculate Mint + phi = 2 * acos((l - rad2) / rad2) + aint = rad2**2 * (PI - (phi - sin(phi)) / 2.0_DP) + lint = 2 * sqrt(rad2**2 - (rad2 - l / 2.0_DP) ** 2) + Mint = aint * lint ![kg] + alpha = (l**2) * (3 * rad2 - l) / (4 * (rad2**3)) + else + alpha = 1.0_DP + Mint = m2 + end if + Rp = (3 * (m1 / den1 + alpha * m2 / den2) / (4 * PI))**(1.0_DP/3.0_DP) ! (Mustill et al. 2018) + c_star = calc_c_star(Rp) + !calculate Vescp + Vescp = sqrt(2 * GC * Mtot / Rp) !Mustill et al. 2018 eq 6 + !calculate rhill + rhill = a1 * (m1 / 3.0_DP / (Mcb + m1))**(1.0_DP/3.0_DP) + !calculate Vhill + if ((rad2 + rad1) < rhill) then + Vhill = sqrt(2 * GC * m1 * ((rhill**2 - rhill * (rad1 + rad2)) / & + (rhill**2 - 0.5_DP * (rad1 + rad2)**2)) / (rad1 + rad2)) + else + Vhill = Vescp + end if + !calculate Qr_pstar + Qrd_pstar = calc_Qrd_pstar(m1, m2, alpha, c_star) * (Vhill / Vescp)**CRUFU !Rufu and Aharaonson eq (3) + !calculate Verosion + Qr_erosion = 2 * (1.0_DP - m1 / Mtot) * Qrd_pstar + Verosion = (2 * Qr_erosion * Mtot / mu)** (1.0_DP / 2.0_DP) + Qr = mu*(Vimp**2) / Mtot / 2.0_DP + !calculate mass largest remnant Mlr + Mlr = (1.0_DP - Qr / Qrd_pstar / 2.0_DP) * Mtot ! [kg] # LS12 eq (5) + !calculate Vsupercat + Qr_supercat = SUPERCAT_QRATIO * Qrd_pstar ! See LS12 Section 4.1 + Vsupercat = sqrt(2 * Qr_supercat * Mtot / mu) + !calculate Vhr + zeta = (m1 - m2) / Mtot + theta = 1.0_DP - b + Vhr = Vescp * (C1 * zeta**2 * theta**(2.5_DP) + C2 * zeta**2 + C3 * theta**(2.5_DP) + C4) ! Kokubo & Genda (2010) eq. (3) + bcrit = rad1 / (rad1 + rad2) + Qloss = 0.0_DP + U_binding = (3.0_DP * Mtot) / (5.0_DP * Rp) ! LS12 eq. 27 + + if ((m1 < mtiny).or.(m2 < mtiny)) then + regime = COLLRESOLVE_REGIME_MERGE !perfect merging regime + Mlr = Mtot + Mslr = 0.0_DP + Qloss = 0.0_DP + write(*,*) "FORCE MERGE" + else + if( Vimp < Vescp) then + regime = COLLRESOLVE_REGIME_MERGE !perfect merging regime + Mlr = Mtot + Mslr = 0.0_DP + Qloss = 0.0_DP + else if (Vimp < Verosion) then + if (b < bcrit) then + regime = COLLRESOLVE_REGIME_MERGE !partial accretion regime" + Mlr = Mtot + Mslr = 0.0_DP + Qloss = 0.0_DP + else if ((b > bcrit) .and. (Vimp < Vhr)) then + regime = COLLRESOLVE_REGIME_MERGE ! graze and merge + Mlr = Mtot + Mslr = 0.0_DP + Qloss = 0.0_DP + else + Mlr = m1 + Mslr = calc_Qrd_rev(m2, m1, Mint, den1, den2, Vimp, c_star) + regime = COLLRESOLVE_REGIME_HIT_AND_RUN !hit and run + Qloss = (c_star + 1.0_DP) * U_binding ! Qr + end if + else if (Vimp > Verosion .and. Vimp < Vsupercat) then + if (m2 < 0.001_DP * m1) then + regime = COLLRESOLVE_REGIME_MERGE !cratering regime" + Mlr = Mtot + Mslr = 0.0_DP + Qloss = 0.0_DP + else + Mslr = Mtot * (3.0_DP - BETA) * (1.0_DP - N1 * Mlr / Mtot) / (N2 * BETA) ! LS12 eq (37) + regime = COLLRESOLVE_REGIME_DISRUPTION !disruption + Qloss = (c_star + 1.0_DP) * U_binding ! Qr - Qr_erosion + end if + else if (Vimp > Vsupercat) then + Mlr = Mtot * 0.1_DP * (Qr / (Qrd_pstar * SUPERCAT_QRATIO))**(-1.5_DP) !LS12 eq (44) + Mslr = Mtot * (3.0_DP - BETA) * (1.0_DP - N1 * Mlr / Mtot) / (N2 * BETA) !LS12 eq (37) + regime = COLLRESOLVE_REGIME_SUPERCATASTROPHIC ! supercatastrophic + Qloss = (c_star + 1.0_DP) * U_binding ! Qr - Qr_supercat + else + write(*,*) "Error no regime found in symba_regime" + end if + end if + Mresidual = Mtot - Mlr - Mslr + if (Mresidual < 0.0_DP) then ! prevents final masses from going negative + Mlr = Mlr + Mresidual + end if + + return + + ! Internal functions + contains + function calc_Qrd_pstar(Mtarg, Mp, alpha, c_star) result(Qrd_pstar) + !! author: Jennifer L.L. Pouplin and Carlisle A. Wishard + !! + !! Calculates the corrected Q* for oblique impacts. See Eq. (15) of LS12. + !! Reference: + !! Leinhardt, Z.M., Stewart, S.T., 2012. Collisions between Gravity-dominated Bodies. I. Outcome Regimes and Scaling + !! Laws 745, 79. https://doi.org/10.1088/0004-637X/745/1/79 + !! + implicit none + ! Arguments + real(DP),intent(in) :: Mtarg, Mp, alpha, c_star + ! Result + real(DP) :: Qrd_pstar + ! Internals + real(DP) :: Qrd_star1, mu_alpha, mu, Qrd_star + + ! calc mu, mu_alpha + mu = (Mtarg * Mp) / (Mtarg + Mp) ! [kg] + mu_alpha = (Mtarg * alpha * Mp) / (Mtarg + alpha * Mp) ! [kg] + ! calc Qrd_star1 + Qrd_star1 = (c_star * 4 * PI * DENSITY1 * GC * Rp**2) / 5.0_DP + ! calc Qrd_star + Qrd_star = Qrd_star1 * (((Mp / Mtarg + 1.0_DP)**2) / (4 * Mp / Mtarg))**(2.0_DP / (3.0_DP * MU_BAR) - 1.0_DP) !(eq 23) + ! calc Qrd_pstar, v_pstar + Qrd_pstar = ((mu / mu_alpha)**(2.0_DP - 3.0_DP * MU_BAR / 2.0_DP)) * Qrd_star ! (eq 15) + + return + end function calc_Qrd_pstar + + function calc_Qrd_rev(Mp, Mtarg, Mint, den1, den2, Vimp, c_star) result(Mslr) + !! author: Jennifer L.L. Pouplin and Carlisle A. Wishard + !! + !! Calculates mass of second largest fragment. + !! + implicit none + ! Arguments + real(DP),intent(in) :: Mp, Mtarg, Mint, den1, den2, Vimp, c_star + ! Result + real(DP) :: Mslr + ! Internals + real(DP) :: mtot_rev, mu_rev, gamma_rev, Qrd_star1, Qrd_star, mu_alpha_rev + real(DP) :: Qrd_pstar, Rc1, Qr_rev, Qrd_pstar_rev, Qr_supercat_rev + + ! calc Mslr, Rc1, mu, gammalr + mtot_rev = Mint + Mp + Rc1 = (3 * (Mint / den1 + Mp / den2) / (4 * PI))**(1.0_DP/3.0_DP) ! [m] Mustill et al 2018 + mu_rev = (Mint * Mp) / mtot_rev ! [kg] eq 49 LS12 + mu_alpha_rev = (Mtarg * alpha * Mp) / (Mtarg + alpha * Mp) + gamma_rev = Mint / Mp ! eq 50 LS12 + !calc Qr_rev + Qr_rev = mu_rev * (Vimp**2) / (2 * mtot_rev) + ! calc Qrd_star1, v_star1 + Qrd_star1 = (c_star * 4 * PI * mtot_rev * GC ) / Rc1 / 5.0_DP + ! calc Qrd_pstar_rev + Qrd_star = Qrd_star1 * (((gamma_rev + 1.0_DP)**2) / (4 * gamma_rev)) ** (2.0_DP / (3.0_DP * MU_BAR) - 1.0_DP) !(eq 52) + Qrd_pstar = Qrd_star * ((mu_rev / mu_alpha_rev)**(2.0_DP - 3.0_DP * MU_BAR / 2.0_DP)) + Qrd_pstar_rev = Qrd_pstar * (Vhill / Vescp)**CRUFU !Rufu and Aharaonson eq (3) + !calc Qr_supercat_rev + Qr_supercat_rev = 1.8_DP * Qrd_pstar_rev + if (Qr_rev > Qr_supercat_rev ) then + Mslr = mtot_rev * (0.1_DP * ((Qr_rev / (Qrd_pstar_rev * 1.8_DP))**(-1.5_DP))) !eq (44) + else if ( Qr_rev < Qrd_pstar_rev ) then + Mslr = Mp + else + Mslr = (1.0_DP - Qr_rev / Qrd_pstar_rev / 2.0_DP) * (mtot_rev) ! [kg] #(eq 5) + end if + + if ( Mslr > Mp ) Mslr = Mp !check conservation of mass + + return + end function calc_Qrd_rev + + function calc_b(proj_pos, proj_vel, targ_pos, targ_vel) result(sintheta) + !! author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! Calculates the impact factor b = sin(theta), where theta is the angle between the relative velocity + !! and distance vectors of the target and projectile bodies. See Fig. 2 of Leinhardt and Stewart (2012) + !! + implicit none + !! Arguments + real(DP), dimension(:), intent(in) :: proj_pos, proj_vel, targ_pos, targ_vel + !! Result + real(DP) :: sintheta + !! Internals + real(DP), dimension(NDIM) :: imp_vel, distance, x_cross_v + + imp_vel(:) = proj_vel(:) - targ_vel(:) + distance(:) = proj_pos(:) - targ_pos(:) + x_cross_v(:) = distance(:) .cross. imp_vel(:) + sintheta = norm2(x_cross_v(:)) / norm2(distance(:)) / norm2(imp_vel(:)) + return + end function calc_b + + function calc_c_star(Rc1) result(c_star) + !! author: David A. Minton + !! + !! Calculates c_star as a function of impact equivalent radius. It inteRpolates between 5 for ~1 km sized bodies to + !! 1.8 for ~10000 km sized bodies. See LS12 Fig. 4 for details. + !! + implicit none + !! Arguments + real(DP), intent(in) :: Rc1 + !! Result + real(DP) :: c_star + !! Internals + real(DP), parameter :: loR = 1.0e3_DP ! Lower bound of inteRpolation size (m) + real(DP), parameter :: hiR = 1.0e7_DP ! Upper bound of inteRpolation size (m) + real(DP), parameter :: loval = 5.0_DP ! Value of C* at lower bound + real(DP), parameter :: hival = 1.9_DP ! Value of C* at upper bound + + if (Rc1 < loR) then + c_star = loval + else if (Rc1 < hiR) then + c_star = loval + (hival - loval) * log(Rc1 / loR) / log(hiR /loR) + else + c_star = hival + end if + return + end function calc_c_star + + end subroutine fragmentation_regime + +end submodule s_fragmentation \ No newline at end of file diff --git a/src/gr/gr.f90 b/src/gr/gr.f90 index 8831a93d5..0c0333907 100644 --- a/src/gr/gr.f90 +++ b/src/gr/gr.f90 @@ -1,7 +1,8 @@ submodule(swiftest_classes) s_gr use swiftest contains - module pure subroutine gr_getaccb_ns_body(self, system, param) + + module pure subroutine gr_kick_getaccb_ns_body(self, system, param) !! author: David A. Minton !! !! Add relativistic correction acceleration for non-symplectic integrators. @@ -11,7 +12,7 @@ module pure subroutine gr_getaccb_ns_body(self, system, param) !! Quinn, T.R., Tremaine, S., Duncan, M., 1991. A three million year integration of the earth’s orbit. !! AJ 101, 2287–2305. https://doi.org/10.1086/115850 !! - !! Adapted from David A. Minton's Swifter routine routine gr_getaccb_ns.f90 + !! Adapted from David A. Minton's Swifter routine routine gr_kick_getaccb_ns.f90 implicit none ! Arguments class(swiftest_body), intent(inout) :: self !! Swiftest generic body object @@ -36,12 +37,41 @@ module pure subroutine gr_getaccb_ns_body(self, system, param) cb%agr(i) = -sum(self%Gmass(1:n) * self%agr(1:n, i) / cb%Gmass) end do end select - end associate return + end subroutine gr_kick_getaccb_ns_body + + + module subroutine gr_kick_getacch(mu, x, lmask, n, inv_c2, agr) + !! author: David A. Minton + !! + !! Compute relativisitic accelerations of massive bodies + !! Based on Saha & Tremaine (1994) Eq. 28 + !! + !! Adapted from David A. Minton's Swifter routine routine gr_whm_kick_getacch.f90 + implicit none + ! Arguments + real(DP), dimension(:), intent(in) :: mu !! Gravitational constant + real(DP), dimension(:,:), intent(in) :: x !! Position vectors + logical, dimension(:), intent(in) :: lmask !! Logical mask indicating which bodies to compute + integer(I4B), intent(in) :: n !! Total number of bodies + real(DP), intent(in) :: inv_c2 !! Inverse speed of light squared: 1 / c**2 + real(DP), dimension(:,:), intent(out) :: agr !! Accelerations + ! Internals + integer(I4B) :: i + real(DP) :: beta, rjmag4 + + agr(:,:) = 0.0_DP + do concurrent (i = 1:n, lmask(i)) + rjmag4 = (dot_product(x(:, i), x(:, i)))**2 + beta = -mu(i)**2 * inv_c2 + agr(:, i) = 2 * beta * x(:, i) / rjmag4 + end do + + return + end subroutine gr_kick_getacch - end subroutine gr_getaccb_ns_body module pure subroutine gr_p4_pos_kick(param, x, v, dt) !! author: David A. Minton @@ -71,6 +101,7 @@ module pure subroutine gr_p4_pos_kick(param, x, v, dt) return end subroutine gr_p4_pos_kick + module pure subroutine gr_pseudovel2vel(param, mu, xh, pv, vh) !! author: David A. Minton !! @@ -98,9 +129,11 @@ module pure subroutine gr_pseudovel2vel(param, mu, xh, pv, vh) grterm = 1.0_DP - inv_c2 * (0.5_DP * vmag2 + 3 * mu / rmag) vh(:) = pv(:) * grterm end associate + return end subroutine gr_pseudovel2vel + module pure subroutine gr_pv2vh_body(self, param) !! author: David A. Minton !! @@ -108,7 +141,7 @@ module pure subroutine gr_pv2vh_body(self, param) implicit none ! Arguments class(swiftest_body), intent(inout) :: self !! Swiftest particle object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of on parameters + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters ! Internals integer(I4B) :: i real(DP), dimension(:,:), allocatable :: vh !! Temporary holder of pseudovelocity for in-place conversion @@ -121,9 +154,11 @@ module pure subroutine gr_pv2vh_body(self, param) end do call move_alloc(vh, self%vh) end associate + return end subroutine gr_pv2vh_body + module pure subroutine gr_vel2pseudovel(param, mu, xh, vh, pv) !! author: David A. Minton !! @@ -200,6 +235,7 @@ module pure subroutine gr_vel2pseudovel(param, mu, xh, vh, pv) return end subroutine gr_vel2pseudovel + module pure subroutine gr_vh2pv_body(self, param) !! author: David A. Minton !! @@ -207,7 +243,7 @@ module pure subroutine gr_vh2pv_body(self, param) implicit none ! Arguments class(swiftest_body), intent(inout) :: self !! Swiftest particle object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of on parameters + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters ! Internals integer(I4B) :: i real(DP), dimension(:,:), allocatable :: pv !! Temporary holder of pseudovelocity for in-place conversion @@ -220,8 +256,8 @@ module pure subroutine gr_vh2pv_body(self, param) end do call move_alloc(pv, self%vh) end associate + return end subroutine gr_vh2pv_body - end submodule s_gr \ No newline at end of file diff --git a/src/helio/helio_coord.f90 b/src/helio/helio_coord.f90 index e14ea4612..f40781810 100644 --- a/src/helio/helio_coord.f90 +++ b/src/helio/helio_coord.f90 @@ -1,6 +1,7 @@ submodule (helio_classes) s_helio_coord use swiftest contains + module subroutine helio_coord_vb2vh_pl(self, cb) !! author: David A. Minton !! @@ -15,17 +16,19 @@ module subroutine helio_coord_vb2vh_pl(self, cb) ! Internals integer(I4B) :: i - associate(npl => self%nbody, vbcb => cb%vb, xh => self%xh, vb => self%vb, & - vh => self%vh, Mcb => cb%Gmass, Mpl => self%Gmass) + if (self%nbody == 0) return + + associate(pl => self, npl => self%nbody) do i = 1, NDIM - vbcb(i) = -sum(Mpl(1:npl) * vb(i, 1:npl)) / Mcb - vh(i, 1:npl) = vb(i, 1:npl) - vbcb(i) + cb%vb(i) = -sum(pl%Gmass(1:npl) * pl%vb(i, 1:npl)) / cb%Gmass + pl%vh(i, 1:npl) = pl%vb(i, 1:npl) - cb%vb(i) end do end associate return end subroutine helio_coord_vb2vh_pl + module subroutine helio_coord_vb2vh_tp(self, vbcb) !! author: David A. Minton !! @@ -38,17 +41,20 @@ module subroutine helio_coord_vb2vh_tp(self, vbcb) class(helio_tp), intent(inout) :: self !! Helio massive body object real(DP), dimension(:), intent(in) :: vbcb !! Barycentric velocity of the central body - associate(ntp => self%nbody, vb => self%vb, vh => self%vh, status => self%status) - where (status(1:ntp) == ACTIVE) - vh(1, 1:ntp) = vb(1, 1:ntp) - vbcb(1) - vh(2, 1:ntp) = vb(2, 1:ntp) - vbcb(2) - vh(3, 1:ntp) = vb(3, 1:ntp) - vbcb(3) + if (self%nbody == 0) return + + associate(tp => self, ntp => self%nbody) + where (tp%lmask(1:ntp)) + tp%vh(1, 1:ntp) = tp%vb(1, 1:ntp) - vbcb(1) + tp%vh(2, 1:ntp) = tp%vb(2, 1:ntp) - vbcb(2) + tp%vh(3, 1:ntp) = tp%vb(3, 1:ntp) - vbcb(3) end where end associate return end subroutine helio_coord_vb2vh_tp + module subroutine helio_coord_vh2vb_pl(self, cb) !! author: David A. Minton !! @@ -62,20 +68,22 @@ 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(npl => self%nbody, vbcb => cb%vb, vb => self%vb, vh => self%vh, & - Mcb => cb%Gmass, Mpl => self%Gmass) - msys = Mcb + sum(Mpl(1:npl)) + associate(pl => self, npl => self%nbody) + Gmtot = cb%Gmass + sum(pl%Gmass(1:npl)) do i = 1, NDIM - vbcb(i) = -sum(Mpl(1:npl) * vh(i, 1:npl)) / msys - vb(i, 1:npl) = vh(i, 1:npl) + vbcb(i) + 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 return end subroutine helio_coord_vh2vb_pl + module subroutine helio_coord_vh2vb_tp(self, vbcb) !! author: David A. Minton !! @@ -88,15 +96,18 @@ module subroutine helio_coord_vh2vb_tp(self, vbcb) class(helio_tp), intent(inout) :: self !! Helio massive body object real(DP), dimension(:), intent(in) :: vbcb !! Barycentric velocity of the central body - associate(ntp => self%nbody, vb => self%vb, vh => self%vh, status => self%status) - where (status(1:ntp) == ACTIVE) - vb(1, 1:ntp) = vh(1, 1:ntp) + vbcb(1) - vb(2, 1:ntp) = vh(2, 1:ntp) + vbcb(2) - vb(3, 1:ntp) = vh(3, 1:ntp) + vbcb(3) + if (self%nbody == 0) return + + associate(tp => self, ntp => self%nbody) + where (tp%lmask(1:ntp)) + tp%vb(1, 1:ntp) = tp%vh(1, 1:ntp) + vbcb(1) + tp%vb(2, 1:ntp) = tp%vh(2, 1:ntp) + vbcb(2) + tp%vb(3, 1:ntp) = tp%vh(3, 1:ntp) + vbcb(3) end where end associate return end subroutine helio_coord_vh2vb_tp + end submodule s_helio_coord diff --git a/src/helio/helio_drift.f90 b/src/helio/helio_drift.f90 index 40da379ee..e2a55e458 100644 --- a/src/helio/helio_drift.f90 +++ b/src/helio/helio_drift.f90 @@ -1,62 +1,79 @@ submodule (helio_classes) s_helio_drift use swiftest contains - module subroutine helio_drift_pl(self, system, param, dt) + module subroutine helio_drift_body(self, system, param, dt) !! author: David A. Minton !! - !! Loop through massive bodies and call Danby drift routine - !! New vectorized version included + !! Loop through bodies and call Danby drift routine on democratic heliocentric coordinates !! !! Adapted from David E. Kaufmann's Swifter routine helio_drift.f90 !! Adapted from Hal Levison's Swift routine drift.f implicit none ! Arguments - class(helio_pl), intent(inout) :: self !! Helio massive body object - class(swiftest_nbody_system), intent(inout) :: system !! WHM nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of - real(DP), intent(in) :: dt !! Stepsize) + 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 + real(DP), intent(in) :: dt !! Stepsize ! Internals integer(I4B) :: i !! Loop counter real(DP) :: rmag, vmag2, energy integer(I4B), dimension(:),allocatable :: iflag !! Vectorized error code flag real(DP), dimension(:), allocatable :: dtp, mu - associate(pl => self, npl => self%nbody, cb => system%cb) - if (npl == 0) return + if (self%nbody == 0) return - allocate(iflag(npl)) + associate(n => self%nbody) + allocate(iflag(n)) iflag(:) = 0 - allocate(dtp(npl)) - allocate(mu(npl)) - mu = cb%Gmass - - if (param%lgr) then - do i = 1,npl - rmag = norm2(pl%xh(:, i)) - vmag2 = dot_product(pl%vb(:, i), pl%vb(:, i)) - energy = 0.5_DP * vmag2 - pl%mu(i) / rmag - dtp(i) = dt * (1.0_DP + 3 * param%inv_c2 * energy) - end do - else - dtp(:) = dt - end if - - call drift_one(mu(1:npl), pl%xh(1,1:npl), pl%xh(2,1:npl), pl%xh(3,1:npl), & - pl%vb(1,1:npl), pl%vb(2,1:npl), pl%vb(3,1:npl), & - dtp(1:npl), iflag(1:npl)) - if (any(iflag(1:npl) /= 0)) then - do i = 1, npl - write(*, *) " Planet ", pl%id(i), " is lost!!!!!!!!!!" - write(*, *) pl%xh(:,i) - write(*, *) pl%vb(:,i) - write(*, *) " stopping " - call util_exit(FAILURE) + allocate(mu(n)) + mu(:) = system%cb%Gmass + call drift_all(mu, self%xh, self%vb, self%nbody, param, dt, self%lmask, iflag) + if (any(iflag(1:n) /= 0)) then + where(iflag(1:n) /= 0) self%status(1:n) = DISCARDED_DRIFTERR + do i = 1, n + if (iflag(i) /= 0) write(*, *) " Body ", self%id(i), " lost due to error in Danby drift" end do end if end associate + + return + end subroutine helio_drift_body + + + module subroutine helio_drift_pl(self, system, param, dt) + !! author: David A. Minton + !! + !! Wrapper function used to call the body drift routine from a helio_pl structure + implicit none + ! Arguments + class(helio_pl), intent(inout) :: self !! Helio massive body object + 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) :: dt !! Stepsize + + call helio_drift_body(self, system, param, dt) + return end subroutine helio_drift_pl + + + module subroutine helio_drift_tp(self, system, param, dt) + !! author: David A. Minton + !! + !! Wrapper function used to call the body drift routine from a helio_pl structure + implicit none + ! Arguments + class(helio_tp), intent(inout) :: self !! Helio massive body object + 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) :: dt !! Stepsize + + call helio_drift_body(self, system, param, dt) + + return + end subroutine helio_drift_tp + module subroutine helio_drift_linear_pl(self, cb, dt, lbeg) !! author: David A. Minton @@ -67,21 +84,25 @@ module subroutine helio_drift_linear_pl(self, cb, dt, lbeg) !! Adapted from Hal Levison's Swift routine helio_lindrift.f implicit none ! Arguments - class(helio_pl), intent(inout) :: self !! Helio massive body object - class(helio_cb), intent(inout) :: cb !! Helio central bod - real(DP), intent(in) :: dt !! Stepsize - logical, intent(in) :: lbeg !! Argument that determines whether or not this is the beginning or end of the step + class(helio_pl), intent(inout) :: self !! Helio massive body object + class(helio_cb), intent(inout) :: cb !! Helio central body + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Argument that determines whether or not this is the beginning or end of the step ! Internals - real(DP), dimension(NDIM) :: pt !! negative barycentric velocity of the central body + real(DP), dimension(NDIM) :: pt !! negative barycentric velocity of the central body + integer(I4B) :: i + + if (self%nbody == 0) return associate(pl => self, npl => self%nbody) - pt(1) = sum(pl%Gmass(1:npl) * pl%vb(1,1:npl)) - pt(2) = sum(pl%Gmass(1:npl) * pl%vb(2,1:npl)) - pt(3) = sum(pl%Gmass(1:npl) * pl%vb(3,1:npl)) + if (npl == 0) return + pt(1) = sum(pl%Gmass(1:npl) * pl%vb(1,1:npl), self%lmask(1:npl)) + pt(2) = sum(pl%Gmass(1:npl) * pl%vb(2,1:npl), self%lmask(1:npl)) + pt(3) = sum(pl%Gmass(1:npl) * pl%vb(3,1:npl), self%lmask(1:npl)) pt(:) = pt(:) / cb%Gmass - pl%xh(1,1:npl) = pl%xh(1,1:npl) + pt(1) * dt - pl%xh(2,1:npl) = pl%xh(2,1:npl) + pt(2) * dt - pl%xh(3,1:npl) = pl%xh(3,1:npl) + pt(3) * dt + do concurrent(i = 1:npl, self%lmask(i)) + pl%xh(:,i) = pl%xh(:,i) + pt(:) * dt + end do if (lbeg) then cb%ptbeg = pt(:) @@ -92,57 +113,7 @@ module subroutine helio_drift_linear_pl(self, cb, dt, lbeg) return end subroutine helio_drift_linear_pl - - module subroutine helio_drift_tp(self, system, param, dt) - !! author: David A. Minton - !! - !! Loop through test particles and call Danby drift routine - !! - !! Adapted from David E. Kaufmann's Swifter routine helio_drift_tp.f90 - !! Adapted from Hal Levison's Swift routine drift_tp.f - implicit none - ! Arguments - class(helio_tp), intent(inout) :: self !! Helio test particle object - class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of - real(DP), intent(in) :: dt !! Stepsize - ! Internals - integer(I4B) :: i !! Loop counter - real(DP) :: rmag, vmag2, energy - real(DP), dimension(:), allocatable :: dtp, mu - integer(I4B), dimension(:),allocatable :: iflag !! Vectorized error code flag - associate(tp => self, ntp => self%nbody, cb => system%cb) - if (ntp == 0) return - allocate(iflag(ntp)) - allocate(dtp(ntp)) - iflag(:) = 0 - allocate(mu(ntp)) - mu = cb%Gmass - - if (param%lgr) then - do i = 1,ntp - rmag = norm2(tp%xh(:, i)) - vmag2 = dot_product(tp%vh(:, i), tp%vh(:, i)) - energy = 0.5_DP * vmag2 - tp%mu(i) / rmag - dtp(i) = dt * (1.0_DP + 3 * param%inv_c2 * energy) - end do - else - dtp(:) = dt - end if - call drift_one(mu(1:ntp), tp%xh(1,1:ntp), tp%xh(2,1:ntp), tp%xh(3,1:ntp), & - tp%vb(1,1:ntp), tp%vb(2,1:ntp), tp%vb(3,1:ntp), & - dtp(1:ntp), iflag(1:ntp)) - if (any(iflag(1:ntp) /= 0)) then - tp%status = DISCARDED_DRIFTERR - do i = 1, ntp - if (iflag(i) /= 0) write(*, *) "Particle ", tp%id(i), " lost due to error in Danby drift" - end do - end if - end associate - - return - end subroutine helio_drift_tp module subroutine helio_drift_linear_tp(self, cb, dt, lbeg) !! author: David A. Minton @@ -154,20 +125,23 @@ module subroutine helio_drift_linear_tp(self, cb, dt, lbeg) !! Adapted from Hal Levison's Swift routine helio_lindrift_tp.f implicit none ! Arguments - class(helio_tp), intent(inout) :: self !! Helio test particleb object - class(helio_cb), intent(in) :: cb !! Helio central body - real(DP), intent(in) :: dt !! Stepsize - logical, intent(in) :: lbeg !! Argument that determines whether or not this is the beginning or end of the step + class(helio_tp), intent(inout) :: self !! Helio test particleb object + class(helio_cb), intent(in) :: cb !! Helio central body + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Argument that determines whether or not this is the beginning or end of the step ! Internals real(DP), dimension(NDIM) :: pt !! negative barycentric velocity of the central body + if (self%nbody == 0) return + associate(tp => self, ntp => self%nbody) + if (ntp == 0) return if (lbeg) then pt(:) = cb%ptbeg else pt(:) = cb%ptend end if - where (tp%status(1:ntp) == ACTIVE) + where (self%lmask(1:ntp)) tp%xh(1, 1:ntp) = tp%xh(1, 1:ntp) + pt(1) * dt tp%xh(2, 1:ntp) = tp%xh(2, 1:ntp) + pt(2) * dt tp%xh(3, 1:ntp) = tp%xh(3, 1:ntp) + pt(3) * dt diff --git a/src/helio/helio_getacch.f90 b/src/helio/helio_getacch.f90 deleted file mode 100644 index 4b598f204..000000000 --- a/src/helio/helio_getacch.f90 +++ /dev/null @@ -1,138 +0,0 @@ -submodule (helio_classes) s_helio_getacch - use swiftest -contains - module subroutine helio_getacch_pl(self, system, param, t, lbeg) - !! author: David A. Minton - !! - !! Compute heliocentric accelerations of massive bodies - !! - !! Adapted from David E. Kaufmann's Swifter routine helio_getacch.f90 - !! Adapted from Hal Levison's Swift routine helio_getacch.f - implicit none - ! Arguments - class(helio_pl), intent(inout) :: self !! Helio massive body particle data structure - class(swiftest_nbody_system), intent(inout) :: system !! WHM nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of - real(DP), intent(in) :: t !! Current simulation time - logical, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step - - associate(cb => system%cb, pl => self, npl => self%nbody) - call helio_getacch_int_pl(pl, t) - if (param%loblatecb) then - cb%aoblbeg = cb%aobl - call pl%accel_obl(system) - cb%aoblend = cb%aobl - end if - if (param%lextra_force) call pl%accel_user(system, param, t) - !if (param%lgr) call pl%gr_accel(param) - end associate - - return - end subroutine helio_getacch_pl - - module subroutine helio_getacch_tp(self, system, param, t, lbeg) - !! author: David A. Minton - !! - !! Compute heliocentric accelerations of test particles - !! - !! Adapted from David E. Kaufmann's Swifter routine helio_getacch_tp.f90 - !! Adapted from Hal Levison's Swift routine helio_getacch_tp.f - implicit none - ! Arguments - class(helio_tp), intent(inout) :: self !! WHM test particle data structure - class(swiftest_nbody_system), intent(inout) :: system !! WHM nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of - real(DP), intent(in) :: t !! Current time - logical, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step - ! Internals - logical, save :: lmalloc = .true. - integer(I4B) :: i - real(DP) :: r2, mu - real(DP), dimension(:), allocatable, save :: irh, irht - - associate(tp => self, ntp => self%nbody, cb => system%cb, npl => system%pl%nbody) - if (present(lbeg)) system%lbeg = lbeg - call helio_getacch_int_tp(tp, system, param, t) - if (param%loblatecb) call tp%accel_obl(system) - if (param%lextra_force) call tp%accel_user(system, param, t) - !if (param%lgr) call tp%gr_accel(param) - end associate - return - end subroutine helio_getacch_tp - - subroutine helio_getacch_int_pl(pl, t) - !! author: David A. Minton - !! - !! Compute direct cross term heliocentric accelerations of massive bodiese - !! - !! Adapted from David E. Kaufmann's Swifter routine helio_getacch_int.f90 - !! Adapted from Hal Levison's Swift routine getacch_ah3.f - implicit none - ! Arguments - class(helio_pl), intent(inout) :: pl !! Helio massive body particle data structure - real(DP), intent(in) :: t !! Current time - ! Internals - integer(I4B) :: i, j - real(DP) :: rji2, irij3, faci, facj - real(DP), dimension(NDIM) :: dx - - associate(npl => pl%nbody) - pl%ah(:,:) = 0.0_DP - do i = 1, npl - 1 - do j = i + 1, npl - dx(:) = pl%xh(:,j) - pl%xh(:,i) - rji2 = dot_product(dx(:), dx(:)) - irij3 = 1.0_DP / (rji2 * sqrt(rji2)) - faci = pl%Gmass(i) * irij3 - facj = pl%Gmass(j) * irij3 - pl%ah(:,i) = pl%ah(:,i) + facj * dx(:) - pl%ah(:,j) = pl%ah(:,j) - faci * dx(:) - end do - end do - end associate - - return - end subroutine helio_getacch_int_pl - - subroutine helio_getacch_int_tp(tp, system, param, t) - !! author: David A. Minton - !! - !! Compute direct cross term heliocentric accelerations of test particles - !! - !! Adapted from David E. Kaufmann's Swifter routine helio_getacch_int_tp.f90 - !! Adapted from Hal Levison's Swift routine getacch_ah3_tp.f - implicit none - ! Arguments - class(helio_tp), intent(inout) :: tp !! Helio test particle object - class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of - real(DP), intent(in) :: t !! Current times - ! Internals - integer(I4B) :: i, j - real(DP) :: r2, fac - real(DP), dimension(NDIM) :: dx - real(DP), dimension(:, :), allocatable :: xhp - - associate(ntp => tp%nbody, pl => system%pl, npl => system%pl%nbody, lbeg => system%lbeg) - if (lbeg) then - allocate(xhp, source=pl%xbeg) - else - allocate(xhp, source=pl%xend) - end if - - tp%ah(:,:) = 0.0_DP - do i = 1, ntp - if (tp%status(i) == ACTIVE) then - do j = 1, npl - dx(:) = tp%xh(:,i) - xhp(:,j) - r2 = dot_product(dx(:), dx(:)) - fac = pl%Gmass(j) / (r2 * sqrt(r2)) - tp%ah(:,i) = tp%ah(:,i) - fac * dx(:) - end do - end if - end do - end associate - return - end subroutine helio_getacch_int_tp - -end submodule s_helio_getacch diff --git a/src/helio/helio_gr.f90 b/src/helio/helio_gr.f90 new file mode 100644 index 000000000..4902c45b8 --- /dev/null +++ b/src/helio/helio_gr.f90 @@ -0,0 +1,111 @@ +submodule(helio_classes) s_helio_gr + use swiftest +contains + + module subroutine helio_gr_kick_getacch_pl(self, param) + !! author: David A. Minton + !! + !! Compute relativisitic accelerations of massive bodies + !! Based on Saha & Tremaine (1994) Eq. 28 + !! + !! Adapted from David A. Minton's Swifter routine routine gr_whm_kick_getacch.f90 + implicit none + ! Arguments + class(helio_pl), intent(inout) :: self !! Helio massive body particle data structure + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + ! Internals + integer(I4B) :: i + real(DP), dimension(NDIM) :: suma + real(DP), dimension(:, :), allocatable :: aj + real(DP) :: beta, rjmag4 + + if (self%nbody == 0) return + + associate(pl => self, npl => self%nbody) + call gr_kick_getacch(pl%mu, pl%xh, pl%lmask, npl, param%inv_c2, pl%agr) + pl%ah(:,1:npl) = pl%ah(:,1:npl) + pl%agr(:,1:npl) + end associate + + return + end subroutine helio_gr_kick_getacch_pl + + + module subroutine helio_gr_kick_getacch_tp(self, param) + !! author: David A. Minton + !! + !! Compute relativisitic accelerations of test particles + !! Based on Saha & Tremaine (1994) Eq. 28 + !! + !! Adapted from David A. Minton's Swifter routine routine gr_helio_kick_getacch.f90 + implicit none + ! Arguments + class(helio_tp), intent(inout) :: self !! Helio massive body particle data structure + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + ! Internals + integer(I4B) :: i + real(DP) :: rjmag4, beta + + if (self%nbody == 0) return + + associate(tp => self, ntp => self%nbody) + call gr_kick_getacch(tp%mu, tp%xh, tp%lmask, ntp, param%inv_c2, tp%agr) + tp%ah(:,1:ntp) = tp%ah(:,1:ntp) + tp%agr(:,1:ntp) + end associate + + return + end subroutine helio_gr_kick_getacch_tp + + + module pure subroutine helio_gr_p4_pl(self, param, dt) + !! author: David A. Minton + !! + !! Position kick to massive bodies due to p**4 term in the post-Newtonian correction + !! Based on Saha & Tremaine (1994) Eq. 28 + !! + !! Adapted from David A. Minton's Swifter routine routine gr_helio_p4.f90 + implicit none + ! Arguments + class(helio_pl), intent(inout) :: self !! Swiftest particle object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(in) :: dt !! Step size + ! Internals + integer(I4B) :: i + + if (self%nbody == 0) return + + associate(pl => self, npl => self%nbody) + do concurrent(i = 1:npl, pl%lmask(i)) + call gr_p4_pos_kick(param, pl%xh(:, i), pl%vb(:, i), dt) + end do + end associate + + return + end subroutine helio_gr_p4_pl + + module pure subroutine helio_gr_p4_tp(self, param, dt) + !! author: David A. Minton + !! + !! Position kick to test particles due to p**4 term in the post-Newtonian correction + !! Based on Saha & Tremaine (1994) Eq. 28 + !! + !! Adapted from David A. Minton's Swifter routine routine gr_helio_p4.f90 + implicit none + ! Arguments + class(helio_tp), intent(inout) :: self !! Swiftest particle object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(in) :: dt !! Step size + ! Internals + integer(I4B) :: i + + if (self%nbody == 0) return + + associate(tp => self, ntp => self%nbody) + do concurrent(i = 1:ntp, tp%lmask(i)) + call gr_p4_pos_kick(param, tp%xh(:, i), tp%vb(:, i), dt) + end do + end associate + + return + end subroutine helio_gr_p4_tp + +end submodule s_helio_gr \ No newline at end of file diff --git a/src/helio/helio_kick.f90 b/src/helio/helio_kick.f90 index 9d5cea3a6..eebd17f53 100644 --- a/src/helio/helio_kick.f90 +++ b/src/helio/helio_kick.f90 @@ -1,53 +1,149 @@ submodule(helio_classes) s_helio_kick use swiftest contains - module subroutine helio_kickvb_pl(self, dt) + + module subroutine helio_kick_getacch_pl(self, system, param, t, lbeg) + !! author: David A. Minton + !! + !! Compute heliocentric accelerations of massive bodies + !! + !! Adapted from David E. Kaufmann's Swifter routine helio_kick_getacch.f90 + !! Adapted from Hal Levison's Swift routine helio_kick_getacch.f + implicit none + ! Arguments + class(helio_pl), intent(inout) :: self !! Helio 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 + + if (self%nbody == 0) return + + associate(cb => system%cb, pl => self, npl => self%nbody) + call pl%accel_int() + if (param%loblatecb) then + call pl%accel_obl(system) + if (lbeg) then + cb%aoblbeg = cb%aobl + else + cb%aoblend = cb%aobl + end if + if (param%ltides) then + call pl%accel_tides(system) + if (lbeg) then + cb%atidebeg = cb%atide + else + cb%atideend = cb%atide + end if + end if + end if + if (param%lextra_force) call pl%accel_user(system, param, t, lbeg) + if (param%lgr) call pl%accel_gr(param) + end associate + + return + end subroutine helio_kick_getacch_pl + + + module subroutine helio_kick_getacch_tp(self, system, param, t, lbeg) + !! author: David A. Minton + !! + !! Compute heliocentric accelerations of test particles + !! + !! Adapted from David E. Kaufmann's Swifter routine helio_kick_getacch_tp.f90 + !! Adapted from Hal Levison's Swift routine helio_kick_getacch_tp.f + implicit none + ! Arguments + class(helio_tp), intent(inout) :: self !! Helio 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 + + if (self%nbody == 0) return + + associate(tp => self, cb => system%cb, pl => system%pl, npl => system%pl%nbody) + system%lbeg = lbeg + if (system%lbeg) then + call tp%accel_int(pl%Gmass(:), pl%xbeg(:,:), npl) + else + call tp%accel_int(pl%Gmass(:), pl%xend(:,:), npl) + end if + if (param%loblatecb) call tp%accel_obl(system) + if (param%lextra_force) call tp%accel_user(system, param, t, lbeg) + if (param%lgr) call tp%accel_gr(param) + end associate + + return + end subroutine helio_kick_getacch_tp + + + module subroutine helio_kick_vb_pl(self, system, param, t, dt, lbeg) !! author: David A. Minton !! !! Kick barycentric velocities of bodies !! !! Adapted from Martin Duncan and Hal Levison's Swift routine kickvh.f - !! Adapted from David E. Kaufmann's Swifter routine helio_kickvb.f90 + !! Adapted from David E. Kaufmann's Swifter routine helio_kick_vb.f90 implicit none ! Arguments - class(helio_pl), intent(inout) :: self !! Swiftest generic body object - real(DP), intent(in) :: dt !! Stepsize + class(helio_pl), intent(inout) :: self !! Swiftest generic body object + 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 + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Logical flag indicating whether this is the beginning of the half step or not. ! Internals integer(I4B) :: i + if (self%nbody == 0) return + associate(pl => self, npl => self%nbody) - if (npl ==0) return - do concurrent(i = 1:npl, pl%status(i) == ACTIVE) + pl%ah(:,:) = 0.0_DP + call pl%accel(system, param, t, lbeg) + if (lbeg) then + call pl%set_beg_end(xbeg = pl%xh) + else + call pl%set_beg_end(xend = pl%xh) + end if + do concurrent(i = 1:npl, pl%lmask(i)) pl%vb(:, i) = pl%vb(:, i) + pl%ah(:, i) * dt end do end associate return - - end subroutine helio_kickvb_pl + end subroutine helio_kick_vb_pl + - module subroutine helio_kickvb_tp(self, dt) + module subroutine helio_kick_vb_tp(self, system, param, t, dt, lbeg) !! author: David A. Minton !! !! Kick barycentric velocities of bodies !! !! Adapted from Martin Duncan and Hal Levison's Swift routine kickvh_tp.f - !! Adapted from David E. Kaufmann's Swifter routine helio_kickvb_tp.f90 + !! Adapted from David E. Kaufmann's Swifter routine helio_kick_vb_tp.f90 implicit none ! Arguments - class(helio_tp), intent(inout) :: self !! Swiftest generic body object - real(DP), intent(in) :: dt !! Stepsize + class(helio_tp), intent(inout) :: self !! Swiftest generic body object + 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 + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Logical flag indicating whether this is the beginning of the half step or not. ! Internals integer(I4B) :: i + if (self%nbody == 0) return + associate(tp => self, ntp => self%nbody) - if (ntp ==0) return - do concurrent(i = 1:ntp, tp%status(i) == ACTIVE) + tp%ah(:,:) = 0.0_DP + call tp%accel(system, param, t, lbeg) + do concurrent(i = 1:ntp, tp%lmask(i)) tp%vb(:, i) = tp%vb(:, i) + tp%ah(:, i) * dt end do end associate return - - end subroutine helio_kickvb_tp + end subroutine helio_kick_vb_tp + end submodule s_helio_kick \ No newline at end of file diff --git a/src/helio/helio_step.f90 b/src/helio/helio_step.f90 index 8557477f7..039884596 100644 --- a/src/helio/helio_step.f90 +++ b/src/helio/helio_step.f90 @@ -1,10 +1,14 @@ submodule(helio_classes) s_helio_step use swiftest contains + module subroutine helio_step_system(self, param, t, dt) !! author: David A. Minton !! - !! Step massive bodies and and active test particles ahead in heliocentric coordinates + !! Step massive bodies and and active test particles ahead in heliocentric coordinates. + !! + !! Currently there's no difference between this and the WHM system stepper, so this is just + !! a wrapper function to keep the method calls consistent for inherited types. !! !! Adapted from Hal Levison's Swift routine step_kdk.f !! Adapted from David E. Kaufmann's Swifter routine helio_step.f90 @@ -15,15 +19,12 @@ module subroutine helio_step_system(self, param, t, dt) real(DP), intent(in) :: t !! Simulation time real(DP), intent(in) :: dt !! Current stepsize - associate(system => self, cb => self%cb, pl => self%pl, tp => self%tp) - tp%lfirst = pl%lfirst - call pl%set_rhill(cb) - call pl%step(system, param, t, dt) - call tp%step(system, param, t, dt) - end associate + call whm_step_system(self, param, t, dt) + return end subroutine helio_step_system + module subroutine helio_step_pl(self, system, param, t, dt) !! author: David A. Minton !! @@ -39,10 +40,10 @@ module subroutine helio_step_pl(self, system, param, t, dt) real(DP), intent(in) :: t !! Current simulation time real(DP), intent(in) :: dt !! Stepsize ! Internals - integer(I4B) :: i - real(DP) :: dth, msys + real(DP) :: dth !! Half step size if (self%nbody == 0) return + associate(pl => self) select type(cb => system%cb) class is (helio_cb) @@ -52,22 +53,20 @@ module subroutine helio_step_pl(self, system, param, t, dt) pl%lfirst = .false. end if call pl%lindrift(cb, dth, lbeg=.true.) - call pl%accel(system, param, t) - call pl%kick(dth) - call pl%set_beg_end(xbeg = pl%xh) + call pl%kick(system, param, t, dth, lbeg=.true.) + if (param%lgr) call pl%gr_pos_kick(param, dth) call pl%drift(system, param, dt) - call pl%set_beg_end(xend = pl%xh) - call pl%accel(system, param, t + dt) - call pl%kick(dth) + call pl%kick(system, param, t + dt, dth, lbeg=.false.) + if (param%lgr) call pl%gr_pos_kick(param, dth) call pl%lindrift(cb, dth, lbeg=.false.) call pl%vb2vh(cb) end select end associate return - end subroutine helio_step_pl + module subroutine helio_step_tp(self, system, param, t, dt) !! author: David A. Minton @@ -82,9 +81,9 @@ module subroutine helio_step_tp(self, system, param, t, dt) class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nboody system class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters real(DP), intent(in) :: t !! Current simulation time - real(DP), intent(in) :: dt !! Stepsiz + real(DP), intent(in) :: dt !! Stepsize ! Internals - real(DP) :: dth !! Half step size + real(DP) :: dth !! Half step size if (self%nbody == 0) return @@ -97,18 +96,17 @@ module subroutine helio_step_tp(self, system, param, t, dt) tp%lfirst = .false. end if call tp%lindrift(cb, dth, lbeg=.true.) - call tp%accel(system, param, t, lbeg=.true.) - call tp%kick(dth) + call tp%kick(system, param, t, dth, lbeg=.true.) + if (param%lgr) call tp%gr_pos_kick(param, dth) call tp%drift(system, param, dt) - call tp%accel(system, param, t + dt, lbeg=.false.) - call tp%kick(dth) + call tp%kick(system, param, t + dt, dth, lbeg=.false.) + if (param%lgr) call tp%gr_pos_kick(param, dth) call tp%lindrift(cb, dth, lbeg=.false.) call tp%vb2vh(vbcb = -cb%ptend) end select end associate return - end subroutine helio_step_tp end submodule s_helio_step diff --git a/src/io/io.f90 b/src/io/io.f90 index 31ceba280..42cc8ddd9 100644 --- a/src/io/io.f90 +++ b/src/io/io.f90 @@ -1,351 +1,93 @@ submodule (swiftest_classes) s_io use swiftest contains - 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 - !! - !! Read in parameters for the integration - !! Currently this procedure does not work in user-defined derived-type input mode - !! e.g. read(unit,'(DT)') param - !! as the newline characters are ignored in the input file when compiled in ifort. + + 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 !! - !! Adapted from David E. Kaufmann's Swifter routine io_init_param.f90 - !! Adapted from Martin Duncan's Swift routine io_init_param.f + !! Reports the current state of energy, mass, and angular momentum conservation in a run implicit none ! Arguments - class(swiftest_parameters), intent(inout) :: 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(:) !! The first element passes the integrator code to the reader - integer, intent(out) :: iostat !! IO status code - character(len=*), intent(inout) :: iomsg !! Message to pass if iostat /= 0 + 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 - logical :: t0_set = .false. !! Is the initial time set in the input file? - logical :: tstop_set = .false. !! Is the final time set in the input file? - logical :: dt_set = .false. !! Is the step size set in the input file? - logical :: mtiny_set = .false. !! Is the mtiny value set? - integer(I4B) :: ilength, ifirst, ilast !! Variables used to parse input file - character(STRMAX) :: line !! Line of the input file - character (len=:), allocatable :: line_trim,param_name, param_value !! Strings used to parse the param file - character(*),parameter :: linefmt = '(A)' !! Format code for simple text string - - ! Parse the file line by line, extracting tokens then matching them up with known parameters if possible - - do - read(unit = unit, fmt = linefmt, iostat = iostat, end = 1) line - line_trim = trim(adjustl(line)) - ilength = len(line_trim) - if ((ilength /= 0)) then - ifirst = 1 - ! Read the pair of tokens. The first one is the parameter name, the second is the value. - param_name = io_get_token(line_trim, ifirst, ilast, iostat) - if (param_name == '') cycle ! No parameter name (usually because this line is commented out) - call io_toupper(param_name) - ifirst = ilast + 1 - param_value = io_get_token(line_trim, ifirst, ilast, iostat) - select case (param_name) - case ("T0") - read(param_value, *) self%t0 - t0_set = .true. - case ("TSTOP") - read(param_value, *) self%tstop - tstop_set = .true. - case ("DT") - read(param_value, *) self%dt - dt_set = .true. - case ("CB_IN") - self%incbfile = param_value - case ("PL_IN") - self%inplfile = param_value - case ("TP_IN") - self%intpfile = param_value - case ("IN_TYPE") - call io_toupper(param_value) - self%in_type = param_value - case ("ISTEP_OUT") - read(param_value, *) self%istep_out - case ("BIN_OUT") - self%outfile = param_value - case ("OUT_TYPE") - call io_toupper(param_value) - self%out_type = param_value - case ("OUT_FORM") - call io_toupper(param_value) - self%out_form = param_value - case ("OUT_STAT") - call io_toupper(param_value) - self%out_stat = param_value - case ("ISTEP_DUMP") - read(param_value, *) self%istep_dump - case ("CHK_CLOSE") - call io_toupper(param_value) - if (param_value == "YES" .or. param_value == 'T') self%lclose = .true. - case ("CHK_RMIN") - read(param_value, *) self%rmin - case ("CHK_RMAX") - read(param_value, *) self%rmax - case ("CHK_EJECT") - read(param_value, *) self%rmaxu - case ("CHK_QMIN") - read(param_value, *) self%qmin - case ("CHK_QMIN_COORD") - call io_toupper(param_value) - self%qmin_coord = param_value - case ("CHK_QMIN_RANGE") - read(param_value, *) self%qmin_alo - ifirst = ilast + 1 - param_value = io_get_token(line, ifirst, ilast, iostat) - read(param_value, *) self%qmin_ahi - case ("ENC_OUT") - self%encounter_file = param_value - case ("EXTRA_FORCE") - call io_toupper(param_value) - if (param_value == "YES" .or. param_value == 'T') self%lextra_force = .true. - case ("BIG_DISCARD") - call io_toupper(param_value) - if (param_value == "YES" .or. param_value == 'T' ) self%lbig_discard = .true. - case ("RHILL_PRESENT") - call io_toupper(param_value) - if (param_value == "YES" .or. param_value == 'T' ) self%lrhill_present = .true. - case ("MU2KG") - read(param_value, *) self%MU2KG - case ("TU2S") - read(param_value, *) self%TU2S - case ("DU2M") - read(param_value, *) self%DU2M - case ("ENERGY") - call io_toupper(param_value) - if (param_value == "YES" .or. param_value == 'T') self%lenergy = .true. - case ("GR") - call io_toupper(param_value) - if (param_value == "YES" .or. param_value == 'T') self%lgr = .true. - case ("ROTATION") - call io_toupper(param_value) - if (param_value == "YES" .or. param_value == 'T') self%lrotation = .true. - case ("TIDES") - call io_toupper(param_value) - if (param_value == "YES" .or. param_value == 'T') self%ltides = .true. - case ("NPLMAX", "NTPMAX", "MTINY", "PARTICLE_FILE", "FRAGMENTATION", "SEED", "YARKOVSKY", "YORP") ! Ignore SyMBA-specific, not-yet-implemented, or obsolete input parameters - case default - write(iomsg,*) "Unknown parameter -> ",param_name - iostat = -1 - return - end select - end if - end do - 1 continue - iostat = 0 - - !! Do basic sanity checks on the input values - if ((.not. t0_set) .or. (.not. tstop_set) .or. (.not. dt_set)) then - write(iomsg,*) 'Valid simulation time not set' - iostat = -1 - return - end if - if (self%dt <= 0.0_DP) then - write(iomsg,*) 'Invalid timestep: ' - iostat = -1 - return - end if - if (self%inplfile == "") then - write(iomsg,*) 'No valid massive body file in input file' - iostat = -1 - return - end if - if ((self%in_type /= REAL8_TYPE) .and. (self%in_type /= "ASCII")) then - write(iomsg,*) 'Invalid input file type:',trim(adjustl(self%in_type)) - iostat = -1 - return - end if - if ((self%istep_out <= 0) .and. (self%istep_dump <= 0)) then - write(iomsg,*) 'Invalid istep' - iostat = -1 - return - end if - if ((self%istep_out > 0) .and. (self%outfile == "")) then - write(iomsg,*) 'Invalid outfile' - iostat = -1 - return - end if - if (self%outfile /= "") then - if ((self%out_type /= REAL4_TYPE) .and. (self%out_type /= REAL8_TYPE) .and. & - (self%out_type /= SWIFTER_REAL4_TYPE) .and. (self%out_type /= SWIFTER_REAL8_TYPE)) then - write(iomsg,*) 'Invalid out_type: ',trim(adjustl(self%out_type)) - iostat = -1 - return - end if - if ((self%out_form /= "EL") .and. (self%out_form /= "XV")) then - write(iomsg,*) 'Invalid out_form: ',trim(adjustl(self%out_form)) - iostat = -1 - return - end if - if ((self%out_stat /= "NEW") .and. (self%out_stat /= "REPLACE") .and. (self%out_stat /= "APPEND") .and. (self%out_stat /= "UNKNOWN")) then - write(iomsg,*) 'Invalid out_stat: ',trim(adjustl(self%out_stat)) - iostat = -1 - return - end if - end if - if (self%qmin > 0.0_DP) then - if ((self%qmin_coord /= "HELIO") .and. (self%qmin_coord /= "BARY")) then - write(iomsg,*) 'Invalid qmin_coord: ',trim(adjustl(self%qmin_coord)) - iostat = -1 - return + 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 - if ((self%qmin_alo <= 0.0_DP) .or. (self%qmin_ahi <= 0.0_DP)) then - write(iomsg,*) 'Invalid qmin vals' - iostat = -1 - return + call system%get_energy_and_momentum(param) + ke_orbit_now = system%ke_orbit + ke_spin_now = system%ke_spin + pe_now = system%pe + Lorbit_now = system%Lorbit + Lspin_now = system%Lspin + 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 - end if - if (self%ltides .and. .not. self%lrotation) then - write(iomsg,*) 'Tides require rotation to be turned on' - iostat = -1 - return - end if - - write(*,*) "T0 = ",self%t0 - write(*,*) "TSTOP = ",self%tstop - write(*,*) "DT = ",self%dt - write(*,*) "CB_IN = ",trim(adjustl(self%incbfile)) - write(*,*) "PL_IN = ",trim(adjustl(self%inplfile)) - write(*,*) "TP_IN = ",trim(adjustl(self%intpfile)) - write(*,*) "IN_TYPE = ",trim(adjustl(self%in_type)) - write(*,*) "ISTEP_OUT = ",self%istep_out - write(*,*) "BIN_OUT = ",trim(adjustl(self%outfile)) - write(*,*) "OUT_TYPE = ",trim(adjustl(self%out_type)) - write(*,*) "OUT_FORM = ",trim(adjustl(self%out_form)) - write(*,*) "OUT_STAT = ",trim(adjustl(self%out_stat)) - write(*,*) "ISTEP_DUMP = ",self%istep_dump - write(*,*) "CHK_CLOSE = ",self%lclose - write(*,*) "CHK_RMIN = ",self%rmin - write(*,*) "CHK_RMAX = ",self%rmax - write(*,*) "CHK_EJECT = ",self%rmaxu - 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 - write(*,*) "ENERGY = ",self%lenergy - write(*,*) "MU2KG = ",self%MU2KG - write(*,*) "TU2S = ",self%TU2S - write(*,*) "DU2M = ",self%DU2M - - 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' - iostat = -1 - return - end if - ! 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 + 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 end if - - ! Determine if the GR flag is set correctly for this integrator - select case(integrator) - case(WHM, RMVS) - write(*,*) "GR = ", self%lgr - case default - write(iomsg, *) 'GR is not yet implemented for this integrator. This parameter will be ignored.' - end select + 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 - 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 + end subroutine io_conservation_report - return - end subroutine io_param_writer module subroutine io_dump_param(self, param_file_name) !! author: David A. Minton @@ -383,6 +125,7 @@ module subroutine io_dump_param(self, param_file_name) return end subroutine io_dump_param + module subroutine io_dump_swiftest(self, param, msg) !! author: David A. Minton !! @@ -393,7 +136,7 @@ module subroutine io_dump_swiftest(self, param, msg) implicit none ! Arguments class(swiftest_base), intent(inout) :: self !! Swiftest base object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters character(*), optional, intent(in) :: msg !! Message to display with dump operation ! Internals integer(I4B) :: ierr !! Error code @@ -421,6 +164,7 @@ module subroutine io_dump_swiftest(self, param, msg) return end subroutine io_dump_swiftest + module subroutine io_dump_system(self, param, msg) !! author: David A. Minton !! @@ -430,7 +174,7 @@ module subroutine io_dump_system(self, param, msg) 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 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 @@ -460,10 +204,12 @@ module subroutine io_dump_system(self, param, msg) ! 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 + if (param%lenergy) call self%conservation_report(param, lterminal=.true.) return end subroutine io_dump_system + module function io_get_args(integrator, param_file_name) result(ierr) !! author: David A. Minton !! @@ -522,8 +268,11 @@ module function io_get_args(integrator, param_file_name) result(ierr) 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 !! @@ -568,9 +317,435 @@ module function io_get_token(buffer, ifirst, ilast, ierr) result(token) 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 + !! + !! Read in parameters for the integration + !! Currently this procedure does not work in user-defined derived-type input mode + !! e.g. read(unit,'(DT)') param + !! as the newline characters are ignored in the input file when compiled in ifort. + !! + !! 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 !! 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(:) !! The first element passes the integrator code to the reader + integer, intent(out) :: iostat !! IO status code + character(len=*), intent(inout) :: iomsg !! Message to pass if iostat /= 0 + ! Internals + logical :: t0_set = .false. !! Is the initial time set in the input file? + logical :: tstop_set = .false. !! Is the final time set in the input file? + logical :: dt_set = .false. !! Is the step size set in the input file? + integer(I4B) :: ilength, ifirst, ilast, i !! Variables used to parse input file + character(STRMAX) :: line !! Line of the input file + character (len=:), allocatable :: line_trim,param_name, param_value !! Strings used to parse the param file + character(*),parameter :: linefmt = '(A)' !! Format code for simple text string + + ! Parse the file line by line, extracting tokens then matching them up with known parameters if possible + associate(param => self) + do + read(unit = unit, fmt = linefmt, iostat = iostat, end = 1) line + line_trim = trim(adjustl(line)) + ilength = len(line_trim) + if ((ilength /= 0)) then + ifirst = 1 + ! Read the pair of tokens. The first one is the parameter name, the second is the value. + param_name = io_get_token(line_trim, ifirst, ilast, iostat) + if (param_name == '') cycle ! No parameter name (usually because this line is commented out) + call io_toupper(param_name) + ifirst = ilast + 1 + param_value = io_get_token(line_trim, ifirst, ilast, iostat) + select case (param_name) + case ("T0") + read(param_value, *) param%t0 + t0_set = .true. + case ("TSTOP") + read(param_value, *) param%tstop + tstop_set = .true. + case ("DT") + read(param_value, *) param%dt + dt_set = .true. + case ("CB_IN") + param%incbfile = param_value + case ("PL_IN") + param%inplfile = param_value + case ("TP_IN") + param%intpfile = param_value + case ("IN_TYPE") + call io_toupper(param_value) + param%in_type = param_value + case ("ISTEP_OUT") + read(param_value, *) param%istep_out + case ("BIN_OUT") + param%outfile = param_value + case ("OUT_TYPE") + call io_toupper(param_value) + param%out_type = param_value + case ("OUT_FORM") + call io_toupper(param_value) + param%out_form = param_value + case ("OUT_STAT") + call io_toupper(param_value) + param%out_stat = param_value + case ("ISTEP_DUMP") + read(param_value, *) param%istep_dump + case ("CHK_CLOSE") + call io_toupper(param_value) + if (param_value == "YES" .or. param_value == 'T') param%lclose = .true. + case ("CHK_RMIN") + read(param_value, *) param%rmin + case ("CHK_RMAX") + read(param_value, *) param%rmax + case ("CHK_EJECT") + read(param_value, *) param%rmaxu + case ("CHK_QMIN") + read(param_value, *) param%qmin + case ("CHK_QMIN_COORD") + call io_toupper(param_value) + param%qmin_coord = param_value + case ("CHK_QMIN_RANGE") + read(param_value, *) param%qmin_alo + ifirst = ilast + 1 + param_value = io_get_token(line, ifirst, ilast, iostat) + read(param_value, *) param%qmin_ahi + case ("ENC_OUT") + param%enc_out = param_value + case ("DISCARD_OUT") + param%discard_out = param_value + case ("EXTRA_FORCE") + call io_toupper(param_value) + if (param_value == "YES" .or. param_value == 'T') param%lextra_force = .true. + case ("BIG_DISCARD") + call io_toupper(param_value) + if (param_value == "YES" .or. param_value == 'T' ) param%lbig_discard = .true. + case ("RHILL_PRESENT") + call io_toupper(param_value) + if (param_value == "YES" .or. param_value == 'T' ) param%lrhill_present = .true. + case ("MU2KG") + read(param_value, *) param%MU2KG + case ("TU2S") + read(param_value, *) param%TU2S + case ("DU2M") + read(param_value, *) param%DU2M + case ("ENERGY") + call io_toupper(param_value) + if (param_value == "YES" .or. param_value == 'T') param%lenergy = .true. + case ("GR") + call io_toupper(param_value) + if (param_value == "YES" .or. param_value == 'T') param%lgr = .true. + case ("ROTATION") + call io_toupper(param_value) + if (param_value == "YES" .or. param_value == 'T') param%lrotation = .true. + case ("TIDES") + call io_toupper(param_value) + if (param_value == "YES" .or. param_value == 'T') param%ltides = .true. + case ("FIRSTKICK") + call io_toupper(param_value) + if (param_value == "NO" .or. param_value == 'F') param%lfirstkick = .false. + case ("FIRSTENERGY") + call io_toupper(param_value) + if (param_value == "NO" .or. param_value == 'F') param%lfirstenergy = .false. + case("EORBIT_ORIG") + read(param_value, *) param%Eorbit_orig + case("MTOT_ORIG") + read(param_value, *) param%Mtot_orig + case("LTOT_ORIG") + read(param_value, *) param%Ltot_orig(1) + do i = 2, NDIM + ifirst = ilast + 1 + param_value = io_get_token(line, ifirst, ilast, iostat) + read(param_value, *) param%Ltot_orig(i) + end do + param%Lmag_orig = norm2(param%Ltot_orig(:)) + case("LORBIT_ORIG") + read(param_value, *) param%Lorbit_orig(1) + do i = 2, NDIM + ifirst = ilast + 1 + param_value = io_get_token(line, ifirst, ilast, iostat) + read(param_value, *) param%Lorbit_orig(i) + end do + case("LSPIN_ORIG") + read(param_value, *) param%Lspin_orig(1) + do i = 2, NDIM + ifirst = ilast + 1 + param_value = io_get_token(line, ifirst, ilast, iostat) + read(param_value, *) param%Lspin_orig(i) + end do + case("LESCAPE") + read(param_value, *) param%Lescape(1) + do i = 2, NDIM + ifirst = ilast + 1 + param_value = io_get_token(line, ifirst, ilast, iostat) + read(param_value, *) param%Lescape(i) + end do + case("MESCAPE") + read(param_value, *) param%Mescape + case("ECOLLISIONS") + read(param_value, *) param%Ecollisions + case("EUNTRACKED") + read(param_value, *) param%Euntracked + case ("NPLMAX", "NTPMAX", "MTINY", "PARTICLE_FILE", "FRAGMENTATION", "SEED", "YARKOVSKY", "YORP") ! Ignore SyMBA-specific, not-yet-implemented, or obsolete input parameters + case default + write(iomsg,*) "Unknown parameter -> ",param_name + iostat = -1 + return + end select + end if + end do + 1 continue + iostat = 0 + + !! Do basic sanity checks on the input values + if ((.not. t0_set) .or. (.not. tstop_set) .or. (.not. dt_set)) then + write(iomsg,*) 'Valid simulation time not set' + iostat = -1 + return + end if + if (param%dt <= 0.0_DP) then + write(iomsg,*) 'Invalid timestep: ' + iostat = -1 + return + end if + if (param%inplfile == "") then + write(iomsg,*) 'No valid massive body file in input file' + iostat = -1 + return + end if + if ((param%in_type /= REAL8_TYPE) .and. (param%in_type /= "ASCII")) then + write(iomsg,*) 'Invalid input file type:',trim(adjustl(param%in_type)) + iostat = -1 + return + end if + if ((param%istep_out <= 0) .and. (param%istep_dump <= 0)) then + write(iomsg,*) 'Invalid istep' + iostat = -1 + return + end if + if ((param%istep_out > 0) .and. (param%outfile == "")) then + write(iomsg,*) 'Invalid outfile' + iostat = -1 + return + end if + if (param%outfile /= "") then + if ((param%out_type /= REAL4_TYPE) .and. (param%out_type /= REAL8_TYPE) .and. & + (param%out_type /= SWIFTER_REAL4_TYPE) .and. (param%out_type /= SWIFTER_REAL8_TYPE)) then + write(iomsg,*) 'Invalid out_type: ',trim(adjustl(param%out_type)) + iostat = -1 + return + end if + if ((param%out_form /= "EL") .and. (param%out_form /= "XV")) then + write(iomsg,*) 'Invalid out_form: ',trim(adjustl(param%out_form)) + iostat = -1 + return + end if + if ((param%out_stat /= "NEW") .and. (param%out_stat /= "REPLACE") .and. (param%out_stat /= "APPEND") .and. (param%out_stat /= "UNKNOWN")) then + write(iomsg,*) 'Invalid out_stat: ',trim(adjustl(param%out_stat)) + iostat = -1 + return + end if + end if + if (param%qmin > 0.0_DP) then + if ((param%qmin_coord /= "HELIO") .and. (param%qmin_coord /= "BARY")) then + write(iomsg,*) 'Invalid qmin_coord: ',trim(adjustl(param%qmin_coord)) + iostat = -1 + return + end if + if ((param%qmin_alo <= 0.0_DP) .or. (param%qmin_ahi <= 0.0_DP)) then + write(iomsg,*) 'Invalid qmin vals' + iostat = -1 + return + end if + end if + if (param%ltides .and. .not. param%lrotation) then + write(iomsg,*) 'Tides require rotation to be turned on' + iostat = -1 + return + end if + + write(*,*) "T0 = ",param%t0 + write(*,*) "TSTOP = ",param%tstop + write(*,*) "DT = ",param%dt + write(*,*) "CB_IN = ",trim(adjustl(param%incbfile)) + write(*,*) "PL_IN = ",trim(adjustl(param%inplfile)) + write(*,*) "TP_IN = ",trim(adjustl(param%intpfile)) + write(*,*) "IN_TYPE = ",trim(adjustl(param%in_type)) + write(*,*) "ISTEP_OUT = ",param%istep_out + write(*,*) "BIN_OUT = ",trim(adjustl(param%outfile)) + write(*,*) "OUT_TYPE = ",trim(adjustl(param%out_type)) + write(*,*) "OUT_FORM = ",trim(adjustl(param%out_form)) + write(*,*) "OUT_STAT = ",trim(adjustl(param%out_stat)) + write(*,*) "ISTEP_DUMP = ",param%istep_dump + write(*,*) "CHK_CLOSE = ",param%lclose + write(*,*) "CHK_RMIN = ",param%rmin + write(*,*) "CHK_RMAX = ",param%rmax + write(*,*) "CHK_EJECT = ",param%rmaxu + write(*,*) "CHK_QMIN = ",param%qmin + write(*,*) "CHK_QMIN_COORD = ",trim(adjustl(param%qmin_coord)) + write(*,*) "CHK_QMIN_RANGE = ",param%qmin_alo, param%qmin_ahi + write(*,*) "EXTRA_FORCE = ",param%lextra_force + write(*,*) "RHILL_PRESENT = ",param%lrhill_present + write(*,*) "ROTATION = ", param%lrotation + write(*,*) "TIDES = ", param%ltides + write(*,*) "ENERGY = ",param%lenergy + write(*,*) "MU2KG = ",param%MU2KG + write(*,*) "TU2S = ",param%TU2S + write(*,*) "DU2M = ",param%DU2M + if (trim(adjustl(param%enc_out)) /= "") then + write(*,*) "ENC_OUT = ",trim(adjustl(param%enc_out)) + else + write(*,*) "! ENC_OUT not set: Encounters will not be recorded to file" + end if + if (trim(adjustl(param%discard_out)) /= "") then + write(*,*) "DISCARD_OUT = ",trim(adjustl(param%discard_out)) + write(*,*) "BIG_DISCARD = ",param%lbig_discard + else + write(*,*) "! DISCARD_OUT not set: Discards will not be recorded to file" + write(*,*) "! BIG_DISCARD = ",param%lbig_discard + end if + + if ((param%MU2KG < 0.0_DP) .or. (param%TU2S < 0.0_DP) .or. (param%DU2M < 0.0_DP)) then + write(iomsg,*) 'Invalid unit conversion factor' + iostat = -1 + return + end if + + ! Calculate the G for the system units + param%GU = GC / (param%DU2M**3 / (param%MU2KG * param%TU2S**2)) + + ! Calculate the inverse speed of light in the system units + param%inv_c2 = einsteinC * param%TU2S / param%DU2M + param%inv_c2 = (param%inv_c2)**(-2) + + associate(integrator => v_list(1)) + if (integrator == RMVS) then + if (.not.param%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 = ", param%lgr + case default + if (param%lgr) write(iomsg, *) 'GR is not yet implemented for this integrator. This parameter will be ignored.' + param%lgr = .false. + end select + end associate + + iostat = 0 + end associate + + 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%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) + + if (param%lenergy) then + write(param_name, Afmt) "FIRSTENERGY"; write(param_value, Lfmt) param%lfirstenergy; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "EORBIT_ORIG"; write(param_value, Rfmt) param%Eorbit_orig; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "MTOT_ORIG"; write(param_value, Rfmt) param%Mtot_orig; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(unit, '("LTOT_ORIG ",3(1X,ES25.17))') param%Ltot_orig(:) + write(unit, '("LORBIT_ORIG",3(1X,ES25.17))') param%Lorbit_orig(:) + write(unit, '("LSPIN_ORIG ",3(1X,ES25.17))') param%Lspin_orig(:) + write(unit, '("LESCAPE ",3(1X,ES25.17))') param%Lescape(:) + + write(param_name, Afmt) "MESCAPE"; write(param_value, Rfmt) param%Mescape; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "ECOLLISIONS"; write(param_value, Rfmt) param%Ecollisions; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + write(param_name, Afmt) "EUNTRACKED"; write(param_value, Rfmt) param%Euntracked; write(unit, Afmt) adjustl(param_name), adjustl(param_value) + end if + write(param_name, Afmt) "FIRSTKICK"; write(param_value, Lfmt) param%lfirstkick; 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_read_body_in(self, param) !! author: The Purdue Swiftest Team - David A. Minton, Carlisle A. Wishard, Jennifer L.L. Pouplin, and Jacob R. Elliott !! @@ -607,50 +782,44 @@ module subroutine io_read_body_in(self, param) case(ASCII_TYPE) open(unit = iu, file = infile, status = 'old', form = 'FORMATTED', iostat = ierr) read(iu, *, iostat = ierr) nbody - call self%setup(nbody) + call self%setup(nbody, param) if (nbody > 0) then do i = 1, nbody select type(self) class is (swiftest_pl) - read(iu, *, iostat = ierr) self%id(i), val - self%Gmass(i) = real(val, kind=DP) - self%mass(i) = real(val / param%GU, kind=DP) - - if (param%lclose) then - if (param%lrhill_present) then - read(iu, *, iostat = ierr) self%radius(i), self%rhill(i) - else - read(iu, *, iostat = ierr) self%radius(i) - end if - if (ierr /= 0 ) exit + if (param%lrhill_present) then + read(iu, *, iostat=ierr, err=100) self%id(i), val, self%rhill(i) else - self%radius(i) = 0.0_DP + read(iu, *, iostat=ierr, err=100) self%id(i), val end if + self%Gmass(i) = real(val, kind=DP) + self%mass(i) = real(val / param%GU, kind=DP) + if (param%lclose) read(iu, *, iostat=ierr, err=100) self%radius(i) if (param%lrotation) then - read(iu, iostat = ierr) self%Ip(:, i) - read(iu, iostat = ierr) self%rot(:, i) + read(iu, iostat=ierr, err=100) self%Ip(:, i) + read(iu, iostat=ierr, err=100) self%rot(:, i) end if if (param%ltides) then - read(iu, iostat = ierr) self%k2(i) - read(iu, iostat = ierr) self%Q(i) + read(iu, iostat=ierr, err=100) self%k2(i) + read(iu, iostat=ierr, err=100) self%Q(i) end if class is (swiftest_tp) - read(iu, *, iostat = ierr) self%id(i) + read(iu, *, iostat=ierr, err=100) self%id(i) end select - if (ierr /= 0 ) exit - read(iu, *, iostat = ierr) self%xh(1, i), self%xh(2, i), self%xh(3, i) - read(iu, *, iostat = ierr) self%vh(1, i), self%vh(2, i), self%vh(3, i) - if (ierr /= 0 ) exit + read(iu, *, iostat=ierr, err=100) self%xh(1, i), self%xh(2, i), self%xh(3, i) + read(iu, *, iostat=ierr, err=100) self%vh(1, i), self%vh(2, i), self%vh(3, i) self%status(i) = ACTIVE + self%lmask(i) = .true. end do end if case (REAL4_TYPE, REAL8_TYPE) !, SWIFTER_REAL4_TYPE, SWIFTER_REAL8_TYPE) - open(unit = iu, file = infile, status = 'old', form = 'UNFORMATTED', iostat = ierr) - read(iu, iostat = ierr) nbody - call self%setup(nbody) + open(unit=iu, file=infile, status='old', form='UNFORMATTED', iostat=ierr) + read(iu, iostat=ierr, err=100) nbody + call self%setup(nbody, param) if (nbody > 0) then call self%read_frame(iu, param, XV, ierr) self%status(:) = ACTIVE + self%lmask(:) = .true. end if case default write(*,*) trim(adjustl(param%in_type)) // ' is an unrecognized file type' @@ -658,7 +827,7 @@ module subroutine io_read_body_in(self, param) end select close(iu) - if (ierr /= 0 ) then + 100 if (ierr /= 0 ) then write(*,*) 'Error reading in initial conditions from ',trim(adjustl(infile)) call util_exit(FAILURE) end if @@ -666,6 +835,7 @@ module subroutine io_read_body_in(self, param) return end subroutine io_read_body_in + module subroutine io_read_cb_in(self, param) !! author: David A. Minton !! @@ -710,49 +880,10 @@ module subroutine io_read_cb_in(self, param) return 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 @@ -764,7 +895,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 @@ -773,7 +904,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" @@ -786,7 +917,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) @@ -801,6 +932,7 @@ function io_read_encounter(t, name1, name2, mass1, mass2, radius1, radius2, & return end function io_read_encounter + module subroutine io_read_frame_body(self, iu, param, form, ierr) !! author: David A. Minton !! @@ -817,45 +949,52 @@ 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) self%id(1:n) - !read(iu, iostat = ierr) self%name(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) - read(iu, iostat = ierr) self%a(1:n) - read(iu, iostat = ierr) self%e(1:n) - read(iu, iostat = ierr) self%inc(1:n) - read(iu, iostat = ierr) self%capom(:) - read(iu, iostat = ierr) self%omega(:) - read(iu, iostat = ierr) self%capm(:) + if (.not.allocated(self%a)) allocate(self%a(n)) + if (.not.allocated(self%e)) allocate(self%e(n)) + if (.not.allocated(self%inc)) allocate(self%inc(n)) + 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(:) + 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) self%xh(1, 1:n) - read(iu, iostat = ierr) self%xh(2, 1:n) - read(iu, iostat = ierr) self%xh(3, 1:n) - read(iu, iostat = ierr) self%vh(1, 1:n) - read(iu, iostat = ierr) self%vh(2, 1:n) - read(iu, iostat = ierr) 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) pl%Gmass(1:n) - pl%mass(1:n) = pl%Gmass / param%GU - read(iu, iostat = ierr) 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) pl%rot(1, 1:n) - read(iu, iostat = ierr) pl%rot(2, 1:n) - read(iu, iostat = ierr) pl%rot(3, 1:n) - read(iu, iostat = ierr) pl%Ip(1, 1:n) - read(iu, iostat = ierr) pl%Ip(2, 1:n) - read(iu, iostat = ierr) 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) pl%k2(1:n) - read(iu, iostat = ierr) pl%Q(1:n) + read(iu, iostat=ierr, err=100) pl%k2(1:n) + read(iu, iostat=ierr, err=100) pl%Q(1:n) end if end select end associate - if (ierr /=0) then + 100 if (ierr /=0) then write(*,*) 'Error reading Swiftest body data' call util_exit(FAILURE) end if @@ -863,6 +1002,7 @@ module subroutine io_read_frame_body(self, iu, param, form, ierr) return end subroutine io_read_frame_body + module subroutine io_read_frame_cb(self, iu, param, form, ierr) !! author: David A. Minton !! @@ -878,29 +1018,30 @@ module subroutine io_read_frame_cb(self, iu, param, form, ierr) character(*), intent(in) :: form !! Input format code ("XV" or "EL") integer(I4B), intent(out) :: ierr !! Error cod - read(iu, iostat = ierr) self%id - !read(iu, iostat = ierr) self%name - read(iu, iostat = ierr) self%Gmass + read(iu, iostat=ierr, err=100) self%id + !read(iu, iostat=ierr, err=100) self%name + read(iu, iostat=ierr, err=100) self%Gmass self%mass = self%Gmass / param%GU - read(iu, iostat = ierr) self%radius - read(iu, iostat = ierr) self%j2rp2 - read(iu, iostat = ierr) self%j4rp4 + read(iu, iostat=ierr, err=100) self%radius + read(iu, iostat=ierr, err=100) self%j2rp2 + read(iu, iostat=ierr, err=100) self%j4rp4 if (param%lrotation) then - read(iu, iostat = ierr) self%Ip(:) - read(iu, iostat = ierr) self%rot(:) + read(iu, iostat=ierr, err=100) self%Ip(:) + read(iu, iostat=ierr, err=100) self%rot(:) end if if (param%ltides) then - read(iu, iostat = ierr) self%k2 - read(iu, iostat = ierr) self%Q + read(iu, iostat=ierr, err=100) self%k2 + read(iu, iostat=ierr, err=100) self%Q end if - if (ierr /=0) then + 100 if (ierr /=0) then write(*,*) 'Error reading central body data' call util_exit(FAILURE) end if 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 !! @@ -936,9 +1077,11 @@ module subroutine io_read_frame_system(self, iu, param, form, ierr) call self%pl%read_frame(iu, param, form, ierr) if (ierr /= 0) return call self%tp%read_frame(iu, param, form, ierr) + return end subroutine io_read_frame_system + function io_read_hdr(iu, t, npl, ntp, out_form, out_type) result(ierr) !! author: David A. Minton !! @@ -976,25 +1119,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_initialize_system(self, param) + module subroutine io_read_param_in(self, param_file_name) !! author: David A. Minton !! - !! Wrapper method to initialize a basic Swiftest nbody system from files + !! 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_nbody_system), intent(inout) :: self !! Swiftest system object - class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters - - call self%cb%initialize(param) - call self%pl%initialize(param) - if (.not.param%lrhill_present) call self%pl%set_rhill(self%cb) - call self%tp%initialize(param) - call self%set_msys() - call self%pl%set_mu(self%cb) - call self%tp%set_mu(self%cb) - - end subroutine io_read_initialize_system + 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 @@ -1018,9 +1183,9 @@ module subroutine io_toupper(string) end do return - end subroutine io_toupper + module subroutine io_write_discard(self, param) !! author: David A. Minton !! @@ -1044,31 +1209,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) @@ -1080,21 +1241,21 @@ 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 + return - 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 @@ -1107,16 +1268,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:" @@ -1140,9 +1301,9 @@ module subroutine io_write_encounter(t, name1, name2, mass1, mass2, radius1, rad end if return - end subroutine io_write_encounter + module subroutine io_write_frame_body(self, iu, param) !! author: David A. Minton !! @@ -1180,6 +1341,7 @@ module subroutine io_write_frame_body(self, iu, param) select type(pl => self) class is (swiftest_pl) ! Additional output if the passed polymorphic object is a massive body write(iu) pl%Gmass(1:n) + write(iu) pl%rhill(1:n) write(iu) pl%radius(1:n) if (param%lrotation) then write(iu) pl%rot(1, 1:n) @@ -1199,6 +1361,7 @@ module subroutine io_write_frame_body(self, iu, param) return end subroutine io_write_frame_body + module subroutine io_write_frame_cb(self, iu, param) !! author: David A. Minton !! @@ -1232,9 +1395,11 @@ module subroutine io_write_frame_cb(self, iu, param) write(iu) cb%Q end if end associate + return end subroutine io_write_frame_cb + module subroutine io_write_frame_system(self, iu, param) !! author: The Purdue Swiftest Team - David A. Minton, Carlisle A. Wishard, Jennifer L.L. Pouplin, and Jacob R. Elliott !! @@ -1310,6 +1475,7 @@ module subroutine io_write_frame_system(self, iu, param) return end subroutine io_write_frame_system + subroutine io_write_hdr(iu, t, npl, ntp, out_form, out_type) !! author: The Purdue Swiftest Team - David A. Minton, Carlisle A. Wishard, Jennifer L.L. Pouplin, and Jacob R. Elliott !! @@ -1349,8 +1515,6 @@ subroutine io_write_hdr(iu, t, npl, ntp, out_form, out_type) write(iu, iostat = ierr) out_form return - end subroutine io_write_hdr - end submodule s_io diff --git a/src/kick/kick.f90 b/src/kick/kick.f90 index a54677dcd..3945a91d0 100644 --- a/src/kick/kick.f90 +++ b/src/kick/kick.f90 @@ -1,28 +1,72 @@ submodule(swiftest_classes) s_kick use swiftest contains - module subroutine kickvh_body(self, dt) + + module pure subroutine kick_getacch_int_pl(self) !! author: David A. Minton !! - !! Kick heliocentric velocities of bodies + !! Compute direct cross (third) term heliocentric accelerations of massive bodies !! - !! Adapted from Martin Duncan and Hal Levison's Swift routine kickvh.f and kickvh_tp.f - !! Adapted from David E. Kaufmann's Swifter routine whm_kickvh.f90 and whm_kickvh_tp.f90 + !! Adapted from Hal Levison's Swift routine getacch_ah3.f + !! Adapted from David E. Kaufmann's Swifter routine whm_kick_getacch_ah3.f90 and helio_kick_getacch_int.f90 implicit none ! Arguments - class(swiftest_body), intent(inout) :: self !! Swiftest generic body object - real(DP), intent(in) :: dt !! Stepsize + class(swiftest_pl), intent(inout) :: self ! Internals - integer(I4B) :: i + integer(I4B) :: k + real(DP) :: rji2, irij3, faci, facj + real(DP), dimension(NDIM) :: dx - associate(n => self%nbody, vh => self%vh, ah => self%ah, status => self%status) - if (n == 0) return - do i = 1, n - if (status(i) == ACTIVE) vh(:, i) = vh(:, i) + ah(:, i) * dt + associate(pl => self, npl => self%nbody, nplpl => self%nplpl) + do k = 1, nplpl + associate(i => pl%k_plpl(1, k), j => pl%k_plpl(2, k)) + if (pl%lmask(i) .and. pl%lmask(j)) then + dx(:) = pl%xh(:, j) - pl%xh(:, i) + rji2 = dot_product(dx(:), dx(:)) + irij3 = 1.0_DP / (rji2 * sqrt(rji2)) + faci = pl%Gmass(i) * irij3 + facj = pl%Gmass(j) * irij3 + pl%ah(:, i) = pl%ah(:, i) + facj * dx(:) + pl%ah(:, j) = pl%ah(:, j) - faci * dx(:) + end if + end associate end do end associate return - end subroutine kickvh_body + end subroutine kick_getacch_int_pl + + + module pure subroutine kick_getacch_int_tp(self, GMpl, xhp, npl) + !! author: David A. Minton + !! + !! Compute direct cross (third) term heliocentric accelerations of test particles by massive bodies + !! + !! Adapted from Hal Levison's Swift routine getacch_ah3_tp.f + !! Adapted from David E. Kaufmann's Swifter routine whm_kick_getacch_ah3.f90 and helio_kick_getacch_int_tp.f90 + implicit none + ! Arguments + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle + real(DP), dimension(:), intent(in) :: GMpl !! Massive body masses + real(DP), dimension(:,:), intent(in) :: xhp !! Massive body position vectors + integer(I4B), intent(in) :: npl !! Number of active massive bodies + ! Internals + integer(I4B) :: i, j + real(DP) :: rji2, irij3, fac, r2 + real(DP), dimension(NDIM) :: dx + + associate(tp => self, ntp => self%nbody) + do concurrent(i = 1:ntp, tp%lmask(i)) + do j = 1, npl + dx(:) = tp%xh(:,i) - xhp(:, j) + r2 = dot_product(dx(:), dx(:)) + fac = GMpl(j) / (r2 * sqrt(r2)) + tp%ah(:, i) = tp%ah(:, i) - fac * dx(:) + end do + end do + end associate + + return + end subroutine kick_getacch_int_tp end submodule s_kick diff --git a/src/main/swiftest_driver.f90 b/src/main/swiftest_driver.f90 index 4ed7cf3fe..55eb1bc89 100644 --- a/src/main/swiftest_driver.f90 +++ b/src/main/swiftest_driver.f90 @@ -10,14 +10,15 @@ program swiftest_driver implicit none class(swiftest_nbody_system), allocatable :: nbody_system !! Polymorphic object containing the nbody system to be integrated - type(swiftest_parameters) :: param + class(swiftest_parameters), allocatable :: param !! Run configuration parameters integer(I4B) :: integrator !! Integrator type code (see swiftest_globals for symbolic names) - character(len=:),allocatable :: param_file_name !! Name of the file containing user-defined parameters + character(len=:),allocatable :: param_file_name !! Name of the file containing user-defined parameters integer(I4B) :: ierr !! I/O error code integer(I8B) :: iloop !! Loop counter integer(I8B) :: idump !! Dump cadence counter integer(I8B) :: iout !! Output cadence counter - integer(I8B), parameter :: LOOPMAX = huge(iloop) !! Maximum loop value before resetting + !integer(I8B), parameter :: LOOPMAX = huge(iloop) !! Maximum loop value before resetting + integer(I8B) :: nloops !! Number of steps to take in the simulation real(DP) :: start_wall_time !! Wall clock time at start of execution real(DP) :: finish_wall_time !! Wall clock time when execution has finished integer(I4B) :: iu !! Unit number of binary file @@ -31,6 +32,12 @@ program swiftest_driver end if !$ start_wall_time = omp_get_wtime() !> Read in the user-defined parameters file and the initial conditions of the system + select case(integrator) + case(symba) + allocate(symba_parameters :: param) + case default + allocate(swiftest_parameters :: param) + end select param%integrator = integrator call setup_construct_system(nbody_system, param) call param%read_from_file(param_file_name) @@ -45,6 +52,7 @@ program swiftest_driver iloop = 0 iout = istep_out idump = istep_dump + nloops = ceiling(tstop / dt) if (istep_out > 0) call nbody_system%write_frame(iu, param) !> Define the maximum number of threads nthreads = 1 ! In the *serial* case @@ -53,13 +61,13 @@ program swiftest_driver !$ write(*,'(a)') ' ------------------' !$ write(*,'(a,i3,/)') ' Number of threads = ', nthreads write(*, *) " *************** Main Loop *************** " - do iloop = 1, LOOPMAX + do iloop = 1, nloops !> Step the system forward in time call nbody_system%step(param, t, dt) 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 @@ -79,7 +87,7 @@ program swiftest_driver idump = istep_dump end if end if - if (t > tstop) exit + !if (t >= tstop) exit end do !> Dump the final state of the system to file @@ -90,5 +98,4 @@ program swiftest_driver call util_exit(SUCCESS) stop - end program swiftest_driver diff --git a/src/modules/helio_classes.f90 b/src/modules/helio_classes.f90 index b7bdf826c..89f4aa055 100644 --- a/src/modules/helio_classes.f90 +++ b/src/modules/helio_classes.f90 @@ -7,20 +7,22 @@ module helio_classes use swiftest_classes, only : swiftest_cb, swiftest_pl, swiftest_tp, swiftest_nbody_system use whm_classes, only : whm_nbody_system implicit none + public !******************************************************************************************************************************** ! helio_nbody_system class definitions and method interfaces !******************************************************************************************************************************** - type, public, extends(whm_nbody_system) :: helio_nbody_system + type, extends(whm_nbody_system) :: helio_nbody_system contains + procedure :: step => helio_step_system !! Advance the Helio nbody system forward in time by one step end type helio_nbody_system !******************************************************************************************************************************** ! helio_cb class definitions and method interfaces !******************************************************************************************************************************* !> Helio central body particle class - type, public, extends(swiftest_cb) :: helio_cb + type, extends(swiftest_cb) :: helio_cb real(DP), dimension(NDIM) :: ptbeg !! negative barycentric velocity of the central body at the beginning of time step real(DP), dimension(NDIM) :: ptend !! negative barycentric velocity of the central body at the end of time step contains @@ -31,15 +33,17 @@ module helio_classes !******************************************************************************************************************************* !! Helio massive body particle class - type, public, extends(swiftest_pl) :: helio_pl + type, extends(swiftest_pl) :: helio_pl contains - procedure, public :: vh2vb => helio_coord_vh2vb_pl !! Convert massive bodies from heliocentric to barycentric coordinates (velocity only) - procedure, public :: vb2vh => helio_coord_vb2vh_pl !! Convert massive bodies from barycentric to heliocentric coordinates (velocity only) - procedure, public :: drift => helio_drift_pl !! Method for Danby drift in Democratic Heliocentric coordinates - procedure, public :: lindrift => helio_drift_linear_pl !! Method for linear drift of massive bodies due to barycentric momentum of Sun - procedure, public :: accel => helio_getacch_pl !! Compute heliocentric accelerations of massive bodies - procedure, public :: kick => helio_kickvb_pl !! Kicks the barycentric velocities - procedure, public :: step => helio_step_pl !! Steps the body forward one stepsize + procedure :: vh2vb => helio_coord_vh2vb_pl !! Convert massive bodies from heliocentric to barycentric coordinates (velocity only) + procedure :: vb2vh => helio_coord_vb2vh_pl !! Convert massive bodies from barycentric to heliocentric coordinates (velocity only) + procedure :: drift => helio_drift_pl !! Method for Danby drift in Democratic Heliocentric coordinates + procedure :: lindrift => helio_drift_linear_pl !! Method for linear drift of massive bodies due to barycentric momentum of Sun + procedure :: accel_gr => helio_gr_kick_getacch_pl !! Acceleration term arising from the post-Newtonian correction + procedure :: gr_pos_kick => helio_gr_p4_pl !! Position kick due to p**4 term in the post-Newtonian correction + procedure :: accel => helio_kick_getacch_pl !! Compute heliocentric accelerations of massive bodies + procedure :: kick => helio_kick_vb_pl !! Kicks the barycentric velocities + procedure :: step => helio_step_pl !! Steps the body forward one stepsize end type helio_pl !******************************************************************************************************************************** @@ -47,15 +51,17 @@ module helio_classes !******************************************************************************************************************************* !! Helio test particle class - type, public, extends(swiftest_tp) :: helio_tp + type, extends(swiftest_tp) :: helio_tp contains - procedure, public :: vh2vb => helio_coord_vh2vb_tp !! Convert test particles from heliocentric to barycentric coordinates (velocity only) - procedure, public :: vb2vh => helio_coord_vb2vh_tp !! Convert test particles from barycentric to heliocentric coordinates (velocity only) - procedure, public :: drift => helio_drift_tp !! Method for Danby drift in Democratic Heliocentric coordinates - procedure, public :: lindrift => helio_drift_linear_tp !! Method for linear drift of massive bodies due to barycentric momentum of Sun - procedure, public :: accel => helio_getacch_tp !! Compute heliocentric accelerations of massive bodies - procedure, public :: kick => helio_kickvb_tp !! Kicks the barycentric velocities - procedure, public :: step => helio_step_tp !! Steps the body forward one stepsize + procedure :: vh2vb => helio_coord_vh2vb_tp !! Convert test particles from heliocentric to barycentric coordinates (velocity only) + procedure :: vb2vh => helio_coord_vb2vh_tp !! Convert test particles from barycentric to heliocentric coordinates (velocity only) + procedure :: lindrift => helio_drift_linear_tp !! Method for linear drift of massive bodies due to barycentric momentum of Sun + procedure :: drift => helio_drift_tp !! Method for Danby drift in Democratic Heliocentric coordinates + procedure :: accel_gr => helio_gr_kick_getacch_tp !! Acceleration term arising from the post-Newtonian correction + procedure :: gr_pos_kick => helio_gr_p4_tp !! Position kick due to p**4 term in the post-Newtonian correction + procedure :: accel => helio_kick_getacch_tp !! Compute heliocentric accelerations of massive bodies + procedure :: kick => helio_kick_vb_tp !! Kicks the barycentric velocities + procedure :: step => helio_step_tp !! Steps the body forward one stepsize end type helio_tp interface @@ -84,81 +90,121 @@ module subroutine helio_coord_vh2vb_tp(self, vbcb) class(helio_tp), intent(inout) :: self !! Helio massive body object real(DP), dimension(:), intent(in) :: vbcb !! Barycentric velocity of the central body end subroutine helio_coord_vh2vb_tp + + module subroutine helio_drift_body(self, system, param, dt) + use swiftest_classes, only : swiftest_body, swiftest_nbody_system, swiftest_parameters + implicit none + class(swiftest_body), 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 parameters + real(DP), intent(in) :: dt !! Stepsize + end subroutine helio_drift_body module subroutine helio_drift_pl(self, system, param, dt) use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters implicit none class(helio_pl), intent(inout) :: self !! Helio massive body object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: dt !! Stepsize end subroutine helio_drift_pl module subroutine helio_drift_tp(self, system, param, dt) use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters implicit none - class(helio_tp), intent(inout) :: self !! Helio test particle object + class(helio_tp), intent(inout) :: self !! Helio massive body object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: dt !! Stepsize end subroutine helio_drift_tp - + module subroutine helio_drift_linear_pl(self, cb, dt, lbeg) implicit none - class(helio_pl), intent(inout) :: self !! Helio massive body object - class(helio_cb), intent(inout) :: cb !! Helio central body object - real(DP), intent(in) :: dt !! Stepsize - logical, intent(in) :: lbeg !! Argument that determines whether or not this is the beginning or end of the step + class(helio_pl), intent(inout) :: self !! Helio massive body object + class(helio_cb), intent(inout) :: cb !! Helio central body + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Argument that determines whether or not this is the beginning or end of the step end subroutine helio_drift_linear_pl module subroutine helio_drift_linear_tp(self, cb, dt, lbeg) implicit none - class(helio_tp), intent(inout) :: self !! Helio test particle object - class(helio_cb), intent(in) :: cb !! Helio nbody system object - real(DP), intent(in) :: dt !! Stepsize - logical, intent(in) :: lbeg !! Argument that determines whether or not this is the beginning or end of the step + class(helio_tp), intent(inout) :: self !! Helio test particle object + class(helio_cb), intent(in) :: cb !! Helio central body + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Argument that determines whether or not this is the beginning or end of the step end subroutine helio_drift_linear_tp - module subroutine helio_getacch_pl(self, system, param, t, lbeg) + module subroutine helio_gr_kick_getacch_pl(self, param) + use swiftest_classes, only : swiftest_parameters + implicit none + class(helio_pl), intent(inout) :: self !! Helio massive body particle data structure + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + end subroutine helio_gr_kick_getacch_pl + + module subroutine helio_gr_kick_getacch_tp(self, param) + use swiftest_classes, only : swiftest_parameters + implicit none + class(helio_tp), intent(inout) :: self !! Helio massive body particle data structure + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + end subroutine helio_gr_kick_getacch_tp + + module pure subroutine helio_gr_p4_pl(self, param, dt) + use swiftest_classes, only : swiftest_parameters + implicit none + class(helio_pl), intent(inout) :: self !! Swiftest particle object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(in) :: dt !! Step size + end subroutine helio_gr_p4_pl + + module pure subroutine helio_gr_p4_tp(self, param, dt) + use swiftest_classes, only : swiftest_parameters + implicit none + class(helio_tp), intent(inout) :: self !! Swiftest particle object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(in) :: dt !! Step size + end subroutine helio_gr_p4_tp + + module subroutine helio_kick_getacch_pl(self, system, param, t, lbeg) use swiftest_classes, only : swiftest_parameters, swiftest_nbody_system implicit none class(helio_pl), intent(inout) :: self !! Helio massive body object class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: t !! Current simulation time - logical, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step - end subroutine helio_getacch_pl + logical, intent(in) :: lbeg !! Logical flag that determines whether or not this is the beginning or end of the step + end subroutine helio_kick_getacch_pl - module subroutine helio_getacch_tp(self, system, param, t, lbeg) - use swiftest_classes, only : swiftest_parameters, swiftest_nbody_system + module subroutine helio_kick_getacch_tp(self, system, param, t, lbeg) + use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters implicit none class(helio_tp), intent(inout) :: self !! Helio test particle object 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, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step - end subroutine helio_getacch_tp + logical, intent(in) :: lbeg !! Logical flag that determines whether or not this is the beginning or end of the step + end subroutine helio_kick_getacch_tp - module subroutine helio_kickvb_pl(self, dt) - implicit none - class(helio_pl), intent(inout) :: self !! Helio massive body object - real(DP), intent(in) :: dt !! Stepsize - end subroutine helio_kickvb_pl - - module subroutine helio_kickvb_tp(self, dt) + module subroutine helio_kick_vb_pl(self, system, param, t, dt, lbeg) + use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters implicit none - class(helio_tp), intent(inout) :: self !! Helio test particle object - real(DP), intent(in) :: dt !! Stepsize - end subroutine helio_kickvb_tp + class(helio_pl), intent(inout) :: self !! Helio massive body object + 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 + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Logical flag indicating whether this is the beginning of the half step or not. + end subroutine helio_kick_vb_pl - module subroutine helio_step_system(self, param, t, dt) - use swiftest_classes, only : swiftest_parameters + module subroutine helio_kick_vb_tp(self, system, param, t, dt, lbeg) + use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters implicit none - class(helio_nbody_system), intent(inout) :: self !! Helio nbody 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 helio_step_system + class(helio_tp), intent(inout) :: self !! Helio test particle object + 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 + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Logical flag indicating whether this is the beginning of the half step or not. + end subroutine helio_kick_vb_tp module subroutine helio_step_pl(self, system, param, t, dt) use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters @@ -170,6 +216,15 @@ module subroutine helio_step_pl(self, system, param, t, dt) real(DP), intent(in) :: dt !! Stepsize end subroutine helio_step_pl + module subroutine helio_step_system(self, param, t, dt) + use swiftest_classes, only : swiftest_parameters + implicit none + class(helio_nbody_system), intent(inout) :: self !! Helio nbody 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 helio_step_system + module subroutine helio_step_tp(self, system, param, t, dt) use swiftest_classes, only : swiftest_cb, swiftest_parameters, swiftest_nbody_system implicit none @@ -180,4 +235,5 @@ module subroutine helio_step_tp(self, system, param, t, dt) real(DP), intent(in) :: dt !! Stepsizee end subroutine helio_step_tp end interface + end module helio_classes diff --git a/src/modules/lambda_function.f90 b/src/modules/lambda_function.f90 new file mode 100644 index 000000000..cd7e180cb --- /dev/null +++ b/src/modules/lambda_function.f90 @@ -0,0 +1,273 @@ +module lambda_function + !! author: David A. Minton + !! + !! Defines a class that can enable objects that behave like lambda functions. + !! + !! To use this class, define a type of either lambda_obj or lambda_obj_err, or extend the lambda_obj class as necessary, such that an interface that matches the function you wish to lambdafy. + !! Once defined, the lambda object can evaluate itself by calling the type-bound procedure eval. e.g. f%eval(x) (or f%eval(x, lerr), f%eval(x, [argument list], etc)) + !! + !! ******************************************************************************************************************************************************************************************** + !! Example - Defining a lambda function f(x,rval,ival) where rval and ival are a real and integer argument, respectively. This implementation uses an abstract interface, though this is not + !! strictly necessary unless you want to bind more than one function with the same interface. + !! ******************************************************************************************************************************************************************************************** + !! + !! module lambda_new + !! use swiftest ! This will bring in the lambda_function module + !! ! Define types in a module + !! + !! type, extends(lambda_obj) :: lambda_obj_ri_args + !! procedure(abstract_lambda_ri_args), pointer, nopass :: lambdaptr_ri_args => null() + !! real(DP) :: rval !! Real parameter + !! integer(I4B) :: ival !! Integer paramete + !! contains + !! generic :: init => lambda_ri_args_init + !! procedure :: eval => lambda_ri_args_eval + !! procedure, nopass :: lambda_ri_args_init + !! final :: lambda_ri_args_destroy + !! end type + !! interface lambda_obj + !! module procedure lambda_ri_args_init + !! end interface + !! + !! abstract interface + !! function abstract_lambda_ri_args(x, rval, ival) result(y) + !! !Template for the lambda function + !! import DP, I4B + !! real(DP), dimension(:), intent(in) :: x !! Dependent variable + !! real(DP), intent(in) :: rval !! Real parameter + !! integer(I4B), intent(in) :: ival !! Integer parameter + !! real(DP) :: y !! Real result + !! end function + !! end interface + !! + !! contains + !! type(lambda_obj_ri_args) function lambda_ri_args_init(lambda, rval, ival) + !! !! Initializes the lambda function parameters (can be used as a structure constructor) + !! implicit none + !! procedure(abstract_lambda_ri_args) :: lambda !! The lambda function that will be passed + !! real(DP), intent(in) :: rval !! Real parameter + !! integer(I4B), intent(in) :: ival !! Integer parameter + !! + !! ! Assign the procedure passed to this function to the procedure pointer + !! lambda_ri_args_init%lambdaptr_ri_args => lambda + !! + !! ! Assign the argument values + !! lambda_ri_args_init%rval = rval + !! lambda_ri_args_init%ival = ival + !! return + !! end function lambda_ri_args_init + !! + !! function lambda_ri_args_eval(self, x) result(y) + !! !! Defines the evaluation method, allowing the lambda function to be called with a single argument + !! implicit none + !! class(lambda_obj_ri_args), intent(inout) :: self + !! real(DP), dimension(:), intent(in) :: x + !! real(DP) :: y + !! + !! if (associated(self%lambdaptr_ri_args)) then + !! y = self%lambdaptr_ri_args(x, self%rval, self%ival) + !! self%lastval = y + !! if (allocated(self%lastarg)) deallocate(self%lastarg) + !! allocate(self%lastarg, source=x) + !! else + !! error stop "Lambda function was not initialized" + !! end if + !! end function lambda_ri_args_eval + !! + !! subroutine lambda_ri_args_destroy(self) + !! !! Finalizer method. Use this as a template for cleaning up the object upon destruction, such as nullifying pointers + !! implicit none + !! type(lambda_obj_ri_args) :: self + !! if (associated(self%lambdaptr_ri_args)) nullify(self%lambdaptr_ri_args) + !! end subroutine lambda_ri_args_destroy + !! + !! function example_function(x, rval, ival) result(y) + !! !This is the actual function you are going to use as the lambda function. Its interface must match the abstract interface previously defined + !! implicit none + !! ! Arguments + !! real(DP), dimension(:), intent(in) :: x + !! real(DP), intent(in) :: rval + !! integer(I4B), intent(in) :: ival + !! ! Result + !! real(DP) :: y + !! ! Internals + !! integer(I4B) :: i, n + !! n = size(x) + !! y = 42._DP * ival + !! do i = 1, n + !! y = y + x(i)**2 + !! end do + !! return + !! end function example_function + !! end module lambda_new + !! + !! program usage + !! use swiftest + !! use lambda_new + !! implicit none + !! type(lambda_obj_ri_args) :: f + !! real(DP) :: sigma_par + !! integer(I4B) :: iwonky, i,j + !! real(DP), dimension(12) :: xarr + !! + !! sigma_par = 3.14_DP + !! iwonky = 13 + !! + !! f = lambda_obj(example_function, sigma_par, iwonky) + !! do i = 1, 10 + !! xarr(:) = [(j * 0.25_DP / i, j=1, 12)] + !! write(*,*) i,f%eval(xarr) + !! end do + !! end program usage + !! ******************************************************************************************************************************************************************************************** + + use swiftest_globals + implicit none + public + + type :: lambda_obj + !! Base class for an lambda function object. This object takes no additional arguments other than the dependent variable x, an array of real numbers + procedure(lambda0), pointer, nopass :: lambdaptr => null() + real(DP) :: lastval + real(DP),dimension(:), allocatable :: lastarg + contains + generic :: init => lambda_init_0 + procedure :: eval => lambda_eval_0 + procedure, nopass :: lambda_init_0 + final :: lambda_destroy + end type + + type, extends(lambda_obj) :: lambda_obj_err + !! Extended class for an lambda function object. This object takes allows for the return of a logical error flag during evaluation of the function. + procedure(lambda0err), pointer, nopass :: lambdaptr_err => null() + logical :: lerr + contains + generic :: init => lambda_init_0_err + procedure :: eval => lambda_eval_0_err + procedure, nopass :: lambda_init_0_err + end type + + type, extends(lambda_obj) :: lambda_obj_tvar + !! Base class for an lambda function object. This object takes no additional arguments other than the dependent variable x, an array of real numbers + procedure(lambda0tvar), pointer, nopass :: lambdaptr_tvar => null() + contains + generic :: init => lambda_init_tvar + procedure :: evalt => lambda_eval_tvar + procedure, nopass :: lambda_init_tvar + end type + interface lambda_obj + module procedure lambda_init_0 + module procedure lambda_init_0_err + module procedure lambda_init_tvar + end interface + + abstract interface + function lambda0(x) result(y) + ! Template for a 0 argument function + import DP + real(DP), dimension(:), intent(in) :: x + real(DP) :: y + end function + + function lambda0err(x, lerr) result(y) + ! Template for a 0 argument function that returns an error value + import DP + real(DP), dimension(:), intent(in) :: x + logical, intent(out) :: lerr + real(DP) :: y + end function + + function lambda0tvar(x, t) result(y) + ! Template for a 0 argument function that returns an error value + import DP + real(DP), dimension(:), intent(in) :: x + real(DP), intent(in) :: t + real(DP), dimension(:), allocatable :: y + end function + end interface + + contains + type(lambda_obj) function lambda_init_0(lambda) + implicit none + ! Arguments + procedure(lambda0) :: lambda + lambda_init_0%lambdaptr => lambda + return + end function lambda_init_0 + + type(lambda_obj_err) function lambda_init_0_err(lambda, lerr) + implicit none + ! Arguments + procedure(lambda0err) :: lambda + logical, intent(in) :: lerr + lambda_init_0_err%lambdaptr_err => lambda + lambda_init_0_err%lerr = lerr + return + end function lambda_init_0_err + + type(lambda_obj_tvar) function lambda_init_tvar(lambda, t) + implicit none + ! Arguments + procedure(lambda0tvar) :: lambda + real(DP), intent(in) :: t + lambda_init_tvar%lambdaptr_tvar => lambda + return + end function lambda_init_tvar + + function lambda_eval_0(self, x) result(y) + implicit none + ! Arguments + class(lambda_obj), intent(inout) :: self + real(DP), dimension(:), intent(in) :: x + ! Result + real(DP) :: y + if (associated(self%lambdaptr)) then + y = self%lambdaptr(x) + self%lastval = y + if (allocated(self%lastarg)) deallocate(self%lastarg) + allocate(self%lastarg, source=x) + else + error stop "Lambda function was not initialized" + end if + end function lambda_eval_0 + + function lambda_eval_0_err(self, x) result(y) + implicit none + ! Arguments + class(lambda_obj_err), intent(inout) :: self + real(DP), dimension(:), intent(in) :: x + ! Result + real(DP) :: y + if (associated(self%lambdaptr_err)) then + y = self%lambdaptr_err(x, self%lerr) + self%lastval = y + if (allocated(self%lastarg)) deallocate(self%lastarg) + allocate(self%lastarg, source=x) + else + error stop "Lambda function was not initialized" + end if + end function lambda_eval_0_err + + function lambda_eval_tvar(self, x, t) result(y) + implicit none + ! Arguments + class(lambda_obj_tvar), intent(inout) :: self + real(DP), dimension(:), intent(in) :: x + real(DP), intent(in) :: t + ! Result + real(DP), dimension(:), allocatable :: y + if (associated(self%lambdaptr_tvar)) then + y = self%lambdaptr_tvar(x,t) + else + error stop "Lambda function was not initialized" + end if + end function lambda_eval_tvar + + subroutine lambda_destroy(self) + implicit none + type(lambda_obj) :: self + if (associated(self%lambdaptr)) nullify(self%lambdaptr) + end subroutine lambda_destroy + +end module lambda_function + diff --git a/src/modules/module_nrutil.f90 b/src/modules/module_nrutil.f90 deleted file mode 100644 index ce8eeabbc..000000000 --- a/src/modules/module_nrutil.f90 +++ /dev/null @@ -1,189 +0,0 @@ -!********************************************************************************************************************************** -! -! Unit Name : module_nrutil -! Unit Type : module -! Project : SWIFTEST -! Package : module -! Language : Fortran 90/95 -! -! Description : Definition of data and utility functions taken from Numerical Recipes in Fortran 90 -! -! Input -! Arguments : N/A -! Terminal : N/A -! File : N/A -! -! Output -! Arguments : N/A -! Terminal : N/A -! File : N/A -! -! Invocation : N/A -! -! Notes : Reference: Press, W. H., Teukolsky, S. A., Vetterling, W. T. & Flannery B. P. 1996. Numerical Recipes in -! Fortran 90, The Art of Scientific Computing, 2nd Edition, Vol. 2 of Fortran Numerical Recipes, -! (Cambridge University Press). -! -!********************************************************************************************************************************** -MODULE module_nrutil - - USE swiftest_globals - IMPLICIT NONE - - INTEGER(I4B), PARAMETER :: NPAR_ARTH = 16 - INTEGER(I4B), PARAMETER :: NPAR2_ARTH = 8 - INTEGER(I4B), PARAMETER :: NPAR_CUMSUM = 16 - - INTERFACE arth - MODULE PROCEDURE arth_d, arth_i - END INTERFACE - - INTERFACE cumsum - MODULE PROCEDURE cumsum_i - END INTERFACE - - INTERFACE outerdiff - MODULE PROCEDURE outerdiff_d, outerdiff_i - END INTERFACE - - INTERFACE outerprod - MODULE PROCEDURE outerprod_d - END INTERFACE - -CONTAINS - - FUNCTION arth_d(first, increment, n) - INTEGER(I4B), INTENT(IN) :: n - REAL(DP), INTENT(IN) :: first, increment - REAL(DP), DIMENSION(n) :: arth_d - INTEGER(I4B) :: k, k2 - REAL(DP) :: temp - IF (n > 0) arth_d(1) = first - IF (n <= NPAR_ARTH) THEN - DO k = 2, n - arth_d(k) = arth_d(k-1) + increment - END DO - ELSE - DO k = 2, NPAR2_ARTH - arth_d(k) = arth_d(k-1) + increment - END DO - temp = increment*NPAR2_ARTH - k = NPAR2_ARTH - DO - IF (k >= n) EXIT - k2 = k + k - arth_d(k+1:MIN(k2, n)) = temp + arth_d(1:MIN(k, n-k)) - temp = temp + temp - k = k2 - END DO - END IF - RETURN - END FUNCTION arth_d - - FUNCTION arth_i(first, increment, n) - INTEGER(I4B), INTENT(IN) :: first, increment, n - INTEGER(I4B), DIMENSION(n) :: arth_i - INTEGER(I4B) :: k, k2, temp - IF (n > 0) arth_i(1) = first - IF (n <= NPAR_ARTH) THEN - DO k = 2, n - arth_i(k) = arth_i(k-1) + increment - END DO - ELSE - DO k = 2, NPAR2_ARTH - arth_i(k) = arth_i(k-1) + increment - END DO - temp = increment*NPAR2_ARTH - k = NPAR2_ARTH - DO - IF (k >= n) EXIT - k2 = k + k - arth_i(k+1:MIN(k2, n)) = temp + arth_i(1:MIN(k, n-k)) - temp = temp + temp - k = k2 - END DO - END IF - RETURN - END FUNCTION arth_i - - RECURSIVE FUNCTION cumsum_i(arr, seed) RESULT(ans) - INTEGER(I4B), DIMENSION(:), INTENT(IN) :: arr - INTEGER(I4B), OPTIONAL, INTENT(IN) :: seed - INTEGER(I4B), DIMENSION(SIZE(arr)) :: ans - INTEGER(I4B) :: n, j, sd - n = SIZE(arr) - IF (n == 0_I4B) RETURN - sd = 0_I4B - IF (PRESENT(seed)) sd = seed - ans(1) = arr(1) + sd - IF (n < NPAR_CUMSUM) THEN - DO j = 2, n - ans(j) = ans(j-1) + arr(j) - END DO - ELSE - ans(2:n:2) = cumsum_i(arr(2:n:2) + arr(1:n-1:2), sd) - ans(3:n:2) = ans(2:n-1:2) + arr(3:n:2) - END IF - RETURN - END FUNCTION cumsum_i - - FUNCTION iminloc(arr) - REAL(DP), DIMENSION(:), INTENT(IN) :: arr - INTEGER(I4B), DIMENSION(1) :: imin - INTEGER(I4B) :: iminloc - imin = MINLOC(arr(:)) - iminloc = imin(1) - RETURN - END FUNCTION iminloc - - FUNCTION outerdiff_d(a, b) - REAL(DP), DIMENSION(:), INTENT(IN) :: a, b - REAL(DP), DIMENSION(SIZE(a), SIZE(b)) :: outerdiff_d - outerdiff_d = SPREAD(a, DIM = 2, NCOPIES = SIZE(b)) - SPREAD(b, DIM = 1, NCOPIES = SIZE(a)) - RETURN - END FUNCTION outerdiff_d - - FUNCTION outerdiff_i(a, b) - INTEGER(I4B), DIMENSION(:), INTENT(IN) :: a, b - INTEGER(I4B), DIMENSION(SIZE(a), SIZE(b)) :: outerdiff_i - outerdiff_i = SPREAD(a, DIM = 2, NCOPIES = SIZE(b)) - SPREAD(b, DIM = 1, NCOPIES = SIZE(a)) - RETURN - END FUNCTION outerdiff_i - - FUNCTION outerprod_d(a, b) - REAL(DP), DIMENSION(:), INTENT(IN) :: a, b - REAL(DP), DIMENSION(SIZE(a), SIZE(b)) :: outerprod_d - outerprod_d = SPREAD(a, DIM = 2, NCOPIES = SIZE(b))*SPREAD(b, DIM = 1, NCOPIES = SIZE(a)) - RETURN - END FUNCTION outerprod_d - - FUNCTION upper_triangle(j, k, extra) - INTEGER(I4B), INTENT(IN) :: j, k - INTEGER(I4B), OPTIONAL, INTENT(IN) :: extra - LOGICAL , DIMENSION(j, k) :: upper_triangle - INTEGER(I4B) :: n - n = 0 - IF (PRESENT(extra)) n = extra - upper_triangle = (outerdiff(arth_i(1, 1, j), arth_i(1, 1, k)) < n) - RETURN - END FUNCTION upper_triangle - -END MODULE module_nrutil -!********************************************************************************************************************************** -! -! Author(s) : David E. Kaufmann -! -! Revision Control System (RCS) Information -! -! Source File : $RCSfile$ -! Full Path : $Source$ -! Revision : $Revision$ -! Date : $Date$ -! Programmer : $Author$ -! Locked By : $Locker$ -! State : $State$ -! -! Modification History: -! -! $Log$ -!********************************************************************************************************************************** diff --git a/src/modules/rmvs_classes.f90 b/src/modules/rmvs_classes.f90 index 7ea5ad2c2..4f7255237 100644 --- a/src/modules/rmvs_classes.f90 +++ b/src/modules/rmvs_classes.f90 @@ -6,41 +6,41 @@ module rmvs_classes use swiftest_globals use whm_classes, only : whm_cb, whm_pl, whm_tp, whm_nbody_system implicit none - public + integer(I4B), private, parameter :: NTENC = 10 integer(I4B), private, parameter :: NTPHENC = 3 integer(I4B), private, parameter :: NTPENC = NTENC * NTPHENC - real(DP), private, parameter :: RHSCALE = 3.5_DP - real(DP), private, parameter :: RHPSCALE = 1.0_DP - real(DP), private, parameter :: FACQDT = 2.0_DP + real(DP), private, parameter :: RHSCALE = 3.5_DP + real(DP), private, parameter :: RHPSCALE = 1.0_DP + real(DP), private, parameter :: FACQDT = 2.0_DP !******************************************************************************************************************************** ! rmvs_nbody_system class definitions and method interfaces !******************************************************************************************************************************** - type, public, extends(whm_nbody_system) :: rmvs_nbody_system + type, extends(whm_nbody_system) :: rmvs_nbody_system !> In the RMVS integrator, only test particles are discarded logical :: lplanetocentric = .false. !! Flag that indicates that the object is a planetocentric set of masive bodies used for close encounter calculations real(DP) :: rts !! fraction of Hill's sphere radius to use as radius of encounter region real(DP), dimension(:,:), allocatable :: vbeg !! Planet velocities at beginning ot step contains - private !> Replace the abstract procedures with concrete ones - procedure, public :: initialize => rmvs_setup_system !! Performs RMVS-specific initilization steps, including generating the close encounter planetocentric structures - procedure, public :: step => rmvs_step_system !! Advance the RMVS nbody system forward in time by one step + procedure :: initialize => rmvs_setup_initialize_system !! Performs RMVS-specific initilization steps, including generating the close encounter planetocentric structures + procedure :: step => rmvs_step_system !! Advance the RMVS nbody system forward in time by one step end type rmvs_nbody_system type, private :: rmvs_interp - real(DP), dimension(:, :), allocatable :: x !! interpolated heliocentric planet position for outer encounter - real(DP), dimension(:, :), allocatable :: v !! interpolated heliocentric planet velocity for outer encounter - real(DP), dimension(:, :), allocatable :: aobl !! Encountering planet's oblateness acceleration value + real(DP), dimension(:, :), allocatable :: x !! interpolated heliocentric planet position for outer encounter + real(DP), dimension(:, :), allocatable :: v !! interpolated heliocentric planet velocity for outer encounter + real(DP), dimension(:, :), allocatable :: aobl !! Encountering planet's oblateness acceleration value + real(DP), dimension(:, :), allocatable :: atide !! Encountering planet's tidal acceleration value end type rmvs_interp !******************************************************************************************************************************** ! rmvs_cb class definitions and method interfaces !******************************************************************************************************************************* !> RMVS central body particle class - type, public, extends(whm_cb) :: rmvs_cb + type, extends(whm_cb) :: rmvs_cb type(rmvs_interp), dimension(:), allocatable :: outer !! interpolated heliocentric central body position for outer encounters type(rmvs_interp), dimension(:), allocatable :: inner !! interpolated heliocentric central body position for inner encounters logical :: lplanetocentric = .false. !! Flag that indicates that the object is a planetocentric set of masive bodies used for close encounter calculations @@ -51,9 +51,9 @@ module rmvs_classes !******************************************************************************************************************************* !! RMVS test particle class - type, public, extends(whm_tp) :: rmvs_tp + type, extends(whm_tp) :: rmvs_tp !! Note to developers: If you add componenets to this class, be sure to update methods and subroutines that traverse the - !! component list, such as rmvs_setup_tp and rmvs_spill_tp + !! component list, such as rmvs_setup_tp and rmvs_util_spill_tp ! encounter steps) logical, dimension(:), allocatable :: lperi !! planetocentric pericenter passage flag (persistent for a full rmvs time step) over a full RMVS time step) integer(I4B), dimension(:), allocatable :: plperP !! index of planet associated with pericenter distance peri (persistent over a full RMVS time step) @@ -66,14 +66,17 @@ module rmvs_classes integer(I4B) :: ipleP !! index value of encountering planet logical :: lplanetocentric = .false. !! Flag that indicates that the object is a planetocentric set of masive bodies used for close encounter calculations contains - private - procedure, public :: discard => rmvs_discard_tp !! Check to see if test particles should be discarded based on pericenter passage distances with respect to planets encountered - procedure, public :: encounter_check => rmvs_encounter_check_tp !! Checks if any test particles are undergoing a close encounter with a massive body - procedure, public :: fill => rmvs_fill_tp !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) - procedure, public :: accel => rmvs_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, public :: setup => rmvs_setup_tp !! Constructor method - Allocates space for number of particles - procedure, public :: spill => rmvs_spill_tp !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) + procedure :: discard => rmvs_discard_tp !! Check to see if test particles should be discarded based on pericenter passage distances with respect to planets encountered + 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 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) end type rmvs_tp !******************************************************************************************************************************** @@ -81,28 +84,37 @@ module rmvs_classes !******************************************************************************************************************************* !> RMVS massive body particle class - type, public, extends(whm_pl) :: rmvs_pl - integer(I4B), dimension(:), allocatable :: nenc !! number of test particles encountering planet this full rmvs time step - integer(I4B), dimension(:), allocatable :: tpenc1P !! index of first test particle encountering planet - integer(I4B), dimension(:), allocatable :: plind ! Connects the planetocentric indices back to the heliocentric planet list - type(rmvs_interp), dimension(:), allocatable :: outer !! interpolated heliocentric central body position for outer encounters - type(rmvs_interp), dimension(:), allocatable :: inner !! interpolated heliocentric central body position for inner encounters - class(rmvs_nbody_system), dimension(:), allocatable :: planetocentric - logical :: lplanetocentric = .false. !! Flag that indicates that the object is a planetocentric set of masive bodies used for close encounter calculations + type, extends(whm_pl) :: rmvs_pl + integer(I4B), dimension(:), allocatable :: nenc !! number of test particles encountering planet this full rmvs time step + integer(I4B), dimension(:), allocatable :: tpenc1P !! index of first test particle encountering planet + integer(I4B), dimension(:), allocatable :: plind !! Connects the planetocentric indices back to the heliocentric planet list + type(rmvs_interp), dimension(:), allocatable :: outer !! interpolated heliocentric central body position for outer encounters + type(rmvs_interp), dimension(:), allocatable :: inner !! interpolated heliocentric central body position for inner encounters + 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 - private - procedure, public :: fill => rmvs_fill_pl !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) - procedure, public :: setup => rmvs_setup_pl !! Constructor method - Allocates space for number of particles - procedure, public :: spill => rmvs_spill_pl !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) + 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 :: 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 interface + module elemental function rmvs_chk_ind(r2, v2, vdotr, dt, r2crit) result(lflag) + implicit none + real(DP), intent(in) :: r2, v2, vdotr, dt, r2crit + logical :: lflag + end function rmvs_chk_ind + module subroutine rmvs_discard_tp(self, system, param) use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters 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) @@ -113,66 +125,126 @@ module function rmvs_encounter_check_tp(self, system, dt) result(lencounter) logical :: lencounter !! Returns true if there is at least one close encounter end function rmvs_encounter_check_tp - module subroutine rmvs_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 - logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps - end subroutine rmvs_fill_pl - - module subroutine rmvs_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 - logical, dimension(:), intent(in) :: lfill_list !! Logical array of bodies to merge into the keeps - end subroutine rmvs_fill_tp - - module subroutine rmvs_getacch_tp(self, system, param, t, lbeg) + module subroutine rmvs_kick_getacch_tp(self, system, param, t, lbeg) use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters implicit none class(rmvs_tp), intent(inout) :: self !! RMVS test particle data structure class(swiftest_nbody_system), intent(inout) :: system !! Swiftest central body particle data structuree class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: t !! Current time - logical, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step - end subroutine rmvs_getacch_tp + logical, intent(in) :: lbeg !! Logical flag that determines whether or not this is the beginning or end of the step + end subroutine rmvs_kick_getacch_tp - module subroutine rmvs_setup_pl(self,n) + module subroutine rmvs_setup_pl(self, n, param) + use swiftest_classes, only : swiftest_parameters implicit none - class(rmvs_pl), intent(inout) :: self !! RMVS test particle object - integer, intent(in) :: n !! Number of test particles to allocate + class(rmvs_pl), intent(inout) :: self !! RMVS 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 rmvs_setup_pl - module subroutine rmvs_setup_system(self, param) + module subroutine rmvs_setup_initialize_system(self, param) use swiftest_classes, only : swiftest_parameters implicit none class(rmvs_nbody_system), intent(inout) :: self !! RMVS system object class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters - end subroutine rmvs_setup_system + end subroutine rmvs_setup_initialize_system - module subroutine rmvs_setup_tp(self,n) + module subroutine rmvs_setup_tp(self, n, param) + use swiftest_classes, only : swiftest_parameters implicit none - class(rmvs_tp), intent(inout) :: self !! RMVS test particle object - integer, intent(in) :: n !! Number of test particles to allocate + class(rmvs_tp), intent(inout) :: self !! RMVS test particle object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parametere end subroutine rmvs_setup_tp - module subroutine rmvs_spill_pl(self, discards, lspill_list) + 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(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 + + 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(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 + 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 rmvs_util_sort_pl + + module subroutine rmvs_util_sort_tp(self, sortby, ascending) + implicit none + class(rmvs_tp), intent(inout) :: self !! RMVS test particle 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 rmvs_util_sort_tp + + module subroutine rmvs_util_sort_rearrange_pl(self, ind) + implicit none + class(rmvs_pl), intent(inout) :: self !! RMVS 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 rmvs_util_sort_rearrange_pl + + module subroutine rmvs_util_sort_rearrange_tp(self, ind) + implicit none + class(rmvs_tp), intent(inout) :: self !! RMVS test particle 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 rmvs_util_sort_rearrange_tp + + 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 - end subroutine rmvs_spill_pl + 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_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 - end subroutine rmvs_spill_tp + 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) use swiftest_classes, only : swiftest_parameters diff --git a/src/modules/swiftest.f90 b/src/modules/swiftest.f90 index fd8ffaf1b..61d45163c 100644 --- a/src/modules/swiftest.f90 +++ b/src/modules/swiftest.f90 @@ -2,7 +2,7 @@ module swiftest !! author: David A. Minton !! graph: false !! - !! Basic parameters, definitions, and global type definitions used throughout the Swiftest project + !! This module serves to combine all of the Swiftest project modules under a single umbrella so that they can be accessed from individual submodule implementations with a simple "use swiftest" line. use swiftest_globals use swiftest_operators use swiftest_classes @@ -10,11 +10,10 @@ module swiftest use rmvs_classes use helio_classes use symba_classes - use module_nrutil + use lambda_function !use advisor_annotate !$ use omp_lib implicit none public - end module swiftest diff --git a/src/modules/swiftest_classes.f90 b/src/modules/swiftest_classes.f90 index 8efe47fe9..2455e77f2 100644 --- a/src/modules/swiftest_classes.f90 +++ b/src/modules/swiftest_classes.f90 @@ -5,22 +5,7 @@ module swiftest_classes !! Adapted from David E. Kaufmann's Swifter routine: module_swifter.f90 use swiftest_globals implicit none - private - public :: discard_pl, discard_system, discard_tp - public :: drift_one - public :: eucl_dist_index_plpl, eucl_dist_index_pltp, eucl_irij3_plpl - public :: gr_getaccb_ns_body, gr_p4_pos_kick, gr_pseudovel2vel, gr_vel2pseudovel - public :: io_dump_param, io_dump_swiftest, io_dump_system, io_get_args, io_get_token, io_param_reader, io_param_writer, io_read_body_in, & - io_read_cb_in, io_read_param_in, io_read_frame_body, io_read_frame_cb, io_read_frame_system, io_read_initialize_system, & - io_toupper, io_write_discard, io_write_encounter, io_write_frame_body, io_write_frame_cb, io_write_frame_system - public :: kickvh_body - public :: obl_acc_body, obl_acc_pl, obl_acc_tp - public :: orbel_el2xv_vec, orbel_xv2el_vec, orbel_scget, orbel_xv2aeq, orbel_xv2aqt - public :: setup_body, setup_construct_system, setup_pl, setup_tp - public :: user_getacch_body - public :: util_coord_b2h_pl, util_coord_b2h_tp, util_coord_h2b_pl, util_coord_h2b_tp, util_exit, util_fill_body, util_fill_pl, util_fill_tp, & - util_index, util_peri_tp, util_reverse_status, util_set_beg_end_cb, util_set_beg_end_pl, util_set_ir3h, util_set_msys, util_set_mu_pl, & - util_set_mu_tp, util_set_rhill, util_sort, util_spill_body, util_spill_pl, util_spill_tp, util_valid, util_version + public !******************************************************************************************************************************** ! swiftest_parameters class definitions @@ -28,7 +13,7 @@ module swiftest_classes !> User defined parameters that are read in from the parameters input file. !> Each paramter is initialized to a default values. - type, public :: swiftest_parameters + type :: swiftest_parameters integer(I4B) :: integrator = UNKNOWN_INTEGRATOR !! Symbolic name of the nbody integrator used integer(I4B) :: nplmax = -1 !! Maximum allowed number of massive bodies integer(I4B) :: ntpmax = -1 !! Maximum allowed number of test particles @@ -53,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 @@ -70,71 +57,86 @@ 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 + real(DP), dimension(NDIM) :: Ltot = 0.0_DP !! System angular momentum vector + real(DP), dimension(NDIM) :: Lescape = 0.0_DP !! Angular momentum of bodies that escaped the system (used for bookeeping) + real(DP) :: Mescape = 0.0_DP !! Mass of bodies that escaped the system (used for bookeeping) + real(DP) :: Ecollisions = 0.0_DP !! Energy lost from system due to collisions + real(DP) :: Euntracked = 0.0_DP !! Energy gained from system due to escaped bodies + 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 logical :: lyorp = .false. !! Turn on YORP effect contains - private - procedure, public :: reader => io_param_reader - procedure, public :: writer => io_param_writer - procedure, public :: dump => io_dump_param - procedure, public :: read_from_file => io_read_param_in + procedure :: reader => io_param_reader + procedure :: writer => io_param_writer + procedure :: dump => io_dump_param + procedure :: read_from_file => io_read_param_in end type swiftest_parameters !******************************************************************************************************************************** ! swiftest_base class definitions and methods !******************************************************************************************************************************** - type, abstract, public :: swiftest_base + type, abstract :: swiftest_base !! An superclass for a generic Swiftest object logical :: lintegrate = .false. !! Flag indicating that this object should be integrated in the current step contains !! The minimal methods that all systems must have - private - procedure :: dump => io_dump_swiftest - procedure(abstract_initialize), public, deferred :: initialize - procedure(abstract_read_frame), public, deferred :: read_frame - procedure(abstract_write_frame), public, deferred :: write_frame + procedure :: dump => io_dump_swiftest + procedure(abstract_initialize), deferred :: initialize + procedure(abstract_read_frame), deferred :: read_frame + procedure(abstract_write_frame), deferred :: write_frame end type swiftest_base !******************************************************************************************************************************** ! swiftest_cb class definitions and methods !******************************************************************************************************************************** !> A concrete lass for the central body in a Swiftest simulation - type, abstract, public, extends(swiftest_base) :: swiftest_cb - 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) - real(DP) :: radius = 0.0_DP !! Central body radius (units DU) - real(DP) :: density = 1.0_DP !! Central body mass density - calculated internally (units MU / DU**3) - real(DP) :: j2rp2 = 0.0_DP !! J2*R^2 term for central body - real(DP) :: j4rp4 = 0.0_DP !! J4*R^2 term for central body - real(DP), dimension(NDIM) :: aobl = 0.0_DP !! Barycentric acceleration due to central body oblatenes - real(DP), dimension(NDIM) :: aoblbeg = 0.0_DP !! Barycentric acceleration due to central body oblatenes at beginning of step - real(DP), dimension(NDIM) :: aoblend = 0.0_DP !! Barycentric acceleration due to central body oblatenes at end of step - real(DP), dimension(NDIM) :: xb = 0.0_DP !! Barycentric position (units DU) - real(DP), dimension(NDIM) :: vb = 0.0_DP !! Barycentric velocity (units DU / TU) - real(DP), dimension(NDIM) :: agr = 0.0_DP !! Acceleration due to post-Newtonian correction - real(DP), dimension(NDIM) :: Ip = 0.0_DP !! Unitless principal moments of inertia (I1, I2, I3) / (MR**2). Principal axis rotation assumed. - real(DP), dimension(NDIM) :: rot = 0.0_DP !! Body rotation vector in inertial coordinate frame (units rad / TU) - real(DP) :: k2 = 0.0_DP !! Tidal Love number - real(DP) :: Q = 0.0_DP !! Tidal quality factor - real(DP), dimension(NDIM) :: L0 = 0.0_DP !! Initial angular momentum of the central body - real(DP), dimension(NDIM) :: dL = 0.0_DP !! Change in angular momentum of the central body + type, abstract, extends(swiftest_base) :: swiftest_cb + 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) + real(DP) :: radius = 0.0_DP !! Central body radius (units DU) + real(DP) :: density = 1.0_DP !! Central body mass density - calculated internally (units MU / DU**3) + real(DP) :: j2rp2 = 0.0_DP !! J2*R^2 term for central body + real(DP) :: j4rp4 = 0.0_DP !! J4*R^2 term for central body + real(DP), dimension(NDIM) :: aobl = 0.0_DP !! Barycentric acceleration due to central body oblatenes + real(DP), dimension(NDIM) :: atide = 0.0_DP !! Barycentric acceleration due to central body oblatenes + real(DP), dimension(NDIM) :: aoblbeg = 0.0_DP !! Barycentric acceleration due to central body oblatenes at beginning of step + real(DP), dimension(NDIM) :: aoblend = 0.0_DP !! Barycentric acceleration due to central body oblatenes at end of step + real(DP), dimension(NDIM) :: atidebeg = 0.0_DP !! Barycentric acceleration due to central body oblatenes at beginning of step + real(DP), dimension(NDIM) :: atideend = 0.0_DP !! Barycentric acceleration due to central body oblatenes at end of step + real(DP), dimension(NDIM) :: xb = 0.0_DP !! Barycentric position (units DU) + real(DP), dimension(NDIM) :: vb = 0.0_DP !! Barycentric velocity (units DU / TU) + real(DP), dimension(NDIM) :: agr = 0.0_DP !! Acceleration due to post-Newtonian correction + real(DP), dimension(NDIM) :: Ip = 0.0_DP !! Unitless principal moments of inertia (I1, I2, I3) / (MR**2). Principal axis rotation assumed. + real(DP), dimension(NDIM) :: rot = 0.0_DP !! Body rotation vector in inertial coordinate frame (units rad / TU) + real(DP) :: k2 = 0.0_DP !! Tidal Love number + real(DP) :: Q = 0.0_DP !! Tidal quality factor + real(DP) :: tlag = 0.0_DP !! Tidal phase lag angle + real(DP), dimension(NDIM) :: L0 = 0.0_DP !! Initial angular momentum of the central body + real(DP), dimension(NDIM) :: dL = 0.0_DP !! Change in angular momentum of the central body contains - private - procedure, public :: initialize => io_read_cb_in !! I/O routine for reading in central body data - procedure, public :: write_frame => io_write_frame_cb !! I/O routine for writing out a single frame of time-series data for the central body - procedure, public :: read_frame => io_read_frame_cb !! I/O routine for reading out a single frame of time-series data for the central body - procedure, public :: set_beg_end => util_set_beg_end_cb !! Sets the beginning and ending oblateness acceleration term + procedure :: initialize => io_read_cb_in !! I/O routine for reading in central body data + procedure :: read_frame => io_read_frame_cb !! I/O routine for reading out a single frame of time-series data for the central body + procedure :: write_frame => io_write_frame_cb !! I/O routine for writing out a single frame of time-series data for the central body end type swiftest_cb !******************************************************************************************************************************** ! swiftest_body definitions and methods !******************************************************************************************************************************** !> An abstract class for a generic collection of Swiftest bodies - type, abstract, public, extends(swiftest_base) :: swiftest_body + type, abstract, extends(swiftest_base) :: swiftest_body !! Superclass that defines the generic elements of a Swiftest particle logical :: lfirst = .true. !! Run the current step as a first integer(I4B) :: nbody = 0 !! Number of bodies @@ -142,12 +144,15 @@ 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 real(DP), dimension(:,:), allocatable :: vb !! Barycentric velocity real(DP), dimension(:,:), allocatable :: ah !! Total heliocentric acceleration real(DP), dimension(:,:), allocatable :: aobl !! Barycentric accelerations of bodies due to central body oblatenes + real(DP), dimension(:,:), allocatable :: atide !! Tanngential component of acceleration of bodies due to tides real(DP), dimension(:,:), allocatable :: agr !! Acceleration due to post-Newtonian correction real(DP), dimension(:), allocatable :: ir3h !! Inverse heliocentric radius term (1/rh**3) real(DP), dimension(:), allocatable :: a !! Semimajor axis (pericentric distance for a parabolic orbit) @@ -156,46 +161,45 @@ 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]) - integer(I4B), dimension(:,:), allocatable :: k_eucl !! Index array used to convert flattened the body-body comparison upper triangular matrix - integer(I8B) :: num_comparisons !! Number of body-body comparisons in the flattened upper triangular matrix !! 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 - private - procedure(abstract_discard_body), public, deferred :: discard - procedure(abstract_set_mu), public, deferred :: set_mu - procedure(abstract_step_body), public, deferred :: step - procedure(abstract_accel), public, deferred :: accel + procedure(abstract_discard_body), deferred :: discard + procedure(abstract_kick_body), deferred :: kick + procedure(abstract_set_mu), deferred :: set_mu + procedure(abstract_step_body), deferred :: step + procedure(abstract_accel), deferred :: accel ! These are concrete because the implementation is the same for all types of particles - procedure, public :: v2pv => gr_vh2pv_body !! Converts from velocity to psudeovelocity for GR calculations using symplectic integrators - procedure, public :: pv2v => gr_pv2vh_body !! Converts from psudeovelocity to velocity for GR calculations using symplectic integrators - procedure, public :: initialize => io_read_body_in !! Read in body initial conditions from a file - procedure, public :: read_frame => io_read_frame_body !! I/O routine for writing out a single frame of time-series data for the central body - procedure, public :: write_frame => io_write_frame_body !! I/O routine for writing out a single frame of time-series data for the central body - procedure, public :: kick => kickvh_body !! Kicks the heliocentric velocities - procedure, public :: accel_obl => obl_acc_body !! Compute the barycentric accelerations of bodies due to the oblateness of the central body - procedure, public :: el2xv => orbel_el2xv_vec !! Convert orbital elements to position and velocity vectors - procedure, public :: xv2el => orbel_xv2el_vec !! Convert position and velocity vectors to orbital elements - procedure, public :: set_ir3 => util_set_ir3h !! Sets the inverse heliocentric radius term (1/rh**3) - procedure, public :: setup => setup_body !! A constructor that sets the number of bodies and allocates all allocatable arrays - procedure, public :: accel_user => user_getacch_body !! Add user-supplied heliocentric accelerations to planets - procedure, public :: fill => util_fill_body !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) - procedure, public :: spill => util_spill_body !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) - procedure, public :: reverse_status => util_reverse_status !! Reverses the active/inactive status of all particles in a structure + procedure :: drift => drift_body !! Loop through bodies and call Danby drift routine on heliocentric variables + procedure :: v2pv => gr_vh2pv_body !! Converts from velocity to psudeovelocity for GR calculations using symplectic integrators + procedure :: pv2v => gr_pv2vh_body !! Converts from psudeovelocity to velocity for GR calculations using symplectic integrators + procedure :: initialize => io_read_body_in !! Read in body initial conditions from a file + procedure :: read_frame => io_read_frame_body !! I/O routine for writing out a single frame of time-series data for the central body + procedure :: write_frame => io_write_frame_body !! I/O routine for writing out a single frame of time-series data for the central body + procedure :: accel_obl => obl_acc_body !! Compute the barycentric accelerations of bodies due to the oblateness of the central body + procedure :: el2xv => orbel_el2xv_vec !! Convert orbital elements to position and velocity vectors + 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 :: 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 + procedure :: spill => util_spill_body !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) end type swiftest_body !******************************************************************************************************************************** ! swiftest_pl definitions and methods !******************************************************************************************************************************** !> An abstract class for a generic collection of Swiftest massive bodies - type, abstract, public, extends(swiftest_body) :: swiftest_pl + type, abstract, extends(swiftest_body) :: swiftest_pl !! Superclass that defines the generic elements of a Swiftest particle real(DP), dimension(:), allocatable :: mass !! Body mass (units MU) real(DP), dimension(:), allocatable :: Gmass !! Mass gravitational term G * mass (units GU * MU) real(DP), dimension(:), allocatable :: rhill !! Body mass (units MU) real(DP), dimension(:), allocatable :: radius !! Body radius (units DU) - real(DP), dimension(:), allocatable :: irij3 !! 1.0_DP / (rji2 * sqrt(rji2)) where rji2 is the square of the Euclidean distance real(DP), dimension(:,:), allocatable :: xbeg !! Position at beginning of step real(DP), dimension(:,:), allocatable :: xend !! Position at end of step real(DP), dimension(:,:), allocatable :: vbeg !! Velocity at beginning of step @@ -204,69 +208,80 @@ module swiftest_classes real(DP), dimension(:,:), allocatable :: rot !! Body rotation vector in inertial coordinate frame (units rad / TU) real(DP), dimension(:), allocatable :: k2 !! Tidal Love number real(DP), dimension(:), allocatable :: Q !! Tidal quality factor + real(DP), dimension(:), allocatable :: tlag !! Tidal phase lag + integer(I4B), dimension(:,:), allocatable :: k_plpl !! Index array used to convert flattened the body-body comparison upper triangular matrix + integer(I8B) :: nplpl !! Number of body-body comparisons in the flattened upper triangular matrix !! 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_pl and util_spill_pl contains - private ! Massive body-specific concrete methods ! These are concrete because they are the same implemenation for all integrators - procedure, public :: discard => discard_pl !! Placeholder method for discarding massive bodies - procedure, public :: eucl_index => eucl_dist_index_plpl !! Sets up the (i, j) -> k indexing used for the single-loop blocking Euclidean distance matrix - procedure, public :: eucl_irij3 => eucl_irij3_plpl !! Parallelized single loop blocking for Euclidean distance matrix calcualtion - procedure, public :: accel_obl => obl_acc_pl !! Compute the barycentric accelerations of bodies due to the oblateness of the central body - procedure, public :: setup => setup_pl !! A base constructor that sets the number of bodies and allocates and initializes all arrays - procedure, public :: set_mu => util_set_mu_pl !! Method used to construct the vectorized form of the central body mass - procedure, public :: set_rhill => util_set_rhill !! Calculates the Hill's radii for each body - procedure, public :: h2b => util_coord_h2b_pl !! Convert massive bodies from heliocentric to barycentric coordinates (position and velocity) - procedure, public :: b2h => util_coord_b2h_pl !! Convert massive bodies from barycentric to heliocentric coordinates (position and velocity) - procedure, public :: fill => util_fill_pl !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) - procedure, public :: set_beg_end => util_set_beg_end_pl !! Sets the beginning and ending positions and velocities of planets. - procedure, public :: spill => util_spill_pl !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) + procedure :: discard => discard_pl !! Placeholder method for discarding massive bodies + procedure :: eucl_index => eucl_dist_index_plpl !! Sets up the (i, j) -> k indexing used for the single-loop blocking Euclidean distance matrix + procedure :: accel_int => kick_getacch_int_pl !! Compute direct cross (third) term heliocentric accelerations of massive bodies + 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 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 + procedure :: sort => util_sort_pl !! Sorts body arrays by a sortable component + procedure :: rearrange => util_sort_rearrange_pl !! Rearranges the order of array elements of body based on an input index array. Used in sorting methods + procedure :: spill => util_spill_pl !! "Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) end type swiftest_pl !******************************************************************************************************************************** ! swiftest_tp definitions and methods !******************************************************************************************************************************** !> An abstract class for a generic collection of Swiftest test particles - type, abstract, public, extends(swiftest_body) :: swiftest_tp + type, abstract, extends(swiftest_body) :: swiftest_tp !! Superclass that defines the generic elements of a Swiftest test particle integer(I4B), dimension(:), allocatable :: isperi !! Perihelion passage flag real(DP), dimension(:), allocatable :: peri !! Perihelion distance real(DP), dimension(:), allocatable :: atp !! Semimajor axis following perihelion passage - real(DP), dimension(:, :), allocatable :: irij3 !! 1.0_DP / (rji2 * sqrt(rji2)) where rji2 is the square of the Euclidean distance betwen each pl-tp !! 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_tp and util_spill_tp contains - private ! Test particle-specific concrete methods ! These are concrete because they are the same implemenation for all integrators - procedure, public :: discard => discard_tp !! Check to see if test particles should be discarded based on their positions relative to the massive bodies - procedure, public :: eucl_index => eucl_dist_index_pltp !! Sets up the (i, j) -> k indexing used for the single-loop blocking Euclidean distance matrix - procedure, public :: accel_obl => obl_acc_tp !! Compute the barycentric accelerations of bodies due to the oblateness of the central body - procedure, public :: setup => setup_tp !! A base constructor that sets the number of bodies and - procedure, public :: set_mu => util_set_mu_tp !! Method used to construct the vectorized form of the central body mass - procedure, public :: h2b => util_coord_h2b_tp !! Convert test particles from heliocentric to barycentric coordinates (position and velocity) - procedure, public :: b2h => util_coord_b2h_tp !! Convert test particles from barycentric to heliocentric coordinates (position and velocity) - procedure, public :: fill => util_fill_tp !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) - procedure, public :: get_peri => util_peri_tp !! Determine system pericenter passages for test particles - procedure, public :: 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 !******************************************************************************************************************************** ! swiftest_nbody_system class definitions and methods !******************************************************************************************************************************** !> An abstract class for a basic Swiftest nbody system - type, abstract, public, extends(swiftest_base) :: swiftest_nbody_system + type, abstract, extends(swiftest_base) :: swiftest_nbody_system !! This superclass contains a minimial system of a set of test particles (tp), massive bodies (pl), and a central body (cb) class(swiftest_cb), allocatable :: cb !! Central body data structure 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) :: ke = 0.0_DP !! System kinetic energy + real(DP) :: Gmtot = 0.0_DP !! Total system mass - used for barycentric coordinate conversion + real(DP) :: ke_orbit = 0.0_DP !! System orbital kinetic energy + real(DP) :: ke_spin = 0.0_DP !! System spin kinetic energy real(DP) :: pe = 0.0_DP !! System potential energy real(DP) :: te = 0.0_DP !! System total energy - real(DP), dimension(NDIM) :: Ltot = 0.0_DP !! System angular momentum vector + real(DP), dimension(NDIM) :: Lorbit = 0.0_DP !! System orbital angular momentum vector + real(DP), dimension(NDIM) :: Lspin = 0.0_DP !! System spin angular momentum vector real(DP), dimension(NDIM) :: Lescape = 0.0_DP !! Angular momentum of bodies that escaped the system (used for bookeeping) real(DP) :: Mescape = 0.0_DP !! Mass of bodies that escaped the system (used for bookeeping) real(DP) :: Ecollisions = 0.0_DP !! Energy lost from system due to collisions @@ -274,36 +289,56 @@ module swiftest_classes logical :: lbeg !! True if this is the beginning of a step. This is used so that test particle steps can be calculated !! separately from massive bodies. Massive body variables are saved at half steps, and passed to !! the test particles + integer(I4B) :: maxid = -1 !! The current maximum particle id number contains - private !> Each integrator will have its own version of the step - procedure(abstract_step_system), public, deferred :: step + procedure(abstract_step_system), deferred :: step ! Concrete classes that are common to the basic integrator (only test particles considered for discard) - procedure, public :: discard => discard_system !! Perform a discard step on the system - procedure, public :: dump => io_dump_system !! Dump the state of the system to a file - procedure, public :: initialize => io_read_initialize_system !! Initialize the system from an input file - procedure, public :: read_frame => io_read_frame_system !! Append a frame of output data to file - procedure, public :: set_msys => util_set_msys !! Sets the value of msys from the masses of system bodies. - procedure, public :: write_discard => io_write_discard !! Append a frame of output data to file - procedure, public :: write_frame => io_write_frame_system !! Append a frame of output data to file + 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) import swiftest_body, swiftest_nbody_system, swiftest_parameters, DP class(swiftest_body), intent(inout) :: self !! Swiftest body data structure class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: t !! Current simulation time - logical, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step + logical, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step end subroutine abstract_accel subroutine abstract_initialize(self, param) @@ -312,6 +347,17 @@ subroutine abstract_initialize(self, param) class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters end subroutine abstract_initialize + subroutine abstract_kick_body(self, system, param, t, dt, lbeg) + import swiftest_body, swiftest_nbody_system, swiftest_parameters, DP + implicit none + class(swiftest_body), intent(inout) :: self !! Swiftest generic body object + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system objec + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(in) :: t !! Current time + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Logical flag indicating whether this is the beginning of the half step or not. + end subroutine abstract_kick_body + subroutine abstract_read_frame(self, iu, param, form, ierr) import DP, I4B, swiftest_base, swiftest_parameters class(swiftest_base), intent(inout) :: self !! Swiftest base object @@ -359,22 +405,41 @@ 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) + implicit none + real(DP), dimension(:), intent(in) :: mu !! Vector of gravitational constants + real(DP), dimension(:,:), intent(inout) :: x, v !! Position and velocity vectors + integer(I4B), intent(in) :: n !! number of bodies + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(in) :: dt !! Stepsize + logical, dimension(:), intent(in) :: mask !! Logical mask of size self%nbody that determines which bodies to drift. + integer(I4B), dimension(:), intent(out) :: iflag !! Vector of error flags. 0 means no problem + end subroutine drift_all + + module subroutine drift_body(self, system, param, dt) + implicit none + class(swiftest_body), intent(inout) :: self !! Swiftest 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) :: dt !! Stepsize + end subroutine drift_body + module pure elemental subroutine drift_one(mu, px, py, pz, vx, vy, vz, dt, iflag) implicit none real(DP), intent(in) :: mu !! G * (Mcb + m), G = gravitational constant, Mcb = mass of central body, m = mass of body to drift @@ -388,23 +453,47 @@ module subroutine eucl_dist_index_plpl(self) class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object end subroutine - module subroutine eucl_dist_index_pltp(self, pl) - implicit none - class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object - class(swiftest_pl), intent(inout) :: pl !! Swiftest massive body object - end subroutine - - module subroutine eucl_irij3_plpl(self) - implicit none - class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object - end subroutine eucl_irij3_plpl - - module pure subroutine gr_getaccb_ns_body(self, system, param) + module subroutine fragmentation_initialize(system, param, family, x, v, L_spin, Ip, mass, radius, & + nfrag, Ip_frag, m_frag, rad_frag, xb_frag, vb_frag, rot_frag, Qloss, lfailure) + implicit none + class(swiftest_nbody_system), intent(inout) :: system + class(swiftest_parameters), intent(in) :: param + integer(I4B), dimension(:), intent(in) :: family + real(DP), dimension(:,:), intent(inout) :: x, v, L_spin, Ip + real(DP), dimension(:), intent(inout) :: mass, radius + integer(I4B), intent(inout) :: nfrag + real(DP), dimension(:), allocatable, intent(inout) :: m_frag, rad_frag + real(DP), dimension(:,:), allocatable, intent(inout) :: Ip_frag + real(DP), dimension(:,:), allocatable, intent(inout) :: xb_frag, vb_frag, rot_frag + logical, intent(out) :: lfailure ! Answers the question: Should this have been a merger instead? + real(DP), intent(inout) :: Qloss + end subroutine fragmentation_initialize + + module subroutine fragmentation_regime(Mcb, m1, m2, rad1, rad2, xh1, xh2, vb1, vb2, den1, den2, regime, Mlr, Mslr, mtiny, Qloss) + implicit none + integer(I4B), intent(out) :: regime + real(DP), intent(out) :: Mlr, Mslr + real(DP), intent(in) :: Mcb, m1, m2, rad1, rad2, den1, den2, mtiny + real(DP), dimension(:), intent(in) :: xh1, xh2, vb1, vb2 + real(DP), intent(out) :: Qloss !! The residual energy after the collision + end subroutine fragmentation_regime + + module pure subroutine gr_kick_getaccb_ns_body(self, system, param) implicit none class(swiftest_body), intent(inout) :: self !! Swiftest generic 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 gr_getaccb_ns_body + end subroutine gr_kick_getaccb_ns_body + + module subroutine gr_kick_getacch(mu, x, lmask, n, inv_c2, agr) + implicit none + real(DP), dimension(:), intent(in) :: mu !! Gravitational constant + real(DP), dimension(:,:), intent(in) :: x !! Position vectors + logical, dimension(:), intent(in) :: lmask !! Logical mask indicating which bodies to compute + integer(I4B), intent(in) :: n !! Total number of bodies + real(DP), intent(in) :: inv_c2 !! Inverse speed of light squared: 1 / c**2 + real(DP), dimension(:,:), intent(out) :: agr !! Accelerations + end subroutine gr_kick_getacch module pure subroutine gr_p4_pos_kick(param, x, v, dt) implicit none @@ -426,7 +515,7 @@ end subroutine gr_pseudovel2vel module pure subroutine gr_pv2vh_body(self, param) implicit none class(swiftest_body), intent(inout) :: self !! Swiftest particle object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of on parameters + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine gr_pv2vh_body module pure subroutine gr_vel2pseudovel(param, mu, xh, vh, pv) @@ -441,9 +530,16 @@ end subroutine gr_vel2pseudovel module pure subroutine gr_vh2pv_body(self, param) implicit none class(swiftest_body), intent(inout) :: self !! Swiftest particle object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of on parameters + 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 @@ -453,14 +549,14 @@ end subroutine io_dump_param module subroutine io_dump_swiftest(self, param, msg) implicit none class(swiftest_base), intent(inout) :: self !! Swiftest base object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters character(*), optional, intent(in) :: msg !! Message to display with dump operation end subroutine io_dump_swiftest module subroutine io_dump_system(self, param, msg) 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 character(*), optional, intent(in) :: msg !! Message to display with dump operation end subroutine io_dump_system @@ -547,12 +643,6 @@ module subroutine io_read_frame_system(self, iu, param, form, ierr) integer(I4B), intent(out) :: ierr !! Error code end subroutine io_read_frame_system - module subroutine io_read_initialize_system(self, param) - implicit none - class(swiftest_nbody_system), intent(inout) :: self !! Swiftest system object - class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters - end subroutine io_read_initialize_system - module subroutine io_write_discard(self, param) implicit none class(swiftest_nbody_system), intent(inout) :: self !! Swiftest system object @@ -565,12 +655,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) @@ -594,11 +684,18 @@ module subroutine io_write_frame_system(self, iu, param) class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine io_write_frame_system - module subroutine kickvh_body(self, dt) + module pure subroutine kick_getacch_int_pl(self) implicit none - class(swiftest_body), intent(inout) :: self !! Swiftest body object - real(DP), intent(in) :: dt !! Stepsize - end subroutine kickvh_body + class(swiftest_pl), intent(inout) :: self + end subroutine kick_getacch_int_pl + + module pure subroutine kick_getacch_int_tp(self, GMpl, xhp, npl) + implicit none + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle + real(DP), dimension(:), intent(in) :: GMpl !! Massive body masses + real(DP), dimension(:,:), intent(in) :: xhp !! Massive body position vectors + integer(I4B), intent(in) :: npl !! Number of active massive bodies + end subroutine kick_getacch_int_tp module subroutine obl_acc_body(self, system) implicit none @@ -618,6 +715,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 @@ -632,16 +740,23 @@ end subroutine orbel_scget module pure subroutine orbel_xv2aeq(mu, x, v, a, e, q) implicit none - real(DP), intent(in) :: mu - real(DP), dimension(:), intent(in) :: x, v - real(DP), intent(out) :: a, e, q + real(DP), intent(in) :: mu !! Gravitational constant + real(DP), dimension(:), intent(in) :: x !! Position vector + real(DP), dimension(:), intent(in) :: v !! Velocity vector + real(DP), intent(out) :: a !! semimajor axis + real(DP), intent(out) :: e !! eccentricity + real(DP), intent(out) :: q !! periapsis end subroutine orbel_xv2aeq module pure subroutine orbel_xv2aqt(mu, x, v, a, q, capm, tperi) implicit none - real(DP), intent(in) :: mu - real(DP), dimension(:), intent(in) :: x, v - real(DP), intent(out) :: a, q, capm, tperi + real(DP), intent(in) :: mu !! Gravitational constant + real(DP), dimension(:), intent(in) :: x !! Position vector + real(DP), dimension(:), intent(in) :: v !! Velocity vector + real(DP), intent(out) :: a !! semimajor axis + real(DP), intent(out) :: q !! periapsis + real(DP), intent(out) :: capm !! mean anomaly + real(DP), intent(out) :: tperi !! time of pericenter passage end subroutine orbel_xv2aqt module subroutine orbel_xv2el_vec(self, cb) @@ -650,38 +765,127 @@ module subroutine orbel_xv2el_vec(self, cb) class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object end subroutine orbel_xv2el_vec - module subroutine setup_body(self,n) + module subroutine setup_body(self, n, param) implicit none - class(swiftest_body), intent(inout) :: self !! Swiftest body object - integer, intent(in) :: n !! Number of particles to allocate space for + class(swiftest_body), intent(inout) :: self !! Swiftest 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 setup_body module subroutine setup_construct_system(system, param) implicit none class(swiftest_nbody_system), allocatable, intent(inout) :: system !! Swiftest system object - type(swiftest_parameters), intent(in) :: param !! Swiftest parameters + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine setup_construct_system - module subroutine setup_pl(self,n) + module subroutine setup_encounter(self, n) implicit none - class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object - integer, intent(in) :: n !! Number of massive bodies to allocate space for + 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 + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters + end subroutine setup_initialize_system + + module subroutine setup_pl(self, n, param) + implicit none + class(swiftest_pl), intent(inout) :: self !! Swiftest 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 setup_pl - module subroutine setup_tp(self, n) + module subroutine setup_tp(self, n, param) implicit none - class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object - integer, intent(in) :: n !! Number of bodies to allocate space for + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parametersr end subroutine setup_tp - module subroutine user_getacch_body(self, system, param, t, lbeg) + module subroutine tides_kick_getacch_pl(self, system) + implicit none + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object + end subroutine tides_kick_getacch_pl + + module subroutine tides_step_spin_system(self, param, t, dt) + 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(in) :: t !! Simulation time + real(DP), intent(in) :: dt !! Current stepsize + end subroutine tides_step_spin_system + + module subroutine user_kick_getacch_body(self, system, param, t, lbeg) implicit none class(swiftest_body), intent(inout) :: self !! Swiftest 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 of user parameters + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: t !! Current time - logical, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step - end subroutine user_getacch_body + 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 @@ -707,6 +911,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 @@ -715,29 +925,73 @@ 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_index(arr, index) + module subroutine util_fill_arr_DP(keeps, inserts, lfill_list) implicit none - integer(I4B), dimension(:), intent(out) :: index - real(DP), dimension(:), intent(in) :: arr - end subroutine util_index + 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 function util_minimize_bfgs(f, N, x0, eps, lerr) result(x1) + use lambda_function + implicit none + integer(I4B), intent(in) :: N + class(lambda_obj), intent(inout) :: f + real(DP), dimension(:), intent(in) :: x0 + real(DP), intent(in) :: eps + logical, intent(out) :: lerr + real(DP), dimension(:), allocatable :: x1 + end function util_minimize_bfgs module subroutine util_peri_tp(self, system, param) implicit none @@ -745,18 +999,103 @@ module subroutine util_peri_tp(self, system, param) 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_solve_linear_system + module function util_solve_linear_system_d(A,b,n,lerr) result(x) + implicit none + integer(I4B), intent(in) :: n + real(DP), dimension(:,:), intent(in) :: A + real(DP), dimension(:), intent(in) :: b + logical, intent(out) :: lerr + real(DP), dimension(n) :: x + end function util_solve_linear_system_d - module subroutine util_reverse_status(self) + module function util_solve_linear_system_q(A,b,n,lerr) result(x) + implicit none + integer(I4B), intent(in) :: n + real(QP), dimension(:,:), intent(in) :: A + real(QP), dimension(:), intent(in) :: b + logical, intent(out) :: lerr + real(QP), dimension(n) :: x + end function util_solve_linear_system_q + end interface + + interface + module function util_solve_rkf45(f, y0in, t1, dt0, tol) result(y1) + use lambda_function + implicit none + class(lambda_obj), intent(inout) :: f !! lambda function object that has been initialized to be a function of derivatives. The object will return with components lastarg and lasteval set + real(DP), dimension(:), intent(in) :: y0in !! Initial value at t=0 + real(DP), intent(in) :: t1 !! Final time + real(DP), intent(in) :: dt0 !! Initial step size guess + real(DP), intent(in) :: tol !! Tolerance on solution + real(DP), dimension(:), allocatable :: y1 !! Final result + end function util_solve_rkf45 + 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 - end subroutine util_reverse_status + 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_set_beg_end_cb(self, aoblbeg, aoblend) + module subroutine util_get_energy_momentum_system(self, param) implicit none - class(swiftest_cb), intent(inout) :: self !! Swiftest central body object - real(DP), dimension(:), intent(in), optional :: aoblbeg !! Oblateness acceleration term at beginning of step - real(DP), dimension(:), intent(in), optional :: aoblend !! Oblateness acceleration term at end of step - end subroutine util_set_beg_end_cb + class(swiftest_nbody_system), intent(inout) :: self !! Swiftest nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + end subroutine util_get_energy_momentum_system module subroutine util_set_beg_end_pl(self, xbeg, xend, vbeg) implicit none @@ -791,8 +1130,14 @@ end subroutine util_set_mu_tp module subroutine util_set_rhill(self,cb) implicit none class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object - class(swiftest_cb), intent(inout) :: cb !! Swiftest massive body object + class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object end subroutine util_set_rhill + + module subroutine util_set_rhill_approximate(self,cb) + implicit none + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object + end subroutine util_set_rhill_approximate end interface interface util_sort @@ -801,37 +1146,149 @@ module subroutine util_sort_i4b(arr) integer(I4B), dimension(:), intent(inout) :: arr end subroutine util_sort_i4b + module subroutine util_sort_index_i4b(arr,ind) + implicit none + integer(I4B), dimension(:), intent(in) :: arr + integer(I4B), dimension(:), intent(out) :: ind + end subroutine util_sort_index_i4b + module subroutine util_sort_sp(arr) implicit none real(SP), dimension(:), intent(inout) :: arr end subroutine util_sort_sp + module subroutine util_sort_index_sp(arr,ind) + implicit none + real(SP), dimension(:), intent(in) :: arr + integer(I4B), dimension(:), intent(out) :: ind + end subroutine util_sort_index_sp + module subroutine util_sort_dp(arr) implicit none real(DP), dimension(:), intent(inout) :: arr end subroutine util_sort_dp - end interface + + module subroutine util_sort_index_dp(arr,ind) + implicit none + real(DP), dimension(:), intent(in) :: arr + integer(I4B), dimension(:), intent(out) :: ind + end subroutine util_sort_index_dp + end interface util_sort interface - module subroutine util_spill_body(self, discards, lspill_list) + module subroutine util_sort_rearrange_body(self, ind) + implicit none + class(swiftest_body), intent(inout) :: self !! Swiftest 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 util_sort_rearrange_body + + module subroutine util_sort_rearrange_pl(self, ind) + implicit none + class(swiftest_pl), intent(inout) :: self !! Swiftest 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 util_sort_rearrange_pl + + module subroutine util_sort_rearrange_tp(self, ind) + implicit none + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle 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 util_sort_rearrange_tp + + module subroutine util_sort_body(self, sortby, ascending) + implicit none + class(swiftest_body), intent(inout) :: self !! Swiftest 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 util_sort_body + + module subroutine util_sort_pl(self, sortby, ascending) 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 + class(swiftest_pl), intent(inout) :: self !! Swiftest 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 util_sort_pl + + module subroutine util_sort_tp(self, sortby, ascending) + implicit none + class(swiftest_tp), intent(inout) :: self !! Swiftest 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 util_sort_tp + 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/swiftest_globals.f90 b/src/modules/swiftest_globals.f90 index 256c4124b..5ec55f6c6 100644 --- a/src/modules/swiftest_globals.f90 +++ b/src/modules/swiftest_globals.f90 @@ -86,6 +86,7 @@ module swiftest_globals integer(I4B), parameter :: SUPERCATASTROPHIC = -10 integer(I4B), parameter :: GRAZE_AND_MERGE = -11 integer(I4B), parameter :: HIT_AND_RUN = -12 + integer(I4B), parameter :: COLLISION = -13 !>Symbolic names for collisional outcomes from collresolve_resolve: integer(I4B), parameter :: COLLRESOLVE_REGIME_MERGE = 1 @@ -100,10 +101,10 @@ module swiftest_globals !> Standard file names integer(I4B), parameter :: NDUMPFILES = 2 - character(*), dimension(2), parameter :: DUMP_CB_FILE = (/ 'dump_cb1.bin', 'dump_cb2.bin' /) - character(*), dimension(2), parameter :: DUMP_PL_FILE = (/ 'dump_pl1.bin', 'dump_pl2.bin' /) - character(*), dimension(2), parameter :: DUMP_TP_FILE = (/ 'dump_tp1.bin', 'dump_tp2.bin' /) - character(*), dimension(2), parameter :: DUMP_PARAM_FILE = (/ 'dump_param1.dat', 'dump_param2.dat' /) + character(*), dimension(2), parameter :: DUMP_CB_FILE = ['dump_cb1.bin', 'dump_cb2.bin' ] + character(*), dimension(2), parameter :: DUMP_PL_FILE = ['dump_pl1.bin', 'dump_pl2.bin' ] + character(*), dimension(2), parameter :: DUMP_TP_FILE = ['dump_tp1.bin', 'dump_tp2.bin' ] + character(*), dimension(2), parameter :: DUMP_PARAM_FILE = ['dump_param1.dat', 'dump_param2.dat'] !> Default file names that can be changed by the user in the parameters file character(*), parameter :: ENC_OUTFILE = 'encounter.out' @@ -116,11 +117,11 @@ module swiftest_globals integer(I4B), parameter :: BINUNIT = 20 !! File unit number for the binary output file !> Miscellaneous constants: - integer(I4B), parameter :: NDIM = 3 !! Number of dimensions in our reality - integer(I4B), parameter :: NDIM2 = 2 * NDIM !! 2x the number of dimensions - real(DP), parameter :: VSMALL = 4.0E-15_DP + integer(I4B), parameter :: NDIM = 3 !! Number of dimensions in our reality + integer(I4B), parameter :: NDIM2 = 2 * NDIM !! 2x the number of dimensions + real(DP), parameter :: VSMALL = 2 * epsilon(1._DP) !! Very small number used to prevent floating underflow - real(DP), parameter :: GC = 6.6743E-11_DP !! Universal gravitational constant in SI units + real(DP), parameter :: GC = 6.6743E-11_DP !! Universal gravitational constant in SI units real(DP), parameter :: einsteinC = 299792458.0_DP !! Speed of light in SI units end module swiftest_globals diff --git a/src/modules/swiftest_operators.f90 b/src/modules/swiftest_operators.f90 index b5b4ce078..2c982f09c 100644 --- a/src/modules/swiftest_operators.f90 +++ b/src/modules/swiftest_operators.f90 @@ -26,6 +26,12 @@ module pure function operator_cross_dp(A, B) result(C) real(DP), dimension(3) :: C end function operator_cross_dp + module pure function operator_cross_qp(A, B) result(C) + implicit none + real(QP), dimension(:), intent(in) :: A, B + real(QP), dimension(3) :: C + end function operator_cross_qp + module pure function operator_cross_i1b(A, B) result(C) implicit none integer(I1B), dimension(:), intent(in) :: A, B @@ -62,6 +68,12 @@ module pure function operator_cross_el_dp(A, B) result(C) real(DP), dimension(:,:), allocatable :: C end function operator_cross_el_dp + module pure function operator_cross_el_qp(A, B) result(C) + implicit none + real(QP), dimension(:,:), intent(in) :: A, B + real(QP), dimension(:,:), allocatable :: C + end function operator_cross_el_qp + module pure function operator_cross_el_i1b(A, B) result(C) implicit none integer(I1B), dimension(:,:), intent(in) :: A, B @@ -87,4 +99,46 @@ module pure function operator_cross_el_i8b(A, B) result(C) end function operator_cross_el_i8b end interface + !******************************************************************************************************************************** + ! Interfaces for .mag. operator + !******************************************************************************************************************************** + + interface operator(.mag.) + module pure function operator_mag_sp(A) result(B) + implicit none + real(SP), dimension(:), intent(in) :: A + real(SP) :: B + end function operator_mag_sp + + module pure function operator_mag_dp(A) result(B) + implicit none + real(DP), dimension(:), intent(in) :: A + real(DP) :: B + end function operator_mag_dp + + module pure function operator_mag_qp(A) result(B) + implicit none + real(QP), dimension(:), intent(in) :: A + real(QP) :: B + end function operator_mag_qp + + module pure function operator_mag_el_sp(A) result(B) + implicit none + real(SP), dimension(:,:), intent(in) :: A + real(SP), dimension(:), allocatable :: B + end function operator_mag_el_sp + + module pure function operator_mag_el_dp(A) result(B) + implicit none + real(DP), dimension(:,:), intent(in) :: A + real(DP), dimension(:), allocatable :: B + end function operator_mag_el_dp + + module pure function operator_mag_el_qp(A) result(B) + implicit none + real(QP), dimension(:,:), intent(in) :: A + real(QP), dimension(:), allocatable :: B + end function operator_mag_el_qp + end interface + end module swiftest_operators diff --git a/src/modules/symba.f90 b/src/modules/symba.f90 deleted file mode 100644 index 41f2de81a..000000000 --- a/src/modules/symba.f90 +++ /dev/null @@ -1,869 +0,0 @@ -module symba - !! author: The Purdue Swiftest Team - David A. Minton, Carlisle A. Wishard, Jennifer L.L. Pouplin, and Jacob R. Elliott - !! - !! Definition of classes and methods specific to the Symplectic Massive Body Algorithm - !! - !! Adapted from David E. Kaufmann's Swifter routine: symba.f90 - use swiftest_globals - use helio - implicit none - - integer(I4B), private, parameter :: NENMAX = 32767 - integer(I4B), private, parameter :: NTENC = 3 - real(DP), private, parameter :: RHSCALE = 6.5_DP - real(DP), private, parameter :: RSHELL = 0.48075_DP - - !******************************************************************************************************************************** - ! symba_tp class definitions and method interfaces - !******************************************************************************************************************************* - - !! SyMBA test particle class - type, public, extends(helio_pl) :: symba_tp - integer(I4B), dimension(:), allocatable :: nplenc !! Number of encounters with massive bodies this time step - integer(I4B), dimension(:), allocatable :: levelg !! Level at which this particle should be moved - integer(I4B), dimension(:), allocatable :: levelm !! Deepest encounter level achieved this time step - contains - procedure, public :: alloc => symba_allocate_tp - final :: symba_deallocate_tp - end type symba_tp - - !******************************************************************************************************************************** - ! symba_pl class definitions and method interfaces - !******************************************************************************************************************************* - - !! SyMBA massive body particle class - type, public, extends(symba_tp) :: symba_pl - real(DP) :: eoffset !! Energy offset (net energy lost in mergers) - logical, dimension(:), allocatable :: lmerged !! Flag indicating whether body has merged with another this time step - integer(I4B), dimension(:), allocatable :: ntpenc !! Number of encounters with test particles this time step - integer(I4B), dimension(:), allocatable :: nchild !! Number of children in merger list - integer(I4B), dimension(:), allocatable :: index_parent !! Position of the parent of id - integer(I4B), dimension(:,:), allocatable :: index_child !! Position of the children of id - contains - procedure, public :: alloc => symba_allocate_pl - final :: symba_deallocate_pl - end type symba_pl - - !******************************************************************************************************************************** - ! symba_encounter class definitions and method interfaces - !******************************************************************************************************************************* - - - !! Generic abstract class structure for a SyMBA encounter class - type, private, extends(swiftest_body) :: symba_encounter - logical , dimension(:), allocatable :: lvdotr !! Relative vdotr flag - integer(I4B), dimension(:), allocatable :: level !! Encounter recursion level - contains - procedure :: alloc => symba_allocate_encounter - procedure :: set_from_file => symba_encounter_dummy_input - final :: symba_deallocate_encounter - end type symba_encounter - - !******************************************************************************************************************************** - ! symba_plplenc class definitions and method interfaces - !******************************************************************************************************************************* - - !! Class structure for a massive body-massive body encounter - type, public, extends(symba_encounter) :: symba_plplenc - integer(I4B), dimension(:), allocatable :: index1 !! Position of the first massive body in encounter - integer(I4B), dimension(:), allocatable :: index2 !! Position of the second massive body in encounter - integer(I4B), dimension(:), allocatable :: enc_child !! The child of the encounter - integer(I4B), dimension(:), allocatable :: enc_parent !! The child of the encounter - contains - procedure :: alloc => symba_allocate_plplenc - final :: symba_deallocate_plplenc - end type symba_plplenc - - !******************************************************************************************************************************** - ! symba_pltpenc class definitions and method interfaces - !******************************************************************************************************************************* - - !! Class structure for a massive body-test particle encounter - type, public, extends(symba_encounter) :: symba_pltpenc - integer(I4B), dimension(:), allocatable :: indexpl !! Index position within the main symba structure for the first massive body in an encounter - integer(I4B), dimension(:), allocatable :: indextp !! Index position within the main symba structure for the second massive body in an encounter - contains - procedure :: alloc => symba_pltpenc_allocate - final :: symba_deallocate_pltpenc - end type symba_pltpenc - - !******************************************************************************************************************************** - ! symba_merger class definitions and method interfaces - !******************************************************************************************************************************** - - !! Class structure for merger structure - type, public, extends(swiftest_pl) :: symba_merger - integer(I4B), dimension(:), allocatable :: index_ps !! Index position within the main symba structure for the body being merged - integer(I4B), dimension(:), allocatable :: ncomp !! Number of component bodies in this one during this merger - contains - procedure :: alloc => symba_allocate_merger - final :: symba_deallocate_merger - end type symba_merger - -!> Only the constructor and destructor method implementations are listed here. All other methods are implemented in the symba submodules. -interface -!! Interfaces for all helio particle methods that are implemented in separate submodules - - module subroutine io_discard_write_symba(t, mtiny, npl, ntp, nsppl, nsptp, nmergeadd, nmergesub, symba_plA, & - discard_plA, discard_tpA, mergeadd_list, mergesub_list, fname, lbig_discard) - implicit none - logical , intent(in) :: lbig_discard - integer(I4B), intent(in) :: npl, ntp, nsppl, nsptp, nmergeadd, nmergesub - real(DP), intent(in) :: t, mtiny - character(*), intent(in) :: fname - type(symba_pl), intent(inout) :: symba_plA - type(swiftest_tp), intent(inout) :: discard_tpA - type(swiftest_pl), intent(inout) :: discard_plA - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - end subroutine io_discard_write_symba - - module subroutine symba_casedisruption (t, dt, index_enc, nmergeadd, nmergesub, mergeadd_list, mergesub_list, eoffset, vbs, & - symba_plA, nplplenc, plplenc_list, fragmax, mres, rres, m1, m2, rad1, rad2, x1, x2, v1, v2, param) - implicit none - integer(I4B), intent(in) :: index_enc - integer(I4B), intent(inout) :: nmergeadd, nmergesub, nplplenc, fragmax - real(DP), intent(in) :: t, dt - real(DP), intent(inout) :: eoffset, m1, m2, rad1, rad2 - real(DP), dimension(:), intent(inout) :: mres, rres - real(DP), dimension(:), intent(in) :: vbs - real(DP), dimension(:), intent(inout) :: x1, x2, v1, v2 - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - type(symba_pl), intent(inout) :: symba_plA - type(swiftest_parameters) :: param - - end subroutine symba_casedisruption - - module subroutine symba_casehitandrun (t, dt, index_enc, nmergeadd, nmergesub, mergeadd_list, mergesub_list, eoffset, vbs, & - symba_plA, nplplenc, plplenc_list, fragmax, mres, rres, m1, m2, rad1, rad2, x1, x2, v1, v2, param) - implicit none - integer(I4B), intent(in) :: index_enc - integer(I4B), intent(inout) :: nmergeadd, nmergesub, nplplenc, fragmax - real(DP), intent(in) :: t, dt - real(DP), intent(inout) :: eoffset, m1, m2, rad1, rad2 - real(DP), dimension(:), intent(inout) :: mres, rres - real(DP), dimension(:), intent(in) :: vbs - real(DP), dimension(:), intent(inout) :: x1, x2, v1, v2 - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - type(symba_pl), intent(inout) :: symba_plA - - end subroutine symba_casehitandrun - - module subroutine symba_casemerge (t, index_enc, nmergeadd, nmergesub, mergeadd_list, mergesub_list, eoffset, vbs, npl, & - symba_plA, nplplenc, plplenc_list, array_index1_child, array_index2_child, m1, m2, rad1, rad2, x1, x2, v1, v2) - implicit none - integer(I4B), intent(in) :: index_enc - integer(I4B), intent(inout) :: npl, nmergeadd, nmergesub, nplplenc - real(DP), intent(in) :: t - real(DP), intent(inout) :: eoffset, m1, m2, rad1, rad2 - real(DP), dimension(:), intent(in) :: vbs - real(DP), dimension(:), intent(inout) :: x1, x2, v1, v2 - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - type(symba_pl), intent(inout) :: symba_plA - integer(I4B), dimension(npl), intent(inout) :: array_index1_child, array_index2_child - end subroutine symba_casemerge - - module subroutine symba_caseresolve (t, dt, index_enc, nmergeadd, nmergesub, mergeadd_list, mergesub_list, & - eoffset, vbs, & - npl, symba_plA, nplplenc, plplenc_list, regime, param%nplmax, param%ntpmax, fragmax, mres, rres, array_index1_child, & - array_index2_child, m1, m2, rad1, rad2, x1, x2, v1, v2) - implicit none - integer(I4B), intent(in) :: index_enc, param%nplmax, param%ntpmax - integer(I4B), intent(inout) :: npl, nmergeadd, nmergesub, nplplenc, fragmax - real(DP), intent(in) :: t, dt - real(DP), intent(inout) :: eoffset, m1, m2, rad1, rad2 - real(DP), dimension(:), intent(inout) :: mres, rres - real(DP), dimension(:), intent(in) :: vbs - real(DP), dimension(:), intent(inout) :: x1, x2, v1, v2 - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - type(symba_pl), intent(inout) :: symba_plA - integer(I4B), intent(in) :: regime - integer(I4B), dimension(npl), intent(inout) :: array_index1_child, array_index2_child - end subroutine symba_caseresolve - - module subroutine symba_casesupercatastrophic (t, dt, index_enc, nmergeadd, nmergesub, mergeadd_list, mergesub_list, & - eoffset, vbs, & - symba_plA, nplplenc, plplenc_list, param%nplmax, param%ntpmax, fragmax, mres, rres, m1, m2, rad1, rad2, x1, x2, v1, v2) - implicit none - integer(I4B), intent(in) :: index_enc, param%nplmax, param%ntpmax - integer(I4B), intent(inout) :: nmergeadd, nmergesub, nplplenc, fragmax - real(DP), intent(in) :: t, dt - real(DP), intent(inout) :: eoffset, m1, m2, rad1, rad2 - real(DP), dimension(:), intent(inout) :: mres, rres - real(DP), dimension(:), intent(in) :: vbs - real(DP), dimension(:), intent(inout) :: x1, x2, v1, v2 - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - TYPE(symba_pl), INTENT(INOUT) :: symba_plA - end subroutine symba_casesupercatastrophic - - module subroutine symba_chk(xr, vr, rhill1, rhill2, dt, irec, lencounter, lvdotr) - implicit none - logical , intent(out) :: lencounter, lvdotr - integer(I4B), intent(in) :: irec - real(DP), intent(in) :: rhill1, rhill2, dt - real(DP), dimension(:), intent(in) :: xr, vr - end subroutine symba_chk - - module subroutine symba_chk_eucl(num_encounters, k_plpl, symba_plA, dt, lencounter, lvdotr, nplplenc) - implicit none - type(symba_pl), intent(in) :: symba_plA - integer(I4B), dimension(num_encounters), intent(out) :: lencounter, lvdotr - integer(I4B), intent(in) :: num_encounters - integer(I4B), dimension(2,num_encounters),intent(in) :: k_plpl - real(DP), intent(in) :: dt - integer(I4B), intent(inout) :: nplplenc - end subroutine symba_chk_eucl - - module subroutine symba_chk_eucl_pltp(num_encounters, k_pltp, symba_plA, symba_tpA, dt, lencounter, lvdotr, npltpenc) - implicit none - type(symba_pl), intent(in) :: symba_plA - type(symba_tp), intent(in) :: symba_tpA - integer(I4B), dimension(num_encounters), intent(out) :: lencounter, lvdotr - integer(I4B), intent(in) :: num_encounters - integer(I4B), dimension(2,num_encounters),intent(in) :: k_pltp - real(DP), intent(in) :: dt - integer(I4B), intent(inout) :: npltpenc - end subroutine symba_chk_eucl_pltp - - module subroutine symba_discard_merge_pl(t, npl, symba_plA, nplplenc, plplenc_list) - implicit none - integer(I4B), intent(in) :: nplplenc - integer(I4B), intent(inout) :: npl - real(DP), intent(in) :: t - type(symba_pl) :: symba_plA - type(symba_plplenc), intent(in) :: plplenc_list - end subroutine symba_discard_merge_pl - - module subroutine symba_discard_peri_pl(t, npl, symba_plA, msys, qmin, qmin_alo, qmin_ahi, qmin_coord, ldiscards) - implicit none - logical , intent(inout) :: ldiscards - integer(I4B), intent(in) :: npl - real(DP), intent(in) :: t, msys, qmin, qmin_alo, qmin_ahi - character(*), intent(in) :: qmin_coord - type(symba_pl), intent(inout) :: symba_plA - end subroutine symba_discard_peri_pl - - module subroutine symba_discard_pl(t, npl, param%nplmax, nsp, symba_plA, rmin, rmax, param%rmaxu, qmin, qmin_coord, & - qmin_alo, qmin_ahi, param%j2rp2, param%j4rp4, eoffset) - implicit none - integer(I4B), intent(in) :: param%nplmax - integer(I4B), intent(inout) :: npl, nsp - real(DP), intent(in) :: t, rmin, rmax, param%rmaxu, qmin, qmin_alo, qmin_ahi, param%j2rp2, param%j4rp4 - real(DP), intent(inout) :: eoffset - character(*), intent(in) :: qmin_coord - type(symba_pl), intent(inout) :: symba_plA - end subroutine symba_discard_pl - - module subroutine symba_discard_sun_pl(t, npl, msys, swiftest_plA, rmin, rmax, param%rmaxu, ldiscards) - implicit none - logical , intent(inout) :: ldiscards - integer(I4B), intent(in) :: npl - real(DP), intent(in) :: t, msys, rmin, rmax, param%rmaxu - type(swiftest_pl), intent(inout) :: swiftest_plA - end subroutine symba_discard_sun_pl - - module subroutine symba_discard_tp(t, npl, ntp, nsp, symba_plA, symba_tpA, dt, & - rmin, rmax, param%rmaxu, qmin, qmin_coord, qmin_alo, qmin_ahi, lclose, lrhill_present) - implicit none - logical , intent(in) :: lclose, lrhill_present - integer(I4B), intent(in) :: npl - integer(I4B), intent(inout) :: ntp, nsp - real(DP), intent(in) :: t, dt, rmin, rmax, param%rmaxu, qmin, qmin_alo, qmin_ahi - character(*), intent(in) :: qmin_coord - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - end subroutine symba_discard_tp - - module subroutine symba_energy(npl, param%nplmax, swiftest_plA, param%j2rp2, param%j4rp4, ke, pe, te, htot) - implicit none - integer(I4B), intent(in) :: npl, param%nplmax - real(DP), intent(in) :: param%j2rp2, param%j4rp4 - real(DP), intent(out) :: ke, pe, te - real(DP), dimension(NDIM), intent(out) :: htot - type(swiftest_pl), intent(inout) :: swiftest_plA - end subroutine symba_energy - - module subroutine symba_fragmentation(t, dt, index_enc, nmergeadd, nmergesub, mergeadd_list, & - mergesub_list, eoffset, vbs, encounter_file, out_type, npl, ntp, & - symba_plA, symba_tpA, nplplenc, npltpenc, pltpenc_list, plplenc_list, & - param%nplmax, param%ntpmax, fragmax) - implicit none - integer(I4B), intent(in) :: index_enc, param%nplmax, param%ntpmax - integer(I4B), intent(inout) :: nmergeadd, nmergesub, nplplenc, npltpenc, fragmax - integer(I4B), intent(inout) :: npl, ntp - real(DP), intent(in) :: t, dt - real(DP), intent(inout) :: eoffset - real(DP), dimension(NDIM), intent(in) :: vbs - character(*), intent(in) :: encounter_file, out_type - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_pltpenc), intent(inout) :: pltpenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - end subroutine symba_fragmentation - - module subroutine symba_getacch(lextra_force, t, npl, nplm, param%nplmax, symba_plA, param%j2rp2, param%j4rp4, nplplenc, & - plplenc_list) - implicit none - logical , intent(in) :: lextra_force - integer(I4B), intent(in) :: npl, nplm, param%nplmax, nplplenc - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4 - type(symba_pl), intent(inout) :: symba_plA - type(symba_plplenc), intent(in) :: plplenc_list - end subroutine symba_getacch - - module subroutine symba_getacch_tp(lextra_force, t, npl, nplm, param%nplmax, ntp, param%ntpmax, symba_plA, symba_tpA, & - xh, param%j2rp2, param%j4rp4, & - npltpenc, pltpenc_list) - implicit none - logical , intent(in) :: lextra_force - integer(I4B), intent(in) :: npl, nplm, param%nplmax, ntp, param%ntpmax, npltpenc - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4 - real(DP), dimension(npl, NDIM), intent(in) :: xh - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - type(symba_pltpenc), intent(in) :: pltpenc_list - end subroutine symba_getacch_tp - - module subroutine symba_getacch_eucl(lextra_force, t, npl, nplm, param%nplmax, symba_plA, param%j2rp2, param%j4rp4, nplplenc, & - plplenc_list, num_plpl_comparisons, k_plpl) - implicit none - logical , intent(in) :: lextra_force - integer(I4B), intent(in) :: npl, nplm, param%nplmax, nplplenc, num_plpl_comparisons - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4 - type(symba_pl), intent(inout) :: symba_plA - type(symba_plplenc), intent(in) :: plplenc_list - integer(I4B), dimension(num_plpl_comparisons,2),intent(in) :: k_plpl - end subroutine symba_getacch_eucl - - module subroutine symba_getacch_tp_eucl(lextra_force, t, npl, nplm, param%nplmax, ntp, param%ntpmax, symba_plA, symba_tpA, & - xh, param%j2rp2, param%j4rp4, npltpenc, pltpenc_list, num_pltp_comparisons, k_pltp) - implicit none - logical , intent(in) :: lextra_force - integer(I4B), intent(in) :: npl, nplm, param%nplmax, ntp, param%ntpmax, npltpenc, num_pltp_comparisons - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4 - real(DP), dimension(npl, NDIM), intent(in) :: xh - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - type(symba_pltpenc), intent(in) :: pltpenc_list - integer(I4B), dimension(num_pltp_comparisons,2), intent(in) :: k_pltp - end subroutine symba_getacch_tp_eucl - - module subroutine symba_helio_drift(irec, npl, symba_plA, dt) - implicit none - integer(I4B), intent(in) :: irec, npl - real(DP), intent(in) :: dt - type(symba_pl), intent(inout) :: symba_plA - end subroutine symba_helio_drift - - module subroutine symba_helio_drift_tp(irec, ntp, symba_tpA, mu, dt) - implicit none - integer(I4B), intent(in) :: irec, ntp - real(DP), intent(in) :: mu, dt - type(symba_tp), intent(inout) :: symba_tpA - end subroutine symba_helio_drift_tp - - module subroutine symba_helio_getacch(lflag, lextra_force, t, npl, nplm, param%nplmax, helio_plA, param%j2rp2, param%j4rp4) - implicit none - logical , intent(in) :: lflag, lextra_force - integer(I4B), intent(in) :: npl, nplm, param%nplmax - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4 - type(helio_pl), intent(inout) :: helio_plA - end subroutine symba_helio_getacch - - module subroutine symba_helio_getacch_int(npl, nplm, helio_plA) - implicit none - integer(I4B), intent(in) :: npl, nplm - type(helio_pl), intent(inout) :: helio_plA - end subroutine symba_helio_getacch_int - - module subroutine symba_kick(irec, nplplenc, npltpenc, plplenc_list, pltpenc_list, dt, sgn, symba_plA, & - symba_tpA) - implicit none - integer(I4B), intent(in) :: irec, nplplenc, npltpenc - real(DP), intent(in) :: dt, sgn - type(symba_plplenc), intent(in) :: plplenc_list - type(symba_pltpenc), intent(in) :: pltpenc_list - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - end subroutine symba_kick - - module subroutine symba_merge_pl(t, dt, index_enc, nplplenc, plplenc_list, nmergeadd, nmergesub, & - mergeadd_list, mergesub_list, eoffset, vbs, encounter_file, out_type, npl, symba_plA, & - symba_tpA) - implicit none - integer(I4B), intent(in) :: index_enc, nplplenc - integer(I4B), intent(inout) :: nmergeadd, nmergesub, npl - real(DP), intent(in) :: t, dt - real(DP), intent(inout) :: eoffset - real(DP), dimension(NDIM), intent(in) :: vbs - character(*), intent(in) :: encounter_file, out_type - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - end subroutine symba_merge_pl - - module subroutine symba_merge_tp(t, dt, index_enc, npltpenc, pltpenc_list, vbs, encounter_file, out_type, symba_plA, symba_tpA) - implicit none - integer(I4B), intent(in) :: index_enc, npltpenc - real(DP), intent(in) :: t, dt - real(DP), dimension(NDIM), intent(in) :: vbs - character(*), intent(in) :: encounter_file, out_type - type(symba_pltpenc), intent(inout) :: pltpenc_list - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - end subroutine symba_merge_tp - - module subroutine symba_peri(lfirst, npl, symba_plA, msys, qmin_coord) - implicit none - logical , intent(in) :: lfirst - integer(I4B), intent(in) :: npl - real(DP), intent(in) :: msys - character(*), intent(in) :: qmin_coord - type(symba_pl), intent(inout) :: symba_plA - end subroutine symba_peri - - module subroutine symba_rearray(t, npl, ntp, nsppl, nsptp, symba_plA, symba_tpA, nmergeadd, & - mergeadd_list, discard_plA, discard_tpA, param%nplmax, param%j2rp2, param%j4rp4) - implicit none - integer(I4B), intent(inout) :: npl, ntp, nsppl, nsptp, nmergeadd, param%nplmax !change to fragadd - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4 - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - type(swiftest_tp), intent(inout) :: discard_tpA - type(swiftest_pl), intent(inout) :: discard_plA - type(symba_merger), intent(inout) :: mergeadd_list !change to fragadd_list - - end subroutine symba_rearray - - module subroutine symba_reorder_pl(npl, symba_plA) - implicit none - integer(I4B), intent(in) :: npl - type(symba_pl), intent(inout) :: symba_plA - integer(I4B) :: i - integer(I4B), dimension(:), allocatable :: index - real(DP), dimension(:), allocatable :: mass - real(DP), dimension(:,:), allocatable :: symba_plwkspa - integer(I4B), dimension(:,:), allocatable :: symba_plwkspa_id_status - end subroutine symba_reorder_pl - - !> Initializes the SyMBA aprticles - module subroutine symba_set_initial_conditions(symba_plA, symba_tpA, param) - implicit none - type(symba_pl), intent(inout) :: symba_plA !! SyMBA massive body structure - type(symba_tp), intent(inout) :: symba_tpA !! SyMBA test particle structure - type(swiftest_parameters) :: param !! Current run configuration parameters of on parameters - end subroutine symba_set_initial_conditions - - !> Method to remove the inactive symba test particles and spill them to a discard object - module subroutine symba_spill_tp(self,discard) - implicit none - class(symba_tp), intent(inout) :: self !! Swiftest test particle object to input - class(symba_tp), intent(inout) :: discard !! Discarded body list - end subroutine symba_spill_tp - - !> Method to remove the inactive symba massive bodies and spill them to a discard object - module subroutine symba_spill_pl(self,discard) - implicit none - class(symba_pl), intent(inout) :: self !! Swiftest test particle object to input - class(symba_pl), intent(inout) :: discard !! Discarded body list - end subroutine symba_spill_pl - - module subroutine symba_step_eucl(lfirst, lextra_force, lclose, t, npl, param%nplmax, ntp, param%ntpmax, symba_plA, symba_tpA, param%j2rp2, param%j4rp4,& - dt,nplplenc, npltpenc, plplenc_list, pltpenc_list, nmergeadd, nmergesub, mergeadd_list, mergesub_list, eoffset,& - mtiny,encounter_file, out_type, num_plpl_comparisons, k_plpl, num_pltp_comparisons, k_pltp) - implicit none - logical , intent(in) :: lextra_force, lclose - logical , intent(inout) :: lfirst - integer(I4B), intent(in) :: npl, param%nplmax, ntp, param%ntpmax - integer(I4B), intent(inout) :: nplplenc, npltpenc, nmergeadd, nmergesub - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4, dt, mtiny - real(DP), intent(inout) :: eoffset - character(*), intent(in) :: encounter_file, out_type - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_pltpenc), intent(inout) :: pltpenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - integer(I4B), intent(in) :: num_plpl_comparisons, num_pltp_comparisons - integer(I4B), dimension(2,num_plpl_comparisons),intent(in) :: k_plpl - integer(I4B), dimension(2,num_pltp_comparisons),intent(in) :: k_pltp - end subroutine symba_step_eucl - - module subroutine symba_step(lfirst, lextra_force, lclose, t, npl, param%nplmax, ntp, param%ntpmax, symba_plA, & - symba_tpA, param%j2rp2, param%j4rp4, dt, nplplenc, npltpenc, plplenc_list, pltpenc_list, nmergeadd, & - nmergesub, mergeadd_list, mergesub_list, eoffset, mtiny, encounter_file, out_type, & - fragmax) - implicit none - logical , intent(in) :: lextra_force, lclose - logical , intent(inout) :: lfirst - integer(I4B), intent(in) :: npl, param%nplmax, ntp, param%ntpmax - integer(I4B), intent(inout) :: nplplenc, npltpenc, nmergeadd, nmergesub, fragmax - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4, dt, mtiny - real(DP), intent(inout) :: eoffset - character(*), intent(in) :: encounter_file, out_type - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_pltpenc), intent(inout) :: pltpenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - end subroutine symba_step - - ! for testing purposes only _ use with symba_step_test - module subroutine symba_step_helio(lfirst, lextra_force, t, npl, nplm, param%nplmax, ntp, param%ntpmax, helio_plA, helio_tpA, param%j2rp2, & - param%j4rp4, dt) - implicit none - logical , intent(in) :: lextra_force - logical , intent(inout) :: lfirst - integer(I4B), intent(in) :: npl, nplm, param%nplmax, ntp, param%ntpmax - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4, dt - type(helio_pl), intent(inout) :: helio_plA - type(helio_tp), intent(inout) :: helio_tpA - end subroutine symba_step_helio - - module subroutine symba_step_helio_pl(lfirst, lextra_force, t, npl, nplm, param%nplmax, helio_plA, param%j2rp2, param%j4rp4, dt, xbeg, xend, & - ptbeg, ptend) - implicit none - logical , intent(in) :: lextra_force - logical , intent(inout) :: lfirst - integer(I4B), intent(in) :: npl, nplm, param%nplmax - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4, dt - real(DP), dimension(npl, NDIMm), intent(out) :: xbeg, xend - real(DP), dimension(NDIM), intent(out) :: ptbeg, ptend - type(helio_pl), intent(inout) :: helio_plA - end subroutine symba_step_helio_pl - - module subroutine symba_step_interp_eucl(lextra_force, lclose, t, npl, nplm, param%nplmax, ntp, param%ntpmax, symba_plA, symba_tpA, param%j2rp2,& - param%j4rp4, dt, eoffset, mtiny, nplplenc, npltpenc, plplenc_list, pltpenc_list, nmergeadd, nmergesub, mergeadd_list,& - mergesub_list, encounter_file, out_type, num_plpl_comparisons, k_plpl, num_pltp_comparisons, k_pltp) - implicit none - logical , intent(in) :: lextra_force, lclose - integer(I4B), intent(in) :: npl, nplm, param%nplmax, ntp, param%ntpmax, nplplenc, npltpenc, num_pltp_comparisons - integer(I4B), intent(inout) :: nmergeadd, nmergesub - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4, dt, mtiny - real(DP), intent(inout) :: eoffset - character(*), intent(in) :: encounter_file, out_type - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_pltpenc), intent(inout) :: pltpenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - integer(I4B), intent(in) :: num_plpl_comparisons - integer(I4B), dimension(num_plpl_comparisons,2),intent(in) :: k_plpl - integer(I4B), dimension(2,num_pltp_comparisons),intent(in) :: k_pltp - end subroutine symba_step_interp_eucl - - module subroutine symba_step_interp(lextra_force, lclose, t, npl, nplm, param%nplmax, ntp, param%ntpmax, symba_plA, symba_tpA, param%j2rp2, & - param%j4rp4, dt, eoffset, mtiny, nplplenc, npltpenc, plplenc_list, pltpenc_list, nmergeadd, nmergesub, mergeadd_list, & - mergesub_list, encounter_file, out_type, fragmax) - implicit none - logical , intent(in) :: lextra_force, lclose - integer(I4B), intent(in) :: npl, nplm, param%nplmax, ntp, param%ntpmax, nplplenc, npltpenc - integer(I4B), intent(inout) :: nmergeadd, nmergesub, fragmax - real(DP), intent(in) :: t, param%j2rp2, param%j4rp4, dt, mtiny - real(DP), intent(inout) :: eoffset - character(*), intent(in) :: encounter_file, out_type - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_pltpenc), intent(inout) :: pltpenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - end subroutine symba_step_interp - - recursive module subroutine symba_step_recur(lclose, t, ireci, npl, nplm, ntp, symba_plA, symba_tpA, dt0, eoffset, nplplenc, & - npltpenc, plplenc_list, pltpenc_list, nmergeadd, nmergesub, mergeadd_list, mergesub_list, encounter_file, & - out_type, param%nplmax, param%ntpmax, fragmax) - implicit none - logical , intent(in) :: lclose - integer(I4B), intent(in) :: ireci, npl, nplm, ntp, nplplenc, npltpenc, param%nplmax, param%ntpmax, fragmax - integer(I4B), intent(inout) :: nmergeadd, nmergesub - real(DP), intent(in) :: t, dt0 - real(DP), intent(inout) :: eoffset - character(*), intent(in) :: encounter_file, out_type - type(symba_pl), intent(inout) :: symba_plA - type(symba_tp), intent(inout) :: symba_tpA - type(symba_plplenc), intent(inout) :: plplenc_list - type(symba_pltpenc), intent(inout) :: pltpenc_list - type(symba_merger), intent(inout) :: mergeadd_list, mergesub_list - end subroutine symba_step_recur - - module subroutine symba_user_getacch(t, npl, symba_plA) - implicit none - integer(I4B), intent(in) :: npl - real(DP), intent(in) :: t - type(symba_pl), intent(inout):: symba_plA - end subroutine symba_user_getacch - - module subroutine symba_user_getacch_tp(t, ntp, symba_tpA) - implicit none - integer(I4B), intent(in) :: ntp - real(DP), intent(in) :: t - type(symba_tp), intent(inout) :: symba_tpA - end subroutine symba_user_getacch_tp - -end interface - -contains - !! SyMBA constructor and desctructor methods - subroutine symba_allocate_tp(self,n) - !! SyMBA test particle constructor method - implicit none - - class(symba_tp), intent(inout) :: self !! Symba test particle object - integer, intent(in) :: n !! Number of test particles to allocate - - call self%helio_tp%alloc(n) - - if (self%is_allocated) then - write(*,*) 'Symba test particle structure already alllocated' - return - end if - write(*,*) 'Allocating the Swiftest test particle structure' - - if (n <= 0) return - allocate(self%nplenc(n)) - allocate(self%levelg(n)) - allocate(self%levelm(n)) - - self%nplenc(:) = 0 - self%levelg(:) = 0 - self%levelm(:) = 0 - return - end subroutine symba_allocate_tp - - subroutine symba_deallocate_tp(self) - !! SyMBA test particle destructor/finalizer - implicit none - - type(symba_tp), intent(inout) :: self - - if (self%is_allocated) then - deallocate(self%nplenc) - deallocate(self%levelg) - deallocate(self%levelm) - end if - return - end subroutine symba_deallocate_tp - - subroutine symba_allocate_pl(self,n) - !! SyMBA massive body constructor method - implicit none - - class(symba_pl), intent(inout) :: self !! SyMBA massive body particle object - integer, intent(in) :: n !! Number of massive body particles to allocate - - call self%helio_pl%alloc(n) - - if (self%is_allocated) then - write(*,*) 'Symba massive body structure already alllocated' - return - end if - if (n <= 0) return - allocate(self%lmerged(n)) - allocate(self%nplenc(n)) - allocate(self%ntpenc(n)) - allocate(self%levelg(n)) - allocate(self%levelm(n)) - allocate(self%nchild(n)) - allocate(self%index_parent(n)) - allocate(self%index_child(n,n)) - - self%lmerged(:) = .false. - self%nplenc(:) = 0 - self%ntpenc(:) = 0 - self%levelg(:) = 0 - self%levelm(:) = 0 - self%nchild(:) = 0 - self%index_parent(:) = 1 - self%index_child(:,:) = 1 - - return - end subroutine symba_allocate_pl - - subroutine symba_deallocate_pl(self) - !! SyMBA massive body destructor/finalizer - implicit none - - type(symba_pl), intent(inout) :: self - if (self%is_allocated) then - deallocate(self%lmerged) - deallocate(self%nplenc) - deallocate(self%ntpenc) - deallocate(self%levelg) - deallocate(self%levelm) - deallocate(self%nchild) - deallocate(self%index_parent) - deallocate(self%index_child) - end if - return - end subroutine symba_deallocate_pl - - subroutine symba_allocate_encounter(self,n) - !! Basic Symba encounter structure constructor method - implicit none - - class(symba_encounter), intent(inout) :: self !! SyMBA encounter super class - integer, intent(in) :: n !! Number of test particles to allocate - - call self%swiftest_body%alloc(n) - if (n <= 0) return - - if (self%is_allocated) then - write(*,*) 'SyMBA encounter structure already alllocated' - return - end if - write(*,*) 'Allocating the Symba encounter superclass' - - allocate(self%lvdotr(n)) - allocate(self%level(n)) - - self%lvdotr(:) = .false. - self%level(:) = 0 - - return - end subroutine symba_allocate_encounter - - subroutine symba_deallocate_encounter(self) - !! SyMBA encounter superclass destructor/finalizer - implicit none - - type(symba_encounter), intent(inout) :: self - - if (self%is_allocated) then - deallocate(self%lvdotr) - deallocate(self%level) - end if - return - end subroutine symba_deallocate_encounter - - subroutine symba_encounter_dummy_input(self,param) - !! This method is needed in order to extend the abstract type swiftest_body. It does nothing - implicit none - class(symba_encounter), intent(inout) :: self !! SyMBA encounter data structure - type(swiftest_parameters),intent(in) :: param !! Current run configuration parameters of on parameters - return - end subroutine symba_encounter_dummy_input - - subroutine symba_pltpenc_allocate(self,n) - !! SyMBA massive body-test particle encounter structure constructor method - implicit none - - class(symba_pltpenc), intent(inout) :: self !! SyMBA massive body-test particle encounter class - integer, intent(in) :: n !! Number of encounter slots to allocate - - call self%symba_encounter%alloc(n) - if (n <= 0) return - - if (self%is_allocated) then - write(*,*) 'SyMBA pl-tp encounter structure already alllocated' - return - end if - write(*,*) 'Allocating the Symba pl-tp encounter class' - - allocate(self%indexpl(n)) - allocate(self%indextp(n)) - - self%indexpl(:) = 1 - self%indextp(:) = 1 - end subroutine symba_pltpenc_allocate - - subroutine symba_deallocate_pltpenc(self) - !! SyMBA massive body-test particle encounter destructor/finalizer - implicit none - - type(symba_pltpenc), intent(inout) :: self - - if (self%is_allocated) then - deallocate(self%indexpl) - deallocate(self%indextp) - end if - return - end subroutine symba_deallocate_pltpenc - - subroutine symba_allocate_plplenc(self,n) - !! SyMBA massive body-massive body particle encounter structure constructor method - implicit none - - class(symba_plplenc), intent(inout) :: self !! SyMBA massive body-massive body encounter class - integer, intent(in) :: n !! Number of encounter slots to allocate - - call self%symba_encounter%alloc(n) - if (n <= 0) return - - if (self%is_allocated) then - write(*,*) 'SyMBA pl-pl encounter structure already alllocated' - return - end if - write(*,*) 'Allocating the Symba pl-pl encounter class' - - allocate(self%index1(n)) - allocate(self%index2(n)) - allocate(self%enc_child(n)) - allocate(self%enc_parent(n)) - - self%index1(:) = 1 - self%index2(:) = 1 - self%enc_child(:) = 1 - self%enc_parent(:) = 1 - - return - end subroutine symba_allocate_plplenc - - subroutine symba_deallocate_plplenc(self) - !! SyMBA massive body-massive body encounter destructor/finalizer - implicit none - - type(symba_plplenc), intent(inout) :: self - - if (self%is_allocated) then - deallocate(self%index1) - deallocate(self%index2) - deallocate(self%enc_child) - deallocate(self%enc_parent) - end if - return - end subroutine symba_deallocate_plplenc - - subroutine symba_allocate_merger(self,n) - !! SyMBA merger encounter structure constructor method - implicit none - - class(symba_merger), intent(inout) :: self !! SyMBA merger class - integer, intent(in) :: n !! Number of encounter slots to allocate - - call self%swiftest_pl%alloc(n) - if (n <= 0) return - - if (self%is_allocated) then - write(*,*) 'SyMBA merger structure already alllocated' - return - end if - write(*,*) 'Allocating the SyMBA merger class' - - allocate(self%index_ps(n)) - allocate(self%ncomp(n)) - - self%index_ps(:) = 1 - self%ncomp(:) = 0 - - end subroutine symba_allocate_merger - - subroutine symba_deallocate_merger(self) - !! SyMBA merger destructor/finalizer - implicit none - - type(symba_merger), intent(inout) :: self - - if (self%is_allocated) then - deallocate(self%index_ps) - deallocate(self%ncomp) - end if - return - end subroutine symba_deallocate_merger - -end module symba diff --git a/src/modules/symba_classes.f90 b/src/modules/symba_classes.f90 index 5b712c9de..0e66ebf7c 100644 --- a/src/modules/symba_classes.f90 +++ b/src/modules/symba_classes.f90 @@ -4,39 +4,39 @@ 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 + public - !integer(I4B), parameter :: NENMAX = 32767 - !integer(I4B), parameter :: NTENC = 3 - !real(DP), parameter :: RHSCALE = 6.5_DP - !real(DP), parameter :: RSHELL = 0.48075_DP - character(*), parameter :: PARTICLE_OUTFILE = 'particle.dat' - integer(I4B), parameter :: PARTICLEUNIT = 44 !! File unit number for the binary particle info output file - - type, public, extends(swiftest_parameters) :: symba_parameters - character(STRMAX) :: particle_file = PARTICLE_OUTFILE !! Name of output particle information file - real(DP) :: MTINY = -1.0_DP !! Smallest mass that is fully gravitating - integer(I4B), dimension(:), allocatable :: seed !! Random seeds - logical :: lfragmentation = .false. !! Do fragmentation modeling instead of simple merger. + integer(I4B), private, parameter :: NENMAX = 32767 + integer(I4B), private, parameter :: NTENC = 3 + real(DP), private, parameter :: RHSCALE = 6.5_DP + real(DP), private, parameter :: RSHELL = 0.48075_DP + character(*), parameter :: PARTICLE_OUTFILE = 'particle.dat' + integer(I4B), parameter :: PARTICLEUNIT = 44 !! File unit number for the binary particle info output file + + type, extends(swiftest_parameters) :: symba_parameters + character(STRMAX) :: particle_file = PARTICLE_OUTFILE !! Name of output particle information file + real(DP) :: MTINY = -1.0_DP !! Smallest mass that is fully gravitating + integer(I4B), dimension(:), allocatable :: seed !! Random seeds + logical :: lfragmentation = .false. !! Do fragmentation modeling instead of simple merger. contains - private - procedure, public :: reader => symba_io_param_reader - procedure, public :: writer => symba_io_param_writer + procedure :: reader => symba_io_param_reader + procedure :: writer => symba_io_param_writer end type symba_parameters !******************************************************************************************************************************** ! symba_cb class definitions and method interfaces !******************************************************************************************************************************* !> SyMBA central body particle class - type, public, extends(helio_cb) :: symba_cb - real(DP) :: M0 = 0.0_DP !! Initial mass of the central body - real(DP) :: dM = 0.0_DP !! Change in mass of the central body - real(DP) :: R0 = 0.0_DP !! Initial radius of the central body - real(DP) :: dR = 0.0_DP !! Change in the radius of the central body + type, extends(helio_cb) :: symba_cb + real(DP) :: M0 = 0.0_DP !! Initial mass of the central body + real(DP) :: dM = 0.0_DP !! Change in mass of the central body + real(DP) :: R0 = 0.0_DP !! Initial radius of the central body + real(DP) :: dR = 0.0_DP !! Change in the radius of the central body contains - private end type symba_cb !******************************************************************************************************************************** @@ -44,17 +44,16 @@ module symba_classes !******************************************************************************************************************************* !> Class definition for the particle origin information object. This object is used to track time, location, and collisional regime !> of fragments produced in collisional events. - type, public, extends(swiftest_base) :: symba_particle_info + type, extends(swiftest_base) :: symba_particle_info character(len=32) :: origin_type !! String containing a description of the origin of the particle (e.g. Initial Conditions, Supercatastrophic, Disruption, etc.) real(DP) :: origin_time !! The time of the particle's formation real(DP), dimension(NDIM) :: origin_xh !! The heliocentric distance vector at the time of the particle's formation real(DP), dimension(NDIM) :: origin_vh !! The heliocentric velocity vector at the time of the particle's formation contains - private - procedure, public :: dump => symba_io_dump_particle_info !! I/O routine for dumping particle info to file - procedure, public :: initialize => symba_io_initialize_particle_info !! I/O routine for reading in particle info data - procedure, public :: read_frame => symba_io_read_frame_info !! I/O routine for reading in a single frame of particle info - procedure, public :: write_frame => symba_io_write_frame_info !! I/O routine for writing out a single frame of particle info + procedure :: dump => symba_io_dump_particle_info !! I/O routine for dumping particle info to file + procedure :: initialize => symba_io_initialize_particle_info !! I/O routine for reading in particle info data + procedure :: read_frame => symba_io_read_frame_info !! I/O routine for reading in a single frame of particle info + procedure :: write_frame => symba_io_write_frame_info !! I/O routine for writing out a single frame of particle info end type symba_particle_info !******************************************************************************************************************************** @@ -62,133 +61,284 @@ module symba_classes !******************************************************************************************************************************* !> Class definition for the kinship relationships used in bookkeeping multiple collisions bodies in a single time step. type symba_kinship - integer(I4B) :: parent ! Index of parent particle - integer(I4B) :: nchild ! number of children in merger list - integer(I4B), dimension(:), allocatable :: child ! Index of children particles + integer(I4B) :: parent !! Index of parent particle + integer(I4B) :: nchild !! number of children in merger list + integer(I4B), dimension(:), allocatable :: child !! Index of children particles end type symba_kinship !******************************************************************************************************************************** ! symba_pl class definitions and method interfaces !******************************************************************************************************************************* !> SyMBA massive body class - type, public, extends(helio_pl) :: symba_pl - logical, dimension(:), allocatable :: lcollision !! flag indicating whether body has merged with another this time step - logical, dimension(:), allocatable :: lencounter !! flag indicating whether body is part of an encounter this time step - integer(I4B), dimension(:), allocatable :: nplenc !! number of encounters with other planets this time step - integer(I4B), dimension(:), allocatable :: ntpenc !! number of encounters with test particles this time step - integer(I4B), dimension(:), allocatable :: levelg !! level at which this body should be moved - integer(I4B), dimension(:), allocatable :: levelm !! deepest encounter level achieved this time step - integer(I4B), dimension(:), allocatable :: isperi !! perihelion passage flag - real(DP), dimension(:), allocatable :: peri !! perihelion distance - real(DP), dimension(:), allocatable :: atp !! semimajor axis following perihelion passage - type(symba_kinship), dimension(:), allocatable :: kin !! Array of merger relationship structures that can account for multiple pairwise mergers in a single step - type(symba_particle_info), dimension(:), allocatable :: info + type, extends(helio_pl) :: symba_pl + logical, dimension(:), allocatable :: lcollision !! flag indicating whether body has merged with another this time step + logical, dimension(:), allocatable :: lencounter !! flag indicating whether body is part of an encounter this time step + logical, dimension(:), allocatable :: lmtiny !! flag indicating whether this body is below the MTINY cutoff value + integer(I4B) :: nplm !! number of bodies above the MTINY limit + integer(I8B) :: nplplm !! Number of body (all massive)-body (only those above MTINY) comparisons in the flattened upper triangular matrix + integer(I4B), dimension(:), allocatable :: nplenc !! number of encounters with other planets this time step + integer(I4B), dimension(:), allocatable :: ntpenc !! number of encounters with test particles this time step + integer(I4B), dimension(:), allocatable :: levelg !! level at which this body should be moved + integer(I4B), dimension(:), allocatable :: levelm !! deepest encounter level achieved this time step + integer(I4B), dimension(:), allocatable :: isperi !! perihelion passage flag + real(DP), dimension(:), allocatable :: peri !! perihelion distance + real(DP), dimension(:), allocatable :: atp !! semimajor axis following perihelion passage + type(symba_kinship), dimension(:), allocatable :: kin !! Array of merger relationship structures that can account for multiple pairwise mergers in a single step + type(symba_particle_info), dimension(:), allocatable :: info contains - private - procedure, public :: discard => symba_discard_pl !! Process massive body discards - procedure, public :: encounter_check => symba_encounter_check_pl !! Checks if massive bodies are going through close encounters with each other - procedure, public :: setup => symba_setup_pl !! Constructor method - Allocates space for number of particle + procedure :: make_family => symba_collision_make_family_pl !! When a single body is involved in more than one collision in a single step, it becomes part of a family + procedure :: discard => symba_discard_pl !! Process massive body discards + 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 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 !******************************************************************************************************************************* !> SyMBA test particle class - type, public, extends(helio_tp) :: symba_tp + type, extends(helio_tp) :: symba_tp integer(I4B), dimension(:), allocatable :: nplenc !! number of encounters with planets this time step integer(I4B), dimension(:), allocatable :: levelg !! level at which this particle should be moved integer(I4B), dimension(:), allocatable :: levelm !! deepest encounter level achieved this time step contains - private - procedure, public :: discard => symba_discard_tp !! process test particle discards - procedure, public :: encounter_check => symba_encounter_check_tp !! Checks if any test particles are undergoing a close encounter with a massive body - procedure, public :: setup => symba_setup_tp !! Constructor method - Allocates space for number of particle + 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 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, public :: 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 :: 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 !******************************************************************************************************************************** ! symba_plplenc class definitions and method interfaces !******************************************************************************************************************************* !> SyMBA class for tracking pl-pl close encounters in a step - type, public, 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 + type, extends(symba_pltpenc) :: symba_plplenc + contains + 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, public, 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 + type, extends(helio_nbody_system) :: symba_nbody_system + 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 - private - procedure, public :: initialize => symba_setup_system !! Performs SyMBA-specific initilization steps - procedure, public :: step => symba_step_system !! Advance the SyMBA nbody system forward in time by one step - procedure, public :: interp => symba_step_interp_system !! Perform an interpolation step on the SymBA nbody system + 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 + module subroutine symba_collision_check_pltpenc(self, system, param, t, dt, irec) + use swiftest_classes, only : swiftest_parameters + implicit none + class(symba_pltpenc), 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 + end subroutine symba_collision_check_pltpenc + + module subroutine symba_collision_encounter_scrub(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(swiftest_parameters), intent(in) :: param !! Current run configuration parameterss + end subroutine + + module subroutine symba_collision_make_family_pl(self,idx) + implicit none + class(symba_pl), intent(inout) :: self !! SyMBA massive body object + 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_discard_tp(self, system, param) + module subroutine symba_drift_pl(self, system, param, dt) + use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters + implicit none + class(symba_pl), intent(inout) :: self !! Helio massive body object + 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) :: dt !! Stepsize + end subroutine symba_drift_pl + + module subroutine symba_drift_tp(self, system, param, dt) use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters implicit none - class(symba_tp), intent(inout) :: self !! SyMBA test particle object + class(symba_tp), intent(inout) :: self !! Helio 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_discard_tp + real(DP), intent(in) :: dt !! Stepsize + end subroutine symba_drift_tp - module function symba_encounter_check_pl(self, system, dt) result(lencounter) + module pure elemental subroutine symba_encounter_check_one(xr, yr, zr, vxr, vyr, vzr, rhill1, rhill2, dt, irec, lencounter, lvdotr) + implicit none + real(DP), intent(in) :: xr, yr, zr, vxr, vyr, vzr + real(DP), intent(in) :: rhill1, rhill2, dt + integer(I4B), intent(in) :: irec + logical, intent(out) :: lencounter, lvdotr + end subroutine symba_encounter_check_one + + module function symba_encounter_check_pl(self, system, dt, irec) result(lany_encounter) implicit none class(symba_pl), intent(inout) :: self !! SyMBA test particle object class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object real(DP), intent(in) :: dt !! step size - logical :: lencounter !! Returns true if there is at least one close encounter + integer(I4B), intent(in) :: irec !! Current recursion level + logical :: lany_encounter !! Returns true if there is at least one close encounter end function symba_encounter_check_pl - module function symba_encounter_check_tp(self, system, dt) result(lencounter) + module function symba_encounter_check_pltpenc(self, system, dt, irec) result(lany_encounter) implicit none - class(symba_tp), intent(inout) :: self !! SyMBA test particle object - class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object - real(DP), intent(in) :: dt !! step size - logical :: lencounter !! Returns true if there is at least one close encounter + class(symba_pltpenc), intent(inout) :: self !! SyMBA pl-pl 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 + logical :: lany_encounter !! Returns true if there is at least one close encounter + end function symba_encounter_check_pltpenc + + module function symba_encounter_check_tp(self, system, dt, irec) result(lany_encounter) + implicit none + class(symba_tp), intent(inout) :: self !! SyMBA test particle 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 + logical :: lany_encounter !! Returns true if there is at least one close encounter end function symba_encounter_check_tp + module function symba_fragmentation_casedisruption(system, param, family, x, v, mass, radius, L_spin, Ip, mass_res, Qloss) result(status) + implicit none + 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(inout) :: x, v, L_spin, Ip !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass, radius !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass_res !! The distribution of fragment mass obtained by the regime calculation + real(DP), intent(inout) :: Qloss !! Energy lost during collisionn + integer(I4B) :: status !! Status flag assigned to this outcome + end function symba_fragmentation_casedisruption + + module function symba_fragmentation_casehitandrun(system, param, family, x, v, mass, radius, L_spin, Ip, mass_res, Qloss) result(status) + implicit none + 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(inout) :: x, v, L_spin, Ip !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass, radius !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass_res !! The distribution of fragment mass obtained by the regime calculation + real(DP), intent(inout) :: Qloss !! Energy lost during collision + integer(I4B) :: status !! Status flag assigned to this outcome + end function symba_fragmentation_casehitandrun + + module function symba_fragmentation_casemerge(system, param, family, x, v, mass, radius, L_spin, Ip) result(status) + implicit none + 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 function symba_fragmentation_casesupercatastrophic(system, param, family, x, v, mass, radius, L_spin, Ip, mass_res, Qloss) result(status) + implicit none + 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(inout) :: x, v, L_spin, Ip !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass, radius !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass_res !! The distribution of fragment mass obtained by the regime calculation + real(DP), intent(inout) :: Qloss !! Energy lost during collision + integer(I4B) :: status !! Status flag assigned to this outcome + end function symba_fragmentation_casesupercatastrophic + + module subroutine symba_io_write_discard(self, param) + use swiftest_classes, only : swiftest_parameters + implicit none + 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 implicit none class(symba_particle_info), intent(inout) :: self !! Swiftest base object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters character(*), optional, intent(in) :: msg !! Message to display with dump operation 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. @@ -199,7 +349,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. @@ -225,49 +375,315 @@ 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 class(symba_particle_info), intent(in) :: self !! SyMBA particle info object - integer(I4B), intent(inout) :: iu !! Unit number for the output file to write frame to - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + integer(I4B), intent(inout) :: iu !! Unit number for the output file to write frame to + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine symba_io_write_frame_info - module subroutine symba_setup_pl(self,n) + module subroutine symba_setup_initialize_system(self, param) + use swiftest_classes, only : swiftest_parameters implicit none - class(symba_pl), intent(inout) :: self !! SyMBA test particle object - integer(I4B), intent(in) :: n !! Number of massive bodies to allocate - end subroutine symba_setup_pl + 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_system(self, param) + module subroutine symba_setup_merger(self, n, 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_system + 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_step_system(self, param, t, dt) + module subroutine symba_setup_pl(self, n, param) use swiftest_classes, only : swiftest_parameters implicit none - class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody 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 symba_step_system + 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 + + module subroutine symba_setup_pltpenc(self,n) + implicit none + class(symba_pltpenc), intent(inout) :: self !! SyMBA pl-tp encounter structure + integer(I4B), intent(in) :: n !! Number of encounters to allocate space for + end subroutine symba_setup_pltpenc - module subroutine symba_setup_tp(self,n) + module subroutine symba_setup_tp(self, n, param) + use swiftest_classes, only : swiftest_parameters implicit none - class(symba_tp), intent(inout) :: self !! SyMBA test particle object - integer(I4B), intent(in) :: n !! Number of test particles to allocate + class(symba_tp), intent(inout) :: self !! SyMBA test particle object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameter end subroutine symba_setup_tp + module subroutine symba_step_system(self, param, t, dt) + use swiftest_classes, only : swiftest_parameters + implicit none + class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody 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 symba_step_system + module subroutine symba_step_interp_system(self, param, t, dt) use swiftest_classes, only : swiftest_parameters implicit none - class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody 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 + class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody 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 symba_step_interp_system + + module subroutine symba_step_set_recur_levels_system(self, ireci) + implicit none + class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody system objec + integer(I4B), intent(in) :: ireci !! Input recursion level + end subroutine symba_step_set_recur_levels_system + + module recursive subroutine symba_step_recur_system(self, param, t, ireci) + use swiftest_classes, only : swiftest_parameters + implicit none + class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody system object + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters + real(DP), value :: t + integer(I4B), value :: ireci !! input recursion level + end subroutine symba_step_recur_system + + 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_rearray_pl(self, system, param) + implicit none + 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 + + interface util_resize + module subroutine symba_util_resize_arr_info(arr, nnew) + implicit none + 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_arr_kin(arr, nnew) + implicit none + 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 + class(symba_pl), intent(inout) :: self !! SyMBA 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 symba_util_sort_pl + + module subroutine symba_util_sort_tp(self, sortby, ascending) + implicit none + class(symba_tp), intent(inout) :: self !! SyMBA test particle 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 symba_util_sort_tp + + module subroutine symba_util_sort_rearrange_pl(self, ind) + implicit none + class(symba_pl), 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_pl + + module subroutine symba_util_sort_rearrange_tp(self, ind) + implicit none + 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 6107a719d..a79f52bca 100644 --- a/src/modules/whm_classes.f90 +++ b/src/modules/whm_classes.f90 @@ -12,7 +12,7 @@ module whm_classes ! whm_cb class definitions and method interfaces !******************************************************************************************************************************* !> Swiftest central body particle class - type, public, extends(swiftest_cb) :: whm_cb + type, extends(swiftest_cb) :: whm_cb contains end type whm_cb @@ -21,28 +21,33 @@ module whm_classes !******************************************************************************************************************************* !> WHM massive body particle class - type, public, extends(swiftest_pl) :: whm_pl + type, extends(swiftest_pl) :: whm_pl real(DP), dimension(:), allocatable :: eta !! Jacobi mass real(DP), dimension(:,:), allocatable :: xj !! Jacobi position real(DP), dimension(:,:), allocatable :: vj !! Jacobi velocity real(DP), dimension(:), allocatable :: muj !! Jacobi mu: GMcb * eta(i) / eta(i - 1) real(DP), dimension(:), allocatable :: ir3j !! Third term of heliocentric acceleration !! 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_setup_pl and whm_spill_pl + !! component list, such as whm_setup_pl and whm_util_spill_pl contains - procedure, public :: h2j => whm_coord_h2j_pl !! Convert position and velcoity vectors from heliocentric to Jacobi coordinates - procedure, public :: j2h => whm_coord_j2h_pl !! Convert position and velcoity vectors from Jacobi to helliocentric coordinates - procedure, public :: vh2vj => whm_coord_vh2vj_pl !! Convert velocity vectors from heliocentric to Jacobi coordinates - procedure, public :: drift => whm_drift_pl !! Loop through massive bodies and call Danby drift routine - procedure, public :: fill => whm_fill_pl !! "Fills" bodies from one object into another depending on the results of a mask (uses the MERGE intrinsic) - procedure, public :: accel => whm_getacch_pl !! Compute heliocentric accelerations of massive bodies - procedure, public :: accel_gr => whm_gr_getacch_pl !! Acceleration term arising from the post-Newtonian correction - procedure, public :: gr_pos_kick => whm_gr_p4_pl !! Position kick due to p**4 term in the post-Newtonian correction - procedure, public :: setup => whm_setup_pl !! Constructor method - Allocates space for number of particles - procedure, public :: set_mu => whm_util_set_mu_eta_pl !! Sets the Jacobi mass value for all massive bodies. - procedure, public :: set_ir3 => whm_setup_set_ir3j !! Sets both the heliocentric and jacobi inverse radius terms (1/rj**3 and 1/rh**3) - procedure, public :: step => whm_step_pl !! Steps the body forward one stepsize - procedure, public :: spill => whm_spill_pl !!"Spills" bodies from one object to another depending on the results of a mask (uses the PACK intrinsic) + procedure :: h2j => whm_coord_h2j_pl !! Convert position and velcoity vectors from heliocentric to Jacobi coordinates + 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 :: 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 :: 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 :: 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 !******************************************************************************************************************************** @@ -50,29 +55,26 @@ module whm_classes !******************************************************************************************************************************* !! WHM test particle class - type, public, extends(swiftest_tp) :: whm_tp + type, extends(swiftest_tp) :: whm_tp !! 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_setup_tp and whm_spill_tp + !! component list, such as whm_util_spill_tp contains - private - procedure, public :: drift => whm_drift_tp !! Loop through test particles and call Danby drift routine - procedure, public :: accel => whm_getacch_tp !! Compute heliocentric accelerations of test particles - procedure, public :: accel_gr => whm_gr_getacch_tp !! Acceleration term arising from the post-Newtonian correction - procedure, public :: gr_pos_kick => whm_gr_p4_tp !! Position kick due to p**4 term in the post-Newtonian correction - procedure, public :: setup => whm_setup_tp !! Allocates new components of the whm class and recursively calls parent allocations - procedure, public :: step => whm_step_tp !! Steps the particle forward one stepsize + 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 !******************************************************************************************************************************** ! whm_nbody_system class definitions and method interfaces !******************************************************************************************************************************** !> An abstract class for the WHM integrator nbody system - type, public, extends(swiftest_nbody_system) :: whm_nbody_system + type, extends(swiftest_nbody_system) :: whm_nbody_system contains - private !> Replace the abstract procedures with concrete ones - procedure, public :: initialize => whm_setup_system !! Performs WHM-specific initilization steps, like calculating the Jacobi masses - procedure, public :: step => whm_step_system !! Advance the WHM nbody system forward in time by one step + procedure :: initialize => whm_setup_initialize_system !! Performs WHM-specific initilization steps, like calculating the Jacobi masses + procedure :: step => whm_step_system !! Advance the WHM nbody system forward in time by one step end type whm_nbody_system interface @@ -102,68 +104,73 @@ module subroutine whm_drift_pl(self, system, param, dt) implicit none class(whm_pl), intent(inout) :: self !! WHM massive body particle data structure class(swiftest_nbody_system), intent(inout) :: system !! WHM nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: dt !! Stepsize end subroutine whm_drift_pl - module subroutine whm_drift_tp(self, system, param, dt) - use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters - implicit none - class(whm_tp), intent(inout) :: self !! WHM test particle data structure - class(swiftest_nbody_system), intent(inout) :: system !! WHM nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of - real(DP), intent(in) :: dt !! Stepsize - end subroutine whm_drift_tp - - module subroutine whm_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_fill_pl - !> Get heliocentric accelration of massive bodies - module subroutine whm_getacch_pl(self, system, param, t, lbeg) + module subroutine whm_kick_getacch_pl(self, system, param, t, lbeg) use swiftest_classes, only : swiftest_cb, swiftest_parameters implicit none class(whm_pl), intent(inout) :: self !! WHM massive body particle data structure class(swiftest_nbody_system), intent(inout) :: system !! WHM nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: t !! Current simulation time - logical, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step - end subroutine whm_getacch_pl + logical, intent(in) :: lbeg !! Logical flag that determines whether or not this is the beginning or end of the step + end subroutine whm_kick_getacch_pl !> Get heliocentric accelration of the test particle - module subroutine whm_getacch_tp(self, system, param, t, lbeg) + module subroutine whm_kick_getacch_tp(self, system, param, t, lbeg) use swiftest_classes, only : swiftest_cb, swiftest_parameters implicit none class(whm_tp), intent(inout) :: self !! WHM test particle data structure class(swiftest_nbody_system), intent(inout) :: system !! WHM nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of + 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 whm_kick_getacch_tp + + module subroutine whm_kick_vh_pl(self, system, param, t, dt, lbeg) + use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters + implicit none + class(whm_pl), intent(inout) :: self !! WHM massive body object + 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 + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Logical flag indicating whether this is the beginning of the half step or not. + end subroutine whm_kick_vh_pl + + module subroutine whm_kick_vh_tp(self, system, param, t, dt, lbeg) + use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters + implicit none + class(whm_tp), intent(inout) :: self !! WHM test particle object + 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, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step - end subroutine whm_getacch_tp + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Logical flag indicating whether this is the beginning of the half step or not. + end subroutine whm_kick_vh_tp - module subroutine whm_gr_getacch_pl(self, param) + module subroutine whm_gr_kick_getacch_pl(self, param) use swiftest_classes, only : swiftest_cb, swiftest_parameters implicit none class(whm_pl), intent(inout) :: self !! WHM massive body particle data structure - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of - end subroutine whm_gr_getacch_pl + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + end subroutine whm_gr_kick_getacch_pl - module subroutine whm_gr_getacch_tp(self, param) + module subroutine whm_gr_kick_getacch_tp(self, param) use swiftest_classes, only : swiftest_cb, swiftest_parameters implicit none class(whm_tp), intent(inout) :: self !! WHM test particle data structure class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters - end subroutine whm_gr_getacch_tp + end subroutine whm_gr_kick_getacch_tp module pure subroutine whm_gr_p4_pl(self, param, dt) use swiftest_classes, only : swiftest_parameters implicit none class(whm_pl), intent(inout) :: self !! WHM massive body object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of on parameters + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: dt !! Step size end subroutine whm_gr_p4_pl @@ -171,42 +178,25 @@ module pure subroutine whm_gr_p4_tp(self, param, dt) use swiftest_classes, only : swiftest_parameters implicit none class(whm_tp), intent(inout) :: self !! WHM test particle object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of on parameters + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: dt !! Step size end subroutine whm_gr_p4_tp !> Reads WHM massive body object in from file - module subroutine whm_setup_pl(self,n) + module subroutine whm_setup_pl(self, n, param) + use swiftest_classes, only : swiftest_parameters implicit none - class(whm_pl), intent(inout) :: self !! WHM massive body objectobject - integer(I4B), intent(in) :: n !! Number of test particles to allocate + class(whm_pl), intent(inout) :: self !! WHM massive body objectobject + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters end subroutine whm_setup_pl - module subroutine whm_setup_set_ir3j(self) - implicit none - class(whm_pl), intent(inout) :: self !! WHM massive body object - end subroutine whm_setup_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_setup_system(self, param) + module subroutine whm_setup_initialize_system(self, param) use swiftest_classes, only : swiftest_parameters implicit none class(whm_nbody_system), intent(inout) :: self !! WHM nbody system object - class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters of on parameters - end subroutine whm_setup_system - - !> Reads WHM test particle object in from file - module subroutine whm_setup_tp(self,n) - implicit none - class(whm_tp), intent(inout) :: self !! WHM test particle data structure - integer, intent(in) :: n !! Number of test particles to allocate - end subroutine whm_setup_tp + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters + end subroutine whm_setup_initialize_system module subroutine whm_step_pl(self, system, param, t, dt) use swiftest_classes, only : swiftest_nbody_system, swiftest_parameters @@ -218,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 @@ -228,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_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 - end subroutine whm_spill_pl + 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 8792f2399..035a54b18 100644 --- a/src/obl/obl.f90 +++ b/src/obl/obl.f90 @@ -17,9 +17,11 @@ module subroutine obl_acc_body(self, system) integer(I4B) :: i real(DP) :: r2, irh, rinv2, t0, t1, t2, t3, fac1, fac2 + if (self%nbody == 0) return + associate(n => self%nbody, cb => system%cb) self%aobl(:,:) = 0.0_DP - do i = 1, n + do concurrent(i = 1:n, self%lmask(i)) r2 = dot_product(self%xh(:, i), self%xh(:, i)) irh = 1.0_DP / sqrt(r2) rinv2 = irh**2 @@ -37,6 +39,7 @@ module subroutine obl_acc_body(self, system) end subroutine obl_acc_body + module subroutine obl_acc_pl(self, system) !! author: David A. Minton !! @@ -51,14 +54,16 @@ module subroutine obl_acc_pl(self, system) ! Internals integer(I4B) :: i + if (self%nbody == 0) return + associate(pl => self, npl => self%nbody, cb => system%cb) call obl_acc_body(pl, system) do i = 1, NDIM - cb%aobl(i) = -sum(pl%Gmass(1:npl) * pl%aobl(i, 1:npl)) / cb%Gmass + cb%aobl(i) = -sum(pl%Gmass(1:npl) * pl%aobl(i, 1:npl), pl%lmask(1:npl)) / cb%Gmass end do - do i = 1, NDIM - pl%ah(i, 1:npl) = pl%ah(i, 1:npl) + pl%aobl(i, 1:npl) - cb%aobl(i) + do concurrent(i = 1:npl, pl%lmask(i)) + pl%ah(:, i) = pl%ah(:, i) + pl%aobl(:, i) - cb%aobl(:) end do end associate @@ -66,6 +71,7 @@ module subroutine obl_acc_pl(self, system) end subroutine obl_acc_pl + module subroutine obl_acc_tp(self, system) !! author: David A. Minton !! @@ -81,6 +87,8 @@ module subroutine obl_acc_tp(self, system) real(DP), dimension(NDIM) :: aoblcb integer(I4B) :: i + if (self%nbody == 0) return + associate(tp => self, ntp => self%nbody, cb => system%cb) call obl_acc_body(tp, system) if (system%lbeg) then @@ -89,8 +97,8 @@ module subroutine obl_acc_tp(self, system) aoblcb = cb%aoblend end if - do i = 1, NDIM - tp%ah(i, 1:ntp) = tp%ah(i, 1:ntp) + tp%aobl(i, 1:ntp) - aoblcb(i) + do concurrent(i = 1:ntp, tp%lmask(i)) + tp%ah(:, i) = tp%ah(:, i) + tp%aobl(:, i) - aoblcb(:) end do end associate @@ -98,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/operators/operator_cross.f90 b/src/operators/operator_cross.f90 index d6fd82944..736dc2696 100644 --- a/src/operators/operator_cross.f90 +++ b/src/operators/operator_cross.f90 @@ -1,4 +1,4 @@ -submodule(swiftest_operators) operator_cross_implementation +submodule(swiftest_operators) s_operator_cross use swiftest !! author: David A. Minton !! @@ -7,58 +7,99 @@ !! Vector list implementations: C(1:3, :) = A(1:3, :) .cross. B(1:3, :) contains - module procedure operator_cross_sp + module pure function operator_cross_sp(A, B) result(C) implicit none + real(SP), dimension(:), intent(in) :: A, B + real(SP), dimension(3) :: C C(1) = A(2) * B(3) - A(3) * B(2) C(2) = A(3) * B(1) - A(1) * B(3) C(3) = A(1) * B(2) - A(2) * B(1) return - end procedure operator_cross_sp + end function operator_cross_sp - module procedure operator_cross_dp + module pure function operator_cross_dp(A, B) result(C) implicit none + real(DP), dimension(:), intent(in) :: A, B + real(DP), dimension(3) :: C C(1) = A(2) * B(3) - A(3) * B(2) C(2) = A(3) * B(1) - A(1) * B(3) C(3) = A(1) * B(2) - A(2) * B(1) return - end procedure operator_cross_dp + end function operator_cross_dp - module procedure operator_cross_i1b + module pure function operator_cross_qp(A, B) result(C) implicit none + real(QP), dimension(:), intent(in) :: A, B + real(QP), dimension(3) :: C C(1) = A(2) * B(3) - A(3) * B(2) C(2) = A(3) * B(1) - A(1) * B(3) C(3) = A(1) * B(2) - A(2) * B(1) return - end procedure operator_cross_i1b + end function operator_cross_qp - module procedure operator_cross_i2b + module pure function operator_cross_i1b(A, B) result(C) implicit none + integer(I1B), dimension(:), intent(in) :: A, B + integer(I1B), dimension(3) :: C C(1) = A(2) * B(3) - A(3) * B(2) C(2) = A(3) * B(1) - A(1) * B(3) C(3) = A(1) * B(2) - A(2) * B(1) return - end procedure operator_cross_i2b + end function operator_cross_i1b - module procedure operator_cross_i4b + module pure function operator_cross_i2b(A, B) result(C) implicit none + integer(I2B), dimension(:), intent(in) :: A, B + integer(I2B), dimension(3) :: C C(1) = A(2) * B(3) - A(3) * B(2) C(2) = A(3) * B(1) - A(1) * B(3) C(3) = A(1) * B(2) - A(2) * B(1) return - end procedure operator_cross_i4b + end function operator_cross_i2b - module procedure operator_cross_i8b + module pure function operator_cross_i4b(A, B) result(C) implicit none + integer(I4B), dimension(:), intent(in) :: A, B + integer(I4B), dimension(3) :: C C(1) = A(2) * B(3) - A(3) * B(2) C(2) = A(3) * B(1) - A(1) * B(3) C(3) = A(1) * B(2) - A(2) * B(1) return - end procedure operator_cross_i8b + end function operator_cross_i4b - module procedure operator_cross_el_sp + module pure function operator_cross_i8b(A, B) result(C) implicit none + integer(I8B), dimension(:), intent(in) :: A, B + integer(I8B), dimension(3) :: C + C(1) = A(2) * B(3) - A(3) * B(2) + C(2) = A(3) * B(1) - A(1) * B(3) + C(3) = A(1) * B(2) - A(2) * B(1) + return + end function operator_cross_i8b + + module pure function operator_cross_el_sp(A, B) result(C) + implicit none + real(SP), dimension(:,:), intent(in) :: A, B + real(SP), dimension(:,:), allocatable :: C + integer(I4B) :: i, n + n = size(A, 2) + if (allocated(C)) deallocate(C) + allocate(C, mold = A) + do concurrent (i = 1:n) + C(1, i) = A(2, i) * B(3, i) - A(3, i) * B(2, i) + C(2, i) = A(3, i) * B(1, i) - A(1, i) * B(3, i) + C(3, i) = A(1, i) * B(2, i) - A(2, i) * B(1, i) + end do + return + end function operator_cross_el_sp + + module pure function operator_cross_el_dp(A, B) result(C) + implicit none + real(DP), dimension(:,:), intent(in) :: A, B + real(DP), dimension(:,:), allocatable :: C integer(I4B) :: i, n n = size(A, 2) + if (allocated(C)) deallocate(C) allocate(C, mold = A) do concurrent (i = 1:n) C(1, i) = A(2, i) * B(3, i) - A(3, i) * B(2, i) @@ -66,12 +107,15 @@ C(3, i) = A(1, i) * B(2, i) - A(2, i) * B(1, i) end do return - end procedure operator_cross_el_sp + end function operator_cross_el_dp - module procedure operator_cross_el_dp + module pure function operator_cross_el_qp(A, B) result(C) implicit none + real(QP), dimension(:,:), intent(in) :: A, B + real(QP), dimension(:,:), allocatable :: C integer(I4B) :: i, n n = size(A, 2) + if (allocated(C)) deallocate(C) allocate(C, mold = A) do concurrent (i = 1:n) C(1, i) = A(2, i) * B(3, i) - A(3, i) * B(2, i) @@ -79,12 +123,15 @@ C(3, i) = A(1, i) * B(2, i) - A(2, i) * B(1, i) end do return - end procedure operator_cross_el_dp + end function operator_cross_el_qp - module procedure operator_cross_el_i1b - implicit none + module pure function operator_cross_el_i1b(A, B) result(C) + implicit none + integer(I1B), dimension(:,:), intent(in) :: A, B + integer(I1B), dimension(:,:), allocatable :: C integer(I4B) :: i, n n = size(A, 2) + if (allocated(C)) deallocate(C) allocate(C, mold = A) do concurrent (i = 1:n) C(1, i) = A(2, i) * B(3, i) - A(3, i) * B(2, i) @@ -92,12 +139,15 @@ C(3, i) = A(1, i) * B(2, i) - A(2, i) * B(1, i) end do return - end procedure operator_cross_el_i1b + end function operator_cross_el_i1b - module procedure operator_cross_el_i2b + module pure function operator_cross_el_i2b(A, B) result(C) implicit none + integer(I2B), dimension(:,:), intent(in) :: A, B + integer(I2B), dimension(:,:), allocatable :: C integer(I4B) :: i, n n = size(A, 2) + if (allocated(C)) deallocate(C) allocate(C, mold = A) do concurrent (i = 1:n) C(1, i) = A(2, i) * B(3, i) - A(3, i) * B(2, i) @@ -105,12 +155,15 @@ C(3, i) = A(1, i) * B(2, i) - A(2, i) * B(1, i) end do return - end procedure operator_cross_el_i2b + end function operator_cross_el_i2b - module procedure operator_cross_el_i4b + module pure function operator_cross_el_i4b(A, B) result(C) implicit none + integer(I4B), dimension(:,:), intent(in) :: A, B + integer(I4B), dimension(:,:), allocatable :: C integer(I4B) :: i, n n = size(A, 2) + if (allocated(C)) deallocate(C) allocate(C, mold = A) do concurrent (i = 1:n) C(1, i) = A(2, i) * B(3, i) - A(3, i) * B(2, i) @@ -118,12 +171,15 @@ C(3, i) = A(1, i) * B(2, i) - A(2, i) * B(1, i) end do return - end procedure operator_cross_el_i4b + end function operator_cross_el_i4b - module procedure operator_cross_el_i8b + module pure function operator_cross_el_i8b(A, B) result(C) implicit none + integer(I8B), dimension(:,:), intent(in) :: A, B + integer(I8B), dimension(:,:), allocatable :: C integer(I4B) :: i, n n = size(A, 2) + if (allocated(C)) deallocate(C) allocate(C, mold = A) do concurrent (i = 1:n) C(1, i) = A(2, i) * B(3, i) - A(3, i) * B(2, i) @@ -131,6 +187,6 @@ C(3, i) = A(1, i) * B(2, i) - A(2, i) * B(1, i) end do return - end procedure operator_cross_el_i8b + end function operator_cross_el_i8b -end submodule operator_cross_implementation \ No newline at end of file +end submodule s_operator_cross \ No newline at end of file diff --git a/src/operators/operator_mag.f90 b/src/operators/operator_mag.f90 new file mode 100644 index 000000000..5a054d5ce --- /dev/null +++ b/src/operators/operator_mag.f90 @@ -0,0 +1,68 @@ +submodule(swiftest_operators) s_operator_mag + !! author: David A. Minton + !! + !! Contains implementations for the .mag. operator for all defined real types + !! Single vector implementations: B = .mag. A(1:3) + !! Vector list implementations: B(:) = .mag. A(1:3, :) + contains + + module pure function operator_mag_sp(A) result(B) + implicit none + real(SP), dimension(:), intent(in) :: A + real(SP) :: B + B = norm2(A(:)) + return + end function operator_mag_sp + + module pure function operator_mag_dp(A) result(B) + implicit none + real(DP), dimension(:), intent(in) :: A + real(DP) :: B + B = norm2(A(:)) + return + end function operator_mag_dp + + module pure function operator_mag_el_sp(A) result(B) + implicit none + real(SP), dimension(:,:), intent(in) :: A + real(SP), dimension(:), allocatable :: B + integer(I4B) :: i,n + n = size(A, 2) + if (allocated(B)) deallocate(B) + allocate(B(n)) + do concurrent (i=1:n) + B(i) = norm2(A(:, i)) + end do + return + end function operator_mag_el_sp + + module pure function operator_mag_el_dp(A) result(B) + implicit none + real(DP), dimension(:,:), intent(in) :: A + real(DP), dimension(:), allocatable :: B + integer(I4B) :: i,n + n = size(A, 2) + if (allocated(B)) deallocate(B) + allocate(B(n)) + do concurrent (i=1:n) + B(i) = norm2(A(:, i)) + end do + return + end function operator_mag_el_dp + + module pure function operator_mag_el_qp(A) result(B) + implicit none + real(QP), dimension(:,:), intent(in) :: A + real(QP), dimension(:), allocatable :: B + integer(I4B) :: i,n + n = size(A, 2) + if (allocated(B)) deallocate(B) + allocate(B(n)) + do concurrent (i=1:n) + B(i) = norm2(A(:, i)) + end do + return + end function operator_mag_el_qp + +end submodule s_operator_mag + diff --git a/src/orbel/orbel_el2xv.f90 b/src/orbel/orbel.f90 similarity index 65% rename from src/orbel/orbel_el2xv.f90 rename to src/orbel/orbel.f90 index 3f1a81fe9..f1ab88825 100644 --- a/src/orbel/orbel_el2xv.f90 +++ b/src/orbel/orbel.f90 @@ -1,21 +1,27 @@ -submodule (swiftest_classes) s_orbel_el2xv +submodule (swiftest_classes) s_orbel use swiftest contains - module procedure orbel_el2xv_vec + + module subroutine orbel_el2xv_vec(self, cb) !! author: David A. Minton !! !! A wrapper method that converts all of the cartesian position and velocity vectors of a Swiftest body object to orbital elements. implicit none + ! Arguments + class(swiftest_body), intent(inout) :: self !! Swiftest body object + class(swiftest_cb), intent(inout) :: cb !! Swiftest central body objec + ! Internals integer(I4B) :: i if (self%nbody == 0) return + call self%set_mu(cb) - !do concurrent (i = 1:self%nbody) do i = 1, self%nbody call orbel_el2xv(self%mu(i), self%a(i), self%e(i), self%inc(i), self%capom(i), & self%omega(i), self%capm(i), self%xh(:, i), self%vh(:, i)) end do - end procedure orbel_el2xv_vec + end subroutine orbel_el2xv_vec + pure subroutine orbel_el2xv(mu, a, ie, inc, capom, omega, capm, x, v) !! author: David A. Minton @@ -118,6 +124,35 @@ pure subroutine orbel_el2xv(mu, a, ie, inc, capom, omega, capm, x, v) return end subroutine orbel_el2xv + + module pure subroutine orbel_scget(angle, sx, cx) + !! author: David A. Minton + !! + !! Efficiently compute the sine and cosine of an input angle + !! Input angle must be in radians + !! + !! Adapted from David E. Kaufmann's Swifter routine: orbel_scget.f90 + !! Adapted from Hal Levison's Swift routine orbel_scget.f + implicit none + ! Arguments + real(DP), intent(in) :: angle + real(DP), intent(out) :: sx, cx + ! Internals + integer(I4B) :: nper + real(DP) :: x + + nper = angle / TWOPI + x = angle - nper * TWOPI + if (x < 0.0_DP) x = x + TWOPI + sx = sin(x) + cx = sqrt(1.0_DP - sx**2) + if ((x > PIBY2) .and. (x < PI3BY2)) cx = -cx + + return + + end subroutine orbel_scget + + !********************************************************************** ! Code converted to Modern Fortran by David A. Minton ! Date: 2020-06-29 @@ -153,6 +188,7 @@ pure subroutine orbel_schget(angle,shx,chx) return end subroutine orbel_schget + !********************************************************************** ! Code converted to Modern Fortran by David A. Minton ! Date: 2020-06-29 @@ -207,8 +243,6 @@ real(DP) pure function orbel_flon(e,icapn) ! set iflag nonzero if capn < 0., in which case solve for -capn ! and change the sign of the final answer for f. ! Begin with a reasonable guess based on solving the cubic for small F - - a = 6 * ( e - 1.d0) / e b = -6 * capn / e sq = SQRT(0.25_DP * b**2 + a**3 / 27._DP) @@ -256,6 +290,7 @@ real(DP) pure function orbel_flon(e,icapn) return end function orbel_flon + !********************************************************************** ! Code converted to Modern Fortran by David A. Minton ! Date: 2020-06-29 @@ -325,6 +360,7 @@ real(DP) pure function orbel_fget(e,capn) return end function orbel_fget + !********************************************************************** ! Code converted to Modern Fortran by David A. Minton ! Date: 2020-06-29 @@ -377,6 +413,7 @@ real(DP) pure function orbel_zget(iq) return end function orbel_zget + !********************************************************************** ! Code converted to Modern Fortran by David A. Minton ! Date: 2020-06-29 @@ -430,6 +467,7 @@ real(DP) pure function orbel_esolmd(e,m) return end function orbel_esolmd + !********************************************************************** ! Code converted to Modern Fortran by David A. Minton ! Date: 2020-06-29 @@ -502,6 +540,7 @@ real(DP) pure function orbel_ehie(e,im) return end function orbel_ehie + !********************************************************************** ! Code converted to Modern Fortran by David A. Minton ! Date: 2020-06-29 @@ -579,6 +618,7 @@ real(DP) pure function orbel_eget(e,m) return end function orbel_eget + !********************************************************************** ! Code converted to Modern Fortran by David A. Minton ! Date: 2020-06-29 @@ -656,6 +696,321 @@ real(DP) pure function orbel_fhybrid(e,n) return end function orbel_fhybrid + + + module pure subroutine orbel_xv2aeq(mu, x, v, a, e, q) + !! author: David A. Minton + !! + !! Compute semimajor axis, eccentricity, and pericentric distance from relative Cartesian position and velocity + !! + !! Adapted from David E. Kaufmann's Swifter routine: orbel_xv2aeq.f90 + !! Adapted from Luke Dones' Swift routine orbel_xv2aeq.f + implicit none + !! Arguments + real(DP), intent(in) :: mu + real(DP), dimension(:), intent(in) :: x, v + real(DP), intent(out) :: a, e, q + integer(I4B) :: iorbit_type + real(DP) :: r, v2, h2, energy, fac + real(DP), dimension(NDIM) :: hvec + + a = 0.0_DP + e = 0.0_DP + q = 0.0_DP + r = sqrt(dot_product(x(:), x(:))) + v2 = dot_product(v(:), v(:)) + hvec(:) = x(:) .cross. v(:) + h2 = dot_product(hvec(:), hvec(:)) + if (h2 == 0.0_DP) return + energy = 0.5_DP * v2 - mu / r + if (abs(energy * r / mu) < sqrt(VSMALL)) then + iorbit_type = PARABOLA + else + a = -0.5_DP * mu / energy + if (a < 0.0_DP) then + fac = -h2 / (mu * a) + if (fac > VSMALL) then + iorbit_type = HYPERBOLA + else + iorbit_type = PARABOLA + end if + else + iorbit_type = ELLIPSE + end if + end if + select case (iorbit_type) + case (ELLIPSE) + fac = 1.0_DP - h2 / (mu * a) + if (fac > VSMALL) e = sqrt(fac) + q = a * (1.0_DP - e) + case (PARABOLA) + a = 0.5_DP * h2 / mu + e = 1.0_DP + q = a + case (HYPERBOLA) + e = sqrt(1.0_DP + fac) + q = a * (1.0_DP - e) + end select + + return + + end subroutine orbel_xv2aeq + + + module pure subroutine orbel_xv2aqt(mu, x, v, a, q, capm, tperi) + !! author: David A. Minton + !! + !! Compute semimajor axis, pericentric distance, mean anomaly, and time to nearest pericenter passage from + !! relative Cartesian position and velocity + !! tperi > 0 means nearest pericenter passage is in the future + !! tperi < 0 means nearest pericenter passage is in the past + !! + !! Adapted from David E. Kaufmann's Swifter routine: orbel_xv2aqt.f90 + implicit none + ! Arguments + real(DP), intent(in) :: mu !! Gravitational constant + real(DP), dimension(:), intent(in) :: x !! Position vector + real(DP), dimension(:), intent(in) :: v !! Velocity vector + real(DP), intent(out) :: a !! semimajor axis + real(DP), intent(out) :: q !! periapsis + real(DP), intent(out) :: capm !! mean anomaly + real(DP), intent(out) :: tperi !! time of pericenter passage + ! Internals + integer(I4B) :: iorbit_type + real(DP) :: r, v2, h2, rdotv, energy, fac, w, face, cape, e, tmpf, capf, mm + real(DP), dimension(NDIM) :: hvec + + a = 0.0_DP + q = 0.0_DP + capm = 0.0_DP + tperi = 0.0_DP + r = sqrt(dot_product(x(:), x(:))) + v2 = dot_product(v(:), v(:)) + hvec(:) = x(:) .cross. v(:) + h2 = dot_product(hvec(:), hvec(:)) + if (h2 == 0.0_DP) return + rdotv = dot_product(x(:), v(:)) + energy = 0.5_DP * v2 - mu / r + if (abs(energy * r / mu) < sqrt(VSMALL)) then + iorbit_type = PARABOLA + else + a = -0.5_DP * mu / energy + if (a < 0.0_DP) then + fac = -h2 / (mu * a) + if (fac > VSMALL) then + iorbit_type = HYPERBOLA + else + iorbit_type = PARABOLA + end if + else + iorbit_type = ELLIPSE + end if + end if + select case (iorbit_type) + case (ELLIPSE) + fac = 1.0_DP - h2 / (mu * a) + if (fac > VSMALL) then + e = sqrt(fac) + cape = 0.0_DP + face = (a - r) / (a * e) + if (face < -1.0_DP) then + cape = PI + else if (face < 1.0_DP) then + cape = acos(face) + end if + if (rdotv < 0.0_DP) cape = TWOPI - cape + else + e = 0.0_DP + cape = 0.0_DP + end if + capm = cape - e * sin(cape) + q = a * (1.0_DP - e) + mm = sqrt(mu / a**3) + if (capm < PI) then + tperi = -1.0_DP * capm / mm + else + tperi = -1.0_DP * (capm - TWOPI) / mm + end if + case (PARABOLA) + a = 0.5_DP * h2 / mu + e = 1.0_DP + w = 0.0_DP + fac = 2 * a / r - 1.0_DP + if (fac < -1.0_DP) then + w = PI + else if (fac < 1.0_DP) then + w = acos(fac) + end if + if (rdotv < 0.0_DP) w = TWOPI - w + tmpf = tan(0.5_DP * w) + capm = tmpf*(1.0_DP + tmpf * tmpf / 3.0_DP) + q = a + mm = sqrt(0.5_DP * mu / q**3) + tperi = -1.0_DP * capm / mm + case (HYPERBOLA) + e = sqrt(1.0_DP + fac) + tmpf = (a - r) / (a * e) + if (tmpf < 1.0_DP) tmpf = 1.0_DP + capf = log(tmpf + sqrt(tmpf * tmpf - 1.0_DP)) + if (rdotv < 0.0_DP) capf = -capf + capm = e * sinh(capf) - capf + q = a * (1.0_DP - e) + mm = sqrt(-mu / a**3) + tperi = -1.0_DP * capm / mm + end select + + return + + end subroutine orbel_xv2aqt + + + module subroutine orbel_xv2el_vec(self, cb) + !! author: David A. Minton + !! + !! A wrapper method that converts all of the cartesian position and velocity vectors of a Swiftest body object to orbital elements. + implicit none + ! Arguments + class(swiftest_body), intent(inout) :: self !! Swiftest body object + class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object + ! internals + integer(I4B) :: i + + if (self%nbody == 0) return + + call self%set_mu(cb) + if (.not.allocated(self%a)) allocate(self%a(self%nbody)) + if (.not.allocated(self%e)) allocate(self%e(self%nbody)) + if (.not.allocated(self%inc)) allocate(self%inc(self%nbody)) + if (.not.allocated(self%capom)) allocate(self%capom(self%nbody)) + if (.not.allocated(self%omega)) allocate(self%omega(self%nbody)) + if (.not.allocated(self%capm)) allocate(self%capm(self%nbody)) + do i = 1, self%nbody + call orbel_xv2el(self%mu(i), self%xh(:, i), self%vh(:, i), self%a(i), self%e(i), self%inc(i), & + self%capom(i), self%omega(i), self%capm(i)) + end do + end subroutine orbel_xv2el_vec + pure subroutine orbel_xv2el(mu, x, v, a, e, inc, capom, omega, capm) + !! author: David A. Minton + !! + !! Compute osculating orbital elements from relative Cartesian position and velocity + !! All angular measures are returned in radians + !! If inclination < TINY, longitude of the ascending node is arbitrarily set to 0 + !! + !! If eccentricity < sqrt(TINY), argument of pericenter is arbitrarily set to 0 + !! + !! References: Danby, J. M. A. 1988. Fundamentals of Celestial Mechanics, (Willmann-Bell, Inc.), 201 - 206. + !! Fitzpatrick, P. M. 1970. Principles of Celestial Mechanics, (Academic Press), 69 - 73. + !! Roy, A. E. 1982. Orbital Motion, (Adam Hilger, Ltd.), 75 - 95 + !! + !! Adapted from David E. Kaufmann's Swifter routine: orbel_xv2el.f90 + !! Adapted from Martin Duncan's Swift routine orbel_xv2el.f + implicit none + real(DP), intent(in) :: mu + real(DP), dimension(:), intent(in) :: x, v + real(DP), intent(out) :: a, e, inc, capom, omega, capm + integer(I4B) :: iorbit_type + real(DP) :: r, v2, h2, h, rdotv, energy, fac, u, w, cw, sw, face, cape, tmpf, capf + real(DP), dimension(NDIM) :: hvec + + a = 0.0_DP + e = 0.0_DP + inc = 0.0_DP + capom = 0.0_DP + omega = 0.0_DP + capm = 0.0_DP + r = sqrt(dot_product(x(:), x(:))) + v2 = dot_product(v(:), v(:)) + hvec = x(:) .cross. v(:) + h2 = dot_product(hvec(:), hvec(:)) + h = sqrt(h2) + if (h2 == 0.0_DP) return + rdotv = dot_product(x(:), v(:)) + energy = 0.5_DP * v2 - mu / r + fac = hvec(3) / h + if (fac < -1.0_DP) then + inc = PI + else if (fac < 1.0_DP) then + inc = acos(fac) + end if + fac = sqrt(hvec(1)**2 + hvec(2)**2) / h + if (fac**2 < VSMALL) then + u = atan2(x(2), x(1)) + if (hvec(3) < 0.0_DP) u = -u + else + capom = atan2(hvec(1), -hvec(2)) + u = atan2(x(3) / sin(inc), x(1) * cos(capom) + x(2) * sin(capom)) + end if + if (capom < 0.0_DP) capom = capom + TWOPI + if (u < 0.0_DP) u = u + TWOPI + if (abs(energy * r / mu) < sqrt(VSMALL)) then + iorbit_type = parabola + else + a = -0.5_DP * mu / energy + if (a < 0.0_DP) then + fac = -h2 / (mu * a) + if (fac > VSMALL) then + iorbit_type = HYPERBOLA + else + iorbit_type = PARABOLA + end if + else + iorbit_type = ELLIPSE + end if + end if + select case (iorbit_type) + case (ELLIPSE) + fac = 1.0_DP - h2 / (mu * a) + if (fac > VSMALL) then + e = sqrt(fac) + cape = 0.0_DP + face = (a - r) / (a * e) + if (face < -1.0_DP) then + cape = PI + else if (face < 1.0_DP) then + cape = acos(face) + end if + if (rdotv < 0.0_DP) cape = TWOPI - cape + fac = 1.0_DP - e * cos(cape) + cw = (cos(cape) - e) / fac + sw = sqrt(1.0_DP - e**2) * sin(cape) / fac + w = atan2(sw, cw) + if (w < 0.0_DP) w = w + TWOPI + else + cape = u + w = u + end if + capm = cape - e * sin(cape) + case (PARABOLA) + a = 0.5_DP * h2 / mu + e = 1.0_DP + w = 0.0_DP + fac = 2 * a / r - 1.0_DP + if (fac < -1.0_DP) then + w = PI + else if (fac < 1.0_DP) then + w = acos(fac) + end if + if (rdotv < 0.0_DP) w = TWOPI - w + tmpf = tan(0.5_DP * w) + capm = tmpf * (1.0_DP + tmpf * tmpf / 3.0_DP) + case (HYPERBOLA) + e = sqrt(1.0_DP + fac) + tmpf = max((a - r) / (a * e), 1.0_DP) + capf = log(tmpf + sqrt(tmpf**2 - 1.0_DP)) + if (rdotv < 0.0_DP) capf = -capf + fac = e * cosh(capf) - 1.0_DP + cw = (e - cosh(capf)) / fac + sw = sqrt(e * e - 1.0_DP) * sinh(capf) / fac + w = atan2(sw, cw) + if (w < 0.0_DP) w = w + TWOPI + capm = e * sinh(capf) - capf + end select + omega = u - w + if (omega < 0.0_DP) omega = omega + TWOPI -end submodule s_orbel_el2xv + return + end subroutine orbel_xv2el + + +end submodule s_orbel diff --git a/src/orbel/orbel_scget.f90 b/src/orbel/orbel_scget.f90 deleted file mode 100644 index 0cdb67c72..000000000 --- a/src/orbel/orbel_scget.f90 +++ /dev/null @@ -1,27 +0,0 @@ -submodule (swiftest_classes) s_orbel_scget - use swiftest -contains - module procedure orbel_scget - !! author: David A. Minton - !! - !! Efficiently compute the sine and cosine of an input angle - !! Input angle must be in radians - !! - !! Adapted from David E. Kaufmann's Swifter routine: orbel_scget.f90 - !! Adapted from Hal Levison's Swift routine orbel_scget.f - implicit none - integer(I4B) :: nper - real(DP) :: x - - ! executable code - nper = angle / TWOPI - x = angle - nper * TWOPI - if (x < 0.0_DP) x = x + TWOPI - sx = sin(x) - cx = sqrt(1.0_DP - sx**2) - if ((x > PIBY2) .and. (x < PI3BY2)) cx = -cx - - return - - end procedure orbel_scget -end submodule s_orbel_scget diff --git a/src/orbel/orbel_xv2aeq.f90 b/src/orbel/orbel_xv2aeq.f90 deleted file mode 100644 index 8338d6559..000000000 --- a/src/orbel/orbel_xv2aeq.f90 +++ /dev/null @@ -1,57 +0,0 @@ -submodule (swiftest_classes) s_orbel_xv2aeq - use swiftest -contains - module procedure orbel_xv2aeq - !! author: David A. Minton - !! - !! Compute semimajor axis, eccentricity, and pericentric distance from relative Cartesian position and velocity - !! - !! Adapted from David E. Kaufmann's Swifter routine: orbel_xv2aeq.f90 - !! Adapted from Luke Dones' Swift routine orbel_xv2aeq.f - implicit none - integer(I4B) :: iorbit_type - real(DP) :: r, v2, h2, energy, fac - real(DP), dimension(NDIM) :: hvec - - a = 0.0_DP - e = 0.0_DP - q = 0.0_DP - r = sqrt(dot_product(x(:), x(:))) - v2 = dot_product(v(:), v(:)) - hvec(:) = x(:) .cross. v(:) - h2 = dot_product(hvec(:), hvec(:)) - if (h2 == 0.0_DP) return - energy = 0.5_DP * v2 - mu / r - if (abs(energy * r / mu) < sqrt(VSMALL)) then - iorbit_type = PARABOLA - else - a = -0.5_DP * mu / energy - if (a < 0.0_DP) then - fac = -h2 / (mu * a) - if (fac > VSMALL) then - iorbit_type = HYPERBOLA - else - iorbit_type = PARABOLA - end if - else - iorbit_type = ELLIPSE - end if - end if - select case (iorbit_type) - case (ELLIPSE) - fac = 1.0_DP - h2 / (mu * a) - if (fac > VSMALL) e = sqrt(fac) - q = a * (1.0_DP - e) - case (PARABOLA) - a = 0.5_DP * h2 / mu - e = 1.0_DP - q = a - case (HYPERBOLA) - e = sqrt(1.0_DP + fac) - q = a * (1.0_DP - e) - end select - - return - - end procedure orbel_xv2aeq -end submodule s_orbel_xv2aeq diff --git a/src/orbel/orbel_xv2aqt.f90 b/src/orbel/orbel_xv2aqt.f90 deleted file mode 100644 index 3c8bf3f3e..000000000 --- a/src/orbel/orbel_xv2aqt.f90 +++ /dev/null @@ -1,100 +0,0 @@ -submodule (swiftest_classes) s_orbel_xv2aqt - use swiftest -contains - module procedure orbel_xv2aqt ! (mu, px, py, pz, vx, vy, vz, a, q, capm, tperi - !! author: David A. Minton - !! - !! Compute semimajor axis, pericentric distance, mean anomaly, and time to nearest pericenter passage from - !! relative Cartesian position and velocity - !! tperi > 0 means nearest pericenter passage is in the future - !! tperi < 0 means nearest pericenter passage is in the past - !! - !! Adapted from David E. Kaufmann's Swifter routine: orbel_xv2aqt.f90 - implicit none - integer(I4B) :: iorbit_type - real(DP) :: r, v2, h2, rdotv, energy, fac, w, face, cape, e, tmpf, capf, mm - real(DP), dimension(NDIM) :: hvec - - a = 0.0_DP - q = 0.0_DP - capm = 0.0_DP - tperi = 0.0_DP - r = sqrt(dot_product(x(:), x(:))) - v2 = dot_product(v(:), v(:)) - hvec(:) = x(:) .cross. v(:) - h2 = dot_product(hvec(:), hvec(:)) - if (h2 == 0.0_DP) return - rdotv = dot_product(x(:), v(:)) - energy = 0.5_DP * v2 - mu / r - if (abs(energy * r / mu) < sqrt(VSMALL)) then - iorbit_type = PARABOLA - else - a = -0.5_DP * mu / energy - if (a < 0.0_DP) then - fac = -h2 / (mu * a) - if (fac > VSMALL) then - iorbit_type = HYPERBOLA - else - iorbit_type = PARABOLA - end if - else - iorbit_type = ELLIPSE - end if - end if - select case (iorbit_type) - case (ELLIPSE) - fac = 1.0_DP - h2 / (mu * a) - if (fac > VSMALL) then - e = sqrt(fac) - cape = 0.0_DP - face = (a - r) / (a * e) - if (face < -1.0_DP) then - cape = PI - else if (face < 1.0_DP) then - cape = acos(face) - end if - if (rdotv < 0.0_DP) cape = TWOPI - cape - else - e = 0.0_DP - cape = 0.0_DP - end if - capm = cape - e * sin(cape) - q = a * (1.0_DP - e) - mm = sqrt(mu / a**3) - if (capm < PI) then - tperi = -1.0_DP * capm / mm - else - tperi = -1.0_DP * (capm - TWOPI) / mm - end if - case (PARABOLA) - a = 0.5_DP * h2 / mu - e = 1.0_DP - w = 0.0_DP - fac = 2 * a / r - 1.0_DP - if (fac < -1.0_DP) then - w = PI - else if (fac < 1.0_DP) then - w = acos(fac) - end if - if (rdotv < 0.0_DP) w = TWOPI - w - tmpf = tan(0.5_DP * w) - capm = tmpf*(1.0_DP + tmpf * tmpf / 3.0_DP) - q = a - mm = sqrt(0.5_DP * mu / q**3) - tperi = -1.0_DP * capm / mm - case (HYPERBOLA) - e = sqrt(1.0_DP + fac) - tmpf = (a - r) / (a * e) - if (tmpf < 1.0_DP) tmpf = 1.0_DP - capf = log(tmpf + sqrt(tmpf * tmpf - 1.0_DP)) - if (rdotv < 0.0_DP) capf = -capf - capm = e * sinh(capf) - capf - q = a * (1.0_DP - e) - mm = sqrt(-mu / a**3) - tperi = -1.0_DP * capm / mm - end select - - return - - end procedure orbel_xv2aqt -end submodule s_orbel_xv2aqt diff --git a/src/orbel/orbel_xv2el.f90 b/src/orbel/orbel_xv2el.f90 deleted file mode 100644 index 434925c7d..000000000 --- a/src/orbel/orbel_xv2el.f90 +++ /dev/null @@ -1,142 +0,0 @@ -submodule (swiftest_classes) s_orbel_xv2el - use swiftest -contains - - module procedure orbel_xv2el_vec - !! author: David A. Minton - !! - !! A wrapper method that converts all of the cartesian position and velocity vectors of a Swiftest body object to orbital elements. - implicit none - integer(I4B) :: i - - if (self%nbody == 0) return - call self%set_mu(cb) - !do concurrent (i = 1:self%nbody) - do i = 1, self%nbody - call orbel_xv2el(self%mu(i), self%xh(:, i), self%vh(:, i), self%a(i), self%e(i), self%inc(i), & - self%capom(i), self%omega(i), self%capm(i)) - end do - end procedure orbel_xv2el_vec - - pure subroutine orbel_xv2el(mu, x, v, a, e, inc, capom, omega, capm) - !! author: David A. Minton - !! - !! Compute osculating orbital elements from relative Cartesian position and velocity - !! All angular measures are returned in radians - !! If inclination < TINY, longitude of the ascending node is arbitrarily set to 0 - !! - !! If eccentricity < sqrt(TINY), argument of pericenter is arbitrarily set to 0 - !! - !! References: Danby, J. M. A. 1988. Fundamentals of Celestial Mechanics, (Willmann-Bell, Inc.), 201 - 206. - !! Fitzpatrick, P. M. 1970. Principles of Celestial Mechanics, (Academic Press), 69 - 73. - !! Roy, A. E. 1982. Orbital Motion, (Adam Hilger, Ltd.), 75 - 95 - !! - !! Adapted from David E. Kaufmann's Swifter routine: orbel_xv2el.f90 - !! Adapted from Martin Duncan's Swift routine orbel_xv2el.f - implicit none - real(DP), intent(in) :: mu - real(DP), dimension(:), intent(in) :: x, v - real(DP), intent(out) :: a, e, inc, capom, omega, capm - integer(I4B) :: iorbit_type - real(DP) :: r, v2, h2, h, rdotv, energy, fac, u, w, cw, sw, face, cape, tmpf, capf - real(DP), dimension(NDIM) :: hvec - - a = 0.0_DP - e = 0.0_DP - inc = 0.0_DP - capom = 0.0_DP - omega = 0.0_DP - capm = 0.0_DP - r = sqrt(dot_product(x(:), x(:))) - v2 = dot_product(v(:), v(:)) - hvec = x(:) .cross. v(:) - h2 = dot_product(hvec(:), hvec(:)) - h = sqrt(h2) - if (h2 == 0.0_DP) return - rdotv = dot_product(x(:), v(:)) - energy = 0.5_DP * v2 - mu / r - fac = hvec(3) / h - if (fac < -1.0_DP) then - inc = PI - else if (fac < 1.0_DP) then - inc = acos(fac) - end if - fac = sqrt(hvec(1)**2 + hvec(2)**2) / h - if (fac**2 < VSMALL) then - u = atan2(x(2), x(1)) - if (hvec(3) < 0.0_DP) u = -u - else - capom = atan2(hvec(1), -hvec(2)) - u = atan2(x(3) / sin(inc), x(1) * cos(capom) + x(2) * sin(capom)) - end if - if (capom < 0.0_DP) capom = capom + TWOPI - if (u < 0.0_DP) u = u + TWOPI - if (abs(energy * r / mu) < sqrt(VSMALL)) then - iorbit_type = parabola - else - a = -0.5_DP * mu / energy - if (a < 0.0_DP) then - fac = -h2 / (mu * a) - if (fac > VSMALL) then - iorbit_type = HYPERBOLA - else - iorbit_type = PARABOLA - end if - else - iorbit_type = ELLIPSE - end if - end if - select case (iorbit_type) - case (ELLIPSE) - fac = 1.0_DP - h2 / (mu * a) - if (fac > VSMALL) then - e = sqrt(fac) - cape = 0.0_DP - face = (a - r) / (a * e) - if (face < -1.0_DP) then - cape = PI - else if (face < 1.0_DP) then - cape = acos(face) - end if - if (rdotv < 0.0_DP) cape = TWOPI - cape - fac = 1.0_DP - e * cos(cape) - cw = (cos(cape) - e) / fac - sw = sqrt(1.0_DP - e**2) * sin(cape) / fac - w = atan2(sw, cw) - if (w < 0.0_DP) w = w + TWOPI - else - cape = u - w = u - end if - capm = cape - e * sin(cape) - case (PARABOLA) - a = 0.5_DP * h2 / mu - e = 1.0_DP - w = 0.0_DP - fac = 2 * a / r - 1.0_DP - if (fac < -1.0_DP) then - w = PI - else if (fac < 1.0_DP) then - w = acos(fac) - end if - if (rdotv < 0.0_DP) w = TWOPI - w - tmpf = tan(0.5_DP * w) - capm = tmpf * (1.0_DP + tmpf * tmpf / 3.0_DP) - case (HYPERBOLA) - e = sqrt(1.0_DP + fac) - tmpf = max((a - r) / (a * e), 1.0_DP) - capf = log(tmpf + sqrt(tmpf**2 - 1.0_DP)) - if (rdotv < 0.0_DP) capf = -capf - fac = e * cosh(capf) - 1.0_DP - cw = (e - cosh(capf)) / fac - sw = sqrt(e * e - 1.0_DP) * sinh(capf) / fac - w = atan2(sw, cw) - if (w < 0.0_DP) w = w + TWOPI - capm = e * sinh(capf) - capf - end select - omega = u - w - if (omega < 0.0_DP) omega = omega + TWOPI - - return - end subroutine orbel_xv2el -end submodule s_orbel_xv2el diff --git a/src/rmvs/rmvs_discard.f90 b/src/rmvs/rmvs_discard.f90 index 14613724e..bcdb9f902 100644 --- a/src/rmvs/rmvs_discard.f90 +++ b/src/rmvs/rmvs_discard.f90 @@ -1,6 +1,7 @@ submodule(rmvs_classes) s_rmvs_discard use swiftest contains + module subroutine rmvs_discard_tp(self, system, param) !! author: David A. Minton !! @@ -12,10 +13,12 @@ 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 + if (self%nbody == 0) return + associate(tp => self, ntp => self%nbody, pl => system%pl, t => param%t) do i = 1, ntp associate(iplperP => tp%plperP(i)) @@ -24,6 +27,7 @@ module subroutine rmvs_discard_tp(self, system, param) tp%status(i) = DISCARDED_PLQ write(*, *) "Particle ",tp%id(i)," q with respect to Planet ",pl%id(iplperP)," is too small at t = ",t tp%ldiscard(i) = .true. + tp%lmask(i) = .false. end if end if end associate @@ -33,4 +37,5 @@ module subroutine rmvs_discard_tp(self, system, param) end associate end subroutine rmvs_discard_tp + end submodule s_rmvs_discard \ No newline at end of file diff --git a/src/rmvs/rmvs_encounter_check.f90 b/src/rmvs/rmvs_encounter_check.f90 index bead4c21b..e4c441472 100644 --- a/src/rmvs/rmvs_encounter_check.f90 +++ b/src/rmvs/rmvs_encounter_check.f90 @@ -1,6 +1,7 @@ submodule (rmvs_classes) s_rmvs_chk use swiftest contains + module function rmvs_encounter_check_tp(self, system, dt) result(lencounter) !! author: David A. Minton !! @@ -22,6 +23,8 @@ module function rmvs_encounter_check_tp(self, system, dt) result(lencounter) real(DP), dimension(system%pl%nbody) :: r2crit logical :: lflag + if (self%nbody == 0) return + select type(pl => system%pl) class is (rmvs_pl) associate(tp => self, ntp => self%nbody, npl => pl%nbody, rts => system%rts) @@ -29,7 +32,7 @@ module function rmvs_encounter_check_tp(self, system, dt) result(lencounter) tp%plencP(:) = 0 do j = 1, npl do i = 1, ntp - if ((tp%status(i) /= ACTIVE).or.(tp%plencP(i) /= 0)) cycle + if ((.not.tp%lmask(i)).or.(tp%plencP(i) /= 0)) cycle xr(:) = tp%xh(:, i) - pl%xbeg(:, j) vr(:) = tp%vh(:, i) - pl%vbeg(:, j) r2 = dot_product(xr(:), xr(:)) @@ -46,7 +49,8 @@ module function rmvs_encounter_check_tp(self, system, dt) result(lencounter) return end function rmvs_encounter_check_tp - elemental function rmvs_chk_ind(r2, v2, vdotr, dt, r2crit) result(lflag) + + module elemental function rmvs_chk_ind(r2, v2, vdotr, dt, r2crit) result(lflag) !! author: David A. Minton !! !! Determine whether a test particle and planet are having or will have an encounter within the next time step @@ -77,6 +81,6 @@ elemental function rmvs_chk_ind(r2, v2, vdotr, dt, r2crit) result(lflag) end if return - end function rmvs_chk_ind + end submodule s_rmvs_chk diff --git a/src/rmvs/rmvs_getacch.f90 b/src/rmvs/rmvs_kick.f90 similarity index 69% rename from src/rmvs/rmvs_getacch.f90 rename to src/rmvs/rmvs_kick.f90 index 0ede99ab5..018ada8f3 100644 --- a/src/rmvs/rmvs_getacch.f90 +++ b/src/rmvs/rmvs_kick.f90 @@ -1,13 +1,14 @@ -submodule(rmvs_classes) s_rmvs_getacch +submodule(rmvs_classes) s_rmvs_kick use swiftest contains - module subroutine rmvs_getacch_tp(self, system, param, t, lbeg) + + module subroutine rmvs_kick_getacch_tp(self, system, param, t, lbeg) !! author: David A. Minton !! !! Compute the oblateness acceleration in the inner encounter region with planets !! - !! Performs a similar task as David E. Kaufmann's Swifter routine rmvs_getacch_tp.f90, but + !! Performs a similar task as David E. Kaufmann's Swifter routine rmvs_kick_getacch_tp.f90, but !! uses object polymorphism, and so is not directly adapted. implicit none ! Arguments @@ -15,13 +16,14 @@ module subroutine rmvs_getacch_tp(self, system, param, t, lbeg) class(swiftest_nbody_system), intent(inout) :: system !! Swiftest central body particle data structuree class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: t !! Current time - logical, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step + logical, intent(in) :: lbeg !! Logical flag that determines whether or not this is the beginning or end of the step ! Internals - type(swiftest_parameters) :: param_planetocen + class(swiftest_parameters), allocatable :: param_planetocen real(DP), dimension(:, :), allocatable :: xh_original real(DP) :: GMcb_original integer(I4B) :: i - real(DP), dimension(:, :), allocatable :: xhp + + if (self%nbody == 0) return associate(tp => self, ntp => self%nbody, ipleP => self%ipleP, inner_index => self%index) select type(system) @@ -33,55 +35,55 @@ module subroutine rmvs_getacch_tp(self, system, param, t, lbeg) select type (cb => system%cb) class is (rmvs_cb) associate(xpc => pl%xh, xpct => self%xh, apct => self%ah, system_planetocen => system) + system_planetocen%lbeg = lbeg - if (present(lbeg)) system_planetocen%lbeg = lbeg - - if (system_planetocen%lbeg) then - allocate(xhp, source=pl%xbeg) - else - allocate(xhp, source=pl%xend) - end if - + ! Save the original heliocentric position for later allocate(xh_original, source=tp%xh) - param_planetocen = param - ! Temporarily turn off the heliocentric-dependent acceleration terms during an inner encounter + + ! Temporarily turn off the heliocentric-dependent acceleration terms during an inner encounter using a copy of the parameter list with all of the heliocentric-specific acceleration terms turned off + allocate(param_planetocen, source=param) param_planetocen%loblatecb = .false. param_planetocen%lextra_force = .false. param_planetocen%lgr = .false. - ! Now compute the planetocentric values of acceleration - call whm_getacch_tp(tp, system_planetocen, param_planetocen, t) + + ! Compute the planetocentric values of acceleration + call whm_kick_getacch_tp(tp, system_planetocen, param_planetocen, t, lbeg) ! Now compute any heliocentric values of acceleration if (tp%lfirst) then - do i = 1, ntp + do concurrent(i = 1:ntp, tp%lmask(i)) tp%xheliocentric(:,i) = tp%xh(:,i) + cb%inner(inner_index - 1)%x(:,1) end do else - do i = 1, ntp + do concurrent(i = 1:ntp, tp%lmask(i)) tp%xheliocentric(:,i) = tp%xh(:,i) + cb%inner(inner_index )%x(:,1) end do end if + ! Swap the planetocentric and heliocentric position vectors and central body masses tp%xh(:,:) = tp%xheliocentric(:,:) GMcb_original = cb%Gmass cb%Gmass = tp%cb_heliocentric%Gmass + + ! If the heliocentric-specifc acceleration terms are requested, compute those now if (param%loblatecb) call tp%accel_obl(system_planetocen) - if (param%lextra_force) call tp%accel_user(system_planetocen, param, t) + if (param%lextra_force) call tp%accel_user(system_planetocen, param, t, lbeg) if (param%lgr) call tp%accel_gr(param) + + ! Put everything back the way we found it tp%xh(:,:) = xh_original(:,:) cb%Gmass = GMcb_original + end associate end select end select else ! Not a close encounter, so just proceded with the standard WHM method - call whm_getacch_tp(tp, system, param, t, lbeg) + call whm_kick_getacch_tp(tp, system, param, t, lbeg) end if end select - end associate return + end subroutine rmvs_kick_getacch_tp - end subroutine rmvs_getacch_tp - -end submodule s_rmvs_getacch \ No newline at end of file +end submodule s_rmvs_kick \ No newline at end of file diff --git a/src/rmvs/rmvs_setup.f90 b/src/rmvs/rmvs_setup.f90 index 4607bfbb0..92043e0fe 100644 --- a/src/rmvs/rmvs_setup.f90 +++ b/src/rmvs/rmvs_setup.f90 @@ -1,7 +1,8 @@ submodule(rmvs_classes) s_rmvs_setup use swiftest contains - module subroutine rmvs_setup_pl(self,n) + + module subroutine rmvs_setup_pl(self, n, param) !! author: David A. Minton !! !! Allocate RMVS test particle structure @@ -9,14 +10,15 @@ module subroutine rmvs_setup_pl(self,n) !! Equivalent in functionality to David E. Kaufmann's Swifter routine rmvs_setup.f90 implicit none ! Arguments - class(rmvs_pl), intent(inout) :: self !! RMVS test particle object - integer(I4B), intent(in) :: n !! Number of massive bodies to allocate + class(rmvs_pl), intent(inout) :: self !! RMVS test particle 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,j + integer(I4B) :: i,j !> Call allocation method for parent class associate(pl => self) - call whm_setup_pl(pl, n) + call whm_setup_pl(pl, n, param) if (n <= 0) return allocate(pl%outer(0:NTENC)) @@ -34,17 +36,28 @@ module subroutine rmvs_setup_pl(self,n) do i = 0, NTPHENC allocate(pl%inner(i)%x(NDIM, n)) allocate(pl%inner(i)%v(NDIM, n)) - allocate(pl%inner(i)%aobl(NDIM, n)) pl%inner(i)%x(:,:) = 0.0_DP pl%inner(i)%v(:,:) = 0.0_DP - pl%inner(i)%aobl(:,:) = 0.0_DP end do + if (param%loblatecb) then + do i = 0, NTPHENC + allocate(pl%inner(i)%aobl(NDIM, n)) + pl%inner(i)%aobl(:,:) = 0.0_DP + end do + end if + if (param%ltides) then + do i = 0, NTPHENC + allocate(pl%inner(i)%atide(NDIM, n)) + pl%inner(i)%atide(:,:) = 0.0_DP + end do + end if end if end associate return end subroutine rmvs_setup_pl - module subroutine rmvs_setup_system(self, param) + + module subroutine rmvs_setup_initialize_system(self, param) !! author: David A. Minton !! !! Initialize an RMVS nbody system from files and sets up the planetocentric structures. @@ -62,7 +75,7 @@ module subroutine rmvs_setup_system(self, param) integer(I4B) :: i, j ! Call parent method - call whm_setup_system(self, param) + call whm_setup_initialize_system(self, param) ! Set up the pl-tp planetocentric encounter structures for pl and cb. The planetocentric tp structures are ! generated as necessary during close encounter steps. @@ -88,8 +101,9 @@ module subroutine rmvs_setup_system(self, param) class is (rmvs_pl) cbenci%lplanetocentric = .true. plenci%lplanetocentric = .true. - call plenci%setup(npl) + call plenci%setup(npl, param) plenci%status(:) = ACTIVE + plenci%lmask(:) = .true. ! plind stores the heliocentric index value of a planetocentric planet ! e.g. Consider an encounter with planet 3. ! Then the following will be the values of plind: @@ -113,10 +127,11 @@ module subroutine rmvs_setup_system(self, param) end select end select end select - - end subroutine rmvs_setup_system + return + end subroutine rmvs_setup_initialize_system + - module subroutine rmvs_setup_tp(self,n) + module subroutine rmvs_setup_tp(self, n, param) !! author: David A. Minton !! !! Allocate WHM test particle structure @@ -124,17 +139,24 @@ module subroutine rmvs_setup_tp(self,n) !! Equivalent in functionality to David E. Kaufmann's Swifter routine whm_setup.f90 implicit none ! Arguments - class(rmvs_tp), intent(inout) :: self !! RMVS test particle object - integer, intent(in) :: n !! Number of test particles to allocate + class(rmvs_tp), intent(inout) :: self !! RMVS test particle object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameter - !> Call allocation method for parent class - call whm_setup_tp(self, n) + !> Call allocation method for parent class. In this case, whm does not have its own setup method, so we use the base method for swiftest_tp + 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_spill_and_fill.f90 b/src/rmvs/rmvs_spill_and_fill.f90 deleted file mode 100644 index ae0ff563b..000000000 --- a/src/rmvs/rmvs_spill_and_fill.f90 +++ /dev/null @@ -1,138 +0,0 @@ -submodule(rmvs_classes) s_rmvs_spill_and_fill - use swiftest -contains - module subroutine rmvs_spill_pl(self, discards, lspill_list) - !! author: David A. Minton - !! - !! Move spilled (discarded) RMVS test particle structure from active list to discard list - !! - !! Adapted from David E. Kaufmann's Swifter routine discard_discard_spill.f90 - implicit none - ! Arguments - class(rmvs_pl), intent(inout) :: self !! Swiftest 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 - ! 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_spill_pl(keeps, discards, lspill_list) - class default - write(*,*) 'Error! spill method called for incompatible return type on rmvs_pl' - end select - end associate - - return - - end subroutine rmvs_spill_pl - - module subroutine rmvs_fill_pl(self, inserts, lfill_list) - !! author: David A. Minton - !! - !! Insert new RMVS massive body structure into an old one. - !! This is the inverse of a fill operation. - !! - implicit none - ! Arguments - class(rmvs_pl), intent(inout) :: self !! RMVS 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 - ! Internals - integer(I4B) :: i - - associate(keeps => self) - select type(inserts) - class is (rmvs_pl) - - keeps%nenc(:) = unpack(keeps%nenc(:), .not.lfill_list(:), keeps%nenc(:)) - keeps%nenc(:) = unpack(inserts%nenc(:), lfill_list(:), keeps%nenc(:)) - - call whm_fill_pl(keeps, inserts, lfill_list) - class default - write(*,*) 'Error! spill method called for incompatible return type on rmvs_pl' - end select - end associate - - return - - end subroutine rmvs_fill_pl - - module subroutine rmvs_spill_tp(self, discards, lspill_list) - !! author: David A. Minton - !! - !! Move spilled (discarded) RMVS 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(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 - ! 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) - class default - write(*,*) 'Error! spill method called for incompatible return type on rmvs_tp' - end select - end associate - - return - - end subroutine rmvs_spill_tp - - module subroutine rmvs_fill_tp(self, inserts, lfill_list) - !! author: David A. Minton - !! - !! Insert new RMVS test particle structure into an old one. - !! This is the inverse of a fill operation. - !! - implicit none - ! Arguments - class(rmvs_tp), intent(inout) :: self !! RMVS 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 - - 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(:)) - - 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) - class default - write(*,*) 'Error! fill method called for incompatible return type on rmvs_tp' - end select - end associate - - return - - end subroutine rmvs_fill_tp - -end submodule s_rmvs_spill_and_fill diff --git a/src/rmvs/rmvs_step.f90 b/src/rmvs/rmvs_step.f90 index bb6c0d843..0385aeecc 100644 --- a/src/rmvs/rmvs_step.f90 +++ b/src/rmvs/rmvs_step.f90 @@ -1,6 +1,7 @@ submodule(rmvs_classes) s_rmvs_step use swiftest contains + module subroutine rmvs_step_system(self, param, t, dt) !! author: David A. Minton !! @@ -35,7 +36,6 @@ module subroutine rmvs_step_system(self, param, t, dt) lencounter = tp%encounter_check(system, dt) if (lencounter) then lfirstpl = pl%lfirst - lfirsttp = tp%lfirst pl%outer(0)%x(:,:) = xbeg(:,:) pl%outer(0)%v(:,:) = vbeg(:,:) call pl%step(system, param, t, dt) @@ -43,13 +43,14 @@ module subroutine rmvs_step_system(self, param, t, dt) pl%outer(NTENC)%v(:,:) = pl%vh(:,:) call rmvs_interp_out(cb, pl, dt) call rmvs_step_out(cb, pl, tp, system, param, t, dt) - call tp%reverse_status() + tp%lmask(1:ntp) = .not. tp%lmask(1:ntp) call pl%set_beg_end(xbeg = xbeg, xend = xend) tp%lfirst = .true. call tp%step(system, param, t, dt) - where (tp%status(:) == INACTIVE) tp%status(:) = ACTIVE + tp%lmask(1:ntp) = .true. pl%lfirst = lfirstpl - tp%lfirst = lfirsttp + tp%lfirst = .true. + if (param%ltides) call system%step_spin(param, t, dt) else call whm_step_system(system, param, t, dt) end if @@ -58,9 +59,9 @@ module subroutine rmvs_step_system(self, param, t, dt) end select end select return - end subroutine rmvs_step_system + subroutine rmvs_interp_out(cb, pl, dt) !! author: David A. Minton !! @@ -137,9 +138,9 @@ subroutine rmvs_interp_out(cb, pl, dt) end associate return - end subroutine rmvs_interp_out + subroutine rmvs_step_out(cb, pl, tp, system, param, t, dt) !! author: David A. Minton !! @@ -165,7 +166,7 @@ subroutine rmvs_step_out(cb, pl, tp, system, param, t, dt) associate(npl => pl%nbody, ntp => tp%nbody) dto = dt / NTENC where(tp%plencP(:) == 0) - tp%status(:) = INACTIVE + tp%lmask(:) = .false. elsewhere tp%lperi(:) = .false. end where @@ -175,7 +176,7 @@ subroutine rmvs_step_out(cb, pl, tp, system, param, t, dt) vbeg = pl%outer(outer_index - 1)%v(:, :), & xend = pl%outer(outer_index )%x(:, :)) system%rts = RHPSCALE - lencounter = tp%encounter_check(system, dt) + lencounter = tp%encounter_check(system, dto) if (lencounter) then ! Interpolate planets in inner encounter region call rmvs_interp_in(cb, pl, system, param, dto, outer_index) @@ -190,14 +191,18 @@ subroutine rmvs_step_out(cb, pl, tp, system, param, t, dt) end if do j = 1, npl if (pl%nenc(j) == 0) cycle - where((tp%plencP(:) == j) .and. (tp%status(:) == INACTIVE)) tp%status(:) = ACTIVE + tp%lfirst = .true. + where((tp%plencP(:) == j) .and. (.not.tp%lmask(:))) + tp%lmask(:) = .true. + end where end do end do end associate - return + return end subroutine rmvs_step_out + subroutine rmvs_interp_in(cb, pl, system, param, dt, outer_index) !! author: David A. Minton !! @@ -239,17 +244,24 @@ subroutine rmvs_interp_in(cb, pl, system, param, dt, outer_index) GMcb(:) = cb%Gmass xtmp(:, :) = pl%inner(0)%x(:, :) vtmp(:, :) = pl%inner(0)%v(:, :) - if (param%loblatecb) then - allocate(xh_original,source=pl%xh) + + if ((param%loblatecb) .or. (param%ltides)) then + allocate(xh_original, source=pl%xh) pl%xh(:, :) = xtmp(:, :) ! Temporarily replace heliocentric position with inner substep values to calculate the oblateness terms + end if + if (param%loblatecb) then call pl%accel_obl(system) pl%inner(0)%aobl(:, :) = pl%aobl(:, :) ! Save the oblateness acceleration on the planet for this substep end if + if (param%ltides) then + call pl%accel_tides(system) + pl%inner(0)%atide(:, :) = pl%atide(:, :) ! Save the oblateness acceleration on the planet for this substep + end if do inner_index = 1, NTPHENC - 1 call drift_one(GMcb(1:npl), xtmp(1,1:npl), xtmp(2,1:npl), xtmp(3,1:npl), & - vtmp(1,1:npl), vtmp(2,1:npl), vtmp(3,1:npl), & - dti(1:npl), iflag(1:npl)) + vtmp(1,1:npl), vtmp(2,1:npl), vtmp(3,1:npl), & + dti(1:npl), iflag(1:npl)) if (any(iflag(1:npl) /= 0)) then do i = 1, npl if (iflag(i) /=0) then @@ -272,8 +284,8 @@ subroutine rmvs_interp_in(cb, pl, system, param, dt, outer_index) do inner_index = NTPHENC - 1, 1, -1 call drift_one(GMcb(1:npl), xtmp(1,1:npl), xtmp(2,1:npl), xtmp(3,1:npl), & - vtmp(1,1:npl), vtmp(2,1:npl), vtmp(3,1:npl), & - -dti(1:npl), iflag(1:npl)) + vtmp(1,1:npl), vtmp(2,1:npl), vtmp(3,1:npl), & + -dti(1:npl), iflag(1:npl)) if (any(iflag(1:npl) /= 0)) then do i = 1, npl if (iflag(i) /=0) then @@ -295,20 +307,29 @@ subroutine rmvs_interp_in(cb, pl, system, param, dt, outer_index) call pl%accel_obl(system) pl%inner(inner_index)%aobl(:, :) = pl%aobl(:, :) end if + if (param%ltides) then + call pl%accel_tides(system) + pl%inner(inner_index)%atide(:, :) = pl%atide(:, :) + end if end do if (param%loblatecb) then ! Calculate the final value of oblateness accelerations at the final inner substep pl%xh(:,:) = pl%inner(NTPHENC)%x(:, :) call pl%accel_obl(system) pl%inner(NTPHENC)%aobl(:, :) = pl%aobl(:, :) - ! Put the planet positions back into place - call move_alloc(xh_original, pl%xh) end if + if (param%ltides) then + call pl%accel_tides(system) + pl%inner(NTPHENC)%atide(:, :) = pl%atide(:, :) + end if + ! Put the planet positions back into place + if (allocated(xh_original)) call move_alloc(xh_original, pl%xh) end associate return end subroutine rmvs_interp_in + subroutine rmvs_step_in(cb, pl, tp, param, outer_time, dto) !! author: David A. Minton !! @@ -331,7 +352,7 @@ subroutine rmvs_step_in(cb, pl, tp, param, outer_time, dto) associate(npl => pl%nbody) dti = dto / NTPHENC - call rmvs_make_planetocentric(cb, pl, tp) + call rmvs_make_planetocentric(param, cb, pl, tp) do i = 1, npl if (pl%nenc(i) == 0) cycle select type(planetocen_system => pl%planetocentric(i)) @@ -353,8 +374,16 @@ subroutine rmvs_step_in(cb, pl, tp, param, outer_time, dto) plenci%xh(:,:) = plenci%inner(inner_index - 1)%x(:,:) call plenci%set_beg_end(xbeg = plenci%inner(inner_index - 1)%x, & xend = plenci%inner(inner_index)%x) - call cbenci%set_beg_end(aoblbeg = cbenci%inner(inner_index - 1)%aobl(:, 1), & - aoblend = cbenci%inner(inner_index )%aobl(:, 1)) + + if (param%loblatecb) then + cbenci%aoblbeg = cbenci%inner(inner_index - 1)%aobl(:, 1) + cbenci%aoblend = cbenci%inner(inner_index )%aobl(:, 1) + end if + if (param%ltides) then + cbenci%atidebeg = cbenci%inner(inner_index - 1)%atide(:, 1) + cbenci%atideend = cbenci%inner(inner_index )%atide(:, 1) + end if + call tpenci%step(planetocen_system, param, inner_time, dti) do j = 1, pl%nenc(i) tpenci%xheliocentric(:, j) = tpenci%xh(:, j) + pl%inner(inner_index)%x(:,i) @@ -362,7 +391,7 @@ subroutine rmvs_step_in(cb, pl, tp, param, outer_time, dto) inner_time = outer_time + j * dti call rmvs_peri_tp(tpenci, pl, inner_time, dti, .false., inner_index, i, param) end do - where(tpenci%status(:) == ACTIVE) tpenci%status(:) = INACTIVE + tpenci%lmask(:) = .false. end associate end select end select @@ -374,7 +403,8 @@ subroutine rmvs_step_in(cb, pl, tp, param, outer_time, dto) return end subroutine rmvs_step_in - subroutine rmvs_make_planetocentric(cb, pl, tp) + + subroutine rmvs_make_planetocentric(param, cb, pl, tp) !! author: David A. Minton !! !! When encounters are detected, this method will call the interpolation methods for the planets and @@ -383,13 +413,14 @@ subroutine rmvs_make_planetocentric(cb, pl, tp) !! implicit none ! Arguments + class(swiftest_parameters), intent(in) :: param !! Current run configuration paramete class(rmvs_cb), intent(inout) :: cb !! RMVS central body object class(rmvs_pl), intent(inout) :: pl !! RMVS massive body object class(rmvs_tp), intent(inout) :: tp !! RMVS test particle object ! Internals - integer(I4B) :: i, j, inner_index, ipc2hc - logical, dimension(:), allocatable :: encmask + integer(I4B) :: i, j, inner_index, ipc2hc + logical, dimension(:), allocatable :: encmask associate (npl => pl%nbody, ntp => tp%nbody) do i = 1, npl @@ -407,9 +438,10 @@ subroutine rmvs_make_planetocentric(cb, pl, tp) select type(tpenci => pl%planetocentric(i)%tp) class is (rmvs_tp) tpenci%lplanetocentric = .true. - call tpenci%setup(pl%nenc(i)) + call tpenci%setup(pl%nenc(i), param) 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(:)) @@ -425,15 +457,25 @@ subroutine rmvs_make_planetocentric(cb, pl, tp) do inner_index = 0, NTPHENC allocate(plenci%inner(inner_index)%x, mold=pl%inner(inner_index)%x) allocate(plenci%inner(inner_index)%v, mold=pl%inner(inner_index)%x) - allocate(plenci%inner(inner_index)%aobl, mold=pl%inner(inner_index)%aobl) allocate(cbenci%inner(inner_index)%x(NDIM,1)) allocate(cbenci%inner(inner_index)%v(NDIM,1)) - allocate(cbenci%inner(inner_index)%aobl(NDIM,1)) cbenci%inner(inner_index)%x(:,1) = pl%inner(inner_index)%x(:, i) cbenci%inner(inner_index)%v(:,1) = pl%inner(inner_index)%v(:, i) - cbenci%inner(inner_index)%aobl(:,1) = pl%inner(inner_index)%aobl(:, i) plenci%inner(inner_index)%x(:,1) = -cbenci%inner(inner_index)%x(:,1) plenci%inner(inner_index)%v(:,1) = -cbenci%inner(inner_index)%v(:,1) + + if (param%loblatecb) then + allocate(plenci%inner(inner_index)%aobl, mold=pl%inner(inner_index)%aobl) + allocate(cbenci%inner(inner_index)%aobl(NDIM,1)) + cbenci%inner(inner_index)%aobl(:,1) = pl%inner(inner_index)%aobl(:, i) + end if + + if (param%ltides) then + allocate(plenci%inner(inner_index)%atide, mold=pl%inner(inner_index)%atide) + allocate(cbenci%inner(inner_index)%atide(NDIM,1)) + cbenci%inner(inner_index)%atide(:,1) = pl%inner(inner_index)%atide(:, i) + end if + do j = 2, npl ipc2hc = plenci%plind(j) plenci%inner(inner_index)%x(:,j) = pl%inner(inner_index)%x(:, ipc2hc) - cbenci%inner(inner_index)%x(:,1) @@ -446,9 +488,11 @@ subroutine rmvs_make_planetocentric(cb, pl, tp) end select end do end associate + return end subroutine rmvs_make_planetocentric + subroutine rmvs_peri_tp(tp, pl, t, dt, lfirst, inner_index, ipleP, param) !! author: David A. Minton !! @@ -458,25 +502,25 @@ subroutine rmvs_peri_tp(tp, pl, t, dt, lfirst, inner_index, ipleP, param) !! Adapted from David E. Kaufmann's Swifter routine rmvs_peri.f90 implicit none ! Arguments - class(rmvs_tp), intent(inout) :: tp !! RMVS test particle object (planetocentric) - class(rmvs_pl), intent(inout) :: pl !! RMVS massive body object (heliocentric) - real(DP), intent(in) :: t !! current time - real(DP), intent(in) :: dt !! step size - logical, intent(in) :: lfirst !! Logical flag indicating whether current invocation is the first - integer(I4B), intent(in) :: inner_index !! Outer substep number within current set - integer(I4B), intent(in) :: ipleP !! index of RMVS planet being closely encountered + class(rmvs_tp), intent(inout) :: tp !! RMVS test particle object (planetocentric) + class(rmvs_pl), intent(inout) :: pl !! RMVS massive body object (heliocentric) + real(DP), intent(in) :: t !! current time + real(DP), intent(in) :: dt !! step size + logical, intent(in) :: lfirst !! Logical flag indicating whether current invocation is the first + integer(I4B), intent(in) :: inner_index !! Outer substep number within current set + integer(I4B), intent(in) :: ipleP !! index of RMVS planet being closely encountered class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters ! Internals - integer(I4B) :: i, id1, id2 - real(DP) :: r2, mu, rhill2, vdotr, a, peri, capm, tperi, rpl - real(DP), dimension(NDIM) :: xh1, xh2, vh1, vh2 + integer(I4B) :: i, id1, id2 + real(DP) :: r2, mu, rhill2, vdotr, a, peri, capm, tperi, rpl + real(DP), dimension(NDIM) :: xh1, xh2, vh1, vh2 rhill2 = pl%rhill(ipleP)**2 mu = pl%Gmass(ipleP) associate(nenc => tp%nbody, xpc => tp%xh, vpc => tp%vh) if (lfirst) then do i = 1, nenc - if (tp%status(i) == ACTIVE) then + if (tp%lmask(i)) then vdotr = dot_product(xpc(:, i), vpc(:, i)) if (vdotr > 0.0_DP) then tp%isperi(i) = 1 @@ -487,7 +531,7 @@ subroutine rmvs_peri_tp(tp, pl, t, dt, lfirst, inner_index, ipleP, param) end do else do i = 1, nenc - if (tp%status(i) == ACTIVE) then + if (tp%lmask(i)) then vdotr = dot_product(xpc(:, i), vpc(:, i)) if (tp%isperi(i) == -1) then if (vdotr >= 0.0_DP) then @@ -495,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) @@ -504,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 @@ -532,6 +576,7 @@ subroutine rmvs_peri_tp(tp, pl, t, dt, lfirst, inner_index, ipleP, param) end subroutine rmvs_peri_tp + subroutine rmvs_end_planetocentric(pl, tp) !! author: David A. Minton !! @@ -539,8 +584,8 @@ subroutine rmvs_end_planetocentric(pl, tp) !! implicit none ! Arguments - class(rmvs_pl), intent(inout) :: pl !! RMVS massive body object - class(rmvs_tp), intent(inout) :: tp !! RMVS test particle objec + class(rmvs_pl), intent(inout) :: pl !! RMVS massive body object + class(rmvs_tp), intent(inout) :: tp !! RMVS test particle objec ! Internals integer(I4B) :: i, j, inner_index integer(I4B), dimension(:), allocatable :: tpind @@ -565,6 +610,7 @@ subroutine rmvs_end_planetocentric(pl, tp) ! Copy the results of the integration back over and shift back to heliocentric reference tp%status(tpind(1:pl%nenc(i))) = tpenci%status(1:pl%nenc(i)) + tp%lmask(tpind(1:pl%nenc(i))) = tpenci%lmask(1:pl%nenc(i)) do j = 1, NDIM tp%xh(j, tpind(1:pl%nenc(i))) = tpenci%xh(j,1:pl%nenc(i)) + pl%inner(NTPHENC)%x(j, i) tp%vh(j, tpind(1:pl%nenc(i))) = tpenci%vh(j,1:pl%nenc(i)) + pl%inner(NTPHENC)%v(j, i) @@ -576,7 +622,8 @@ subroutine rmvs_end_planetocentric(pl, tp) do inner_index = 0, NTPHENC deallocate(plenci%inner(inner_index)%x) deallocate(plenci%inner(inner_index)%v) - deallocate(plenci%inner(inner_index)%aobl) + if (allocated(plenci%inner(inner_index)%aobl)) deallocate(plenci%inner(inner_index)%aobl) + if (allocated(plenci%inner(inner_index)%atide)) deallocate(plenci%inner(inner_index)%atide) end do end select end select diff --git a/src/rmvs/rmvs_util.f90 b/src/rmvs/rmvs_util.f90 new file mode 100644 index 000000000..9f9cf0037 --- /dev/null +++ b/src/rmvs/rmvs_util.f90 @@ -0,0 +1,380 @@ +submodule(rmvs_classes) s_rmvs_util + 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 + !! + !! Insert new RMVS massive body structure into an old one. + !! This is the inverse of a fill operation. + !! + implicit none + ! Arguments + class(rmvs_pl), intent(inout) :: self !! RMVS 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 + ! Internals + integer(I4B) :: i + + 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) + + call whm_util_fill_pl(keeps, inserts, lfill_list) + class default + 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 + + return + end subroutine rmvs_util_fill_pl + + + module subroutine rmvs_util_fill_tp(self, inserts, lfill_list) + !! author: David A. Minton + !! + !! Insert new RMVS test particle structure into an old one. + !! This is the inverse of a fill operation. + !! + implicit none + ! Arguments + class(rmvs_tp), intent(inout) :: self !! RMVS 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 (rmvs_tp) + 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) + + 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(*,*) "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 + !! + !! Sort a RMVS massive body object in-place. + !! sortby is a string indicating which array component to sort. + implicit none + ! Arguments + class(rmvs_pl), intent(inout) :: self !! RMVS 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 + ! Internals + integer(I4B), dimension(self%nbody) :: ind + integer(I4B) :: direction + + if (ascending) then + direction = 1 + else + direction = -1 + end if + + associate(pl => self, npl => self%nbody) + select case(sortby) + case("nenc") + call util_sort(direction * pl%nenc(1:npl), ind(1:npl)) + case("tpenc1P") + call util_sort(direction * pl%tpenc1P(1:npl), ind(1:npl)) + case("plind") + call util_sort(direction * pl%plind(1:npl), ind(1:npl)) + case("outer", "inner", "planetocentric", "lplanetocentric") + write(*,*) 'Cannot sort by ' // trim(adjustl(sortby)) // '. Component not sortable!' + case default ! Look for components in the parent class + call whm_util_sort_pl(pl, sortby, ascending) + return + end select + + call pl%rearrange(ind) + + end associate + return + end subroutine rmvs_util_sort_pl + + + module subroutine rmvs_util_sort_tp(self, sortby, ascending) + !! author: David A. Minton + !! + !! Sort a RMVS test particle object in-place. + !! sortby is a string indicating which array component to sort. + implicit none + ! Arguments + class(rmvs_tp), intent(inout) :: self !! RMVS test particle 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 + ! Internals + integer(I4B), dimension(self%nbody) :: ind + integer(I4B) :: direction + + if (ascending) then + direction = 1 + else + direction = -1 + end if + + associate(tp => self, ntp => self%nbody) + select case(sortby) + case("plperP") + call util_sort(direction * tp%plperP(1:ntp), ind(1:ntp)) + case("plencP") + call util_sort(direction * tp%plencP(1:ntp), ind(1:ntp)) + case("lperi", "cb_heliocentric", "xheliocentric", "index", "ipleP", "lplanetocentric") + write(*,*) 'Cannot sort by ' // trim(adjustl(sortby)) // '. Component not sortable!' + case default ! Look for components in the parent class (*NOTE whm_tp does not need its own sort method, so we go straight to the swiftest_tp method) + call util_sort_tp(tp, sortby, ascending) + return + end select + + call tp%rearrange(ind) + + end associate + return + end subroutine rmvs_util_sort_tp + + module subroutine rmvs_util_sort_rearrange_pl(self, ind) + !! author: David A. Minton + !! + !! Rearrange RMVS massive body structure in-place from an index list. + !! This is a helper utility used to make polymorphic sorting work on Swiftest structures. + implicit none + ! Arguments + class(rmvs_pl), intent(inout) :: self !! RMVS 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) + ! Internals + class(rmvs_pl), allocatable :: pl_sorted !! Temporary holder for sorted body + integer(I4B) :: i + + if (self%nbody == 0) return + + associate(pl => self, npl => self%nbody) + call util_sort_rearrange_pl(pl,ind) + allocate(pl_sorted, source=self) + 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 + + return + end subroutine rmvs_util_sort_rearrange_pl + + + module subroutine rmvs_util_sort_rearrange_tp(self, ind) + !! author: David A. Minton + !! + !! Rearrange RMVS test particle object in-place from an index list. + !! This is a helper utility used to make polymorphic sorting work on Swiftest structures. + implicit none + ! Arguments + class(rmvs_tp), intent(inout) :: self !! RMVS test particle 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) + ! Internals + class(rmvs_tp), allocatable :: tp_sorted !! Temporary holder for sorted body + + if (self%nbody == 0) return + + associate(tp => self, ntp => self%nbody) + call util_sort_rearrange_tp(tp,ind) + allocate(tp_sorted, source=self) + 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 + + return + end subroutine rmvs_util_sort_rearrange_tp + + + 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 + !! + !! 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 + 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) + 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(*,*) "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 + + return + end subroutine rmvs_util_spill_pl + + + 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 + !! + !! Adapted from David E. Kaufmann's Swifter routine whm_discard_spill.f90 + implicit none + ! Arguments + 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) + 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(*,*) "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 + + return + end subroutine rmvs_util_spill_tp + +end submodule s_rmvs_util diff --git a/src/setup/setup.f90 b/src/setup/setup.f90 index 402ef62a4..6cba6d27b 100644 --- a/src/setup/setup.f90 +++ b/src/setup/setup.f90 @@ -1,6 +1,7 @@ submodule (swiftest_classes) s_setup use swiftest contains + module subroutine setup_construct_system(system, param) !! author: David A. Minton !! @@ -9,7 +10,7 @@ module subroutine setup_construct_system(system, param) implicit none ! Arguments class(swiftest_nbody_system), allocatable, intent(inout) :: system !! Swiftest system object - type(swiftest_parameters), intent(in) :: param !! Swiftest parameters + class(swiftest_parameters), intent(in) :: param !! Swiftest parameters select case(param%integrator) case (BS) @@ -52,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 @@ -69,21 +69,105 @@ module subroutine setup_construct_system(system, param) return end subroutine setup_construct_system - module subroutine setup_body(self,n) + + 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 + !! + !! Wrapper method to initialize a basic Swiftest nbody system from files + !! + implicit none + ! Arguments + class(swiftest_nbody_system), intent(inout) :: self !! Swiftest system object + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters + + call self%cb%initialize(param) + call self%pl%initialize(param) + call self%tp%initialize(param) + call util_valid(self%pl, self%tp) + self%maxid = maxval([self%pl%id(:), self%tp%id(:)]) + call self%set_msys() + call self%pl%set_mu(self%cb) + call self%tp%set_mu(self%cb) + call self%pl%eucl_index() + if (.not.param%lrhill_present) call self%pl%set_rhill(self%cb) + !if (param%lfirstenergy) then + return + end subroutine setup_initialize_system + + + module subroutine setup_body(self, n, param) !! author: David A. Minton !! !! Constructor for base Swiftest particle class. Allocates space for all particles and !! initializes all components with a value. !! Note: Timing tests indicate that (NDIM, n) is more efficient than (NDIM, n) implicit none - class(swiftest_body), intent(inout) :: self !! Swiftest generic body object - integer, intent(in) :: n !! Number of particles to allocate space for + ! Arguments + class(swiftest_body), intent(inout) :: self !! Swiftest generic body object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameter self%nbody = n if (n <= 0) return self%lfirst = .true. - !write(*,*) 'Allocating the basic Swiftest particle' + 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)) @@ -93,83 +177,127 @@ module subroutine setup_body(self,n) allocate(self%xb(NDIM, n)) allocate(self%vb(NDIM, n)) allocate(self%ah(NDIM, n)) - allocate(self%aobl(NDIM, n)) - allocate(self%agr(NDIM, n)) allocate(self%ir3h(n)) - allocate(self%a(n)) - allocate(self%e(n)) - allocate(self%inc(n)) - allocate(self%capom(n)) - allocate(self%omega(n)) - allocate(self%capm(n)) allocate(self%mu(n)) + allocate(self%lmask(n)) self%id(:) = 0 self%name(:) = "UNNAMED" self%status(:) = INACTIVE + self%lmask(:) = .false. self%ldiscard(:) = .false. self%xh(:,:) = 0.0_DP self%vh(:,:) = 0.0_DP self%xb(:,:) = 0.0_DP self%vb(:,:) = 0.0_DP self%ah(:,:) = 0.0_DP - self%aobl(:,:) = 0.0_DP self%ir3h(:) = 0.0_DP - self%a(:) = 0.0_DP - self%e(:) = 0.0_DP - self%inc(:) = 0.0_DP - self%capom(:) = 0.0_DP - self%omega(:) = 0.0_DP - self%capm(:) = 0.0_DP - self%a(:) = 0.0_DP 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 + return end subroutine setup_body - module subroutine setup_pl(self,n) + + module subroutine setup_pl(self, n, param) !! author: David A. Minton !! !! Constructor for base Swiftest massive body class. Allocates space for all particles and !! initializes all components with a value. implicit none - class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object - integer, intent(in) :: n !! Number of massive bodies to allocate space for + ! Arguments + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameter !> Call allocation method for parent class !> The parent class here is the abstract swiftest_body class, so we can't use the type-bound procedure - call setup_body(self, n) + 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)) - allocate(self%radius(n)) - allocate(self%density(n)) self%mass(:) = 0.0_DP self%Gmass(:) = 0.0_DP self%rhill(:) = 0.0_DP - self%radius(:) = 0.0_DP - self%density(:) = 0.0_DP - self%num_comparisons = 0 + + 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 + self%density(:) = 1.0_DP + 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 + self%Ip(:,:) = 0.0_DP + 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)) + self%k2(:) = 0.0_DP + self%Q(:) = 0.0_DP + self%tlag(:) = 0.0_DP + end if + return end subroutine setup_pl + - module subroutine setup_tp(self, n) + module subroutine setup_tp(self, n, param) !! author: David A. Minton !! !! Constructor for base Swiftest test particle particle class. Allocates space for !! all particles and initializes all components with a value. implicit none - class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object - integer, intent(in) :: n !! Number of bodies to allocate space for + ! Arguments + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameter !> Call allocation method for parent class !> The parent class here is the abstract swiftest_body class, so we can't use the type-bound procedure - call setup_body(self, n) + 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 new file mode 100644 index 000000000..952d59709 --- /dev/null +++ b/src/symba/symba_collision.f90 @@ -0,0 +1,525 @@ +submodule (symba_classes) s_symba_collision + use swiftest +contains + + 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.f90 and symba_merge_tp.f90 + !! + !! Adapted from Hal Levison's Swift routine symba5_merge.f + implicit none + ! Arguments + class(symba_pltpenc), 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 + 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(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(nenc)) + lcollision(:) = .false. + + 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 + 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 + end select + end select + + return + end subroutine symba_collision_check_pltpenc + + + pure elemental function symba_collision_check_one(xr, yr, zr, vxr, vyr, vzr, Gmtot, rlim, dt, lvdotr) result(lcollision) + !! author: David A. Minton + !! + !! Check for a merger between a single pair of particles + !! + !! Adapted from David E. Kaufmann's Swifter routines symba_merge_tp.f90 and symba_merge_pl.f90 + !! + !! Adapted from Hal Levison's Swift routine symba5_merge.f + implicit none + ! Arguments + real(DP), intent(in) :: xr, yr, zr !! Relative position vector components + real(DP), intent(in) :: vxr, vyr, vzr !! Relative velocity vector components + real(DP), intent(in) :: Gmtot !! Sum of G*mass of colliding bodies + real(DP), intent(in) :: rlim !! Collision limit - Typically the sum of the radii of colliding bodies + real(DP), intent(in) :: dt !! Step size + logical, intent(in) :: lvdotr !! Logical flag indicating that these two bodies are approaching in the current substep + ! Result + logical :: lcollision !! Logical flag indicating whether these two bodies will collide or not + ! Internals + real(DP) :: r2, rlim2, a, e, q, vdotr, tcr2, dt2 + + r2 = xr**2 + yr**2 + zr**2 + rlim2 = rlim**2 + + if (r2 <= rlim2) then ! checks if bodies are actively colliding in this time step + lcollision = .true. + else ! if they are not actively colliding in this time step, checks if they are going to collide next time step based on velocities and q + lcollision = .false. + vdotr = xr * vxr + yr * vyr + zr * vzr + if (lvdotr .and. (vdotr > 0.0_DP)) then + tcr2 = r2 / (vxr**2 + vyr**2 + vzr**2) + dt2 = dt**2 + if (tcr2 <= dt2) then + call orbel_xv2aeq(Gmtot, [xr, yr, zr], [vxr, vyr, vzr], a, e, q) + lcollision = (q < rlim) + end if + end if + end if + + 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 + !! + !! When a single body is involved in more than one collision in a single step, it becomes part of a family. + !! The largest body involved in a multi-body collision is the "parent" and all bodies that collide with it are its "children," + !! including those that collide with the children. + !! + !! 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_pl), intent(inout) :: self !! SyMBA massive body object + integer(I4B), dimension(2), intent(in) :: idx !! Array holding the indices of the two bodies involved in the collision + ! Internals + integer(I4B) :: i, j, index_parent, index_child, p1, p2 + integer(I4B) :: nchild_inherit, nchild_orig, nchild_new + integer(I4B), dimension(:), allocatable :: temp + + associate(pl => self) + p1 = pl%kin(idx(1))%parent + p2 = pl%kin(idx(2))%parent + if (p1 == p2) return ! This is a collision between to children of a shared parent. We will ignore it. + + if (pl%Gmass(p1) > pl%Gmass(p2)) then + index_parent = p1 + index_child = p2 + else + index_parent = p2 + index_child = p1 + end if + + ! Expand the child array (or create it if necessary) and copy over the previous lists of children + nchild_orig = pl%kin(index_parent)%nchild + nchild_inherit = pl%kin(index_child)%nchild + nchild_new = nchild_orig + nchild_inherit + 1 + allocate(temp(nchild_new)) + + if (nchild_orig > 0) temp(1:nchild_orig) = pl%kin(index_parent)%child(1:nchild_orig) + ! Find out if the child body has any children of its own. The new parent wil inherit these children + if (nchild_inherit > 0) then + temp(nchild_orig+1:nchild_orig+nchild_inherit) = pl%kin(index_child)%child(1:nchild_inherit) + do i = 1, nchild_inherit + j = pl%kin(index_child)%child(i) + ! Set the childrens' parent to the new parent + pl%kin(j)%parent = index_parent + end do + end if + if (allocated(pl%kin(index_child)%child)) deallocate(pl%kin(index_child)%child) + pl%kin(index_child)%nchild = 0 + ! Add the new child to its parent + pl%kin(index_child)%parent = index_parent + temp(nchild_new) = index_child + ! Save the new child array to the parent + pl%kin(index_parent)%nchild = nchild_new + call move_alloc(from=temp, to=pl%kin(index_parent)%child) + end associate + + 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 + ! 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, jtarg, jproj, regime + real(DP), dimension(2) :: radius_si, mass_si, density_si + real(DP) :: mtiny_si, Mcb_si + real(DP), dimension(NDIM) :: x1_si, v1_si, x2_si, v2_si + real(DP) :: mlr, mslr, mtot, dentot, msys, msys_new, Qloss, impact_parameter + integer(I4B), parameter :: NRES = 3 !! Number of collisional product results + real(DP), dimension(NRES) :: mass_res + + associate(plpl_collisions => self, ncollisions => self%nenc, idx1 => self%index1, idx2 => self%index2, cb => system%cb) + 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 + + ! Convert all quantities to SI units and determine which of the pair is the projectile vs. target before sending them + ! to symba_regime + if (mass(1) > mass(2)) then + jtarg = 1 + jproj = 2 + else + jtarg = 2 + jproj = 1 + end if + mass_si(:) = (mass(:)) * param%MU2KG !! The collective mass of the parent and its children + radius_si(:) = radius(:) * param%DU2M !! The collective radius of the parent and its children + x1_si(:) = plpl_collisions%x1(:,i) * param%DU2M !! The position of the parent from inside the step (at collision) + v1_si(:) = plpl_collisions%v1(:,i) * param%DU2M / param%TU2S !! The velocity of the parent from inside the step (at collision) + x2_si(:) = plpl_collisions%x2(:,i) * param%DU2M !! The position of the parent from inside the step (at collision) + v2_si(:) = plpl_collisions%v2(:,i) * param%DU2M / param%TU2S !! The velocity of the parent from inside the step (at collision) + density_si(:) = mass_si(:) / (4.0_DP / 3._DP * PI * radius_si(:)**3) !! The collective density of the parent and its children + Mcb_si = cb%mass * param%MU2KG + mtiny_si = (param%MTINY / param%GU) * param%MU2KG + + mass_res(:) = 0.0_DP + + mtot = sum(mass_si(:)) + dentot = sum(mass_si(:) * density_si(:)) / mtot + + !! Use the positions and velocities of the parents from indside the step (at collision) to calculate the collisional regime + call fragmentation_regime(Mcb_si, mass_si(jtarg), mass_si(jproj), radius_si(jtarg), radius_si(jproj), x1_si(:), x2_si(:),& + v1_si(:), v2_si(:), density_si(jtarg), density_si(jproj), regime, mlr, mslr, mtiny_si, Qloss) + + mass_res(1) = min(max(mlr, 0.0_DP), mtot) + mass_res(2) = min(max(mslr, 0.0_DP), mtot) + mass_res(3) = min(max(mtot - mlr - mslr, 0.0_DP), mtot) + mass_res(:) = (mass_res(:) / param%MU2KG) * param%GU + Qloss = Qloss * (param%GU / param%MU2KG) * (param%TU2S / param%DU2M)**2 + + select case (regime) + case (COLLRESOLVE_REGIME_DISRUPTION) + status = symba_fragmentation_casedisruption(system, param, family, x, v, mass, radius, L_spin, Ip, mass_res, Qloss) + case (COLLRESOLVE_REGIME_SUPERCATASTROPHIC) + status = symba_fragmentation_casesupercatastrophic(system, param, family, x, v, mass, radius, L_spin, Ip, mass_res, Qloss) + case (COLLRESOLVE_REGIME_HIT_AND_RUN) + status = symba_fragmentation_casehitandrun(system, param, family, x, v, mass, radius, L_spin, Ip, mass_res, Qloss) + case (COLLRESOLVE_REGIME_MERGE, COLLRESOLVE_REGIME_GRAZE_AND_MERGE) + status = symba_fragmentation_casemerge(system, param, family, x, v, mass, radius, L_spin, Ip) + case default + write(*,*) "Error in symba_collision, unrecognized collision regime" + call util_exit(FAILURE) + end select + end do + end select + end associate + + 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 8bafdb2b5..5f6d3926a 100644 --- a/src/symba/symba_discard.f90 +++ b/src/symba/symba_discard.f90 @@ -2,24 +2,322 @@ use swiftest contains - module subroutine symba_discard_pl(self, system, param) + 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) :: self !! SyMBA test particle object + 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 - + 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_pl + 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(:) + 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 + - module subroutine symba_discard_tp(self, system, param) + 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_tp), intent(inout) :: self !! SyMBA test particle object + 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(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_tp + end subroutine symba_discard_pl end submodule s_symba_discard \ No newline at end of file diff --git a/src/symba/symba_drift.f90 b/src/symba/symba_drift.f90 new file mode 100644 index 000000000..c4efee05f --- /dev/null +++ b/src/symba/symba_drift.f90 @@ -0,0 +1,52 @@ + submodule (symba_classes) s_symba_drift + use swiftest +contains + + module subroutine symba_drift_pl(self, system, param, dt) + !! author: David A. Minton + !! + !! Wrapper function used to call the body drift routine from a symba_pl structure + implicit none + ! Arguments + class(symba_pl), intent(inout) :: self !! Helio massive body object + 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) :: dt !! Stepsize + + if (self%nbody == 0) return + + select type(system) + class is (symba_nbody_system) + self%lmask(:) = self%status(:) /= INACTIVE .and. self%levelg(:) == system%irec + call helio_drift_body(self, system, param, dt) + self%lmask(:) = self%status(:) /= INACTIVE + end select + + return + end subroutine symba_drift_pl + + + module subroutine symba_drift_tp(self, system, param, dt) + !! author: David A. Minton + !! + !! Wrapper function used to call the body drift routine from a symba_pl structure + implicit none + ! Arguments + class(symba_tp), intent(inout) :: self !! Helio massive body object + 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) :: dt !! Stepsize + + if (self%nbody == 0) return + + select type(system) + class is (symba_nbody_system) + self%lmask(:) = self%status(:) /= INACTIVE .and. self%levelg(:) == system%irec + call helio_drift_body(self, system, param, dt) + self%lmask(:) = self%status(:) /= INACTIVE + end select + + return + end subroutine symba_drift_tp + +end submodule s_symba_drift diff --git a/src/symba/symba_encounter_check.f90 b/src/symba/symba_encounter_check.f90 index ce4b53dff..808ee2347 100644 --- a/src/symba/symba_encounter_check.f90 +++ b/src/symba/symba_encounter_check.f90 @@ -1,30 +1,223 @@ submodule (symba_classes) s_symba_encounter_check use swiftest contains - module function symba_encounter_check_pl(self, system, dt) result(lencounter) + + module function symba_encounter_check_pl(self, system, dt, irec) result(lany_encounter) + !! author: David A. Minton + !! + !! Check for an encounter between massive bodies. + !! implicit none ! Arguments - class(symba_pl), intent(inout) :: self !! SyMBA test particle object - class(symba_nbody_system), intent(inout) :: system !! SyMBA nbody system object - real(DP), intent(in) :: dt !! step size + class(symba_pl), intent(inout) :: self !! SyMBA test particle 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 ! Result - logical :: lencounter !! Returns true if there is at least one close encounter + logical :: lany_encounter !! Returns true if there is at least one close encounter + ! Internals + integer(I8B) :: k + integer(I4B) :: nenc + real(DP), dimension(NDIM) :: xr, vr + logical, dimension(:), allocatable :: lencounter, loc_lvdotr + + if (self%nbody == 0) return + + associate(pl => self, npl => self%nbody, nplpl => self%nplpl) + allocate(lencounter(nplpl), loc_lvdotr(nplpl)) + lencounter(:) = .false. + + do k = 1, nplpl + associate(i => pl%k_plpl(1, k), j => pl%k_plpl(2, k)) + xr(:) = pl%xh(:, j) - pl%xh(:, i) + vr(:) = pl%vh(:, j) - pl%vh(:, i) + call symba_encounter_check_one(xr(1), xr(2), xr(3), vr(1), vr(2), vr(3), pl%rhill(i), pl%rhill(j), dt, irec, lencounter(k), loc_lvdotr(k)) + end associate + end do - lencounter = .false. + nenc = count(lencounter(:)) + lany_encounter = nenc > 0 + if (lany_encounter) then + associate(plplenc_list => system%plplenc_list) + call plplenc_list%resize(nenc) + plplenc_list%lvdotr(1:nenc) = pack(loc_lvdotr(:), lencounter(:)) + plplenc_list%index1(1:nenc) = pack(pl%k_plpl(1,:), lencounter(:)) + plplenc_list%index2(1:nenc) = pack(pl%k_plpl(2,:), lencounter(:)) + do k = 1, nenc + plplenc_list%status(k) = ACTIVE + plplenc_list%level(k) = irec + pl%lencounter(plplenc_list%index1(k)) = .true. + pl%lencounter(plplenc_list%index2(k)) = .true. + end do + end associate + end if + end associate return end function symba_encounter_check_pl - module function symba_encounter_check_tp(self, system, dt) result(lencounter) + + module function symba_encounter_check_pltpenc(self, system, dt, irec) result(lany_encounter) + !! author: David A. Minton + !! + !! Check for an encounter between test particles and massive bodies in the pltpenc list. + !! Note: This method works for the polymorphic symba_pltpenc and symba_plplenc types. + !! + !! Adapted from portions of David E. Kaufmann's Swifter routine: symba_step_recur.f90 + implicit none + ! Arguments + class(symba_pltpenc), intent(inout) :: self !! SyMBA pl-pl 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 + logical :: lany_encounter !! Returns true if there is at least one close encounter + ! Internals + integer(I4B) :: k + real(DP), dimension(NDIM) :: xr, vr + logical :: lencounter, isplpl + real(DP) :: rlim2, rji2 + logical, dimension(:), allocatable :: lencmask + + lany_encounter = .false. + if (self%nenc == 0) return + + select type(self) + class is (symba_plplenc) + isplpl = .true. + class is (symba_pltpenc) + isplpl = .false. + end select + + select type(pl => system%pl) + class is (symba_pl) + select type(tp => system%tp) + class is (symba_tp) + allocate(lencmask(self%nenc)) + lencmask(:) = (self%status(1:self%nenc) == ACTIVE) .and. (self%level(1:self%nenc) == irec - 1) + if (.not.any(lencmask(:))) return + associate(ind1 => self%index1, ind2 => self%index2) + do concurrent(k = 1:self%nenc, lencmask(k)) + if (isplpl) then + xr(:) = pl%xh(:,ind2(k)) - pl%xh(:,ind1(k)) + vr(:) = pl%vb(:,ind2(k)) - pl%vb(:,ind1(k)) + call symba_encounter_check_one(xr(1), xr(2), xr(3), vr(1), vr(2), vr(3), pl%rhill(ind1(k)), pl%rhill(ind2(k)), dt, irec, lencounter, self%lvdotr(k)) + else + xr(:) = tp%xh(:,ind2(k)) - pl%xh(:,ind1(k)) + vr(:) = tp%vb(:,ind2(k)) - pl%vb(:,ind1(k)) + call symba_encounter_check_one(xr(1), xr(2), xr(3), vr(1), vr(2), vr(3), pl%rhill(ind1(k)), 0.0_DP, dt, irec, lencounter, self%lvdotr(k)) + end if + if (lencounter) then + if (isplpl) then + rlim2 = (pl%radius(ind1(k)) + pl%radius(ind2(k)))**2 + else + rlim2 = (pl%radius(ind1(k)))**2 + end if + rji2 = dot_product(xr(:), xr(:))! Check to see if these are physically overlapping bodies first, which we should ignore + if (rji2 > rlim2) then + lany_encounter = .true. + pl%levelg(ind1(k)) = irec + pl%levelm(ind1(k)) = MAX(irec, pl%levelm(ind1(k))) + if (isplpl) then + pl%levelg(ind2(k)) = irec + pl%levelm(ind2(k)) = MAX(irec, pl%levelm(ind2(k))) + else + tp%levelg(ind2(k)) = irec + tp%levelm(ind2(k)) = MAX(irec, tp%levelm(ind2(k))) + end if + self%level(k) = irec + end if + end if + end do + end associate + end select + end select + + return + end function symba_encounter_check_pltpenc + + + module function symba_encounter_check_tp(self, system, dt, irec) result(lany_encounter) + !! author: David A. Minton + !! + !! Check for an encounter between test particles and massive bodies. + !! implicit none ! Arguments class(symba_tp), intent(inout) :: self !! SyMBA test particle 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 ! Result - logical :: lencounter !! Returns true if there is at least one close encounter + logical :: lany_encounter !! Returns true if there is at least one close encounter + ! Internals + real(DP) :: r2crit, vdotr, r2, v2, tmin, r2min, term2 + integer(I4B) :: i, j, k,nenc + real(DP), dimension(NDIM) :: xr, vr + logical, dimension(:,:), allocatable :: lencounter, loc_lvdotr + + if (self%nbody == 0) return + + associate(tp => self, ntp => self%nbody, pl => system%pl, npl => system%pl%nbody) + allocate(lencounter(ntp, npl), loc_lvdotr(ntp, npl)) + lencounter(:,:) = .false. + + do j = 1, npl + do i = 1, ntp + xr(:) = tp%xh(:, i) - pl%xh(:, j) + vr(:) = tp%vh(:, i) - pl%vh(:, j) + call symba_encounter_check_one(xr(1), xr(2), xr(3), vr(1), vr(2), vr(3), pl%rhill(j), 0.0_DP, dt, irec, lencounter(i,j), loc_lvdotr(i,j)) + end do + end do + + nenc = count(lencounter(:,:)) + lany_encounter = nenc > 0 + if (lany_encounter) then + associate(pltpenc_list => system%pltpenc_list) + call pltpenc_list%resize(nenc) + pltpenc_list%status(1:nenc) = ACTIVE + pltpenc_list%level(1:nenc) = irec + pltpenc_list%lvdotr(1:nenc) = pack(loc_lvdotr(:,:), lencounter(:,:)) + pltpenc_list%index1(1:nenc) = pack(spread([(i, i = 1, npl)], dim=1, ncopies=ntp), lencounter(:,:)) + pltpenc_list%index2(1:nenc) = pack(spread([(i, i = 1, ntp)], dim=2, ncopies=npl), lencounter(:,:)) + select type(pl) + class is (symba_pl) + pl%lencounter(:) = .false. + do k = 1, nenc + pl%lencounter(pltpenc_list%index1(k)) = .true. + end do + end select + end associate + end if + end associate - lencounter = .false. return end function symba_encounter_check_tp + + module pure elemental subroutine symba_encounter_check_one(xr, yr, zr, vxr, vyr, vzr, rhill1, rhill2, dt, irec, lencounter, lvdotr) + !! author: David A. Minton + !! + !! Check for an encounter. + !! + !! Adapted from David E. Kaufmann's Swifter routine: symba_chk.f90 + !! Adapted from Hal Levison's Swift routine symba5_chk.f + implicit none + ! Arguments + real(DP), intent(in) :: xr, yr, zr, vxr, vyr, vzr + real(DP), intent(in) :: rhill1, rhill2, dt + integer(I4B), intent(in) :: irec + logical, intent(out) :: lencounter, lvdotr + ! Internals + real(DP) :: r2, v2, rcrit, r2crit, vdotr + + rcrit = (rhill1 + rhill2)*RHSCALE*(RSHELL**(irec)) + r2crit = rcrit**2 + r2 = xr**2 + yr**2 + zr**2 + v2 = vxr**2 + vyr**2 + vzr**2 + vdotr = xr * vxr + yr * vyr + zr * vzr + lencounter = rmvs_chk_ind(r2, v2, vdotr, dt, r2crit) + lvdotr = (vdotr < 0.0_DP) + + return + end subroutine symba_encounter_check_one + end submodule s_symba_encounter_check \ No newline at end of file diff --git a/src/symba/symba_fragmentation.f90 b/src/symba/symba_fragmentation.f90 new file mode 100644 index 000000000..efdd8c0d7 --- /dev/null +++ b/src/symba/symba_fragmentation.f90 @@ -0,0 +1,581 @@ +submodule (symba_classes) s_symba_fragmentation + use swiftest + + integer(I4B), parameter :: NFRAG_DISRUPT = 12 + integer(I4B), parameter :: NFRAG_SUPERCAT = 20 +contains + + module function symba_fragmentation_casedisruption(system, param, family, x, v, mass, radius, L_spin, Ip, mass_res, Qloss) result(status) + !! author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! Create the fragments resulting from a non-catastrophic disruption collision + !! + 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(inout) :: x, v, L_spin, Ip !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass, radius !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass_res !! The distribution of fragment mass obtained by the regime calculation + real(DP), intent(inout) :: Qloss !! Energy lost during collision + ! Result + integer(I4B) :: status !! Status flag assigned to this outcome + ! Internals + integer(I4B) :: i, istart, nfrag, ibiggest, nfamily, nstart, nend + real(DP) :: mtot, avg_dens + real(DP), dimension(NDIM) :: xcom, vcom, Ip_new + real(DP), dimension(2) :: vol + real(DP), dimension(:, :), allocatable :: vb_frag, xb_frag, rot_frag, Ip_frag + real(DP), dimension(:), allocatable :: m_frag, rad_frag + logical :: lfailure + 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) + ! Collisional fragments will be uniformly distributed around the pre-impact barycenter + nfrag = NFRAG_DISRUPT + allocate(m_frag(nfrag)) + allocate(rad_frag(nfrag)) + allocate(xb_frag(NDIM, nfrag)) + allocate(vb_frag(NDIM, nfrag)) + allocate(rot_frag(NDIM, nfrag)) + allocate(Ip_frag(NDIM, nfrag)) + + mtot = sum(mass(:)) + xcom(:) = (mass(1) * x(:,1) + mass(2) * x(:,2)) / mtot + vcom(:) = (mass(1) * v(:,1) + mass(2) * v(:,2)) / mtot + + ! Get mass weighted mean of Ip and average density + Ip_new(:) = (mass(1) * Ip(:,1) + mass(2) * Ip(:,2)) / mtot + vol(:) = 4._DP / 3._DP * PI * radius(:)**3 + avg_dens = mtot / sum(vol(:)) + + ! Distribute the mass among fragments, with a branch to check for the size of the second largest fragment + m_frag(1) = mass_res(1) + if (mass_res(2) > mass_res(1) / 3._DP) then + m_frag(2) = mass_res(2) + istart = 3 + else + istart = 2 + end if + ! Distribute remaining mass among the remaining bodies + do i = istart, nfrag + m_frag(i) = (mtot - sum(m_frag(1:istart - 1))) / (nfrag - istart + 1) + end do + + ! Distribute any residual mass if there is any and set the radius + m_frag(nfrag) = m_frag(nfrag) + (mtot - sum(m_frag(:))) + rad_frag(:) = (3 * m_frag(:) / (4 * PI * avg_dens))**(1.0_DP / 3.0_DP) + + do i = 1, nfrag + Ip_frag(:, i) = Ip_new(:) + end do + + call fragmentation_initialize(system, param, family, x, v, L_spin, Ip, mass, radius, & + nfrag, Ip_frag, m_frag, rad_frag, xb_frag, vb_frag, rot_frag, Qloss, lfailure) + + if (lfailure) then + write(*,*) 'No fragment solution found, so treat as a pure hit-and-run' + status = ACTIVE + nfrag = 0 + else + ! Populate the list of new bodies + write(*,'("Generating ",I2.0," fragments")') nfrag + status = DISRUPTION + + ! Add the family bodies to the subtraction list + nfamily = size(family(:)) + 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 + + allocate(plnew, mold=pl) + call plnew%setup(nfrag, param) + + plnew%id(:) = [(i, i = system%maxid + 1, system%maxid + nfrag)] + system%maxid = system%maxid + nfrag + plnew%status(:) = ACTIVE + plnew%lcollision(:) = .false. + plnew%ldiscard(:) = .false. + plnew%xb(:,:) = xb_frag(:, :) + plnew%vb(:,:) = vb_frag(:, :) + do i = 1, nfrag + plnew%xh(:,i) = xb_frag(:, i) - cb%xb(:) + plnew%vh(:,i) = vb_frag(:, i) - cb%vb(:) + end do + plnew%mass(:) = m_frag(:) + plnew%Gmass(:) = param%GU * m_frag(:) + plnew%density(:) = avg_dens + plnew%radius(:) = rad_frag(:) + plnew%info(:)%origin_type = "Disruption" + plnew%info(:)%origin_time = param%t + do i = 1, nfrag + plnew%info(i)%origin_xh(:) = plnew%xh(:,i) + plnew%info(i)%origin_vh(:) = plnew%vh(:,i) + end do + if (param%lrotation) then + plnew%Ip(:,:) = Ip_frag(:,:) + plnew%rot(:,:) = rot_frag(:,:) + end if + if (param%ltides) then + ibiggest = maxloc(pl%Gmass(family(:)), dim=1) + 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 if + + end associate + end select + + return + end function symba_fragmentation_casedisruption + + + module function symba_fragmentation_casehitandrun(system, param, family, x, v, mass, radius, L_spin, Ip, mass_res, Qloss) result(status) + !! author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! Create the fragments resulting from a non-catastrophic hit-and-run collision + !! + 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(inout) :: x, v, L_spin, Ip !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass, radius !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass_res !! The distribution of fragment mass obtained by the regime calculation + real(DP), intent(inout) :: Qloss !! Energy lost during collision + ! Result + integer(I4B) :: status !! Status flag assigned to this outcome + ! Internals + integer(I4B) :: i, nfrag, jproj, jtarg, idstart, ibiggest, nfamily, nstart, nend + real(DP) :: mtot, avg_dens + real(DP), dimension(NDIM) :: xcom, vcom + real(DP), dimension(2) :: vol + real(DP), dimension(:, :), allocatable :: vb_frag, xb_frag, rot_frag, Ip_frag + real(DP), dimension(:), allocatable :: m_frag, rad_frag + integer(I4B), dimension(:), allocatable :: id_frag + logical :: lpure + 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) + mtot = sum(mass(:)) + xcom(:) = (mass(1) * x(:,1) + mass(2) * x(:,2)) / mtot + vcom(:) = (mass(1) * v(:,1) + mass(2) * v(:,2)) / mtot + lpure = .false. + + ! The largest body will stay untouched + if (mass(1) > mass(2)) then + jtarg = 1 + jproj = 2 + else + jtarg = 2 + jproj = 1 + end if + + if (mass_res(2) > 0.9_DP * mass(jproj)) then ! Pure hit and run, so we'll just keep the two bodies untouched + write(*,*) 'Pure hit and run. No new fragments generated.' + nfrag = 0 + lpure = .true. + else ! Imperfect hit and run, so we'll keep the largest body and destroy the other + nfrag = NFRAG_DISRUPT - 1 + lpure = .false. + allocate(m_frag(nfrag)) + allocate(id_frag(nfrag)) + allocate(rad_frag(nfrag)) + allocate(xb_frag(NDIM, nfrag)) + allocate(vb_frag(NDIM, nfrag)) + allocate(rot_frag(NDIM, nfrag)) + allocate(Ip_frag(NDIM, nfrag)) + m_frag(1) = mass(jtarg) + ibiggest = maxloc(pl%Gmass(family(:)), dim=1) + id_frag(1) = pl%id(ibiggest) + rad_frag(1) = radius(jtarg) + xb_frag(:, 1) = x(:, jtarg) + vb_frag(:, 1) = v(:, jtarg) + Ip_frag(:,1) = Ip(:, jtarg) + + ! Get mass weighted mean of Ip and average density + vol(:) = 4._DP / 3._DP * pi * radius(:)**3 + avg_dens = mass(jproj) / vol(jproj) + m_frag(2:nfrag) = (mtot - m_frag(1)) / (nfrag - 1) + rad_frag(2:nfrag) = (3 * m_frag(2:nfrag) / (4 * PI * avg_dens))**(1.0_DP / 3.0_DP) + m_frag(nfrag) = m_frag(nfrag) + (mtot - sum(m_frag(:))) + + do i = 1, nfrag + Ip_frag(:, i) = Ip(:, jproj) + end do + + ! Put the fragments on the circle surrounding the center of mass of the system + call fragmentation_initialize(system, param, family, x, v, L_spin, Ip, mass, radius, & + nfrag, Ip_frag, m_frag, rad_frag, xb_frag, vb_frag, rot_frag, Qloss, lpure) + if (lpure) then + write(*,*) 'Should have been a pure hit and run instead' + nfrag = 0 + else + write(*,'("Generating ",I2.0," fragments")') nfrag + end if + end if + if (lpure) then + status = ACTIVE + else + status = HIT_AND_RUN + + ! Add the family bodies to the subtraction list + nfamily = size(family(:)) + 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 + + allocate(plnew, mold=pl) + call plnew%setup(nfrag, param) + + plnew%id(:) = [(i, i = system%maxid + 1, system%maxid + nfrag)] + system%maxid = system%maxid + nfrag + plnew%status(:) = ACTIVE + plnew%lcollision(:) = .false. + plnew%ldiscard(:) = .false. + plnew%xb(:,:) = xb_frag(:, :) + plnew%vb(:,:) = vb_frag(:, :) + do i = 1, nfrag + plnew%xh(:,i) = xb_frag(:, i) - cb%xb(:) + plnew%vh(:,i) = vb_frag(:, i) - cb%vb(:) + end do + plnew%mass(:) = m_frag(:) + plnew%Gmass(:) = param%GU * m_frag(:) + plnew%density(:) = avg_dens + plnew%radius(:) = rad_frag(:) + plnew%info(:)%origin_type = "Hit and run fragment" + plnew%info(:)%origin_time = param%t + do i = 1, nfrag + plnew%info(i)%origin_xh(:) = plnew%xh(:,i) + plnew%info(i)%origin_vh(:) = plnew%vh(:,i) + end do + if (param%lrotation) then + plnew%Ip(:,:) = Ip_frag(:,:) + plnew%rot(:,:) = rot_frag(:,:) + end if + if (param%ltides) then + ibiggest = maxloc(pl%Gmass(family(:)), dim=1) + 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 if + end associate + end select + + return + end function symba_fragmentation_casehitandrun + + + 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 + plnew%Ip(:,1) = Ip_new(:) + plnew%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 + + + module function symba_fragmentation_casesupercatastrophic(system, param, family, x, v, mass, radius, L_spin, Ip, mass_res, Qloss) result(status) + !! author: Jennifer L.L. Pouplin, Carlisle A. Wishard, and David A. Minton + !! + !! Create the fragments resulting from a supercatastrophic collision + !! + 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(inout) :: x, v, L_spin, Ip !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass, radius !! Input values that represent a 2-body equivalent of a possibly 2+ body collision + real(DP), dimension(:), intent(inout) :: mass_res !! The distribution of fragment mass obtained by the regime calculation + real(DP), intent(inout) :: Qloss !! Energy lost during collision + ! Result + integer(I4B) :: status !! Status flag assigned to this outcome + ! Internals + integer(I4B) :: i, nfrag, ibiggest, nfamily, nstart, nend + real(DP) :: mtot, avg_dens, min_frag_mass + real(DP), dimension(NDIM) :: xcom, vcom + real(DP), dimension(2) :: vol + real(DP), dimension(NDIM) :: Ip_new + real(DP), dimension(:, :), allocatable :: vb_frag, xb_frag, rot_frag, Ip_frag + real(DP), dimension(:), allocatable :: m_frag, rad_frag + logical :: lfailure + 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) + ! Collisional fragments will be uniformly distributed around the pre-impact barycenter + nfrag = NFRAG_SUPERCAT + allocate(m_frag(nfrag)) + allocate(rad_frag(nfrag)) + allocate(xb_frag(NDIM, nfrag)) + allocate(vb_frag(NDIM, nfrag)) + allocate(rot_frag(NDIM, nfrag)) + allocate(Ip_frag(NDIM, nfrag)) + + mtot = sum(mass(:)) + xcom(:) = (mass(1) * x(:,1) + mass(2) * x(:,2)) / mtot + vcom(:) = (mass(1) * v(:,1) + mass(2) * v(:,2)) / mtot + + ! Get mass weighted mean of Ip and average density + Ip_new(:) = (mass(1) * Ip(:,1) + mass(2) * Ip(:,2)) / mtot + vol(:) = 4._DP / 3._DP * pi * radius(:)**3 + avg_dens = mtot / sum(vol(:)) + + ! If we are adding the first and largest fragment (lr), check to see if its mass is SMALLER than an equal distribution of + ! mass between all fragments. If so, we will just distribute the mass equally between the fragments + min_frag_mass = mtot / nfrag + if (mass_res(1) < min_frag_mass) then + m_frag(:) = min_frag_mass + else + m_frag(1) = mass_res(1) + m_frag(2:nfrag) = (mtot - mass_res(1)) / (nfrag - 1) + end if + ! Distribute any residual mass if there is any and set the radius + m_frag(nfrag) = m_frag(nfrag) + (mtot - sum(m_frag(:))) + rad_frag(:) = (3 * m_frag(:) / (4 * PI * avg_dens))**(1.0_DP / 3.0_DP) + + do i = 1, nfrag + Ip_frag(:, i) = Ip_new(:) + end do + + call fragmentation_initialize(system, param, family, x, v, L_spin, Ip, mass, radius, & + nfrag, Ip_frag, m_frag, rad_frag, xb_frag, vb_frag, rot_frag, Qloss, lfailure) + + if (lfailure) then + write(*,*) 'No fragment solution found, so treat as a pure hit-and-run' + status = ACTIVE + nfrag = 0 + else + ! Populate the list of new bodies + write(*,'("Generating ",I2.0," fragments")') nfrag + status = SUPERCATASTROPHIC + + ! Add the family bodies to the subtraction list + nfamily = size(family(:)) + 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 + + allocate(plnew, mold=pl) + call plnew%setup(nfrag, param) + + plnew%id(:) = [(i, i = system%maxid + 1, system%maxid + nfrag)] + system%maxid = system%maxid + nfrag + plnew%status(:) = ACTIVE + plnew%lcollision(:) = .false. + plnew%ldiscard(:) = .false. + plnew%xb(:,:) = xb_frag(:, :) + plnew%vb(:,:) = vb_frag(:, :) + do i = 1, nfrag + plnew%xh(:,i) = xb_frag(:, i) - cb%xb(:) + plnew%vh(:,i) = vb_frag(:, i) - cb%vb(:) + end do + plnew%mass(:) = m_frag(:) + plnew%Gmass(:) = param%GU * m_frag(:) + plnew%density(:) = avg_dens + plnew%radius(:) = rad_frag(:) + plnew%info(:)%origin_type = "Supercatastrophic" + plnew%info(:)%origin_time = param%t + do i = 1, nfrag + plnew%info(i)%origin_xh(:) = plnew%xh(:,i) + plnew%info(i)%origin_vh(:) = plnew%vh(:,i) + end do + if (param%lrotation) then + plnew%Ip(:,:) = Ip_frag(:,:) + plnew%rot(:,:) = rot_frag(:,:) + end if + if (param%ltides) then + ibiggest = maxloc(pl%Gmass(family(:)), dim=1) + 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 if + + end associate + end select + + return + end function symba_fragmentation_casesupercatastrophic + +end submodule s_symba_fragmentation diff --git a/src/symba/symba_io.f90 b/src/symba/symba_io.f90 index bebb225b5..2e568dd7e 100644 --- a/src/symba/symba_io.f90 +++ b/src/symba/symba_io.f90 @@ -1,16 +1,18 @@ submodule (symba_classes) s_symba_io use swiftest contains + module subroutine symba_io_dump_particle_info(self, param, msg) !! author: David A. Minton !! !! Dumps the particle information data to a file implicit none class(symba_particle_info), intent(inout) :: self !! Swiftest base object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters character(*), optional, intent(in) :: msg !! Message to display with dump operation end subroutine symba_io_dump_particle_info + module subroutine symba_io_initialize_particle_info(self, param) !! author: David A. Minton !! @@ -21,6 +23,7 @@ module subroutine symba_io_initialize_particle_info(self, param) class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters end subroutine symba_io_initialize_particle_info + module subroutine symba_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 !! @@ -51,6 +54,7 @@ module subroutine symba_io_param_reader(self, unit, iotype, v_list, iostat, ioms call random_seed(size = nseeds) if (allocated(param%seed)) deallocate(param%seed) allocate(param%seed(nseeds)) + rewind(unit) do read(unit = unit, fmt = linefmt, iostat = iostat, end = 1) line line_trim = trim(adjustl(line)) @@ -121,10 +125,13 @@ module subroutine symba_io_param_reader(self, unit, iotype, v_list, iostat, ioms return end if end associate - return + iostat = 0 + + return end subroutine symba_io_param_reader + module subroutine symba_io_param_writer(self, unit, iotype, v_list, iostat, iomsg) !! author: David A. Minton !! @@ -184,9 +191,9 @@ module subroutine symba_io_param_writer(self, unit, iotype, v_list, iostat, ioms end associate return - end subroutine symba_io_param_writer + module subroutine symba_io_read_frame_info(self, iu, param, form, ierr) !! author: David A. Minton !! @@ -201,6 +208,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 class(symba_particle_info), intent(in) :: self !! SyMBA particle info object @@ -208,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 new file mode 100644 index 000000000..8625b3d81 --- /dev/null +++ b/src/symba/symba_kick.f90 @@ -0,0 +1,200 @@ +submodule(symba_classes) s_symba_kick + use swiftest +contains + + module subroutine symba_kick_getacch_pl(self, system, param, t, lbeg) + !! author: David A. Minton + !! + !! Compute heliocentric accelerations of massive bodies + !! + !! Adapted from David E. Kaufmann's Swifter routine symba_kick_getacch.f90 + !! Adapted from Hal Levison's Swift routine symba5_kick_getacch.f + implicit none + ! Arguments + 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 + ! Internals + integer(I4B) :: k + real(DP) :: irij3, rji2, rlim2, faci, facj + real(DP), dimension(NDIM) :: dx + + if (self%nbody == 0) return + select type(system) + class is (symba_nbody_system) + associate(pl => self, cb => system%cb, plplenc_list => system%plplenc_list, nplplenc => system%plplenc_list%nenc) + call helio_kick_getacch_pl(pl, system, param, t, lbeg) + ! Remove accelerations from encountering pairs + do k = 1, nplplenc + associate(i => plplenc_list%index1(k), j => plplenc_list%index2(k)) + dx(:) = pl%xh(:, j) - pl%xh(:, i) + rji2 = dot_product(dx(:), dx(:)) + irij3 = 1.0_DP / (rji2 * sqrt(rji2)) + faci = pl%Gmass(i) * irij3 + facj = pl%Gmass(j) * irij3 + pl%ah(:, i) = pl%ah(:, i) - facj * dx(:) + pl%ah(:, j) = pl%ah(:, j) + faci * dx(:) + end associate + end do + end associate + end select + + return + end subroutine symba_kick_getacch_pl + + + module subroutine symba_kick_getacch_tp(self, system, param, t, lbeg) + !! author: David A. Minton + !! + !! Compute heliocentric accelerations of test particles + !! + !! Adapted from David E. Kaufmann's Swifter routine symba_kick_getacch_tp.f90 + !! Adapted from Hal Levison's Swift routine symba5_kick_getacch.f + implicit none + ! Arguments + 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 + ! Internals + integer(I4B) :: k + real(DP) :: rji2, fac, rlim2 + real(DP), dimension(NDIM) :: dx + + if (self%nbody == 0) return + select type(system) + class is (symba_nbody_system) + associate(tp => self, cb => system%cb, pl => system%pl, pltpenc_list => system%pltpenc_list, npltpenc => system%pltpenc_list%nenc) + call helio_kick_getacch_tp(tp, system, param, t, lbeg) + ! Remove accelerations from encountering pairs + do k = 1, npltpenc + associate(i => pltpenc_list%index1(k), j => pltpenc_list%index2(k)) + if (tp%lmask(j)) THEN + if (lbeg) then + dx(:) = tp%xh(:,j) - pl%xbeg(:,i) + else + dx(:) = tp%xh(:,j) - pl%xend(:,i) + end if + rji2 = dot_product(dx(:), dx(:)) + fac = pl%Gmass(i) / (rji2 * sqrt(rji2)) + tp%ah(:,j) = tp%ah(:,j) + fac * dx(:) + end IF + end associate + end do + end associate + end select + return + end subroutine symba_kick_getacch_tp + + + module subroutine symba_kick_pltpenc(self, system, dt, irec, sgn) + !! author: David A. Minton + !! + !! Kick barycentric velocities of massive bodies and ACTIVE test particles within SyMBA recursion. + !! Note: This method works for the polymorphic symba_pltpenc and symba_plplenc types + !! + !! Adapted from David E. Kaufmann's Swifter routine: symba_kick.f90 + !! Adapted from Hal Levison's Swift routine symba5_kick.f + implicit none + ! Arguments + 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 + ! Internals + integer(I4B) :: k, irm1, irecl + real(DP) :: r, rr, ri, ris, rim1, r2, ir3, fac, faci, facj + real(DP), dimension(NDIM) :: dx + logical :: isplpl, lgoodlevel + + if (self%nenc == 0) return + + select type(self) + class is (symba_plplenc) + isplpl = .true. + class is (symba_pltpenc) + isplpl = .false. + end select + select type(pl => system%pl) + class is (symba_pl) + select type(tp => system%tp) + class is (symba_tp) + associate(ind1 => self%index1, ind2 => self%index2) + 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 + irecl = irec - 1 + else + irecl = irec + end if + do k = 1, self%nenc + if (isplpl) then + pl%ah(:,ind1(k)) = 0.0_DP + pl%ah(:,ind2(k)) = 0.0_DP + else + tp%ah(:,ind2(k)) = 0.0_DP + end if + if (isplpl) then + lgoodlevel = (pl%levelg(ind1(k)) >= irm1) .and. (pl%levelg(ind2(k)) >= irm1) + else + lgoodlevel = (pl%levelg(ind1(k)) >= irm1) .and. (tp%levelg(ind2(k)) >= irm1) + end if + 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) + dx(:) = pl%xh(:,ind2(k)) - pl%xh(:,ind1(k)) + else + ri = ((pl%rhill(ind1(k)))**2) * (RHSCALE**2) * (RSHELL**(2*irecl)) + rim1 = ri * (RSHELL**2) + dx(:) = tp%xh(:,ind2(k)) - pl%xh(:,ind1(k)) + end if + r2 = dot_product(dx(:), dx(:)) + if (r2 < rim1) then + fac = 0.0_DP + else if (r2 < ri) then + ris = sqrt(ri) + r = sqrt(r2) + rr = (ris - r) / (ris * (1.0_DP - RSHELL)) + fac = (r2**(-1.5_DP)) * (1.0_DP - 3 * (rr**2) + 2 * (rr**3)) + else + ir3 = 1.0_DP / (r2 * sqrt(r2)) + fac = ir3 + end if + faci = fac * pl%Gmass(ind1(k)) + if (isplpl) then + facj = fac * pl%Gmass(ind2(k)) + pl%ah(:,ind1(k)) = pl%ah(:,ind1(k)) + facj * dx(:) + pl%ah(:,ind2(k)) = pl%ah(:,ind2(k)) - faci * dx(:) + else + tp%ah(:,ind2(k)) = tp%ah(:,ind2(k)) - faci * dx(:) + end if + end if + end do + if (isplpl) then + do k = 1, self%nenc + pl%vb(:,ind1(k)) = pl%vb(:,ind1(k)) + sgn * dt * pl%ah(:,ind1(k)) + pl%vb(:,ind2(k)) = pl%vb(:,ind2(k)) + sgn * dt * pl%ah(:,ind2(k)) + pl%ah(:,ind1(k)) = 0.0_DP + pl%ah(:,ind1(k)) = 0.0_DP + end do + else + do k = 1, self%nenc + tp%vb(:,ind2(k)) = tp%vb(:,ind2(k)) + sgn * dt * tp%ah(:,ind2(k)) + tp%ah(:,ind2(k)) = 0.0_DP + end do + end if + end associate + end select + end select + + return + end subroutine symba_kick_pltpenc + +end submodule s_symba_kick \ No newline at end of file diff --git a/src/symba/symba_setup.f90 b/src/symba/symba_setup.f90 index 6449013dd..021873a70 100644 --- a/src/symba/symba_setup.f90 +++ b/src/symba/symba_setup.f90 @@ -1,7 +1,41 @@ submodule(symba_classes) s_symba_setup use swiftest contains - module subroutine symba_setup_pl(self,n) + + module subroutine symba_setup_initialize_system(self, param) + !! author: David A. Minton + !! + !! Initialize an SyMBA nbody system from files and sets up the planetocentric structures. + !! This subroutine will also sort the massive bodies in descending order by mass + !! + implicit none + ! Arguments + class(symba_nbody_system), intent(inout) :: self !! SyMBA system object + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters + ! Internals + integer(I4B) :: i, j + + ! Call parent method + associate(system => self) + call whm_setup_initialize_system(system, param) + call system%pltpenc_list%setup(0) + call system%plplenc_list%setup(0) + select type(pl => system%pl) + class is (symba_pl) + call pl%sort("mass", ascending=.false.) + select type(param) + class is (symba_parameters) + pl%lmtiny(:) = pl%Gmass(:) > param%MTINY + pl%nplm = count(pl%lmtiny(:)) + end select + end select + end associate + + return + end subroutine symba_setup_initialize_system + + + module subroutine symba_setup_merger(self, n, param) !! author: David A. Minton !! !! Allocate SyMBA test particle structure @@ -9,48 +43,107 @@ module subroutine symba_setup_pl(self,n) !! Equivalent in functionality to David E. Kaufmann's Swifter routine symba_setup.f90 implicit none ! Arguments - class(symba_pl), intent(inout) :: self !! SyMBA test particle object - integer(I4B), intent(in) :: n !! Number of massive bodies to allocate + 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,j + integer(I4B) :: i - !> Call allocation method for parent class - !call helio_setup_pl(self, n) - call setup_pl(self, n) + !> 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_pl + end subroutine symba_setup_merger + - module subroutine symba_setup_system(self, param) + module subroutine symba_setup_pl(self, n, param) !! author: David A. Minton !! - !! Initialize an SyMBA nbody system from files and sets up the planetocentric structures. - !! + !! Allocate SyMBA test particle structure + !! + !! Equivalent in functionality to David E. Kaufmann's Swifter routine symba_setup.f90 implicit none ! Arguments - class(symba_nbody_system), intent(inout) :: self !! SyMBA system object - class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters + 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 parameter ! Internals - integer(I4B) :: i, j + integer(I4B) :: i - ! Call parent method - call whm_setup_system(self, param) + !> 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 setup_pl(self, n, param) + if (n <= 0) return - select type(pl => self%pl) - class is(symba_pl) - select type(cb => self%cb) - class is (symba_cb) - select type (tp => self%tp) - class is (symba_tp) + 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)) + allocate(self%nplenc(n)) + allocate(self%ntpenc(n)) + allocate(self%levelg(n)) + allocate(self%levelm(n)) + allocate(self%isperi(n)) + allocate(self%peri(n)) + allocate(self%atp(n)) + allocate(self%kin(n)) + allocate(self%info(n)) + + self%lcollision(:) = .false. + self%lencounter(:) = .false. + self%lmtiny(:) = .false. + self%nplenc(:) = 0 + self%ntpenc(:) = 0 + self%levelg(:) = -1 + self%levelm(:) = -1 + self%isperi(:) = 0 + self%peri(:) = 0.0_DP + self%atp(:) = 0.0_DP + self%kin(:)%nchild = 0 + self%kin(:)%parent = [(i, i=1, n)] + return + end subroutine symba_setup_pl - end select - end select - end select - - end subroutine symba_setup_system - module subroutine symba_setup_tp(self,n) + module subroutine symba_setup_pltpenc(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_pltpenc), intent(inout) :: self !! SyMBA pl-tp encounter structure + integer(I4B), intent(in) :: n !! Number of encounters to allocate space for + + call setup_encounter(self, n) + if (n == 0) return + + if (allocated(self%level)) deallocate(self%level) + allocate(self%level(n)) + + self%level(:) = -1 + + return + end subroutine symba_setup_pltpenc + + + module subroutine symba_setup_tp(self, n, param) !! author: David A. Minton !! !! Allocate WHM test particle structure @@ -58,13 +151,26 @@ module subroutine symba_setup_tp(self,n) !! Equivalent in functionality to David E. Kaufmann's Swifter routine whm_setup.f90 implicit none ! Arguments - class(symba_tp), intent(inout) :: self !! SyMBA test particle object - integer, intent(in) :: n !! Number of test particles to allocate + class(symba_tp), intent(inout) :: self !! SyMBA test particle object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameter - !> Call allocation method for parent class - !call helio_setup_tp(self, n) - call setup_tp(self, n) + !> Call allocation method for parent class. In this case, helio_tp does not have its own setup method so we use the base method for swiftest_tp + 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 + return end subroutine symba_setup_tp diff --git a/src/symba/symba_step.f90 b/src/symba/symba_step.f90 index b04caa74e..41e7a3a74 100644 --- a/src/symba/symba_step.f90 +++ b/src/symba/symba_step.f90 @@ -1,6 +1,7 @@ submodule (symba_classes) s_symba_step use swiftest contains + module subroutine symba_step_system(self, param, t, dt) !! author: David A. Minton !! @@ -16,15 +17,19 @@ module subroutine symba_step_system(self, param, t, dt) real(DP), intent(in) :: t !! Simulation time real(DP), intent(in) :: dt !! Current stepsize ! Internals - logical :: lencounter_pl, lencounter_tp, lencounter + logical :: lencounter + call self%reset() select type(pl => self%pl) class is (symba_pl) select type(tp => self%tp) class is (symba_tp) - lencounter = pl%encounter_check(self, dt) .or. tp%encounter_check(self, dt) + lencounter = pl%encounter_check(self, dt, 0) .or. tp%encounter_check(self, dt, 0) if (lencounter) then + tp%lfirst = pl%lfirst call self%interp(param, t, dt) + pl%lfirst = .true. + tp%lfirst = .true. else call helio_step_system(self, param, t, dt) end if @@ -32,16 +37,241 @@ module subroutine symba_step_system(self, param, t, dt) end select return - end subroutine symba_step_system + module subroutine symba_step_interp_system(self, param, t, dt) + !! author: David A. Minton + !! + !! Step planets and active test particles ahead in democratic heliocentric coordinates, calling the recursive + !! subroutine to descend to the appropriate level to handle close encounters + !! + !! Adapted from David E. Kaufmann's Swifter routine: symba_step_interp.f90 + !! Adapted from Hal Levison's Swift routine symba5_step_interp.f implicit none - class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody 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 + ! Arguments + class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody 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 + ! Internals + real(DP) :: dth !! Half step size + + dth = 0.5_DP * dt + associate(system => self) + select type(pl => system%pl) + class is (symba_pl) + select type(tp => system%tp) + class is (symba_tp) + select type(cb => system%cb) + class is (symba_cb) + system%irec = -1 + call pl%vh2vb(cb) + call pl%lindrift(cb, dth, lbeg=.true.) + call pl%kick(system, param, t, dth, lbeg=.true.) + call pl%drift(system, param, dt) + + call tp%vh2vb(vbcb = -cb%ptbeg) + call tp%lindrift(cb, dth, lbeg=.true.) + call tp%kick(system, param, t, dth, lbeg=.true.) + call tp%drift(system, param, dt) + + call system%recursive_step(param, t, 0) + + call pl%kick(system, param, t, dth, lbeg=.false.) + call pl%vb2vh(cb) + call pl%lindrift(cb, dth, lbeg=.false.) + + call tp%kick(system, param, t, dth, lbeg=.false.) + call tp%vb2vh(vbcb = -cb%ptend) + call tp%lindrift(cb, dth, lbeg=.false.) + end select + end select + end select + end associate return end subroutine symba_step_interp_system + + + module subroutine symba_step_set_recur_levels_system(self, ireci) + !! author: David A. Minton + !! + !! Resets pl, tp,and encounter structures at the start of a new step + !! + !! Adapted from David E. Kaufmann's Swifter routine: symba_step_recur.f90 + !! Adapted from Hal Levison's Swift routine symba5_step_recur.f + implicit none + ! Arguments + class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody system object + integer(I4B), intent(in) :: ireci !! Input recursion level + ! Internals + integer(I4B) :: k, irecp + + associate(system => self, plplenc_list => self%plplenc_list, pltpenc_list => self%pltpenc_list) + select type(pl => self%pl) + class is (symba_pl) + select type(tp => self%tp) + class is (symba_tp) + irecp = ireci + 1 + + if (plplenc_list%nenc > 0) then + do k = 1, plplenc_list%nenc + associate(i => plplenc_list%index1(k), j => plplenc_list%index2(k)) + if (pl%levelg(i) == irecp) pl%levelg(i) = ireci + if (pl%levelg(j) == irecp) pl%levelg(j) = ireci + end associate + end do + where(plplenc_list%level(1:plplenc_list%nenc) == irecp) plplenc_list%level(1:plplenc_list%nenc) = ireci + end if + + if (pltpenc_list%nenc > 0) then + do k = 1, pltpenc_list%nenc + associate(i => pltpenc_list%index1(k), j => pltpenc_list%index2(k)) + if (pl%levelg(i) == irecp) pl%levelg(i) = ireci + if (tp%levelg(j) == irecp) tp%levelg(j) = ireci + end associate + end do + where(pltpenc_list%level(1:pltpenc_list%nenc) == irecp) pltpenc_list%level(1:pltpenc_list%nenc) = ireci + end if + + system%irec = ireci + + end select + end select + end associate + + return + end subroutine symba_step_set_recur_levels_system + + + module recursive subroutine symba_step_recur_system(self, param, t, ireci) + !! author: David A. Minton + !! + !! 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 necessarys + !! + !! Adapted from David E. Kaufmann's Swifter routine: symba_step_recur.f90 + !! Adapted from Hal Levison's Swift routine symba5_step_recur.f + implicit none + ! Arguments + class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody system object + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters + real(DP), value :: t + integer(I4B), value :: ireci !! input recursion level + ! Internals + integer(I4B) :: i, j, irecp, nloops + real(DP) :: dtl, dth + real(DP), dimension(NDIM) :: xr, vr + logical :: lencounter + + associate(system => self, plplenc_list => self%plplenc_list, pltpenc_list => self%pltpenc_list) + select type(pl => self%pl) + class is (symba_pl) + select type(tp => self%tp) + class is (symba_tp) + system%irec = ireci + dtl = param%dt / (NTENC**ireci) + dth = 0.5_DP * dtl + IF (dtl / param%dt < VSMALL) THEN + write(*, *) "SWIFTEST Warning:" + write(*, *) " In symba_step_recur_system, local time step is too small" + write(*, *) " Roundoff error will be important!" + call util_exit(FAILURE) + END IF + irecp = ireci + 1 + if (ireci == 0) then + nloops = 1 + else + nloops = NTENC + end if + do j = 1, nloops + lencounter = plplenc_list%encounter_check(system, dtl, irecp) .or. pltpenc_list%encounter_check(system, dtl, irecp) + + call plplenc_list%kick(system, dth, irecp, 1) + call pltpenc_list%kick(system, dth, irecp, 1) + if (ireci /= 0) then + call plplenc_list%kick(system, dth, irecp, -1) + call pltpenc_list%kick(system, dth, irecp, -1) + end if + + call pl%drift(system, param, dtl) + call tp%drift(system, param, dtl) + + if (lencounter) call system%recursive_step(param, t+dth,irecp) + system%irec = ireci + + call plplenc_list%kick(system, dth, irecp, 1) + call pltpenc_list%kick(system, dth, irecp, 1) + if (ireci /= 0) then + call plplenc_list%kick(system, dth, irecp, -1) + call pltpenc_list%kick(system, dth, irecp, -1) + end if + + if (param%lclose) then + call plplenc_list%collision_check(system, param, t+dtl, dtl, ireci) + call pltpenc_list%collision_check(system, param, t+dtl, dtl, ireci) + end if + + call self%set_recur_levels(ireci) + + end do + end select + end select + end associate + + return + end subroutine symba_step_recur_system + + + module subroutine symba_step_reset_system(self) + !! author: David A. Minton + !! + !! Resets pl, tp,and encounter structures at the start of a new step + !! + !! Adapted from David E. Kaufmann's Swifter routine: symba_step.f90 + !! Adapted from Hal Levison's Swift routine symba5_step.f + implicit none + ! Arguments + class(symba_nbody_system), intent(inout) :: self !! SyMBA nbody system object + ! Internals + integer(I4B) :: i + + associate(system => self, pltpenc_list => self%pltpenc_list, plplenc_list => self%plplenc_list, mergeadd_list => self%mergeadd_list, mergesub_list => self%mergesub_list) + select type(pl => system%pl) + class is (symba_pl) + select type(tp => system%tp) + class is (symba_tp) + if (pl%nbody > 0) then + pl%lcollision(:) = .false. + pl%kin(:)%parent = [(i, i=1, pl%nbody)] + pl%kin(:)%nchild = 0 + do i = 1, pl%nbody + if (allocated(pl%kin(i)%child)) deallocate(pl%kin(i)%child) + end do + pl%nplenc(:) = 0 + pl%ntpenc(:) = 0 + pl%levelg(:) = 0 + pl%levelm(:) = 0 + pl%lencounter = .false. + pl%lcollision = .false. + plplenc_list%nenc = 0 + end if + + if (tp%nbody > 0) then + tp%nplenc(:) = 0 + tp%levelg(:) = 0 + tp%levelm(:) = 0 + pltpenc_list%nenc = 0 + end if + + call mergeadd_list%resize(0) + call mergesub_list%resize(0) + end select + end select + end associate + + return + end subroutine symba_step_reset_system + end submodule s_symba_step diff --git a/src/symba/symba_util.f90 b/src/symba/symba_util.f90 new file mode 100644 index 000000000..98c8889d8 --- /dev/null +++ b/src/symba/symba_util.f90 @@ -0,0 +1,861 @@ +submodule(symba_classes) s_symba_util + use swiftest +contains + + module subroutine symba_util_append_arr_info(arr, source, lsource_mask) + !! author: David A. Minton + !! + !! 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 + 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 + + + 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_fill_arr_info + + + module subroutine symba_util_fill_arr_kin(keeps, inserts, lfill_list) + !! author: David A. Minton + !! + !! Performs a fill operation on a single array of particle kinship types + !! 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(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_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_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_arr_info(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_particle_info), dimension(:), allocatable, intent(inout) :: arr !! Array to resize + integer(I4B), intent(in) :: nnew !! New size + ! Internals + type(symba_particle_info), 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 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 + tmp(1:nnew) = arr(1:nnew) + end if + 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_tp + + + module subroutine symba_util_sort_pl(self, sortby, ascending) + !! author: David A. Minton + !! + !! Sort a SyMBA massive body object in-place. + !! sortby is a string indicating which array component to sort. + implicit none + ! Arguments + class(symba_pl), intent(inout) :: self !! SyMBA 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 + ! Internals + integer(I4B), dimension(self%nbody) :: ind + integer(I4B) :: direction + + if (self%nbody == 0) return + + if (ascending) then + direction = 1 + else + direction = -1 + end if + + associate(pl => self, npl => self%nbody) + select case(sortby) + case("nplenc") + call util_sort(direction * pl%nplenc(1:npl), ind(1:npl)) + case("ntpenc") + call util_sort(direction * pl%ntpenc(1:npl), ind(1:npl)) + case("levelg") + call util_sort(direction * pl%levelg(1:npl), ind(1:npl)) + case("levelm") + call util_sort(direction * pl%levelm(1:npl), ind(1:npl)) + case("peri") + call util_sort(direction * pl%peri(1:npl), ind(1:npl)) + case("atp") + call util_sort(direction * pl%atp(1:npl), ind(1:npl)) + case("lcollision", "lencounter", "lmtiny", "nplm", "nplplm", "kin", "info") + write(*,*) 'Cannot sort by ' // trim(adjustl(sortby)) // '. Component not sortable!' + case default ! Look for components in the parent class + call util_sort_pl(pl, sortby, ascending) + return + end select + + call pl%rearrange(ind) + + end associate + return + end subroutine symba_util_sort_pl + + + module subroutine symba_util_sort_tp(self, sortby, ascending) + !! author: David A. Minton + !! + !! Sort a SyMBA test particle object in-place. + !! sortby is a string indicating which array component to sort. + implicit none + ! Arguments + class(symba_tp), intent(inout) :: self !! SyMBA test particle 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 + ! Internals + integer(I4B), dimension(self%nbody) :: ind + integer(I4B) :: direction + + if (self%nbody == 0) return + + if (ascending) then + direction = 1 + else + direction = -1 + end if + + associate(tp => self, ntp => self%nbody) + select case(sortby) + case("nplenc") + call util_sort(direction * tp%nplenc(1:ntp), ind(1:ntp)) + case("levelg") + call util_sort(direction * tp%levelg(1:ntp), ind(1:ntp)) + case("levelm") + call util_sort(direction * tp%levelm(1:ntp), ind(1:ntp)) + case default ! Look for components in the parent class + call util_sort_tp(tp, sortby, ascending) + return + end select + + call tp%rearrange(ind) + end associate + + return + end subroutine symba_util_sort_tp + + + module subroutine symba_util_sort_rearrange_pl(self, ind) + !! author: David A. Minton + !! + !! Rearrange SyMBA massive body structure in-place from an index list. + !! This is a helper utility used to make polymorphic sorting work on Swiftest structures. + implicit none + ! Arguments + class(symba_pl), 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) + ! Internals + class(symba_pl), allocatable :: pl_sorted !! Temporary holder for sorted body + integer(I4B) :: i, j + + associate(pl => self, npl => self%nbody) + call util_sort_rearrange_pl(pl,ind) + allocate(pl_sorted, source=self) + 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 if + deallocate(pl_sorted) + end associate + + return + end subroutine symba_util_sort_rearrange_pl + + + module subroutine symba_util_sort_rearrange_tp(self, ind) + !! author: David A. Minton + !! + !! Rearrange SyMBA test particle object in-place from an index list. + !! This is a helper utility used to make polymorphic sorting work on Swiftest structures. + implicit none + ! Arguments + class(symba_tp), intent(inout) :: self !! SyMBA test particle 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) + ! Internals + class(symba_tp), allocatable :: tp_sorted !! Temporary holder for sorted body + + associate(tp => self, ntp => self%nbody) + call util_sort_rearrange_tp(tp,ind) + allocate(tp_sorted, source=self) + 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/tides/tides_getacch_pl.f90 b/src/tides/tides_getacch_pl.f90 new file mode 100644 index 000000000..f0bf64cc7 --- /dev/null +++ b/src/tides/tides_getacch_pl.f90 @@ -0,0 +1,65 @@ +submodule(swiftest_classes) s_tides_kick_getacch + use swiftest +contains + + module subroutine tides_kick_getacch_pl(self, system) + !! author: Jennifer L.L. Pouplin, Carlisle A. wishard, and David A. Minton + !! + !! Calculated tidal torques from central body to any planet and from any planet to central body + !! planet - planet interactions are considered negligable. + !! This is a constant time lag model. + !! + !! Adapted from Mercury-T code from Bolmont et al. (2015) + !! + !! Reference: + !! Bolmont, E., Raymond, S.N., Leconte, J., Hersant, F., Correia, A.C.M., 2015. + !! Mercury-T : A new code to study tidally evolving multi-planet systems. + !! Applications to Kepler-62. A&A 583, A116. https://doi.org/10.1051/0004-6361/201525909 + implicit none + ! Arguments + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest nbody system object + ! Internals + integer(I4B) :: i + real(DP) :: rmag, vmag + real(DP), dimension(NDIM) :: r_unit, v_unit, h_unit, theta_unit, theta_dot, F_T + real(DP) :: Ftr, Ptopl, Ptocb, r5cbterm, r5plterm + + associate(pl => self, npl => self%nbody, cb => system%cb) + pl%atide(:,:) = 0.0_DP + cb%atide(:) = 0.0_DP + do i = 1, npl + rmag = norm2(pl%xh(:,i)) + vmag = norm2(pl%vh(:,i)) + r_unit(:) = pl%xh(:,i) / rmag + v_unit(:) = pl%vh(:,i) / vmag + h_unit(:) = r_unit(:) .cross. v_unit(:) + theta_unit(:) = h_unit(:) .cross. r_unit(:) + theta_dot = dot_product(pl%vh(:,i), theta_unit(:)) + + ! First calculate the tangential component of the force vector (eq. 5 & 6 of Bolmont et al. 2015) + ! The radial component is already computed in the obl_acc methods + r5cbterm = pl%Gmass(i)**2 * cb%k2 * cb%radius**5 + r5plterm = cb%Gmass**2 * pl%k2(i) * pl%radius(i)**5 + + Ptopl = 3 * r5plterm * pl%tlag(i) / rmag**7 + Ptocb = 3 * r5cbterm * cb%tlag / rmag**7 + + Ftr = -3 / rmag**7 * (r5cbterm + r5plterm) - 3 * vmag / rmag * (Ptocb + Ptopl) + + F_T(:) = (Ftr + (Ptocb + Ptopl) * dot_product(v_unit, r_unit) / rmag) * r_unit(:) & + + Ptopl * (pl%rot(:,i) - theta_dot(:)) .cross. r_unit(:) & + + Ptocb * (cb%rot(:) - theta_dot(:)) .cross. r_unit(:) + cb%atide(:) = cb%atide(:) + F_T(:) / cb%Gmass + pl%atide(:,i) = F_T(:) / pl%Gmass(i) + end do + + do i = 1, npl + pl%ah(:,i) = pl%ah(:,i) + pl%atide(:,i) + cb%atide(:) + end do + end associate + + return + end subroutine tides_kick_getacch_pl + +end submodule s_tides_kick_getacch \ No newline at end of file diff --git a/src/tides/tides_spin_step.f90 b/src/tides/tides_spin_step.f90 new file mode 100644 index 000000000..576aff8d7 --- /dev/null +++ b/src/tides/tides_spin_step.f90 @@ -0,0 +1,132 @@ +submodule(swiftest_classes) s_tides_step_spin + use swiftest + + type, extends(lambda_obj_tvar) :: tides_derivs_func + !! Base class for an lambda function object. This object takes no additional arguments other than the dependent variable x, an array of real numbers + procedure(tidederiv), pointer, nopass :: lambdaptr_tides_deriv + real(DP), dimension(:,:), allocatable :: xbeg + real(DP), dimension(:,:), allocatable :: xend + real(DP) :: dt + contains + generic :: init => tides_derivs_init + procedure :: evalt => tides_derivs_eval + procedure, nopass :: tides_derivs_init + end type + interface lambda_obj + module procedure tides_derivs_init + end interface + abstract interface + function tidederiv(x, t, dt, xbeg, xend) result(y) + ! Template for a 0 argument function + import DP, swiftest_nbody_system + real(DP), dimension(:), intent(in) :: x + real(DP), intent(in) :: t + real(DP), intent(in) :: dt + real(DP), dimension(:,:), intent(in) :: xbeg + real(DP), dimension(:,:), intent(in) :: xend + real(DP), dimension(:), allocatable :: y + end function + end interface + +contains + + module subroutine tides_step_spin_system(self, param, t, dt) + !! author: Jennifer L.L. Pouplin and David A. Minton + !! + !! Integrates the spin equations for central and massive bodies of the system subjected to tides. + implicit none + ! Arguments + class(swiftest_nbody_system), intent(inout) :: self !! Swiftest nbody system object + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters + real(DP), intent(in) :: t !! Simulation time + real(DP), intent(in) :: dt !! Current stepsize + ! Internals + real(DP), dimension(:), allocatable :: rot0, rot1 + real(DP) :: subt + real(DP), parameter :: tol=1e-6_DP !! Just a guess at the moment + real(DP) :: subdt + + associate(pl => self%pl, npl => self%pl%nbody, cb => self%cb) + allocate(rot0(NDIM*(npl+1))) + rot0 = [pack(pl%rot(:,1:npl),.true.), pack(cb%rot(:),.true.)] + ! Use this space call the ode_solver, passing tides_spin_derivs as the function: + subdt = dt / 20._DP + !rot1(:) = util_solve_rkf45(lambda_obj(tides_spin_derivs, subdt, pl%xbeg, pl%xend), rot0, dt, subdt tol) + ! Recover with unpack + !pl%rot(:,1:npl) = unpack(rot1... + !cb%rot(:) = unpack(rot1... + end associate + + return + end subroutine tides_step_spin_system + + + function tides_spin_derivs(rot_pl_cb, t, dt, xbeg, xend) result(drot) !! Need to add more arguments so we can pull in mass, radius, Ip, J2, etc... + !! author: Jennifer L.L. Pouplin and David A. Minton + !! + !! function used to calculate the derivatives that are fed to the ODE solver + implicit none + ! Arguments + real(DP), dimension(:,:), intent(in) :: rot_pl_cb !! Array of rotations. The last element is the central body, and all others are massive bodies + real(DP), intent(in) :: t !! Current time, which is used to interpolate the massive body positions + real(DP), intent(in) :: dt !! Total step size + real(DP), dimension(:,:), intent(in) :: xbeg + real(DP), dimension(:,:), intent(in) :: xend + ! Internals + real(DP), dimension(:,:), allocatable :: drot + real(DP), dimension(:), allocatable :: flatrot + real(DP), dimension(NDIM) :: N_Tcb, N_Rcb, N_Tpl, N_Rpl, xinterp + real(DP) :: C_cb, C_pl, r_dot_rot_cb, r_dot_rot_pl, rmag + integer(I4B) :: i, n + + + n = size(rot_pl_cb,2) + if (allocated(drot)) deallocate(drot) + allocate(drot, mold=rot_pl_cb) + drot(:,:) = 0.0_DP + do i = 1,n-1 + xinterp(:) = xbeg(:,i) + t / dt * (xend(:,i) - xbeg(:,i)) + ! Calculate Ncb and Npl as a function of xinterp + !drot(:,i) = -Mcb / (Mcb + Mpl(i)) * (N_Tpl + N_Rpl) + !drot(:,n) = drot(:,n) - Mcb / (Mcb + Mpl(i) * (N_Tcb + N_Rcb) + ! + end do + + return + end function tides_spin_derivs + + function tides_derivs_eval(self, x, t) result(y) + implicit none + ! Arguments + class(tides_derivs_func), intent(inout) :: self + real(DP), dimension(:), intent(in) :: x + real(DP), intent(in) :: t + ! Result + real(DP), dimension(:), allocatable :: y + if (associated(self%lambdaptr_tides_deriv)) then + y = self%lambdaptr_tides_deriv(x, t, self%dt, self%xbeg, self%xend) + else + error stop "Lambda function was not initialized" + end if + + return + end function tides_derivs_eval + + function tides_derivs_init(lambda, dt, xbeg, xend) result(f) + implicit none + ! Arguments + procedure(tidederiv) :: lambda + real(DP), intent(in) :: dt + real(DP), dimension(:,:), intent(in) :: xbeg + real(DP), dimension(:,:), intent(in) :: xend + ! Result + type(tides_derivs_func) :: f + f%lambdaptr_tides_deriv => lambda + f%dt = dt + allocate(f%xbeg, source = xbeg) + allocate(f%xend, source = xend) + + return + end function tides_derivs_init + +end submodule s_tides_step_spin \ No newline at end of file diff --git a/src/user/user_getacch.f90 b/src/user/user_getacch.f90 index c54c21693..2775de3dd 100644 --- a/src/user/user_getacch.f90 +++ b/src/user/user_getacch.f90 @@ -1,21 +1,21 @@ -submodule(swiftest_classes) s_user_getacch +submodule(swiftest_classes) s_user_kick_getacch use swiftest contains - module subroutine user_getacch_body(self, system, param, t, lbeg) + module subroutine user_kick_getacch_body(self, system, param, t, lbeg) !! author: David A. Minton !! !! Add user-supplied heliocentric accelerations to planets. !! - !! Adapted from David E. Kaufmann's Swifter routine whm_user_getacch.f90 + !! Adapted from David E. Kaufmann's Swifter routine whm_user_kick_getacch.f90 implicit none ! Arguments class(swiftest_body), intent(inout) :: self !! Swiftest 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 of user parameters + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters user parameters real(DP), intent(in) :: t !! Current time - logical, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the ste + logical, intent(in) :: lbeg !! Logical flag that determines whether or not this is the beginning or end of the ste return - end subroutine user_getacch_body + end subroutine user_kick_getacch_body -end submodule s_user_getacch +end submodule s_user_kick_getacch 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 387fc8f6b..c10dbace7 100644 --- a/src/util/util_coord.f90 +++ b/src/util/util_coord.f90 @@ -1,6 +1,7 @@ submodule(swiftest_classes) s_util_coord use swiftest contains + module subroutine util_coord_h2b_pl(self, cb) !! author: David A. Minton !! @@ -14,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(:) @@ -37,6 +39,7 @@ module subroutine util_coord_h2b_pl(self, cb) return end subroutine util_coord_h2b_pl + module subroutine util_coord_h2b_tp(self, cb) !! author: David A. Minton !! @@ -49,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) @@ -66,6 +70,7 @@ module subroutine util_coord_h2b_tp(self, cb) return end subroutine util_coord_h2b_tp + module subroutine util_coord_b2h_pl(self, cb) !! author: David A. Minton !! @@ -80,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 @@ -91,6 +98,7 @@ module subroutine util_coord_b2h_pl(self, cb) return end subroutine util_coord_b2h_pl + module subroutine util_coord_b2h_tp(self, cb) !! author: David A. Minton !! @@ -103,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) @@ -118,4 +128,5 @@ module subroutine util_coord_b2h_tp(self, cb) return end subroutine util_coord_b2h_tp + end submodule s_util_coord \ No newline at end of file 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 4413bd9b3..e770c10f5 100644 --- a/src/util/util_exit.f90 +++ b/src/util/util_exit.f90 @@ -1,6 +1,7 @@ submodule (swiftest_classes) s_util_exit use swiftest contains + module subroutine util_exit(code) !! author: David A. Minton !! @@ -25,9 +26,11 @@ module subroutine util_exit(code) case default write(*, FAIL_MSG) VERSION_NUMBER write(*, BAR) + error stop end select stop end subroutine util_exit + end submodule s_util_exit 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..38701229d --- /dev/null +++ b/src/util/util_get_energy_momentum.f90 @@ -0,0 +1,117 @@ +submodule (swiftest_classes) s_util_get_energy_momentum + use swiftest +contains + module subroutine util_get_energy_momentum_system(self, param) + !! 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 + ! 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 + + associate(system => self, pl => self%pl, npl => self%pl%nbody, cb => self%cb) + system%Lorbit(:) = 0.0_DP + system%Lspin(:) = 0.0_DP + system%ke_orbit = 0.0_DP + system%ke_spin = 0.0_DP + + 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 + + system%ke_orbit = 0.5_DP * sum(kepl(1:npl), lstatus(:)) + if (param%lrotation) system%ke_spin = 0.5_DP * sum(kespinpl(1:npl), lstatus(:)) + + system%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) + system%pe = system%pe + oblpot + end if + + system%Lorbit(1) = sum(Lplorbitx(1:npl), lstatus(1:npl)) + system%Lorbit(2) = sum(Lplorbity(1:npl), lstatus(1:npl)) + system%Lorbit(3) = sum(Lplorbitz(1:npl), lstatus(1:npl)) + + system%Lspin(1) = sum(Lplspinx(1:npl), lstatus(1:npl)) + system%Lspin(2) = sum(Lplspiny(1:npl), lstatus(1:npl)) + system%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_index.f90 b/src/util/util_index.f90 deleted file mode 100644 index fcece8809..000000000 --- a/src/util/util_index.f90 +++ /dev/null @@ -1,103 +0,0 @@ -submodule (swiftest_classes) s_util_index - use swiftest -contains - module subroutine util_index(arr, index) - !! author: David A. Minton - !! - !! Index input real array into ascending numerical order using Quicksort algorithm - !! - !! Adapted from David E. Kaufmann's Swifter routine: util_index.f90 - !! Adapted from Numerical Recipes in Fortran 90: The Art of Parallel Scientific Computing, by Press, Teukolsky, - !! Vetterling, and Flannery, 2nd ed., pp. 1173-4 - implicit none - ! Arguments - integer(I4B), dimension(:), intent(out) :: index - real(DP), dimension(:), intent(in) :: arr - ! Internals - integer(I4B), parameter :: nn = 15, nstack = 50 - integer(I4B) :: n, k, i, j, indext, jstack, l, r, dum - integer(I4B), dimension(nstack) :: istack - real(DP) :: a - - n = size(arr) - if (n /= size(index)) then - write(*, *) "Swiftest Error:" - write(*, *) " array size mismatch in util_index" - call util_exit(FAILURE) - end if - index = arth(1, 1, n) - jstack = 0 - ! l is the counter ie 'the one we are at' - l = 1 - ! r is the length of the array ie 'the total number of particles' - r = n - do - if ((r - l) < nn) then - do j = l + 1, r - indext = index(j) - a = arr(indext) - do i = j - 1, l, -1 - if (arr(index(i)) <= a) exit - index(i+1) = index(i) - end do - index(i+1) = indext - end do - if (jstack == 0) return - r = istack(jstack) - l = istack(jstack-1) - jstack = jstack - 2 - else - k = (l + r)/2 - dum = index(k); index(k) = index(l+1); index(l+1) = dum - ! if the mass of the particle we are at in our counting is greater than the mass of the last particle then put the particle we are at above the last one - if (arr(index(l)) > arr(index(r))) then - dum = index(l); index(l) = index(r); index(r) = dum - end if - ! if the mass of the particle above the one we are at in our counting is greater than the last particle then put that particle above the last one - if (arr(index(l+1)) > arr(index(r))) then - dum = index(l+1); index(l+1) = index(r); index(r) = dum - end if - ! if the mass of teh particle we are at in our counting is greater than the one above it, then put it above the one above it - if (arr(index(l)) > arr(index(l+1))) then - dum = index(l); index(l) = index(l+1); index(l+1) = dum - end if - i = l + 1 - j = r - indext = index(l+1) - a = arr(indext) - do - do - i = i + 1 - if (arr(index(i)) >= a) exit - end do - do - j = j - 1 - if (arr(index(j)) <= a) exit - end do - if (j < i) exit - dum = index(i); index(i) = index(j); index(j) = dum - end do - index(l+1) = index(j) - index(j) = indext - jstack = jstack + 2 - if (jstack > nstack) then - write(*, *) "Swiftest Error:" - write(*, *) " nstack too small in util_sort" - call util_exit(FAILURE) - end if - if ((r - i + 1) >= (j - l)) then - istack(jstack) = r - istack(jstack-1) = i - r = j - 1 - else - istack(jstack) = j - 1 - istack(jstack-1) = l - l = i - end if - end if - end do - - return - - end subroutine util_index -end submodule s_util_index diff --git a/src/util/util_minimize_bfgs.f90 b/src/util/util_minimize_bfgs.f90 new file mode 100644 index 000000000..fbd48c8c2 --- /dev/null +++ b/src/util/util_minimize_bfgs.f90 @@ -0,0 +1,584 @@ +submodule (swiftest_classes) s_util_minimize_bfgs + use swiftest +contains + module function util_minimize_bfgs(f, N, x0, eps, lerr) result(x1) + !! author: David A. Minton + !! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + !! This function implements the Broyden-Fletcher-Goldfarb-Shanno method to determine the minimum of a function of N variables. + !! It recieves as input: + !! f%eval(x) : lambda function object containing the objective function as the eval metho + !! N : Number of variables of function f + !! x0 : Initial starting value of x + !! eps : Accuracy of 1 - dimensional minimization at each step + !! The outputs include + !! lerr : Returns .true. if it could not find the minimum + !! Returns + !! x1 : Final minimum (all 0 if none found) + !! 0 = No miniumum found + !! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + use, intrinsic :: ieee_exceptions + implicit none + ! Arguments + integer(I4B), intent(in) :: N + class(lambda_obj), intent(inout) :: f + real(DP), dimension(:), intent(in) :: x0 + real(DP), intent(in) :: eps + logical, intent(out) :: lerr + ! Result + real(DP), dimension(:), allocatable :: x1 + ! Internals + integer(I4B) :: i, j, k, l, conv, num + integer(I4B), parameter :: MAXLOOP = 1000 !! Maximum number of loops before method is determined to have failed + real(DP), parameter :: graddelta = 1e-4_DP !! Delta x for gradient calculations + real(DP), dimension(N) :: S !! Direction vectors + real(DP), dimension(N,N) :: H !! Approximated inverse Hessian matrix + real(DP), dimension(N) :: grad1 !! gradient of f + real(DP), dimension(N) :: grad0 !! old value of gradient + real(DP) :: astar !! 1D minimized value + real(DP), dimension(N) :: y, P + real(DP), dimension(N,N) :: PP, PyH, HyP + real(DP) :: yHy, Py + type(ieee_status_type) :: original_fpe_status + logical, dimension(:), allocatable :: fpe_flag + + call ieee_get_status(original_fpe_status) ! Save the original floating point exception status + call ieee_set_flag(ieee_all, .false.) ! Set all flags to quiet + allocate(fpe_flag(size(ieee_usual))) + + lerr = .false. + allocate(x1, source=x0) + ! Initialize approximate Hessian with the identity matrix (i.e. begin with method of steepest descent) + ! Get initial gradient and initialize arrays for updated values of gradient and x + H(:,:) = reshape([((0._DP, i=1, j-1), 1._DP, (0._DP, i=j+1, N), j=1, N)], [N,N]) + grad0 = gradf(f, N, x0(:), graddelta, lerr) + if (lerr) then + call ieee_set_status(original_fpe_status) + return + end if + grad1(:) = grad0(:) + do i = 1, MAXLOOP + !check for convergence + conv = count(abs(grad1(:)) > eps) + if (conv == 0) then + !write(*,*) "BFGS converged on gradient after ",i," iterations" + exit + end if + S(:) = -matmul(H(:,:), grad1(:)) + astar = minimize1D(f, x1, S, N, graddelta, lerr) + if (lerr) then + !write(*,*) "Exiting BFGS with error in minimize1D step" + exit + end if + ! Get new x values + P(:) = astar * S(:) + x1(:) = x1(:) + P(:) + ! Calculate new gradient + grad0(:) = grad1(:) + grad1 = gradf(f, N, x1, graddelta, lerr) + y(:) = grad1(:) - grad0(:) + Py = sum(P(:) * y(:)) + ! set up factors for H matrix update + yHy = 0._DP + do k = 1, N + do j = 1, N + yHy = yHy + y(j) * H(j,k) * y(k) + end do + end do + ! prevent divide by zero (convergence) + if (abs(Py) < tiny(Py)) then + !write(*,*) "BFGS Converged on tiny Py after ",i," iterations" + exit + end if + ! set up update + PyH(:,:) = 0._DP + HyP(:,:) = 0._DP + do k = 1, N + do j = 1, N + PP(j, k) = P(j) * P(k) + do l = 1, N + PyH(j, k) = PyH(j, k) + P(j) * y(l) * H(l,k) + HyP(j, k) = HyP(j, k) + P(k) * y(l) * H(j,l) + end do + end do + end do + ! update H matrix + H(:,:) = H(:,:) + ((1._DP - yHy / Py) * PP(:,:) - PyH(:,:) - HyP(:,:)) / Py + ! Normalize to prevent it from blowing up if it takes many iterations to find a solution + H(:,:) = H(:,:) / norm2(H(:,:)) + ! Stop everything if there are any exceptions to allow the routine to fail gracefully + call ieee_get_flag(ieee_usual, fpe_flag) + if (any(fpe_flag)) exit + if (i == MAXLOOP) then + lerr = .true. + !write(*,*) "BFGS ran out of loops!" + end if + end do + call ieee_get_flag(ieee_usual, fpe_flag) + lerr = lerr .or. any(fpe_flag) + !if (any(fpe_flag)) write(*,*) 'BFGS did not converge due to fpe' + !if (lerr) write(*,*) "BFGS did not converge!" + call ieee_set_status(original_fpe_status) + + return + + contains + + function gradf(f, N, x1, dx, lerr) result(grad) + !! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + !! Purpose: Estimates the gradient of a function using a central difference + !! approximation + !! Inputs: + !! f%eval(x) : lambda function object containing the objective function as the eval metho + !! N : number of variables N + !! x1 : x value array + !! dx : step size to use when calculating derivatives + !! Outputs: + !! lerr : .true. if an error occurred. Otherwise returns .false. + !! Returns + !! grad : N sized array containing estimated gradient of f at x1 + !! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + implicit none + ! Arguments + integer(I4B), intent(in) :: N + class(lambda_obj), intent(inout) :: f + real(DP), dimension(:), intent(in) :: x1 + real(DP), intent(in) :: dx + logical, intent(out) :: lerr + ! Result + real(DP), dimension(N) :: grad + ! Internals + integer(I4B) :: i, j + real(DP), dimension(N) :: xp, xm + real(DP) :: fp, fm + logical :: lerrp, lerrm + + do i = 1, N + do j = 1, N + if (j == i) then + xp(j) = x1(j) + dx + xm(j) = x1(j) - dx + else + xp(j) = x1(j) + xm(j) = x1(j) + end if + end do + select type (f) + class is (lambda_obj_err) + fp = f%eval(xp) + lerrp = f%lerr + fm = f%eval(xm) + lerrm = f%lerr + lerr = lerrp .or. lerrm + class is (lambda_obj) + fp = f%eval(xp) + fm = f%eval(xm) + lerr = .false. + end select + grad(i) = (fp - fm) / (2 * dx) + if (lerr) return + end do + return + end function gradf + + function minimize1D(f, x0, S, N, eps, lerr) result(astar) + !! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + !! This program find the minimum of a function of N variables in a single direction + !! S using in sequence: + !! 1. A Bracketing method + !! 2. The golden section method + !! 3. A quadratic polynomial fit + !! Inputs + !! f%eval(x) : lambda function object containing the objective function as the eval metho + !! x0 : Array of size N of initial x values + !! S : Array of size N that determines the direction of minimization + !! N : Number of variables of function f + !! eps : Accuracy of 1 - dimensional minimization at each step + !! Output + !! lerr : .true. if an error occurred. Otherwise returns .false. + !! Returns + !! astar : Final minimum along direction S + !! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + implicit none + ! Arguments + integer(I4B), intent(in) :: N + class(lambda_obj), intent(inout) :: f + real(DP), dimension(:), intent(in) :: x0, S + real(DP), intent(in) :: eps + logical, intent(out) :: lerr + ! Result + real(DP) :: astar + ! Internals + integer(I4B) :: num = 0 + real(DP), parameter :: step = 0.7_DP !! Bracketing method step size + real(DP), parameter :: gam = 1.2_DP !! Bracketing method expansion parameter + real(DP), parameter :: greduce = 0.2_DP !! Golden section method reduction factor + real(DP), parameter :: greduce2 = 0.1_DP ! Secondary golden section method reduction factor + real(DP) :: alo, ahi !! High and low values for 1 - D minimization routines + real(DP), parameter :: a0 = epsilon(1.0_DP) !! Initial guess of alpha + + alo = a0 + call bracket(f, x0, S, N, gam, step, alo, ahi, lerr) + if (lerr) then + !write(*,*) "BFGS bracketing step failed!" + return + end if + if (abs(alo - ahi) < eps) then + astar = alo + lerr = .false. + return + end if + call golden(f, x0, S, N, greduce, alo, ahi, lerr) + if (lerr) then + !write(*,*) "BFGS golden section step failed!" + return + end if + if (abs(alo - ahi) < eps) then + astar = alo + lerr = .false. + return + end if + call quadfit(f, x0, S, N, eps, alo, ahi, lerr) + if (lerr) then + !write(*,*) "BFGS quadfit failed!" + return + end if + if (abs(alo - ahi) < eps) then + astar = alo + lerr = .false. + return + end if + ! Quadratic fit method won't converge, so finish off with another golden section + call golden(f, x0, S, N, greduce2, alo, ahi, lerr) + if (.not. lerr) astar = (alo + ahi) / 2.0_DP + return + end function minimize1D + + function n2one(f, x0, S, N, a, lerr) result(fnew) + implicit none + ! Arguments + integer(I4B), intent(in) :: N + class(lambda_obj), intent(inout) :: f + real(DP), dimension(:), intent(in) :: x0, S + real(DP), intent(in) :: a + logical, intent(out) :: lerr + + ! Return + real(DP) :: fnew + ! Internals + real(DP), dimension(N) :: xnew + integer(I4B) :: i + + xnew(:) = x0(:) + a * S(:) + fnew = f%eval(xnew(:)) + select type(f) + class is (lambda_obj_err) + lerr = f%lerr + class is (lambda_obj) + lerr = .false. + end select + return + end function n2one + + ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + subroutine bracket(f, x0, S, N, gam, step, lo, hi, lerr) + ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + !! This subroutine brackets the minimum. It recieves as input: + !! f%eval(x) : lambda function object containing the objective function as the eval metho + !! x0 : Array of size N of initial x values + !! S : Array of size N that determines the direction of minimization + !! gam : expansion parameter + !! step : step size + !! lo : initial guess of lo bracket value + !! The outputs include + !! lo : lo bracket + !! hi : hi bracket + !! lerr : .true. if an error occurred. Otherwise returns .false. + !! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + implicit none + ! Arguments + integer(I4B), intent(in) :: N + class(lambda_obj), intent(inout) :: f + real(DP), dimension(:), intent(in) :: x0, S + real(DP), intent(in) :: gam, step + real(DP), intent(inout) :: lo + real(DP), intent(out) :: hi + logical, intent(out) :: lerr + ! Internals + real(DP) :: a0, a1, a2, atmp, da + real(DP) :: f0, f1, f2 + integer(I4B) :: i, j + integer(I4B), parameter :: MAXLOOP = 100 ! maximum number of loops before method is determined to have failed + real(DP), parameter :: eps = epsilon(lo) ! small number precision to test floating point equality + + ! set up initial bracket points + a0 = lo + da = step + a1 = a0 + da + a2 = a0 + 2 * da + f0 = n2one(f, x0, S, N, a0, lerr) + if (lerr) return + f1 = n2one(f, x0, S, N, a1, lerr) + if (lerr) return + f2 = n2one(f, x0, S, N, a2, lerr) + if (lerr) return + ! loop over bracket method until either min is bracketed method fails + do i = 1, MAXLOOP + if ((f0 > f1) .and. (f1 < f2)) then ! Minimum was found + lo = a0 + hi = a2 + return + else if ((f0 >= f1) .and. (f1 > f2)) then ! Function appears to decrease + da = da * gam + atmp = a2 + da + a0 = a1 + a1 = a2 + a2 = atmp + f0 = f1 + f1 = f2 + f2 = n2one(f, x0, S, N, a2, lerr) + else if ((f0 < f1) .and. (f1 <= f2)) then ! Function appears to increase + da = da * gam + atmp = a0 - da + a2 = a1 + a1 = a0 + a0 = atmp + f2 = f1 + f0 = n2one(f, x0, S, N, a0, lerr) + else if ((f0 < f1) .and. (f1 > f2)) then ! We are at a peak. Pick the direction that descends the fastest + da = da * gam + if (f2 > f0) then ! LHS is lower than RHS + atmp = a2 + da + a0 = a1 + a1 = a2 + a2 = atmp + f0 = f1 + f1 = f2 + f2 = n2one(f, x0, S, N, a2, lerr) + else ! RHS is lower than LHS + atmp = a0 - da + a2 = a1 + a1 = a0 + a0 = atmp + f2 = f1 + f1 = f2 + f0 = n2one(f, x0, S, N, a0, lerr) + end if + else if ((f0 > f1) .and. (abs(f2 - f1) <= eps)) then ! Decrasging but RHS equal + da = da * gam + atmp = a2 + da + a2 = atmp + f2 = n2one(f, x0, S, N, a2, lerr) + else if ((abs(f0 - f1) < eps) .and. (f1 < f2)) then ! Increasing but LHS equal + da = da * gam + atmp = a0 - da + a0 = atmp + f0 = n2one(f, x0, S, N, a0, lerr) + else ! all values equal. Expand in either direction and try again + a0 = a0 - da + a2 = a2 + da + f0 = n2one(f, x0, S, N, a0, lerr) + if (lerr) exit ! An error occurred while evaluating the function + f2 = n2one(f, x0, S, N, a2, lerr) + end if + if (lerr) exit ! An error occurred while evaluating the function + end do + lerr = .true. + return ! no minimum found + end subroutine bracket + + ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + subroutine golden(f, x0, S, N, eps, lo, hi, lerr) + ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + !! This function uses the golden section method to reduce the starting interval lo, hi by some amount sigma. + !! It recieves as input: + !! f%eval(x) : lambda function object containing the objective function as the eval metho + !! x0 : Array of size N of initial x values + !! S : Array of size N that determines the direction of minimization + !! gam : expansion parameter + !! eps : reduction interval in range (0 < sigma < 1) such that: + !! hi(new) - lo(new) = eps * (hi(old) - lo(old)) + !! lo : initial guess of lo bracket value + !! The outputs include + !! lo : lo bracket + !! hi : hi bracket + !! lerr : .true. if an error occurred. Otherwise returns .false. + !! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + implicit none + ! Arguments + integer(I4B), intent(in) :: N + class(lambda_obj), intent(inout) :: f + real(DP), dimension(:), intent(in) :: x0, S + real(DP), intent(in) :: eps + real(DP), intent(inout) :: lo + real(DP), intent(out) :: hi + logical, intent(out) :: lerr + ! Internals + real(DP), parameter :: tau = 0.5_DP * (sqrt(5.0_DP) - 1.0_DP) ! Golden section constant + integer(I4B), parameter :: MAXLOOP = 40 ! maximum number of loops before method is determined to have failed (unlikely, but could occur if no minimum exists between lo and hi) + real(DP) :: i0 ! Initial interval value + real(DP) :: a1, a2 + real(DP) :: f1, f2 + integer(I4B) :: i, j + + i0 = hi - lo + a1 = hi - tau * i0 + a2 = lo + tau * i0 + f1 = n2one(f, x0, S, N, a1, lerr) + if (lerr) return + f2 = n2one(f, x0, S, N, a2, lerr) + if (lerr) return + do i = 1, MAXLOOP + if (abs((hi - lo) / i0) <= eps) return ! interval reduced to input amount + if (f2 > f1) then + hi = a2 + a2 = a1 + f2 = f1 + a1 = hi - tau * (hi - lo) + f1 = n2one(f, x0, S, N, a1, lerr) + else + lo = a1 + a1 = a2 + f2 = f1 + a2 = hi - (1.0_DP - tau) * (hi - lo) + f2 = n2one(f, x0, S, N, a2, lerr) + end if + if (lerr) exit + end do + lerr = .true. + return ! search took too many iterations - no minimum found + end subroutine golden + + ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + subroutine quadfit(f, x0, S, N, eps, lo, hi, lerr) + ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + !! This function uses a quadratic polynomial fit to locate the minimum of a function + !! to some accuracy eps. It recieves as input: + !! f%eval(x) : lambda function object containing the objective function as the eval metho + !! lo : low bracket value + !! hi : high bracket value + !! eps : desired accuracy of final minimum location + !! The outputs include + !! lo : final minimum location + !! hi : final minimum location + !! Notes: Uses the ieee_exceptions intrinsic module to allow for graceful failure due to floating point exceptions, which won't terminate the run. + !! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + implicit none + ! Arguments + integer(I4B), intent(in) :: N + class(lambda_obj), intent(inout) :: f + real(DP), dimension(:), intent(in) :: x0, S + real(DP), intent(in) :: eps + real(DP), intent(inout) :: lo + real(DP), intent(out) :: hi + logical, intent(out) :: lerr + ! Internals + integer(I4B), parameter :: MAXLOOP = 20 ! maximum number of loops before method is determined to have failed. + real(DP) :: a1, a2, a3, astar ! three points for the polynomial fit and polynomial minimum + real(DP) :: f1, f2, f3, fstar ! three function values for the polynomial and polynomial minimum + real(DP), dimension(3) :: row_1, row_2, row_3, rhs, soln ! matrix for 3 equation solver (gaussian elimination) + real(DP), dimension(3,3) :: lhs + real(DP) :: d1, d2, d3, aold, denom, errval + integer(I4B) :: i + + lerr = .false. + ! Get initial a1, a2, a3 values + a1 = lo + a2 = lo + 0.5_DP * (hi - lo) + a3 = hi + aold = a1 + astar = a2 + f1 = n2one(f, x0, S, N, a1, lerr) + if (lerr) return + f2 = n2one(f, x0, S, N, a2, lerr) + if (lerr) return + f3 = n2one(f, x0, S, N, a3, lerr) + if (lerr) return + do i = 1, MAXLOOP + ! check to see if convergence is reached and exit + errval = abs((astar - aold) / astar) + call ieee_get_flag(ieee_usual, fpe_flag) + if (any(fpe_flag)) then + !write(*,*) 'quadfit fpe' + !write(*,*) 'aold : ',aold + !write(*,*) 'astar: ',astar + lerr = .true. + exit + end if + if (errval < eps) then + lo = astar + hi = astar + exit + end if + ! Set up system for gaussian elimination equation solver + row_1 = [1.0_DP, a1, a1**2] + row_2 = [1.0_DP, a2, a2**2] + row_3 = [1.0_DP, a3, a3**2] + rhs = [f1, f2, f3] + lhs(1, :) = row_1 + lhs(2, :) = row_2 + lhs(3, :) = row_3 + ! Solve system of equations + soln(:) = util_solve_linear_system(lhs, rhs, 3, lerr) + call ieee_set_flag(ieee_all, .false.) ! Set all flags back to quiet + call ieee_set_halting_mode(ieee_divide_by_zero, .false.) + if (lerr) then + !write(*,*) 'quadfit fpe:' + !write(*,*) 'util_solve_linear_system failed' + exit + end if + aold = astar + if (soln(2) == soln(3)) then ! Handles the case where they are both 0. 0/0 is an unhandled exception + astar = -0.5_DP + else + astar = -soln(2) / (2 * soln(3)) + end if + call ieee_get_flag(ieee_usual, fpe_flag) + if (any(fpe_flag)) then + !write(*,*) 'quadfit fpe' + !write(*,*) 'soln(2:3): ',soln(2:3) + !write(*,*) 'a1, a2, a3' + !write(*,*) a1, a2, a3 + !write(*,*) 'f1, f2, f3' + !write(*,*) f1, f2, f3 + lerr = .true. + exit + end if + fstar = n2one(f, x0, S, N, astar, lerr) + if (lerr) exit + ! keep the three closest a values to astar and discard the fourth + d1 = abs(a1 - astar) + d2 = abs(a2 - astar) + d3 = abs(a3 - astar) + + if (d1 > d2) then + if (d1 > d3) then + f1 = fstar + a1 = astar + else if (d3 > d2) then + f3 = fstar + a3 = astar + end if + else + if (d2 > d3) then + f2 = fstar + a2 = astar + else if (d3 > d1) then + f3 = fstar + a3 = astar + end if + end if + end do + if (lerr) return + lo = a1 + hi = a3 + return + end subroutine quadfit + + end function util_minimize_bfgs +end submodule s_util_minimize_bfgs \ No newline at end of file diff --git a/src/util/util_peri.f90 b/src/util/util_peri.f90 index 1884728da..66f2254e1 100644 --- a/src/util/util_peri.f90 +++ b/src/util/util_peri.f90 @@ -1,6 +1,7 @@ submodule (swiftest_classes) s_util_peri use swiftest contains + module subroutine util_peri_tp(self, system, param) !! author: David A. Minton !! @@ -44,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 @@ -56,7 +57,8 @@ module subroutine util_peri_tp(self, system, param) end do end if end associate - return + return end subroutine util_peri_tp + end submodule s_util_peri 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_reverse_status.f90 b/src/util/util_reverse_status.f90 deleted file mode 100644 index 5fc0d0f22..000000000 --- a/src/util/util_reverse_status.f90 +++ /dev/null @@ -1,18 +0,0 @@ -submodule (swiftest_classes) s_util_reverse_status - use swiftest -contains - module subroutine util_reverse_status(self) - !! author: David A. Minton - !! - !! Reverses the active/inactive status of all particles in a structure - implicit none - ! Arguments - class(swiftest_body), intent(inout) :: self !! Swiftest body object - - where (self%status(:) == ACTIVE) - self%status(:) = INACTIVE - elsewhere (self%status(:) == INACTIVE) - self%status(:) = ACTIVE - end where - end subroutine util_reverse_status -end submodule s_util_reverse_status \ No newline at end of file diff --git a/src/util/util_set.f90 b/src/util/util_set.f90 index b77579de1..86e021ab6 100644 --- a/src/util/util_set.f90 +++ b/src/util/util_set.f90 @@ -4,22 +4,6 @@ use swiftest contains - module subroutine util_set_beg_end_cb(self, aoblbeg, aoblend) - !! author: David A. Minton - !! - !! Sets one or more of the values of aoblbeg and aoblend - implicit none - ! Arguments - class(swiftest_cb), intent(inout) :: self !! Swiftest central body object - real(DP), dimension(:), intent(in), optional :: aoblbeg !! Oblateness acceleration term at beginning of step - real(DP), dimension(:), intent(in), optional :: aoblend !! Oblateness acceleration term at end of step - - if (present(aoblbeg)) self%aoblbeg = aoblbeg - if (present(aoblend)) self%aoblend = aoblend - return - - end subroutine util_set_beg_end_cb - module subroutine util_set_beg_end_pl(self, xbeg, xend, vbeg) !! author: David A. Minton !! @@ -43,25 +27,53 @@ module subroutine util_set_beg_end_pl(self, xbeg, xend, vbeg) end if return - end subroutine util_set_beg_end_pl + + module subroutine util_set_ir3h(self) + !! author: David A. Minton + !! + !! Sets the inverse heliocentric radius term (1/rh**3) for all bodies in a structure + implicit none + ! Arguments + class(swiftest_body), intent(inout) :: self !! Swiftest generic body object + ! Internals + integer(I4B) :: i + real(DP) :: r2, irh + + if (self%nbody > 0) then + + do i = 1, self%nbody + r2 = dot_product(self%xh(:, i), self%xh(:, i)) + irh = 1.0_DP / sqrt(r2) + self%ir3h(i) = irh / r2 + end do + end if + + return + end subroutine util_set_ir3h + + module subroutine util_set_msys(self) !! author: David A. Minton !! !! Sets the value of msys and the vector mass quantities based on the total mass of the system implicit none - class(swiftest_nbody_system), intent(inout) :: self !! Swiftest system objec - self%msys = self%cb%mass + sum(self%pl%mass(1:self%pl%nbody)) + ! Arguments + class(swiftest_nbody_system), intent(inout) :: self !! Swiftest nobdy system object + + 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 + module subroutine util_set_mu_pl(self, cb) !! author: David A. Minton !! !! Computes G * (M + m) for each massive body implicit none + ! Arguments class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object @@ -70,11 +82,13 @@ module subroutine util_set_mu_pl(self, cb) return end subroutine util_set_mu_pl + module subroutine util_set_mu_tp(self, cb) !! author: David A. Minton !! !! Converts certain scalar values to arrays so that they can be used in elemental functions implicit none + ! Arguments class(swiftest_tp), intent(inout) :: self !! Swiftest test particle object class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object @@ -83,13 +97,15 @@ module subroutine util_set_mu_tp(self, cb) return end subroutine util_set_mu_tp + module subroutine util_set_rhill(self,cb) !! author: David A. Minton !! !! Sets the value of the Hill's radius implicit none - class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object - class(swiftest_cb), intent(inout) :: cb !! Swiftest massive body object + ! Arguments + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object if (self%nbody > 0) then call self%xv2el(cb) @@ -99,25 +115,24 @@ module subroutine util_set_rhill(self,cb) return end subroutine util_set_rhill - module subroutine util_set_ir3h(self) + + module subroutine util_set_rhill_approximate(self,cb) !! author: David A. Minton !! - !! Sets the inverse heliocentric radius term (1/rh**3) for all bodies in a structure + !! Sets the approximate value of the Hill's radius using the heliocentric radius instead of computing the semimajor axis implicit none - class(swiftest_body), intent(inout) :: self !! Swiftest generic body object - - integer(I4B) :: i - real(DP) :: r2, irh + ! Arguments + class(swiftest_pl), intent(inout) :: self !! Swiftest massive body object + class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object + ! Internals + real(DP), dimension(:), allocatable :: rh if (self%nbody > 0) then - - do i = 1, self%nbody - r2 = dot_product(self%xh(:, i), self%xh(:, i)) - irh = 1.0_DP / sqrt(r2) - self%ir3h(i) = irh / r2 - end do + rh(:) = .mag. self%xh(:,:) + self%rhill(:) = rh(:) * (self%Gmass(:) / cb%Gmass / 3)**THIRD end if return - end subroutine util_set_ir3h + end subroutine util_set_rhill_approximate + end submodule s_util_set \ No newline at end of file diff --git a/src/util/util_solve.f90 b/src/util/util_solve.f90 new file mode 100644 index 000000000..057ed1182 --- /dev/null +++ b/src/util/util_solve.f90 @@ -0,0 +1,228 @@ +submodule(swiftest_classes) s_util_solve + use swiftest +contains + + module function util_solve_linear_system_d(A,b,n,lerr) result(x) + !! Author: David A. Minton + !! + !! Solves the linear equation of the form A*x = b for x. + !! A is an (n,n) arrays + !! x and b are (n) arrays + !! Uses Gaussian elimination, so will have issues if system is ill-conditioned. + !! Uses quad precision intermidiate values, so works best on small arrays. + use, intrinsic :: ieee_exceptions + implicit none + ! Arguments + integer(I4B), intent(in) :: n + real(DP), dimension(:,:), intent(in) :: A + real(DP), dimension(:), intent(in) :: b + logical, intent(out) :: lerr + ! Result + real(DP), dimension(n) :: x + ! Internals + real(QP), dimension(:), allocatable :: qx + type(ieee_status_type) :: original_fpe_status + logical, dimension(:), allocatable :: fpe_flag + + call ieee_get_status(original_fpe_status) ! Save the original floating point exception status + call ieee_set_flag(ieee_all, .false.) ! Set all flags to quiet + allocate(fpe_flag(size(ieee_usual))) + + qx = solve_wbs(ge_wpp(real(A, kind=QP), real(b, kind=QP))) + + call ieee_get_flag(ieee_usual, fpe_flag) + lerr = any(fpe_flag) + if (lerr .or. (any(abs(qx) > huge(x))) .or. (any(abs(qx) < tiny(x)))) then + x = 0.0_DP + else + x = real(qx, kind=DP) + end if + call ieee_set_status(original_fpe_status) + + return + end function util_solve_linear_system_d + + module function util_solve_linear_system_q(A,b,n,lerr) result(x) + !! Author: David A. Minton + !! + !! Solves the linear equation of the form A*x = b for x. + !! A is an (n,n) arrays + !! x and b are (n) arrays + !! Uses Gaussian elimination, so will have issues if system is ill-conditioned. + !! Uses quad precision intermidiate values, so works best on small arrays. + use, intrinsic :: ieee_exceptions + implicit none + ! Arguments + integer(I4B), intent(in) :: n + real(QP), dimension(:,:), intent(in) :: A + real(QP), dimension(:), intent(in) :: b + logical, intent(out) :: lerr + ! Result + real(QP), dimension(n) :: x + ! Internals + type(ieee_status_type) :: original_fpe_status + logical, dimension(:), allocatable :: fpe_flag + + call ieee_get_status(original_fpe_status) ! Save the original floating point exception status + call ieee_set_flag(ieee_all, .false.) ! Set all flags to quiet + allocate(fpe_flag(size(ieee_usual))) + + x = solve_wbs(ge_wpp(A, b)) + + call ieee_get_flag(ieee_usual, fpe_flag) + lerr = any(fpe_flag) + if (lerr) x = 0.0_DP + call ieee_set_status(original_fpe_status) + + return + end function util_solve_linear_system_q + + function solve_wbs(u) result(x) ! solve with backward substitution + !! Based on code available on Rosetta Code: https://rosettacode.org/wiki/Gaussian_elimination#Fortran + use, intrinsic :: ieee_exceptions + use swiftest + implicit none + ! Arguments + real(QP), intent(in), dimension(:,:), allocatable :: u + ! Result + real(QP), dimension(:), allocatable :: x + ! Internals + integer(I4B) :: i,n + + n = size(u, 1) + if (allocated(x)) deallocate(x) + if (.not.allocated(x)) allocate(x(n)) + if (any(abs(u) < tiny(1._DP)) .or. any(abs(u) > huge(1._DP))) then + x(:) = 0._DP + return + end if + call ieee_set_halting_mode(ieee_divide_by_zero, .false.) + do i = n, 1, -1 + x(i) = (u(i, n + 1) - sum(u(i, i + 1:n) * x(i + 1:n))) / u(i, i) + end do + return + end function solve_wbs + + function ge_wpp(A, b) result(u) ! gaussian eliminate with partial pivoting + !! Solve Ax=b using Gaussian elimination then backwards substitution. + !! A being an n by n matrix. + !! x and b are n by 1 vectors. + !! Based on code available on Rosetta Code: https://rosettacode.org/wiki/Gaussian_elimination#Fortran + use, intrinsic :: ieee_exceptions + use swiftest + implicit none + ! Arguments + real(QP), dimension(:,:), intent(in) :: A + real(QP), dimension(:), intent(in) :: b + ! Result + real(QP), dimension(:,:), allocatable :: u + ! Internals + integer(I4B) :: i,j,n,p + real(QP) :: upi + + n = size(a, 1) + allocate(u(n, (n + 1))) + u = reshape([A, b], [n, n + 1]) + call ieee_set_halting_mode(ieee_divide_by_zero, .false.) + do j = 1, n + p = maxloc(abs(u(j:n, j)), 1) + j - 1 ! maxloc returns indices between (1, n - j + 1) + if (p /= j) u([p, j], j) = u([j, p], j) + u(j + 1:, j) = u(j + 1:, j) / u(j, j) + do i = j + 1, n + 1 + upi = u(p, i) + if (p /= j) u([p, j], i) = u([j, p], i) + u(j + 1:n, i) = u(j + 1:n, i) - upi * u(j + 1:n, j) + end do + end do + return + end function ge_wpp + + module function util_solve_rkf45(f, y0in, t1, dt0, tol) result(y1) + !! author: David A. Minton + !! + !! Implements the 4th order Runge-Kutta-Fehlberg ODE solver for initial value problems of the form f=dy/dt, y0 = y(t=0), solving for y1 = y(t=t1). Uses a 5th order adaptive step size control. + !! Uses a lambda function object as defined in the lambda_function module + implicit none + ! Arguments + class(lambda_obj), intent(inout) :: f !! lambda function object that has been initialized to be a function of derivatives. The object will return with components lastarg and lasteval set + real(DP), dimension(:), intent(in) :: y0in !! Initial value at t=0 + real(DP), intent(in) :: t1 !! Final time + real(DP), intent(in) :: dt0 !! Initial step size guess + real(DP), intent(in) :: tol !! Tolerance on solution + ! Result + real(DP), dimension(:), allocatable :: y1 !! Final result + ! Internals + integer(I4B), parameter :: MAXREDUX = 1000 !! Maximum number of times step size can be reduced + real(DP), parameter :: DTFAC = 0.95_DP !! Step size reduction safety factor (Value just under 1.0 to prevent adaptive step size control from discarding steps too aggressively) + integer(I4B), parameter :: RKS = 6 !! Number of RK stages + real(DP), dimension(RKS, RKS - 1), parameter :: rkf45_btab = reshape( & !! Butcher tableau for Runge-Kutta-Fehlberg method + (/ 1./4., 1./4., 0., 0., 0., 0.,& + 3./8., 3./32., 9./32., 0., 0., 0.,& + 12./13., 1932./2197., -7200./2197., 7296./2197., 0., 0.,& + 1., 439./216., -8., 3680./513., -845./4104., 0.,& + 1./2., -8./27., 2., -3544./2565., 1859./4104., -11./40./), shape(rkf45_btab)) + real(DP), dimension(RKS), parameter :: rkf4_coeff = (/ 25./216., 0., 1408./2565. , 2197./4104. , -1./5., 0. /) + real(DP), dimension(RKS), parameter :: rkf5_coeff = (/ 16./135., 0., 6656./12825., 28561./56430., -9./50., 2./55. /) + real(DP), dimension(:, :), allocatable :: k !! Runge-Kutta coefficient vector + real(DP), dimension(:), allocatable :: ynorm !! Normalized y value used for adaptive step size control + real(DP), dimension(:), allocatable :: y0 !! Value of y at the beginning of each substep + integer(I4B) :: Nvar !! Number of variables in problem + integer(I4B) :: rkn !! Runge-Kutta loop index + real(DP) :: t, x1, dt, trem !! Current time, step size and total time remaining + real(DP) :: s, yerr, yscale !! Step size reduction factor, error in dependent variable, and error scale factor + integer(I4B) :: i, n + + allocate(y0, source=y0in) + allocate(y1, mold=y0) + allocate(ynorm, mold=y0) + Nvar = size(y0) + allocate(k(Nvar, RKS)) + + dt = dt0 + + trem = t1 + t = 0._DP + do + yscale = norm2(y0(:)) + do i = 1, MAXREDUX + select type(f) + class is (lambda_obj_tvar) + do rkn = 1, RKS + y1(:) = y0(:) + matmul(k(:, 1:rkn - 1), rkf45_btab(2:rkn, rkn - 1)) + if (rkn == 1) then + x1 = t + else + x1 = t + rkf45_btab(1,rkn-1) + end if + k(:, rkn) = dt * f%evalt(y1(:), t) + end do + class is (lambda_obj) + do rkn = 1, RKS + y1(:) = y0(:) + matmul(k(:, 1:rkn - 1), rkf45_btab(2:rkn, rkn - 1)) + k(:, rkn) = dt * f%eval(y1(:)) + end do + end select + ! Now determine if the step size needs adjusting + ynorm(:) = matmul(k(:,:), (rkf5_coeff(:) - rkf4_coeff(:))) / yscale + yerr = norm2(ynorm(:)) + s = (tol / (2 * yerr))**(0.25_DP) + dt = min(s * DTFAC * dt, trem) ! Alter step size either up or down, but never bigger than the remaining time + if (s >= 1.0_DP) exit ! Good step! + if (i == MAXREDUX) then + write(*,*) "Something has gone wrong in util_solve_rkf45!! Step size reduction has gone too far this time!" + call util_exit(FAILURE) + end if + end do + + ! Compute new value then step ahead in time + y1(:) = y0(:) + matmul(k(:, :), rkf4_coeff(:)) + trem = trem - dt + t = t + dt + if (trem <= 0._DP) exit + y0(:) = y1(:) + end do + + return + end function util_solve_rkf45 + +end submodule s_util_solve \ No newline at end of file diff --git a/src/util/util_sort.f90 b/src/util/util_sort.f90 index 126f4f12d..752e78ab7 100644 --- a/src/util/util_sort.f90 +++ b/src/util/util_sort.f90 @@ -1,263 +1,419 @@ submodule (swiftest_classes) s_util_sort use swiftest contains + + module subroutine util_sort_body(self, sortby, ascending) + !! author: David A. Minton + !! + !! Sort a Swiftest body structure in-place. + !! sortby is a string indicating which array component to sort. + implicit none + ! Arguments + class(swiftest_body), intent(inout) :: self !! Swiftest 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 + ! Internals + integer(I4B), dimension(self%nbody) :: ind + integer(I4B) :: direction + + if (ascending) then + direction = 1 + else + direction = -1 + end if + + associate(body => self, n => self%nbody) + select case(sortby) + case("id") + call util_sort(direction * body%id(1:n), ind(1:n)) + case("status") + call util_sort(direction * body%status(1:n), ind(1:n)) + case("ir3h") + call util_sort(direction * body%ir3h(1:n), ind(1:n)) + case("a") + call util_sort(direction * body%a(1:n), ind(1:n)) + case("e") + call util_sort(direction * body%e(1:n), ind(1:n)) + case("inc") + call util_sort(direction * body%inc(1:n), ind(1:n)) + case("capom") + call util_sort(direction * body%capom(1:n), ind(1:n)) + case("mu") + call util_sort(direction * body%mu(1:n), ind(1:n)) + case("lfirst", "nbody", "ldiscard", "xh", "vh", "xb", "vb", "ah", "aobl", "atide", "agr") + write(*,*) 'Cannot sort by ' // trim(adjustl(sortby)) // '. Component not sortable!' + case default + write(*,*) 'Cannot sort by ' // trim(adjustl(sortby)) // '. Component not found!' + return + end select + + call body%rearrange(ind) + + end associate + + return + end subroutine util_sort_body + + + module subroutine util_sort_pl(self, sortby, ascending) + !! author: David A. Minton + !! + !! Sort a Swiftest massive body object in-place. + !! sortby is a string indicating which array component to sort. + implicit none + ! Arguments + class(swiftest_pl), intent(inout) :: self !! Swiftest 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 + ! Internals + integer(I4B), dimension(self%nbody) :: ind + integer(I4B) :: direction + + if (ascending) then + direction = 1 + else + direction = -1 + end if + + associate(pl => self, npl => self%nbody) + select case(sortby) + case("Gmass","mass") + call util_sort(direction * pl%Gmass(1:npl), ind(1:npl)) + case("rhill") + call util_sort(direction * pl%rhill(1:npl), ind(1:npl)) + case("radius") + call util_sort(direction * pl%radius(1:npl), ind(1:npl)) + case("density") + call util_sort(direction * pl%density(1:npl), ind(1:npl)) + case("k2") + call util_sort(direction * pl%k2(1:npl), ind(1:npl)) + case("Q") + call util_sort(direction * pl%Q(1:npl), ind(1:npl)) + case("tlag") + call util_sort(direction * pl%tlag(1:npl), ind(1:npl)) + case("xbeg", "xend", "vbeg", "Ip", "rot", "k_plpl", "nplpl") + write(*,*) 'Cannot sort by ' // trim(adjustl(sortby)) // '. Component not sortable!' + case default ! Look for components in the parent class + call util_sort_body(pl, sortby, ascending) + return + end select + + call pl%rearrange(ind) + + end associate + + return + end subroutine util_sort_pl + + + module subroutine util_sort_tp(self, sortby, ascending) + !! author: David A. Minton + !! + !! Sort a Swiftest test particle object in-place. + !! sortby is a string indicating which array component to sort. + implicit none + ! Arguments + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle 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 + ! Internals + integer(I4B), dimension(self%nbody) :: ind + integer(I4B) :: direction + + if (ascending) then + direction = 1 + else + direction = -1 + end if + + associate(tp => self, ntp => self%nbody) + select case(sortby) + case("peri") + call util_sort(direction * tp%peri(1:ntp), ind(1:ntp)) + case("atp") + call util_sort(direction * tp%atp(1:ntp), ind(1:ntp)) + case("isperi") + write(*,*) 'Cannot sort by ' // trim(adjustl(sortby)) // '. Component not sortable!' + case default ! Look for components in the parent class + call util_sort_body(tp, sortby, ascending) + return + end select + + call tp%rearrange(ind) + + end associate + + return + end subroutine util_sort_tp + + + module subroutine util_sort_rearrange_body(self, ind) + !! author: David A. Minton + !! + !! Rearrange Swiftest body structure in-place from an index list. + !! This is a helper utility used to make polymorphic sorting work on Swiftest structures. + implicit none + ! Arguments + class(swiftest_body), intent(inout) :: self !! Swiftest 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) + ! Internals + class(swiftest_body), allocatable :: body_sorted !! Temporary holder for sorted body + + associate(n => self%nbody) + allocate(body_sorted, source=self) + 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 + + return + end subroutine util_sort_rearrange_body + + + module subroutine util_sort_rearrange_pl(self, ind) + !! author: David A. Minton + !! + !! Rearrange Swiftest massive body structure in-place from an index list. + !! This is a helper utility used to make polymorphic sorting work on Swiftest structures. + implicit none + class(swiftest_pl), intent(inout) :: self !! Swiftest 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) + ! Internals + class(swiftest_pl), allocatable :: pl_sorted !! Temporary holder for sorted body + + associate(pl => self, npl => self%nbody) + call util_sort_rearrange_body(pl,ind) + allocate(pl_sorted, source=self) + 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)) + if (allocated(pl%radius)) pl%radius(1:npl) = pl_sorted%radius(ind(1:npl)) + if (allocated(pl%density)) pl%density(1:npl) = pl_sorted%density(ind(1:npl)) + if (allocated(pl%Ip)) pl%Ip(:,1:npl) = pl_sorted%Ip(:,ind(1:npl)) + if (allocated(pl%rot)) pl%rot(:,1:npl) = pl_sorted%rot(:,ind(1:npl)) + if (allocated(pl%k2)) pl%k2(1:npl) = pl_sorted%k2(ind(1:npl)) + if (allocated(pl%Q)) pl%Q(1:npl) = pl_sorted%Q(ind(1:npl)) + if (allocated(pl%tlag)) pl%tlag(1:npl) = pl_sorted%tlag(ind(1:npl)) + + deallocate(pl_sorted) + end associate + + return + end subroutine util_sort_rearrange_pl + + + module subroutine util_sort_rearrange_tp(self, ind) + !! author: David A. Minton + !! + !! Rearrange Swiftest massive body structure in-place from an index list. + !! This is a helper utility used to make polymorphic sorting work on Swiftest structures. + implicit none + ! Arguments + class(swiftest_tp), intent(inout) :: self !! Swiftest test particle 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) + ! Internals + class(swiftest_tp), allocatable :: tp_sorted !! Temporary holder for sorted body + + associate(tp => self, ntp => self%nbody) + call util_sort_rearrange_body(tp,ind) + allocate(tp_sorted, source=self) + 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 + + return + end subroutine util_sort_rearrange_tp + + module subroutine util_sort_dp(arr) !! author: David A. Minton !! - !! Sort input double precision array into ascending numerical order using Quicksort algorithm + !! Sort input double precision array in place into ascending numerical order using insertion sort. + !! This algorithm works well for partially sorted arrays (which is usually the case here) !! - !! Adapted from David E. Kaufmann's Swifter routine: util_sort_dp.f90 - !! Adapted from Numerical Recipes in Fortran 90: The Art of Parallel Scientific Computing, by Press, Teukolsky, - !! Vetterling, and Flannery, 2nd ed., pp. 1169-70 implicit none ! Arguments real(DP), dimension(:), intent(inout) :: arr ! Internals - integer(I4B), parameter :: NN = 15, NSTACK = 50 - real(DP) :: a, dum - integer(I4B) :: n, k, i, j, jstack, l, r - integer(I4B), dimension(NSTACK) :: istack + real(DP) :: tmp + integer(I4B) :: n, i, j - ! executable code n = size(arr) - jstack = 0 - l = 1 - r = n - do - if ((r - l) < NN) then - do j = l + 1, r - a = arr(j) - do i = j - 1, l, -1 - if (arr(i) <= a) exit - arr(i+1) = arr(i) - end do - arr(i+1) = a - end do - if (jstack == 0) return - r = istack(jstack) - l = istack(jstack-1) - jstack = jstack - 2 - else - k = (l + r)/2 - dum = arr(k); arr(k) = arr(l+1); arr(l+1) = dum - if (arr(l) > arr(r)) then - dum = arr(l); arr(l) = arr(r); arr(r) = dum - end if - if (arr(l+1) > arr(r)) then - dum = arr(l+1); arr(l+1) = arr(r); arr(r) = dum - end if - if (arr(l) > arr(l+1)) then - dum = arr(l); arr(l) = arr(l+1); arr(l+1) = dum - end if - i = l + 1 - j = r - a = arr(l+1) - do - do - i = i + 1 - if (arr(i) >= a) exit - end do - do - j = j - 1 - if (arr(j) <= a) exit - end do - if (j < i) exit - dum = arr(i); arr(i) = arr(j); arr(j) = dum - end do - arr(l+1) = arr(j) - arr(j) = a - jstack = jstack + 2 - if (jstack > NSTACK) then - write(*, *) "Swiftest Error:" - write(*, *) " NSTACK too small in util_sort_I4B" - call util_exit(FAILURE) - end if - if ((r - i + 1) >= (j - l)) then - istack(jstack) = r - istack(jstack-1) = i - r = j - 1 - else - istack(jstack) = j - 1 - istack(jstack-1) = l - l = i - end if - end if + do i = 2, n + tmp = arr(i) + do j = i - 1, 1, -1 + if (arr(j) <= tmp) exit + arr(j + 1) = arr(j) + end do + arr(j + 1) = tmp end do return - end subroutine util_sort_dp + + module subroutine util_sort_index_dp(arr, ind) + !! author: David A. Minton + !! + !! Sort input double precision array by index in ascending numerical order using insertion sort. + !! This algorithm works well for partially sorted arrays (which is usually the case here) + !! + implicit none + ! Arguments + real(DP), dimension(:), intent(in) :: arr + integer(I4B), dimension(:), intent(out) :: ind + ! Internals + real(DP) :: tmp + integer(I4B) :: n, i, j + + n = size(arr) + ind = [(i, i=1, n)] + do i = 2, n + tmp = arr(ind(i)) + do j = i - 1, 1, -1 + if (arr(ind(j)) <= tmp) exit + ind(j + 1) = ind(j) + end do + ind(j + 1) = i + end do + + return + end subroutine util_sort_index_dp + + module subroutine util_sort_i4b(arr) !! author: David A. Minton !! - !! Sort input double precision array into ascending numerical order using Quicksort algorithm + !! Sort input integer array in place into ascending numerical order using insertion sort. + !! This algorithm works well for partially sorted arrays (which is usually the case here) !! - !! Adapted from David E. Kaufmann's Swifter routine: util_sort_i4b.f90 - !! Adapted from Numerical Recipes in Fortran 90: The Art of Parallel Scientific Computing, by Press, Teukolsky, - !! Vetterling, and Flannery, 2nd ed., pp. 1169-70 implicit none ! Arguments integer(I4B), dimension(:), intent(inout) :: arr ! Internals - integer(I4B), parameter :: NN = 15, NSTACK = 50 - integer(I4B) :: a, n, k, i, j, jstack, l, r, dum - integer(I4B), dimension(NSTACK) :: istack - - ! executable code + integer(I4B) :: tmp + integer(I4B) :: n, i, j + n = size(arr) - jstack = 0 - l = 1 - r = n - do - if ((r - l) < NN) then - do j = l + 1, r - a = arr(j) - do i = j - 1, l, -1 - if (arr(i) <= a) exit - arr(i+1) = arr(i) - end do - arr(i+1) = a - end do - if (jstack == 0) return - r = istack(jstack) - l = istack(jstack-1) - jstack = jstack - 2 - else - k = (l + r)/2 - dum = arr(k); arr(k) = arr(l+1); arr(l+1) = dum - if (arr(l) > arr(r)) then - dum = arr(l); arr(l) = arr(r); arr(r) = dum - end if - if (arr(l+1) > arr(r)) then - dum = arr(l+1); arr(l+1) = arr(r); arr(r) = dum - end if - if (arr(l) > arr(l+1)) then - dum = arr(l); arr(l) = arr(l+1); arr(l+1) = dum - end if - i = l + 1 - j = r - a = arr(l+1) - do - do - i = i + 1 - if (arr(i) >= a) exit - end do - do - j = j - 1 - if (arr(j) <= a) exit - end do - if (j < i) exit - dum = arr(i); arr(i) = arr(j); arr(j) = dum - end do - arr(l+1) = arr(j) - arr(j) = a - jstack = jstack + 2 - if (jstack > NSTACK) then - write(*, *) "Swiftest Error:" - write(*, *) " NSTACK too small in util_sort_i4b" - call util_exit(FAILURE) - end if - if ((r - i + 1) >= (j - l)) then - istack(jstack) = r - istack(jstack-1) = i - r = j - 1 - else - istack(jstack) = j - 1 - istack(jstack-1) = l - l = i - end if - end if + do i = 2, n + tmp = arr(i) + do j = i - 1, 1, -1 + if (arr(j) <= tmp) exit + arr(j + 1) = arr(j) + end do + arr(j + 1) = tmp end do - + return - - end subroutine util_sort_i4b - - module subroutine util_sort_sp(arr) - !! author: David A. Minton - !! - !! Sort input single precision array into ascending numerical order using Quicksort algorithm - !! - !! Adapted from David E. Kaufmann's Swifter routine: util_sort_DP.f90 - !! Adapted from Numerical Recipes in Fortran 90: The Art of Parallel Scientific Computing, by Press, Teukolsky, - !! Vetterling, and Flannery, 2nd ed., pp. 1169-70 - implicit none - ! Arguments - real(SP), dimension(:), intent(inout) :: arr - ! Internals - integer(I4B), parameter :: NN = 15, NSTACK = 50 - real(SP) :: a, dum - integer(I4B) :: n, k, i, j, jstack, l, r - integer(I4B), dimension(NSTACK) :: istack - - ! executable code - n = size(arr) - jstack = 0 - l = 1 - r = n - do - if ((r - l) < NN) then - do j = l + 1, r - a = arr(j) - do i = j - 1, l, -1 - if (arr(i) <= a) exit - arr(i+1) = arr(i) - end do - arr(i+1) = a - end do - if (jstack == 0) return - r = istack(jstack) - l = istack(jstack-1) - jstack = jstack - 2 - else - k = (l + r)/2 - dum = arr(k); arr(k) = arr(l+1); arr(l+1) = dum - if (arr(l) > arr(r)) then - dum = arr(l); arr(l) = arr(r); arr(r) = dum - end if - if (arr(l+1) > arr(r)) then - dum = arr(l+1); arr(l+1) = arr(r); arr(r) = dum - end if - if (arr(l) > arr(l+1)) then - dum = arr(l); arr(l) = arr(l+1); arr(l+1) = dum - end if - i = l + 1 - j = r - a = arr(l+1) - do - do - i = i + 1 - if (arr(i) >= a) exit - end do - do - j = j - 1 - if (arr(j) <= a) exit - end do - if (j < i) exit - dum = arr(i); arr(i) = arr(j); arr(j) = dum - end do - arr(l+1) = arr(j) - arr(j) = a - jstack = jstack + 2 - if (jstack > NSTACK) then - write(*, *) "Swiftest Error:" - write(*, *) " NSTACK too small in util_sort_I4B" - call util_exit(FAILURE) - end if - if ((r - i + 1) >= (j - l)) then - istack(jstack) = r - istack(jstack-1) = i - r = j - 1 - else - istack(jstack) = j - 1 - istack(jstack-1) = l - l = i - end if - end if + end subroutine util_sort_i4b + + + module subroutine util_sort_index_i4b(arr, ind) + !! author: David A. Minton + !! + !! Sort input integer array by index in ascending numerical order using insertion sort. + !! This algorithm works well for partially sorted arrays (which is usually the case here) + !! + implicit none + ! Arguments + integer(I4B), dimension(:), intent(in) :: arr + integer(I4B), dimension(:), intent(out) :: ind + ! Internals + integer(I4B) :: tmp + integer(I4B) :: n, i, j + + n = size(arr) + ind = [(i, i=1, n)] + do i = 2, n + tmp = arr(ind(i)) + do j = i - 1, 1, -1 + if (arr(ind(j)) <= tmp) exit + ind(j + 1) = ind(j) + end do + ind(j + 1) = i + end do + + return + end subroutine util_sort_index_i4b + + + module subroutine util_sort_sp(arr) + !! author: David A. Minton + !! + !! Sort input single precision array in place into ascending numerical order using insertion sort. + !! This algorithm works well for partially sorted arrays (which is usually the case here) + ! + implicit none + ! Arguments + real(SP), dimension(:), intent(inout) :: arr + ! Internals + real(SP) :: tmp + integer(I4B) :: n, i, j + + n = size(arr) + do i = 2, n + tmp = arr(i) + do j = i - 1, 1, -1 + if (arr(j) <= tmp) exit + arr(j + 1) = arr(j) + end do + arr(j + 1) = tmp + end do + + return + end subroutine util_sort_sp + + + module subroutine util_sort_index_sp(arr, ind) + !! author: David A. Minton + !! + !! Sort input single precision array by index in ascending numerical order using insertion sort. + !! This algorithm works well for partially sorted arrays (which is usually the case here) + !! + implicit none + ! Arguments + real(SP), dimension(:), intent(in) :: arr + integer(I4B), dimension(:), intent(out) :: ind + ! Internals + real(SP) :: tmp + integer(I4B) :: n, i, j + + n = size(arr) + ind = [(i, i=1, n)] + do i = 2, n + tmp = arr(ind(i)) + do j = i - 1, 1, -1 + if (arr(ind(j)) <= tmp) exit + ind(j + 1) = ind(j) end do - - return - - end subroutine util_sort_sp + ind(j + 1) = i + end do + + return + end subroutine util_sort_index_sp + end submodule s_util_sort 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 1a51c06c5..000000000 --- a/src/util/util_spill_and_fill.f90 +++ /dev/null @@ -1,288 +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%a(:) = pack(keeps%a(:), lspill_list(:)) - discards%e(:) = pack(keeps%e(:), lspill_list(:)) - discards%capom(:) = pack(keeps%capom(:), lspill_list(:)) - discards%omega(:) = pack(keeps%omega(:), lspill_list(:)) - discards%capm(:) = pack(keeps%capm(:), lspill_list(:)) - discards%mu(:) = pack(keeps%mu(:), 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(:)) - discards%aobl(i, :) = pack(keeps%aobl(i, :), lspill_list(:)) - discards%agr(i, :) = pack(keeps%agr(i, :), lspill_list(:)) - end do - 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%a(:) = pack(keeps%a(:), .not. lspill_list(:)) - keeps%e(:) = pack(keeps%e(:), .not. lspill_list(:)) - keeps%inc(:) = pack(keeps%inc(:), .not. lspill_list(:)) - keeps%capom(:) = pack(keeps%capom(:), .not. lspill_list(:)) - keeps%omega(:) = pack(keeps%omega(:), .not. lspill_list(:)) - keeps%capm(:) = pack(keeps%capm(:), .not. lspill_list(:)) - keeps%mu(:) = pack(keeps%mu(:), .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(:)) - keeps%aobl(i, :) = pack(keeps%aobl(i, :), .not. lspill_list(:)) - keeps%agr(i, :) = pack(keeps%agr(i, :), .not. lspill_list(:)) - end do - 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 - - 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(:)) - - 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, :)) - - 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, :)) - - 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 - - keeps%a(:) = unpack(keeps%a(:), .not.lfill_list(:), keeps%a(:)) - keeps%a(:) = unpack(inserts%a(:), lfill_list(:), keeps%a(:)) - - keeps%e(:) = unpack(keeps%e(:), .not.lfill_list(:), keeps%e(:)) - keeps%e(:) = unpack(inserts%e(:), lfill_list(:), keeps%e(:)) - - keeps%inc(:) = unpack(keeps%inc(:), .not.lfill_list(:), keeps%inc(:)) - keeps%inc(:) = unpack(inserts%inc(:), lfill_list(:), keeps%inc(:)) - - keeps%capom(:) = unpack(keeps%capom(:),.not.lfill_list(:), keeps%capom(:)) - keeps%capom(:) = unpack(inserts%capom(:),lfill_list(:), keeps%capom(:)) - - keeps%omega(:) = unpack(keeps%omega(:),.not.lfill_list(:), keeps%omega(:)) - keeps%omega(:) = unpack(inserts%omega(:),lfill_list(:), keeps%omega(:)) - - keeps%capm(:) = unpack(keeps%capm(:), .not.lfill_list(:), keeps%capm(:)) - keeps%capm(:) = unpack(inserts%capm(:), lfill_list(:), keeps%capm(:)) - - keeps%mu(:) = unpack(keeps%mu(:), .not.lfill_list(:), keeps%mu(:)) - keeps%mu(:) = unpack(inserts%mu(:), lfill_list(:), keeps%mu(:)) - - - ! This is the base class, so will be the last to be called in the cascade. - keeps%nbody = size(keeps%id(:)) - end associate - - end subroutine util_fill_body - - module procedure util_spill_pl - !! 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 - - 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(:)) - discards%radius(:) = pack(keeps%radius(:), lspill_list(:)) - discards%density(:) = pack(keeps%density(:), lspill_list(:)) - 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(:)) - keeps%radius(:) = pack(keeps%radius(:), .not. lspill_list(:)) - keeps%density(:) = pack(keeps%density(:), .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_pl' - end select - end associate - return - - end procedure util_spill_pl - - module procedure util_fill_pl - !! author: David A. Minton - !! - !! Insert new Swiftest massive body structure into an old one. - !! This is the inverse of a fill operation. - implicit none - - 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(:)) - - keeps%radius(:) = unpack(keeps%radius(:),.not.lfill_list(:), keeps%radius(:)) - keeps%radius(:) = unpack(inserts%radius(:),lfill_list(:), keeps%radius(:)) - - keeps%density(:) = unpack(keeps%density(:),.not.lfill_list(:), keeps%density(:)) - keeps%density(:) = unpack(inserts%density(:),lfill_list(:), keeps%density(:)) - - 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 procedure util_fill_pl - - module procedure util_spill_tp - !! 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 - - 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 procedure util_spill_tp - - module procedure util_fill_tp - !! author: David A. Minton - !! - !! Insert new Swiftest test particle structure into an old one. - !! This is the inverse of a fill operation. - implicit none - - 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 procedure util_fill_tp - -end submodule s_util_spill_and_fill - - - - - - diff --git a/src/util/util_valid.f90 b/src/util/util_valid.f90 index ac81673ca..c5923b38e 100644 --- a/src/util/util_valid.f90 +++ b/src/util/util_valid.f90 @@ -1,6 +1,7 @@ submodule (swiftest_classes) s_util_valid use swiftest contains + module subroutine util_valid(pl, tp) !! author: David A. Minton !! @@ -32,10 +33,9 @@ module subroutine util_valid(pl, tp) call util_exit(FAILURE) end if end do - deallocate(idarr) end associate return - end subroutine util_valid + end submodule s_util_valid diff --git a/src/util/util_version.f90 b/src/util/util_version.f90 index 2b2c351be..54ef0e14a 100644 --- a/src/util/util_version.f90 +++ b/src/util/util_version.f90 @@ -1,6 +1,7 @@ submodule (swiftest_classes) s_util_version use swiftest contains + module subroutine util_version() !! author: David A. Minton !! diff --git a/src/whm/whm_coord.f90 b/src/whm/whm_coord.f90 index af5368aa8..e7aa63e1f 100644 --- a/src/whm/whm_coord.f90 +++ b/src/whm/whm_coord.f90 @@ -1,6 +1,7 @@ submodule (whm_classes) s_whm_coord use swiftest contains + module subroutine whm_coord_h2j_pl(self, cb) !! author: David A. Minton !! @@ -19,9 +20,10 @@ module subroutine whm_coord_h2j_pl(self, cb) integer(I4B) :: i real(DP), dimension(NDIM) :: sumx, sumv, cap, capv + if (self%nbody == 0) return + associate(npl => self%nbody, GMpl => self%Gmass, eta => self%eta, xh => self%xh, vh => self%vh, & xj => self%xj, vj => self%vj) - if (npl == 0) return xj(:, 1) = xh(:, 1) vj(:, 1) = vh(:, 1) sumx(:) = 0.0_DP @@ -39,6 +41,7 @@ module subroutine whm_coord_h2j_pl(self, cb) return end subroutine whm_coord_h2j_pl + module subroutine whm_coord_j2h_pl(self, cb) !! author: David A. Minton !! @@ -58,9 +61,10 @@ module subroutine whm_coord_j2h_pl(self, cb) integer(I4B) :: i real(DP), dimension(NDIM) :: sumx, sumv + if (self%nbody == 0) return + associate(npl => self%nbody, GMpl => self%Gmass, eta => self%eta, xh => self%xh, vh => self%vh, & xj => self%xj, vj => self%vj) - if (npl == 0) return xh(:, 1) = xj(:, 1) vh(:, 1) = vj(:, 1) sumx(:) = 0.0_DP @@ -76,6 +80,7 @@ module subroutine whm_coord_j2h_pl(self, cb) return end subroutine whm_coord_j2h_pl + module subroutine whm_coord_vh2vj_pl(self, cb) !! author: David A. Minton !! @@ -94,8 +99,9 @@ module subroutine whm_coord_vh2vj_pl(self, cb) integer(I4B) :: i real(DP), dimension(NDIM) :: sumv, capv + if (self%nbody == 0) return + associate(npl => self%nbody, GMpl => self%Gmass, vh => self%vh, vj => self%vj, eta => self%eta) - if (npl == 0) return vj(:, 1) = vh(:, 1) sumv(:) = 0.0_DP do i = 2, npl @@ -107,5 +113,6 @@ module subroutine whm_coord_vh2vj_pl(self, cb) return end subroutine whm_coord_vh2vj_pl + end submodule s_whm_coord diff --git a/src/whm/whm_drift.f90 b/src/whm/whm_drift.f90 index a27897cfa..f68fcaeb7 100644 --- a/src/whm/whm_drift.f90 +++ b/src/whm/whm_drift.f90 @@ -1,6 +1,7 @@ submodule(whm_classes) whm_drift use swiftest contains + module subroutine whm_drift_pl(self, system, param, dt) !! author: David A. Minton !! @@ -12,101 +13,37 @@ module subroutine whm_drift_pl(self, system, param, dt) ! Arguments class(whm_pl), intent(inout) :: self !! WHM massive body particle data structure class(swiftest_nbody_system), intent(inout) :: system !! WHM nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: dt !! Stepsize ! Internals integer(I4B) :: i - real(DP) :: energy, vmag2, rmag !! Variables used in GR calculation integer(I4B), dimension(:), allocatable :: iflag - real(DP), dimension(:), allocatable :: dtp - - associate(pl => self, npl => self%nbody) - if (npl == 0) return + if (self%nbody == 0) return + associate(pl => self, npl => self%nbody) allocate(iflag(npl)) iflag(:) = 0 - allocate(dtp(npl)) - - if (param%lgr) then - do i = 1,npl - rmag = norm2(pl%xj(:, i)) - vmag2 = dot_product(pl%vj(:, i), pl%vj(:, i)) - energy = 0.5_DP * vmag2 - pl%muj(i) / rmag - dtp(i) = dt * (1.0_DP + 3 * param%inv_c2 * energy) - end do - else - dtp(:) = dt - end if - - call drift_one(pl%muj(1:npl), pl%xj(1,1:npl), pl%xj(2,1:npl), pl%xj(3,1:npl), & - pl%vj(1,1:npl), pl%vj(2,1:npl), pl%vj(3,1:npl), & - dtp(1:npl), iflag(1:npl)) + call drift_all(pl%muj, pl%xj, pl%vj, npl, param, dt, pl%lmask, iflag) if (any(iflag(1:npl) /= 0)) then + where(iflag(1:npl) /= 0) + pl%status(1:npl) = DISCARDED_DRIFTERR + pl%lmask(1:npl) = .false. + end where do i = 1, npl - if (iflag(i) /= 0) then - write(*, *) " Planet ", self%id(i), " is lost!!!!!!!!!!" - write(*, *) pl%xj(:,i) - write(*, *) pl%vj(:,i) - write(*, *) " stopping " - call util_exit(FAILURE) + if (iflag(i) /= 0) then + write(*, *) " Planet ", pl%id(i), " is lost!!!!!!!!!!!!" + WRITE(*, *) pl%muj(i), dt + WRITE(*, *) pl%xj(:,i) + WRITE(*, *) pl%vj(:,i) + WRITE(*, *) " STOPPING " end if end do + call util_exit(FAILURE) end if end associate return - end subroutine whm_drift_pl - module subroutine whm_drift_tp(self, system, param, dt) - !! author: David A. Minton - !! - !! Loop through test particles and call Danby drift routine - !! - !! Adapted from Hal Levison's Swift routine drift_tp.f - !! Includes - !! Adapted from David E. Kaufmann's Swifter routine whm_drift_tp.f90 - implicit none - ! Arguments - class(whm_tp), intent(inout) :: self !! WHM test particle data structure - class(swiftest_nbody_system), intent(inout) :: system !! WHM nbody system object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of - real(DP), intent(in) :: dt !! Stepsize - ! Internals - integer(I4B) :: i - real(DP) :: energy, vmag2, rmag !! Variables used in GR calculation - integer(I4B), dimension(:), allocatable :: iflag - real(DP), dimension(:), allocatable :: dtp - - associate(tp => self, ntp => self%nbody) - if (ntp == 0) return - allocate(iflag(ntp)) - iflag(:) = 0 - allocate(dtp(ntp)) - if (param%lgr) then - do i = 1, ntp - rmag = norm2(tp%xh(:, i)) - vmag2 = dot_product(tp%vh(:, i), tp%vh(:, i)) - energy = 0.5_DP * vmag2 - tp%mu(i) / rmag - dtp(i) = dt * (1.0_DP + 3 * param%inv_c2 * energy) - end do - else - dtp(:) = dt - end if - do concurrent(i = 1:ntp, tp%status(i) == ACTIVE) - call drift_one(tp%mu(i), tp%xh(1,i), tp%xh(2,i), tp%xh(3,i), & - tp%vh(1,i), tp%vh(2,i), tp%vh(3,i), & - dtp(i), iflag(i)) - end do - if (any(iflag(1:ntp) /= 0)) then - where(iflag(1:ntp) /= 0) tp%status(1:ntp) = DISCARDED_DRIFTERR - do i = 1, ntp - if (iflag(i) /= 0) write(*, *) "Particle ", self%id(i), " lost due to error in Danby drift" - end do - end if - end associate - - return - end subroutine whm_drift_tp end submodule whm_drift diff --git a/src/whm/whm_getacch.f90 b/src/whm/whm_getacch.f90 deleted file mode 100644 index c4ee90592..000000000 --- a/src/whm/whm_getacch.f90 +++ /dev/null @@ -1,245 +0,0 @@ -submodule(whm_classes) s_whm_getacch - use swiftest -contains - module subroutine whm_getacch_pl(self, system, param, t, lbeg) - !! author: David A. Minton - !! - !! Compute heliocentric accelerations of planets - !! - !! Adapted from Hal Levison's Swift routine getacch.f - !! Adapted from David E. Kaufmann's Swifter routine whm_getacch.f90 - implicit none - ! Arguments - class(whm_pl), intent(inout) :: self !! WHM massive body particle data structure - class(swiftest_nbody_system), intent(inout) :: system !! Swiftest central body particle data structure - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of - real(DP), intent(in) :: t !! Current time - logical, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step - ! Internals - integer(I4B) :: i - real(DP), dimension(NDIM) :: ah0 - - associate(cb => system%cb, pl => self, npl => self%nbody) - if (npl == 0) return - call pl%set_ir3() - - ah0 = whm_getacch_ah0(pl%Gmass(2:npl), pl%xh(:,2:npl), npl-1) - do i = 1, npl - pl%ah(:, i) = ah0(:) - end do - - call whm_getacch_ah1(cb, pl) - call whm_getacch_ah2(cb, pl) - call whm_getacch_ah3(pl) - - if (param%loblatecb) then - cb%aoblbeg = cb%aobl - call pl%accel_obl(system) - cb%aoblend = cb%aobl - end if - if (param%lextra_force) call pl%accel_user(system, param, t) - if (param%lgr) call pl%accel_gr(param) - - end associate - return - end subroutine whm_getacch_pl - - module subroutine whm_getacch_tp(self, system, param, t, lbeg) - !! author: David A. Minton - !! - !! Compute heliocentric accelerations of test particles - !! - !! Adapted from Hal Levison's Swift routine getacch_tp.f - !! Adapted from David E. Kaufmann's Swifter routine whm_getacch_tp.f90 - implicit none - ! Arguments - class(whm_tp), intent(inout) :: self !! WHM test particle data structure - class(swiftest_nbody_system), intent(inout) :: system !! Swiftest central body particle data structure - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of - real(DP), intent(in) :: t !! Current time - logical, optional, intent(in) :: lbeg !! Optional argument that determines whether or not this is the beginning or end of the step - ! Internals - integer(I4B) :: i - real(DP), dimension(NDIM) :: ah0 - real(DP), dimension(:,:), allocatable :: xhp - - associate(tp => self, ntp => self%nbody, pl => system%pl, cb => system%cb, npl => system%pl%nbody) - if (ntp == 0 .or. npl == 0) return - if (present(lbeg)) system%lbeg = lbeg - - if (system%lbeg) then - allocate(xhp, source=pl%xbeg) - else - allocate(xhp, source=pl%xend) - end if - - ah0(:) = whm_getacch_ah0(pl%Gmass(:), xhp(:,:), npl) - do i = 1, ntp - tp%ah(:, i) = ah0(:) - end do - call whm_getacch_ah3_tp(system, xhp) - if (param%loblatecb) call tp%accel_obl(system) - if (param%lextra_force) call tp%accel_user(system, param, t) - if (param%lgr) call tp%accel_gr(param) - end associate - return - end subroutine whm_getacch_tp - - function whm_getacch_ah0(mu, xhp, n) result(ah0) - !! author: David A. Minton - !! - !! Compute zeroth term heliocentric accelerations of planets - implicit none - ! Arguments - real(DP), dimension(:), intent(in) :: mu - real(DP), dimension(:,:), intent(in) :: xhp - integer(I4B), intent(in) :: n - ! Result - real(DP), dimension(NDIM) :: ah0 - ! Internals - real(DP) :: fac, r2, ir3h, irh - integer(I4B) :: i - - ah0(:) = 0.0_DP - do i = 1, n - r2 = dot_product(xhp(:, i), xhp(:, i)) - irh = 1.0_DP / sqrt(r2) - ir3h = irh / r2 - fac = mu(i) * ir3h - ah0(:) = ah0(:) - fac * xhp(:, i) - end do - - return - end function whm_getacch_ah0 - - pure subroutine whm_getacch_ah1(cb, pl) - !! author: David A. Minton - !! - !! Compute first term heliocentric accelerations of planets - !! - !! Adapted from Hal Levison's Swift routine getacch_ah1.f - !! Adapted from David E. Kaufmann's Swifter routine whm_getacch_ah1.f90 - implicit none - ! Arguments - class(swiftest_cb), intent(in) :: cb !! WHM central body object - class(whm_pl), intent(inout) :: pl !! WHM massive body object - ! Internals - integer(I4B) :: i - real(DP), dimension(NDIM) :: ah1h, ah1j - - associate(npl => pl%nbody) - do i = 2, npl - ah1j(:) = pl%xj(:, i) * pl%ir3j(i) - ah1h(:) = pl%xh(:, i) * pl%ir3h(i) - pl%ah(:, i) = pl%ah(:, i) + cb%Gmass * (ah1j(:) - ah1h(:)) - end do - end associate - - return - - end subroutine whm_getacch_ah1 - - pure subroutine whm_getacch_ah2(cb, pl) - !! author: David A. Minton - !! - !! Compute second term heliocentric accelerations of planets - !! - !! Adapted from Hal Levison's Swift routine getacch_ah2.f - !! Adapted from David E. Kaufmann's Swifter routine whm_getacch_ah2.f90 - implicit none - ! Arguments - class(swiftest_cb), intent(in) :: cb !! Swiftest central body object - class(whm_pl), intent(inout) :: pl !! WHM massive body object - ! Internals - integer(I4B) :: i - real(DP) :: etaj, fac - real(DP), dimension(NDIM) :: ah2, ah2o - - associate(npl => pl%nbody) - ah2(:) = 0.0_DP - ah2o(:) = 0.0_DP - etaj = cb%Gmass - do i = 2, npl - etaj = etaj + pl%Gmass(i - 1) - fac = pl%Gmass(i) * cb%Gmass * pl%ir3j(i) / etaj - ah2(:) = ah2o + fac * pl%xj(:, i) - pl%ah(:,i) = pl%ah(:, i) + ah2(:) - ah2o(:) = ah2(:) - end do - end associate - - return - end subroutine whm_getacch_ah2 - - pure subroutine whm_getacch_ah3(pl) - !! author: David A. Minton - !! - !! Compute direct cross (third) term heliocentric accelerations of planets - !! - !! Adapted from Hal Levison's Swift routine getacch_ah3.f - !! Adapted from David E. Kaufmann's Swifter routine whm_getacch_ah3.f90 - implicit none - - class(whm_pl), intent(inout) :: pl - integer(I4B) :: i, j - real(DP) :: rji2, irij3, faci, facj - real(DP), dimension(NDIM) :: dx - real(DP), dimension(:,:), allocatable :: ah3 - - associate(npl => pl%nbody) - allocate(ah3, mold=pl%ah) - ah3(:, :) = 0.0_DP - - do i = 1, npl - 1 - do j = i + 1, npl - dx(:) = pl%xh(:, j) - pl%xh(:, i) - rji2 = dot_product(dx(:), dx(:)) - irij3 = 1.0_DP / (rji2 * sqrt(rji2)) - faci = pl%Gmass(i) * irij3 - facj = pl%Gmass(j) * irij3 - ah3(:, i) = ah3(:, i) + facj * dx(:) - ah3(:, j) = ah3(:, j) - faci * dx(:) - end do - end do - do i = 1, NDIM - pl%ah(i, 1:npl) = pl%ah(i, 1:npl) + ah3(i, 1:npl) - end do - deallocate(ah3) - end associate - - return - end subroutine whm_getacch_ah3 - - pure subroutine whm_getacch_ah3_tp(system, xhp) - !! author: David A. Minton - !! - !! Compute direct cross (third) term heliocentric accelerations of test particles - !! - !! Adapted from Hal Levison's Swift routine getacch_ah3_tp.f - !! Adapted from David E. Kaufmann's Swifter routine whm_getacch_ah3.f90 - implicit none - ! Arguments - class(swiftest_nbody_system), intent(inout) :: system !! WHM nbody system object - real(DP), dimension(:,:), intent(in) :: xhp !! Heliocentric positions of planets at the current substep - ! Internals - integer(I4B) :: i, j - real(DP) :: rji2, irij3, fac - real(DP), dimension(NDIM) :: dx, acc - - associate(ntp => system%tp%nbody, npl => system%pl%nbody, tp => system%tp, pl => system%pl) - if (ntp == 0) return - do i = 1, ntp - acc(:) = 0.0_DP - do j = 1, npl - dx(:) = tp%xh(:, i) - xhp(:, j) - rji2 = dot_product(dx(:), dx(:)) - irij3 = 1.0_DP / (rji2 * sqrt(rji2)) - fac = pl%Gmass(j) * irij3 - acc(:) = acc(:) - fac * dx(:) - end do - tp%ah(:, i) = tp%ah(:, i) + acc(:) - end do - end associate - return - end subroutine whm_getacch_ah3_tp -end submodule s_whm_getacch diff --git a/src/whm/whm_gr.f90 b/src/whm/whm_gr.f90 index 3cf159504..bfba5c6a2 100644 --- a/src/whm/whm_gr.f90 +++ b/src/whm/whm_gr.f90 @@ -1,47 +1,47 @@ submodule(whm_classes) s_whm_gr use swiftest contains - module subroutine whm_gr_getacch_pl(self, param) !! author: David A. Minton + + module subroutine whm_gr_kick_getacch_pl(self, param) + !! author: David A. Minton !! !! Compute relativisitic accelerations of massive bodies !! Based on Saha & Tremaine (1994) Eq. 28 !! - !! Adapted from David A. Minton's Swifter routine routine gr_whm_getacch.f90 + !! Adapted from David A. Minton's Swifter routine routine gr_whm_kick_getacch.f90 implicit none ! Arguments class(whm_pl), intent(inout) :: self !! WHM massive body particle data structure - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters ! Internals integer(I4B) :: i real(DP), dimension(NDIM) :: suma real(DP), dimension(:, :), allocatable :: aj real(DP) :: beta, rjmag4 + if (self%nbody == 0) return + associate(pl => self, npl => self%nbody, inv_c2 => param%inv_c2) - if (npl == 0) return - allocate(aj, mold = pl%ah) - do i = 1, npl - rjmag4 = (dot_product(pl%xj(:, i), pl%xj(:, i)))**2 - beta = -pl%muj(i)**2 * inv_c2 - aj(:, i) = 2 * beta * pl%xj(:, i) / rjmag4 - end do + call gr_kick_getacch(pl%muj, pl%xj, pl%lmask, npl, param%inv_c2, pl%agr) suma(:) = 0.0_DP - pl%ah(:, 1) = pl%ah(:, 1) + aj(:, 1) + pl%ah(:, 1) = pl%ah(:, 1) + pl%agr(:, 1) do i = 2, npl - suma(:) = suma(:) + pl%Gmass(i) * aj(:, i) / pl%eta(i) - pl%ah(:, i) = pl%ah(:, i) + aj(:, i) + suma(:) + suma(:) = suma(:) + pl%Gmass(i) * pl%agr(:, i) / pl%eta(i) + pl%ah(:, i) = pl%ah(:, i) + pl%agr(:, i) + suma(:) end do end associate + return - end subroutine whm_gr_getacch_pl + end subroutine whm_gr_kick_getacch_pl - module subroutine whm_gr_getacch_tp(self, param) + + module subroutine whm_gr_kick_getacch_tp(self, param) !! author: David A. Minton !! !! Compute relativisitic accelerations of test particles !! Based on Saha & Tremaine (1994) Eq. 28 !! - !! Adapted from David A. Minton's Swifter routine routine gr_whm_getacch.f90 + !! Adapted from David A. Minton's Swifter routine routine gr_whm_kick_getacch.f90 implicit none ! Arguments class(whm_tp), intent(inout) :: self !! WHM massive body particle data structure @@ -50,16 +50,16 @@ module subroutine whm_gr_getacch_tp(self, param) integer(I4B) :: i real(DP) :: rjmag4, beta + if (self%nbody == 0) return + associate(tp => self, ntp => self%nbody, inv_c2 => param%inv_c2) - if (ntp == 0) return - do i = 1, ntp - rjmag4 = (dot_product(tp%xh(:, i), tp%xh(:, i)))**2 - beta = - tp%mu(i)**2 * inv_c2 - tp%ah(:, i) = tp%ah(:, i) + beta * tp%xh(:, i) / rjmag4 - end do + call gr_kick_getacch(tp%mu, tp%xh, tp%lmask, ntp, param%inv_c2, tp%agr) + tp%ah(:,1:ntp) = tp%ah(:,1:ntp) + tp%agr(:,1:ntp) end associate + return - end subroutine whm_gr_getacch_tp + end subroutine whm_gr_kick_getacch_tp + module pure subroutine whm_gr_p4_pl(self, param, dt) !! author: David A. Minton @@ -71,14 +71,14 @@ module pure subroutine whm_gr_p4_pl(self, param, dt) implicit none ! Arguments class(whm_pl), intent(inout) :: self !! Swiftest particle object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of on parameters + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: dt !! Step size ! Internals integer(I4B) :: i associate(pl => self, npl => self%nbody) if (npl == 0) return - do i = 1, npl + do concurrent(i = 1:npl, pl%lmask(i)) call gr_p4_pos_kick(param, pl%xj(:, i), pl%vj(:, i), dt) end do end associate @@ -96,14 +96,14 @@ module pure subroutine whm_gr_p4_tp(self, param, dt) implicit none ! Arguments class(whm_tp), intent(inout) :: self !! Swiftest particle object - class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters of on parameters + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters real(DP), intent(in) :: dt !! Step size ! Internals integer(I4B) :: i associate(tp => self, ntp => self%nbody) if (ntp == 0) return - do i = 1, ntp + do concurrent(i = 1:ntp, tp%lmask(i)) call gr_p4_pos_kick(param, tp%xh(:, i), tp%vh(:, i), dt) end do end associate diff --git a/src/whm/whm_kick.f90 b/src/whm/whm_kick.f90 new file mode 100644 index 000000000..2da00c332 --- /dev/null +++ b/src/whm/whm_kick.f90 @@ -0,0 +1,281 @@ +submodule(whm_classes) s_whm_kick + use swiftest +contains + + module subroutine whm_kick_getacch_pl(self, system, param, t, lbeg) + !! author: David A. Minton + !! + !! Compute heliocentric accelerations of planets + !! + !! Adapted from Hal Levison's Swift routine getacch.f + !! Adapted from David E. Kaufmann's Swifter routine whm_kick_getacch.f90 + implicit none + ! Arguments + class(whm_pl), intent(inout) :: self !! WHM massive body particle data structure + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest central body particle data structure + 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 + ! Internals + integer(I4B) :: i + real(DP), dimension(NDIM) :: ah0 + + if (self%nbody == 0) return + + associate(cb => system%cb, pl => self, npl => self%nbody) + call pl%set_ir3() + + ah0(:) = whm_kick_getacch_ah0(pl%Gmass(2:npl), pl%xh(:,2:npl), npl-1) + do i = 1, npl + pl%ah(:, i) = pl%ah(:, i) + ah0(:) + end do + + call whm_kick_getacch_ah1(cb, pl) + call whm_kick_getacch_ah2(cb, pl) + call pl%accel_int() + + if (param%loblatecb) then + call pl%accel_obl(system) + if (lbeg) then + cb%aoblbeg = cb%aobl + else + cb%aoblend = cb%aobl + end if + if (param%ltides) then + cb%atidebeg = cb%aobl + call pl%accel_tides(system) + cb%atideend = cb%atide + end if + end if + + if (param%lgr) call pl%accel_gr(param) + + if (param%lextra_force) call pl%accel_user(system, param, t, lbeg) + end associate + + return + end subroutine whm_kick_getacch_pl + + + module subroutine whm_kick_getacch_tp(self, system, param, t, lbeg) + !! author: David A. Minton + !! + !! Compute heliocentric accelerations of test particles + !! + !! Adapted from Hal Levison's Swift routine getacch_tp.f + !! Adapted from David E. Kaufmann's Swifter routine whm_kick_getacch_tp.f90 + implicit none + ! Arguments + class(whm_tp), intent(inout) :: self !! WHM test particle data structure + class(swiftest_nbody_system), intent(inout) :: system !! Swiftest central body particle data structure + 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 + ! Internals + integer(I4B) :: i + real(DP), dimension(NDIM) :: ah0 + + associate(tp => self, ntp => self%nbody, pl => system%pl, cb => system%cb, npl => system%pl%nbody) + if (ntp == 0 .or. npl == 0) return + system%lbeg = lbeg + + if (lbeg) then + ah0(:) = whm_kick_getacch_ah0(pl%Gmass(:), pl%xbeg(:,:), npl) + do concurrent(i = 1:ntp, tp%lmask(i)) + tp%ah(:, i) = tp%ah(:, i) + ah0(:) + end do + call tp%accel_int(pl%Gmass(:), pl%xbeg(:,:), npl) + else + ah0(:) = whm_kick_getacch_ah0(pl%Gmass(:), pl%xend(:,:), npl) + do concurrent(i = 1:ntp, tp%lmask(i)) + tp%ah(:, i) = tp%ah(:, i) + ah0(:) + end do + call tp%accel_int(pl%Gmass(:), pl%xend(:,:), npl) + end if + + if (param%loblatecb) call tp%accel_obl(system) + if (param%lextra_force) call tp%accel_user(system, param, t, lbeg) + if (param%lgr) call tp%accel_gr(param) + end associate + + return + end subroutine whm_kick_getacch_tp + + + function whm_kick_getacch_ah0(mu, xhp, n) result(ah0) + !! author: David A. Minton + !! + !! Compute zeroth term heliocentric accelerations of planets + implicit none + ! Arguments + real(DP), dimension(:), intent(in) :: mu + real(DP), dimension(:,:), intent(in) :: xhp + integer(I4B), intent(in) :: n + ! Result + real(DP), dimension(NDIM) :: ah0 + ! Internals + real(DP) :: fac, r2, ir3h, irh + integer(I4B) :: i + + ah0(:) = 0.0_DP + do i = 1, n + r2 = dot_product(xhp(:, i), xhp(:, i)) + irh = 1.0_DP / sqrt(r2) + ir3h = irh / r2 + fac = mu(i) * ir3h + ah0(:) = ah0(:) - fac * xhp(:, i) + end do + + return + end function whm_kick_getacch_ah0 + + + pure subroutine whm_kick_getacch_ah1(cb, pl) + !! author: David A. Minton + !! + !! Compute first term heliocentric accelerations of planets + !! + !! Adapted from Hal Levison's Swift routine getacch_ah1.f + !! Adapted from David E. Kaufmann's Swifter routine whm_kick_getacch_ah1.f90 + implicit none + ! Arguments + class(swiftest_cb), intent(in) :: cb !! WHM central body object + class(whm_pl), intent(inout) :: pl !! WHM massive body object + ! Internals + integer(I4B) :: i + real(DP), dimension(NDIM) :: ah1h, ah1j + + associate(npl => pl%nbody) + do concurrent (i = 2:npl, pl%lmask(i)) + ah1j(:) = pl%xj(:, i) * pl%ir3j(i) + ah1h(:) = pl%xh(:, i) * pl%ir3h(i) + pl%ah(:, i) = pl%ah(:, i) + cb%Gmass * (ah1j(:) - ah1h(:)) + end do + end associate + + return + end subroutine whm_kick_getacch_ah1 + + + pure subroutine whm_kick_getacch_ah2(cb, pl) + !! author: David A. Minton + !! + !! Compute second term heliocentric accelerations of planets + !! + !! Adapted from Hal Levison's Swift routine getacch_ah2.f + !! Adapted from David E. Kaufmann's Swifter routine whm_kick_getacch_ah2.f90 + implicit none + ! Arguments + class(swiftest_cb), intent(in) :: cb !! Swiftest central body object + class(whm_pl), intent(inout) :: pl !! WHM massive body object + ! Internals + integer(I4B) :: i + real(DP) :: etaj, fac + real(DP), dimension(NDIM) :: ah2, ah2o + + associate(npl => pl%nbody) + ah2(:) = 0.0_DP + ah2o(:) = 0.0_DP + etaj = cb%Gmass + do concurrent(i = 2:npl, pl%lmask(i)) + etaj = etaj + pl%Gmass(i - 1) + fac = pl%Gmass(i) * cb%Gmass * pl%ir3j(i) / etaj + ah2(:) = ah2o + fac * pl%xj(:, i) + pl%ah(:,i) = pl%ah(:, i) + ah2(:) + ah2o(:) = ah2(:) + end do + end associate + + return + end subroutine whm_kick_getacch_ah2 + + + module subroutine whm_kick_vh_pl(self, system, param, t, dt, lbeg) + !! author: David A. Minton + !! + !! Kick heliocentric velocities of massive bodies + !! + !! Adapted from Martin Duncan and Hal Levison's Swift routine kickvh.f + !! Adapted from David E. Kaufmann's Swifter routine whm_kickvh.f90 + implicit none + ! Arguments + class(whm_pl), intent(inout) :: self !! WHM massive body object + 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 + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Logical flag indicating whether this is the beginning of the half step or not. + ! Internals + integer(I4B) :: i + + associate(pl => self, npl => self%nbody, cb => system%cb) + if (npl == 0) return + if (lbeg) then + if (pl%lfirst) then + call pl%h2j(cb) + pl%ah(:,:) = 0.0_DP + call pl%accel(system, param, t, lbeg) + pl%lfirst = .false. + end if + call pl%set_beg_end(xbeg = pl%xh) + else + pl%ah(:,:) = 0.0_DP + call pl%accel(system, param, t, lbeg) + call pl%set_beg_end(xend = pl%xh) + end if + do concurrent(i = 1:npl, pl%lmask(i)) + pl%vh(:, i) = pl%vh(:, i) + pl%ah(:, i) * dt + end do + end associate + + return + end subroutine whm_kick_vh_pl + + + module subroutine whm_kick_vh_tp(self, system, param, t, dt, lbeg) + !! author: David A. Minton + !! + !! Kick heliocentric velocities of test particles + !! + !! Adapted from Martin Duncan and Hal Levison's Swift routine kickvh_tp.f + !! Adapted from David E. Kaufmann's Swifter routine whm_kickvh_tp.f90 + implicit none + ! Arguments + class(whm_tp), intent(inout) :: self !! WHM massive body object + 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 + real(DP), intent(in) :: dt !! Stepsize + logical, intent(in) :: lbeg !! Logical flag indicating whether this is the beginning of the half step or not. + ! Internals + integer(I4B) :: i + + if (self%nbody == 0) return + + associate(tp => self, ntp => self%nbody) + if (tp%lfirst) then + where(tp%lmask(1:ntp)) + tp%ah(1,1:ntp) = 0.0_DP + tp%ah(2,1:ntp) = 0.0_DP + tp%ah(3,1:ntp) = 0.0_DP + end where + call tp%accel(system, param, t, lbeg=.true.) + tp%lfirst = .false. + end if + if (.not.lbeg) then + where(tp%lmask(1:ntp)) + tp%ah(1,1:ntp) = 0.0_DP + tp%ah(2,1:ntp) = 0.0_DP + tp%ah(3,1:ntp) = 0.0_DP + end where + call tp%accel(system, param, t, lbeg) + end if + do concurrent(i = 1:ntp, tp%lmask(i)) + tp%vh(:, i) = tp%vh(:, i) + tp%ah(:, i) * dt + end do + end associate + + return + end subroutine whm_kick_vh_tp + +end submodule s_whm_kick diff --git a/src/whm/whm_setup.f90 b/src/whm/whm_setup.f90 index 9f0f9b1b7..cbf36cc90 100644 --- a/src/whm/whm_setup.f90 +++ b/src/whm/whm_setup.f90 @@ -1,7 +1,8 @@ submodule(whm_classes) s_whm_setup use swiftest contains - module subroutine whm_setup_pl(self,n) + + module subroutine whm_setup_pl(self, n, param) !! author: David A. Minton !! !! Allocate WHM planet structure @@ -9,12 +10,20 @@ module subroutine whm_setup_pl(self,n) !! Equivalent in functionality to David E. Kaufmann's Swifter routine whm_setup.f90 implicit none ! Arguments - class(whm_pl), intent(inout) :: self !! Swiftest test particle object - integer(I4B), intent(in) :: n !! Number of test particles to allocate + class(whm_pl), intent(inout) :: self !! Swiftest test particle object + integer(I4B), intent(in) :: n !! Number of particles to allocate space for + class(swiftest_parameters), intent(in) :: param !! Current run configuration parameter + !> Call allocation method for parent class - call setup_pl(self, n) + 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)) @@ -30,22 +39,6 @@ module subroutine whm_setup_pl(self,n) return end subroutine whm_setup_pl - module subroutine whm_setup_tp(self,n) - !! author: David A. Minton - !! - !! Allocate WHM test particle structure - !! - !! Equivalent in functionality to David E. Kaufmann's Swifter routine whm_setup.f90 - implicit none - ! Arguments - class(whm_tp), intent(inout) :: self !! WHM test particle data structure - integer, intent(in) :: n !! Number of test particles to allocate - !> Call allocation method for parent class - call setup_tp(self, n) - if (n <= 0) return - - return - end subroutine whm_setup_tp module subroutine whm_util_set_mu_eta_pl(self, cb) !! author: David A. Minton @@ -53,8 +46,8 @@ module subroutine whm_util_set_mu_eta_pl(self, cb) !! Sets the Jacobi mass value eta for all massive bodies implicit none ! Arguments - class(whm_pl), intent(inout) :: self !! Swiftest system object - class(swiftest_cb), intent(inout) :: cb !! Swiftest central body particle data structure + class(whm_pl), intent(inout) :: self !! WHM system object + class(swiftest_cb), intent(inout) :: cb !! Swiftest central body object ! Internals integer(I4B) :: i @@ -69,9 +62,11 @@ module subroutine whm_util_set_mu_eta_pl(self, cb) end do end associate + return end subroutine whm_util_set_mu_eta_pl - module subroutine whm_setup_system(self, param) + + module subroutine whm_setup_initialize_system(self, param) !! author: David A. Minton !! !! Initialize a WHM nbody system from files @@ -79,11 +74,15 @@ module subroutine whm_setup_system(self, param) implicit none ! Arguments class(whm_nbody_system), intent(inout) :: self !! Swiftest system object - class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters of on parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters + + call setup_initialize_system(self, param) + ! First we need to make sure that the massive bodies are sorted by heliocentric distance before computing jacobies + call util_set_ir3h(self%pl) + call self%pl%sort("ir3h", ascending=.false.) - call io_read_initialize_system(self, param) ! Make sure that the discard list gets allocated initially - call self%tp_discards%setup(self%tp%nbody) + call self%tp_discards%setup(self%tp%nbody, param) call self%pl%set_mu(self%cb) call self%tp%set_mu(self%cb) if (param%lgr) then @@ -91,29 +90,7 @@ module subroutine whm_setup_system(self, param) call self%tp%v2pv(param) end if - end subroutine whm_setup_system - - module subroutine whm_setup_set_ir3j(self) - !! author: David A. Minton - !! - !! Sets the inverse Jacobi and heliocentric radii cubed (1/rj**3 and 1/rh**3) - implicit none - ! Arguments - class(whm_pl), intent(inout) :: self !! WHM massive body object - ! Internals - integer(I4B) :: i - real(DP) :: r2, ir - - if (self%nbody > 0) then - do i = 1, self%nbody - r2 = dot_product(self%xh(:, i), self%xh(:, i)) - ir = 1.0_DP / sqrt(r2) - self%ir3h(i) = ir / r2 - r2 = dot_product(self%xj(:, i), self%xj(:, i)) - ir = 1.0_DP / sqrt(r2) - self%ir3j(i) = ir / r2 - end do - end if - end subroutine whm_setup_set_ir3j + return + end subroutine whm_setup_initialize_system end submodule s_whm_setup \ No newline at end of file diff --git a/src/whm/whm_spill_and_fill.f90 b/src/whm/whm_spill_and_fill.f90 deleted file mode 100644 index f5edf894c..000000000 --- a/src/whm/whm_spill_and_fill.f90 +++ /dev/null @@ -1,92 +0,0 @@ -submodule(whm_classes) s_whm_spill_and_fill - use swiftest -contains - module subroutine whm_spill_pl(self, discards, lspill_list) - !! 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 - ! 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 - - return - - end subroutine whm_spill_pl - - module subroutine whm_fill_pl(self, inserts, lfill_list) - !! author: David A. Minton - !! - !! Insert new WHM test particle structure into an old one. - !! This is the inverse of a fill operation. - !! - !! 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) :: inserts !! inserted object - 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) - 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_pl(keeps, inserts, lfill_list) - class default - write(*,*) 'Error! fill method called for incompatible return type on whm_pl' - end select - end associate - - return - - end subroutine whm_fill_pl - -end submodule s_whm_spill_and_fill diff --git a/src/whm/whm_step.f90 b/src/whm/whm_step.f90 index ce00b86b1..d194e2c02 100644 --- a/src/whm/whm_step.f90 +++ b/src/whm/whm_step.f90 @@ -12,17 +12,20 @@ module subroutine whm_step_system(self, param, t, dt) implicit none ! Arguments class(whm_nbody_system), intent(inout) :: self !! WHM nbody system object - class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters of on parameters + class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters real(DP), intent(in) :: t !! Current simulation time real(DP), intent(in) :: dt !! Current stepsize associate(system => self, cb => self%cb, pl => self%pl, tp => self%tp) + tp%lfirst = pl%lfirst call pl%step(system, param, t, dt) call tp%step(system, param, t, dt) + if (param%ltides) call system%step_spin(param, t, dt) end associate return end subroutine whm_step_system + module subroutine whm_step_pl(self, system, param, t, dt) !! author: David A. Minton !! @@ -45,25 +48,19 @@ module subroutine whm_step_pl(self, system, param, t, dt) associate(pl => self, cb => system%cb) dth = 0.5_DP * dt - if (pl%lfirst) then - call pl%h2j(cb) - call pl%accel(system, param, t) - pl%lfirst = .false. - end if - call pl%set_beg_end(xbeg = pl%xh) - call pl%kick(dth) + call pl%kick(system, param, t, dth,lbeg=.true.) call pl%vh2vj(cb) if (param%lgr) call pl%gr_pos_kick(param, dth) call pl%drift(system, param, dt) if (param%lgr) call pl%gr_pos_kick(param, dth) call pl%j2h(cb) - call pl%accel(system, param, t + dt) - call pl%kick(dth) - call pl%set_beg_end(xend = pl%xh) + call pl%kick(system, param, t + dt, dth, lbeg=.false.) end associate + return end subroutine whm_step_pl + module subroutine whm_step_tp(self, system, param, t, dt) !! author: David A. Minton !! @@ -87,18 +84,14 @@ module subroutine whm_step_tp(self, system, param, t, dt) class is (whm_nbody_system) associate(tp => self, cb => system%cb, pl => system%pl) dth = 0.5_DP * dt - if (tp%lfirst) then - call tp%accel(system, param, t, lbeg=.true.) - tp%lfirst = .false. - end if - call tp%kick(dth) + call tp%kick(system, param, t, dth, lbeg=.true.) if (param%lgr) call tp%gr_pos_kick(param, dth) call tp%drift(system, param, dt) if (param%lgr) call tp%gr_pos_kick(param, dth) - call tp%accel(system, param, t + dt, lbeg=.false.) - call tp%kick(dth) + call tp%kick(system, param, t + dt, dth, lbeg=.false.) end associate end select + return end subroutine whm_step_tp diff --git a/src/whm/whm_util.f90 b/src/whm/whm_util.f90 new file mode 100644 index 000000000..f3dc15d3e --- /dev/null +++ b/src/whm/whm_util.f90 @@ -0,0 +1,220 @@ +submodule(whm_classes) s_whm_util + use swiftest +contains + + module subroutine whm_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(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_append_pl + + module subroutine whm_util_fill_pl(self, inserts, lfill_list) + !! author: David A. Minton + !! + !! Insert new WHM test particle structure into an old one. + !! This is the inverse of a fill operation. + !! + !! 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(in) :: inserts !! inserted object + 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) + class is (whm_pl) + 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(*,*) "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 + + return + 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 + !! + !! Sets the inverse Jacobi and heliocentric radii cubed (1/rj**3 and 1/rh**3) + implicit none + ! Arguments + class(whm_pl), intent(inout) :: self !! WHM massive body object + ! Internals + integer(I4B) :: i + real(DP) :: r2, ir + + if (self%nbody > 0) then + do i = 1, self%nbody + r2 = dot_product(self%xh(:, i), self%xh(:, i)) + ir = 1.0_DP / sqrt(r2) + self%ir3h(i) = ir / r2 + r2 = dot_product(self%xj(:, i), self%xj(:, i)) + ir = 1.0_DP / sqrt(r2) + self%ir3j(i) = ir / r2 + end do + end if + + return + end subroutine whm_util_set_ir3j + + + module subroutine whm_util_sort_pl(self, sortby, ascending) + !! author: David A. Minton + !! + !! Sort a WHM massive body object in-place. + !! sortby is a string indicating which array component to sort. + implicit none + ! Arguments + 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 + ! Internals + integer(I4B), dimension(self%nbody) :: ind + integer(I4B) :: direction + + if (ascending) then + direction = 1 + else + direction = -1 + end if + + associate(pl => self, npl => self%nbody) + select case(sortby) + case("eta") + call util_sort(direction * pl%eta(1:npl), ind(1:npl)) + case("muj") + call util_sort(direction * pl%muj(1:npl), ind(1:npl)) + case("ir3j") + call util_sort(direction * pl%ir3j(1:npl), ind(1:npl)) + case("xj", "vj") + write(*,*) 'Cannot sort by ' // trim(adjustl(sortby)) // '. Component not sortable!' + case default + call util_sort_pl(pl, sortby, ascending) + return + end select + + call pl%rearrange(ind) + end associate + + return + end subroutine whm_util_sort_pl + + + module subroutine whm_util_sort_rearrange_pl(self, ind) + !! author: David A. Minton + !! + !! Rearrange WHM massive body structure in-place from an index list. + !! This is a helper utility used to make polymorphic sorting work on Swiftest structures. + implicit none + ! Arguments + 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) + ! Internals + class(whm_pl), allocatable :: pl_sorted !! Temporary holder for sorted body + integer(I4B) :: i + + if (self%nbody == 0) return + + associate(pl => self, npl => self%nbody) + call util_sort_rearrange_pl(pl,ind) + allocate(pl_sorted, source=self) + 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