diff --git a/configure.ac b/configure.ac index 29e1b3543cae74c1400821bdbf799125b0dbb353..b0173c6954c23dc0f7725c94c720fd8c69d78e77 100644 --- a/configure.ac +++ b/configure.ac @@ -818,6 +818,42 @@ AC_SUBST([GSL_LIBS]) AC_SUBST([GSL_INCS]) AM_CONDITIONAL([HAVEGSL],[test -n "$GSL_LIBS"]) +# Check for GMP. We test for this in the standard directories by default, +# and only disable if using --with-gmp=no or --without-gmp. When a value +# is given GMP must be found. +have_gmp="no" +AC_ARG_WITH([gmp], + [AS_HELP_STRING([--with-gmp=PATH], + [root directory where GMP is installed @<:@yes/no@:>@] + )], + [with_gmp="$withval"], + [with_gmp="test"] +) +if test "x$with_gmp" != "xno"; then + if test "x$with_gmp" != "xyes" -a "x$with_gmp" != "xtest" -a "x$with_gmp" != "x"; then + GMP_LIBS="-L$with_gmp/lib -lgmp" + else + GMP_LIBS="-lgmp" + fi + # GMP is not specified, so just check if we have it. + if test "x$with_gmp" = "xtest"; then + AC_CHECK_LIB([gmp],[__gmpz_inits],[have_gmp="yes"],[have_gmp="no"],$GMP_LIBS) + if test "x$have_gmp" != "xno"; then + AC_DEFINE([HAVE_LIBGMP],1,[The GMP library appears to be present.]) + fi + else + AC_CHECK_LIB([gmp],[__gmpz_inits], + AC_DEFINE([HAVE_LIBGMP],1,[The GMP library appears to be present.]), + AC_MSG_ERROR(something is wrong with the GMP library!), $GMP_LIBS) + have_gmp="yes" + fi + if test "$have_gmp" = "no"; then + GMP_LIBS="" + fi +fi +AC_SUBST([GMP_LIBS]) +AM_CONDITIONAL([HAVEGMP],[test -n "$GMP_LIBS"]) + # Check for pthreads. AX_PTHREAD([LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" CC="$PTHREAD_CC" LDFLAGS="$LDFLAGS $PTHREAD_LIBS $LIBS"], @@ -1587,16 +1623,26 @@ else have_chealpix="no" fi -# Check for floating-point exception support. -# CLANG based compilers can generate SIMD divides with partially loaded -# registers so we see a 0/0 NaN which results in an FPE which is not an -# error. So we do not allow this with those compilers. If you want to do -# that then beware and use the -ffp-exception-behavior=maytrap flag. -if test "$ax_cv_c_compiler_vendor" != "oneapi" -a \ - "$ax_cv_c_compiler_vendor" != "clang"; then - AC_CHECK_FUNC(feenableexcept, AC_DEFINE([HAVE_FE_ENABLE_EXCEPT],[1], - [Defined if the floating-point exception can be enabled using non-standard GNU functions.])) +# Check for floating-point exception trapping support. +# +# We do not allow this to be enabled when optimizing as compilers do operations +# which are unsafe for speed. This can result in FPEs on valid vector +# operations when additional padding is used. This has been seen on clang and +# GCC based compilers. +if test "$enable_opt" != "yes"; then + if test "$ax_cv_c_compiler_vendor" != "oneapi"; then + AC_CHECK_FUNC(feenableexcept, AC_DEFINE([HAVE_FE_ENABLE_EXCEPT],[1], + [Defined if floating-point exceptions can be trapped.])) + else + # Default optimization for Intel is too high , -O2, so we also need + # to have debugging enabled which uses -O0 as well. + if test "$ax_enable_debug" != "no"; then + AC_CHECK_FUNC(feenableexcept, AC_DEFINE([HAVE_FE_ENABLE_EXCEPT],[1], + [Defined if floating-point exceptions can be trapped.])) + fi + fi fi + # Check for setaffinity. AC_CHECK_FUNC(pthread_setaffinity_np, AC_DEFINE([HAVE_SETAFFINITY],[1], [Defined if pthread_setaffinity_np exists.]) ) @@ -2099,7 +2145,7 @@ fi # Hydro scheme. AC_ARG_WITH([hydro], [AS_HELP_STRING([--with-hydro=<scheme>], - [Hydro dynamics to use @<:@gadget2, minimal, pressure-entropy, pressure-energy, pressure-energy-monaghan, phantom, gizmo-mfv, gizmo-mfm, shadowfax, planetary, sphenix, gasoline, anarchy-pu default: sphenix@:>@] + [Hydro dynamics to use @<:@gadget2, minimal, pressure-entropy, pressure-energy, pressure-energy-monaghan, phantom, gizmo-mfv, gizmo-mfm, shadowswift, planetary, sphenix, gasoline, anarchy-pu default: sphenix@:>@] )], [with_hydro="$withval"], [with_hydro="sphenix"] @@ -2136,9 +2182,12 @@ case "$with_hydro" in AC_DEFINE([GIZMO_MFM_SPH], [1], [GIZMO MFM SPH]) need_riemann_solver=yes ;; - shadowfax) - AC_DEFINE([SHADOWFAX_SPH], [1], [Shadowfax SPH]) + shadowswift) + AC_DEFINE([SHADOWSWIFT], [1], [ShadowSWIFT hydrodynamics]) + AC_DEFINE([MOVING_MESH_HYDRO], [1], [Moving mesh hydrodynamics]) + need_moving_mesh=yes need_riemann_solver=yes + hydro_does_mass_flux=yes ;; planetary) AC_DEFINE([PLANETARY_SPH], [1], [Planetary SPH]) @@ -2185,8 +2234,8 @@ fi if test "$with_hydro" = "gizmo-mfv" -a "$with_spmhd" != "none"; then AC_MSG_ERROR([Cannot use an SPMHD scheme alongside a gizmo hydro solver!"]) fi -if test "$with_hydro" = "shadowfax" -a "$with_spmhd" != "none"; then - AC_MSG_ERROR([Cannot use an SPMHD scheme alongside a gizmo hydro solver!"]) +if test "$with_hydro" = "shadowswift" -a "$with_spmhd" != "none"; then + AC_MSG_ERROR([Cannot use an SPMHD scheme alongside a moving mesh hydro solver!"]) fi # Check if debugging interactions stars is switched on. @@ -2415,6 +2464,27 @@ if test "x$need_riemann_solver" = "xyes" -a "$with_riemann" = "none"; then AC_MSG_ERROR([Hydro scheme $with_hydro requires selection of a Riemann solver!]) fi +# Moving mesh +AC_ARG_ENABLE([moving-mesh], + [AS_HELP_STRING([--enable-moving-mesh], + [enable the moving mesh computation] + )], + [enable_moving_mesh="${enableval}"], + [enable_moving_mesh="no"] +) +if test "x$need_moving_mesh" = "xyes"; then + enable_moving_mesh="yes" +fi +if test "$enable_moving_mesh" = "yes"; then + if test "$have_gmp" = "no"; then + AC_MSG_ERROR([GMP is required when using moving mesh!]) + fi + if test "$have_gsl" = "no"; then + AC_MSG_ERROR([GSL is required when using moving mesh!]) + fi + AC_DEFINE([MOVING_MESH], [1], [Unstructured Voronoi mesh]) +fi + # Hydro does mass flux? if test "x$hydro_does_mass_flux" = "xyes"; then AC_DEFINE([HYDRO_DOES_MASS_FLUX], [1], [Hydro scheme with mass fluxes]) @@ -3012,6 +3082,7 @@ case "$with_rt" in AC_DEFINE([RT_GEAR], [1], [GEAR M1 closure scheme]) number_group=${with_rt#*_} AC_DEFINE_UNQUOTED([RT_NGROUPS], [$number_group], [Number of photon groups to follow]) + AC_DEFINE([MPI_SYMMETRIC_FORCE_INTERACTION_RT], [1], [Do symmetric MPI interactions]) if test "$number_group" = "0"; then AC_MSG_ERROR([GEAR-RT: Cannot work with zero photon groups]) @@ -3026,9 +3097,14 @@ case "$with_rt" in AC_DEFINE([SWIFT_RT_DEBUG_CHECKS], [1], [additional debugging checks for RT]) fi - if test "$with_hydro" != "gizmo-mfv"; then - AC_MSG_ERROR([GEAR-RT: Cannot work without gizmo-mfv hydro. Compile using --with-hydro=gizmo-mfv]) - fi + case "$with_hydro" in + "gizmo-mfv" | "sphenix") + # allowed. + ;; + *) + AC_MSG_ERROR([GEAR-RT: Cannot work without gizmo-mfv or sphenix hydro. Compile using --with-hydro=gizmo-mfv or --with-hydro=sphenix]) + ;; + esac if test "$with_rt_riemann_solver" = "none"; then AC_MSG_ERROR([GEAR-RT: You need to select an RT Riemann solver (--with-rt-riemann-solver=...)]) @@ -3145,6 +3221,9 @@ AM_CONDITIONAL([HAVESPHM1RTRT], [test "${with_rt:0:7}" = "SPHM1RT"]) # Check if using GEAR-RT radiative transfer AM_CONDITIONAL([HAVEGEARRT], [test "${with_rt:0:4}" = "GEAR"]) +# Check if using Moving mesh +AM_CONDITIONAL([HAVE_MOVING_MESH], [test "$enable_moving_mesh" = "yes"]) + @@ -3202,6 +3281,7 @@ AC_MSG_RESULT([ - MPI : $have_mpi_fftw - ARM : $have_arm_fftw GSL enabled : $have_gsl + GMP enabled : $have_gmp HEALPix C enabled : $have_chealpix libNUMA enabled : $have_numa GRACKLE enabled : $have_grackle @@ -3212,6 +3292,7 @@ AC_MSG_RESULT([ VELOCIraptor enabled : $have_velociraptor FoF activated: : $enable_fof Lightcones enabled : $enable_lightcone + Moving-mesh enabled : $enable_moving_mesh Hydro scheme : $with_hydro Dimensionality : $with_dimension diff --git a/doc/RTD/source/HydroSchemes/index.rst b/doc/RTD/source/HydroSchemes/index.rst index 9b54a0b76d43556971f807c46c941ae1d5ab376e..3b511e03c0d0e92c6b663050d4c85df87860316f 100644 --- a/doc/RTD/source/HydroSchemes/index.rst +++ b/doc/RTD/source/HydroSchemes/index.rst @@ -41,5 +41,6 @@ In case the case of a 2 loop scheme, SWIFT removes the gradient loop and the ext phantom_sph adaptive_softening gizmo + shadowswift adding_your_own diff --git a/doc/RTD/source/HydroSchemes/shadowswift.rst b/doc/RTD/source/HydroSchemes/shadowswift.rst new file mode 100644 index 0000000000000000000000000000000000000000..40409f725ed8ac9aa63a8bb90c3d8b825f07f238 --- /dev/null +++ b/doc/RTD/source/HydroSchemes/shadowswift.rst @@ -0,0 +1,48 @@ +.. ShadowSWIFT (Moving mesh hydrodynamics) + Yolan Uyttenhove September 2023 + +ShadowSWIFT (moving mesh hydrodynamics) +======================================= + +.. warning:: + The moving mesh hydrodynamics solver is currently in the process of being merged into master and will **NOT** + work on the master branch. To use it, compile the code using the ``moving_mesh`` branch. + +This is an implementation of the moving-mesh finite-volume method for hydrodynamics in SWIFT. +To use this scheme, a Riemann solver is also needed. Configure SWIFT as follows: + +.. code-block:: bash + + ./configure --with-hydro="shadowswift" --with-riemann-solver="hllc" + + +Current status +~~~~~~~~~~~~~~ + +Due to the completely different task structure compared to SPH hydrodynamics, currently only a subset of the features of +SWIFT is supported in this scheme. + +- Hydrodynamics is fully supported in 1D, 2D and 3D and over MPI. + +- Both self-gravity and external potentials are supported. + +- Cosmological time-integration is supported. + +- Cooling and chemistry are supported, with the exception of the ``GEAR_diffusion`` chemistry scheme. Metals are + properly according to mass fluxes. + +- Choice between periodic, reflective, open, inflow and vacuum boundary conditions (for non-periodic boundary + conditions, the desired variant must be selected in ``const.h``). Additionally, reflective boundary conditions + are applied to SWIFT's boundary particles. Configure with ``--with-boundary-particles=<N>`` to use this (e.g. to + simulate walls). + + +Caveats +~~~~~~~ +These are currently the main limitations of the ShadowSWIFT hydro scheme: + +- Unlike SPH the cells of the moving mesh must form a partition of the entire simulation volume. This means that there + cannot be empty SWIFT cells and vacuum must be explicitly represented by zero (or negligible) mass particles. +- Most other subgrid physics, most notably, star formation and stellar feedback are not supported yet. +- No MHD schemes are supported. +- No radiative-transfer schemes are supported. diff --git a/doc/RTD/source/NewOption/index.rst b/doc/RTD/source/NewOption/index.rst index 08f1ff04efa9508145c1f7e04d72d2f40fe22f0d..c05acd6f1d053118766482f89fae72f8271df899 100644 --- a/doc/RTD/source/NewOption/index.rst +++ b/doc/RTD/source/NewOption/index.rst @@ -34,3 +34,8 @@ In order to add a new scheme, you will need to: ``nobase_noinst_HEADERS``, add your new header files. 6. Update the documentation. Add your equations/documentation to ``doc/RTD``. + +.. toctree:: + :caption: Table of Contents + + sink_adding_new_scheme diff --git a/doc/RTD/source/NewOption/sink_adding_new_scheme.rst b/doc/RTD/source/NewOption/sink_adding_new_scheme.rst new file mode 100644 index 0000000000000000000000000000000000000000..b25829a18ae63f677df961982d607ee7b52c8cd5 --- /dev/null +++ b/doc/RTD/source/NewOption/sink_adding_new_scheme.rst @@ -0,0 +1,49 @@ +.. Adding new schemes + Darwin Roduit, 16 Ocotber 2024 + +.. _new_option_sink: + +How to add your sink scheme +------------------------------- + +Here, we provide comprehensive information to guide you in adding your sink scheme into Swift. To better understand how to add new schemes within Swift, read the general information provided on :ref:`new_option` page. + +The default sink scheme is empty and gives you an idea of the minimum required fields and functions for the code to compile. The GEAR sink module has the base functions plus some extra ones for its operations. It can provide you with a working example. However, it can only work with the GEAR feedback module since it relies on IMF properties that are only located there. + +As discussed in the GEAR sink :ref:`sink_GEAR_model_summary`, the physics relies on the following tasks: sink formation, gas and sink particle flagging, gas swallowing, sink swallowing and star formation. You do not need to care about the tasks, only the core functions within the sink module. However, you may need to locate where the code calls these functions. The file ``src/runner_others.c`` contains the ``runner_do_star_formation_sink()`` and ``runner_do_sink_formation()``. These functions are responsible for generating stars out of sinks and sinks from gas particles. The other general task-related functions are in ``src/runner_sinks.c``. + +The following presents the most essential functions you need to implement. This will give you an idea of the workload. + + +Sink formation +~~~~~~~~~~~~~~ + +Before forming a sink, the code loops over all gas and sink particles to gather data about its neighbourhood. This is performed in ``sink_prepare_part_sink_formation_gas_criteria()`` and ``sink_prepare_part_sink_formation_sink_criteria()`` functions. For instance, in GEAR, we compute the total potential energy, thermal energy, etc. + +Then, to decide if we can turn a gas particle into a sink particle, the function ``sink_is_forming()`` is called. Before forming a sink particle, there is a call to ``sink_should_convert_to_sink()``. This function determines whether the gas particle must transform into a sink. Both functions return either 0 or 1. + +Gas and sink flagging: finding whom to eat +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before accreting the gas/sink particles, the sink needs to look for eligible particles. The gas swallow interactions are performed within ``runner_iact_nonsym_sinks_gas_swallow()`` and the sink swallow in ``runner_iact_nonsym_sinks_sink_swallow()``. + + +Gas and sink swallowing: updating the sink properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When the sink swallows gas particles, it updates its internal properties based on the gas particles' properties. The ``sink_swallow_part()`` function takes care of this. + +Similarly, when the sink swallows sink particles, it updates its properties from the to-be-swallowed sink particles. The ``sink_swallow_sink()`` performs the update. + +There is no more than that. The code properly removes the swallowed particles. + +Star formation +~~~~~~~~~~~~~~ + +The most important function is ``sink_spawn_star()``. It controls whether the code should continue creating stars and must return 0 or 1. + +The ``sink_copy_properties_to_star()`` does what it says. This function is also responsible for adequately initialising the stars' properties. In GEAR, we give new positions and velocities within this function. + +The following three functions allow you to update your sink particles (e.g. their masses) before, during and after the star formation loop: ``sink_update_sink_properties_before_star_formation()``, ``sink_update_sink_properties_during_star_formation()`` and ``sink_update_sink_properties_after_star_formation()``. + +These functions are located in ``sink/Default/sink.h`` diff --git a/doc/RTD/source/ParameterFiles/parameter_description.rst b/doc/RTD/source/ParameterFiles/parameter_description.rst index 290acdf3f4c666e764f8b1f1c6be0a121159f9d0..522f000816b024f2d8e3b3bf365a5d3bf86db7d0 100644 --- a/doc/RTD/source/ParameterFiles/parameter_description.rst +++ b/doc/RTD/source/ParameterFiles/parameter_description.rst @@ -466,7 +466,10 @@ can be either drawn randomly by setting the parameter ``generate_random_ids`` newly generated IDs do not clash with any other pre-existing particle. If this option is set to :math:`0` (the default setting) then the new IDs are created in increasing order from the maximal pre-existing value in the simulation, hence -preventing any clash. +preventing any clash. Finally, if the option +``particle_splitting_log_extra_splits`` is set, the code will log all the splits +that go beyond the maximal allowed (typically 64) in a file so that the split tree +for these particles can still be reconstructed. The final set of parameters in this section determine the initial and minimum temperatures of the particles. diff --git a/doc/RTD/source/Snapshots/index.rst b/doc/RTD/source/Snapshots/index.rst index 4735a89d5c637b2c2e3f487bf9589f69624aa8d9..03dc411dd2a9f05afd3ee8b78b5115a5b50f1942 100644 --- a/doc/RTD/source/Snapshots/index.rst +++ b/doc/RTD/source/Snapshots/index.rst @@ -371,12 +371,16 @@ the combination of ``ProgenitorID`` and this binary tree corresponds to a fully traceable, unique, identifier for every particle in the simulation volume. Note that we can only track 64 splitting events for a given particle, and after -this the binary tree is meaningless. In practice, however, such a high number -of splitting events is extremely unlikely to occur. - -An example is provided in ``examples/SubgridTests/ParticleSplitting``, with -a figure showing how one particle is split (eventually) into 16 descendants -that makes use of this metadata. +this the binary tree is meaningless. In practice, however, such a high number of +splitting events is extremely unlikely to occur. The logging of extra splits can +optionally be activated. When particles reach 64 splits, their tree information +is reset but the status prior to the reset is stored in a log file allowing for +the reconstruction of the full history even in the cases where the maximum is +reached. + +An example is provided in ``examples/SubgridTests/ParticleSplitting``, with a +figure showing how one particle is split (eventually) into 16 descendants that +makes use of this metadata. Quick access to particles via hash-tables ----------------------------------------- diff --git a/doc/RTD/source/SubgridModels/GEAR/sinks/introduction.rst b/doc/RTD/source/SubgridModels/GEAR/sinks/introduction.rst index 77bca1545ef5c074bf69646b69a5b9eef5eb224f..a90c83a2f8e1aed1146818ea9f4a78418e39648e 100644 --- a/doc/RTD/source/SubgridModels/GEAR/sinks/introduction.rst +++ b/doc/RTD/source/SubgridModels/GEAR/sinks/introduction.rst @@ -17,9 +17,13 @@ In ``theory.rst`` we outline all of the theory which is implemented as part of t Compiling and running the model ------------------------------- -You can configure the model with ``--with-sink=GEAR`` in combination with other configure options of the GEAR model. The model will then be used when the ``--sinks`` flag is among the runtime options. +You can configure the model with ``--with-sink=GEAR`` in combination with other configure options of the GEAR model. The model will then be used when the ``--sinks`` flag is among the runtime options. In particular, the sink particles require ``--feedback`` runtime option. + +Notice that you also need to compile with ``--with-star-formation=GEAR``. The star formation module is required to collect and write star formation data. However, you do not need to run Swift with the option ``--star-formation``. Then, you do not need to do anything special. Sink particles will be created during your runs. If you want, you can have sink particles in your ICs. At the moment, sink particles do not have any special fields to be set. A full list of all relevant parameters of the model is in :ref:`sink_GEAR_parameters`. We also briefly describe the most important parameters which need to be set to run the model, as well as how to run it in different configurations. +.. warning:: + Currently, MPI is not implemented for the sink particles. If you try to run with MPI, you will encounter an error. We thus recommend configuring Swift with ``--disable-mpi`` to avoid any surprises. diff --git a/doc/RTD/source/SubgridModels/GEAR/sinks/params.rst b/doc/RTD/source/SubgridModels/GEAR/sinks/params.rst index 59bb55bb77372aee02e82d1cd49a6e713e1ae6aa..b652ea52bb8eff53b42c134154c31c67adb54f2d 100644 --- a/doc/RTD/source/SubgridModels/GEAR/sinks/params.rst +++ b/doc/RTD/source/SubgridModels/GEAR/sinks/params.rst @@ -3,9 +3,6 @@ .. sink_GEAR_model: -.. warning:: - This page is under construction. It may lack information. - .. _sink_GEAR_parameters: Model parameters @@ -23,19 +20,18 @@ The ``f_acc`` parameter is optional. Its default value is :math:`0.8`. Its value The next two mandatory parameters are: * the gas maximal temperature to form a sink:``maximal_temperature``, -* the gas threshold density to form a sink:``density_threshold``. +* the gas threshold density to form a sink:``density_threshold_g_per_cm3``. These two parameters govern the first two criteria of the sink formation scheme. If these criteria are not passed, sink particles are not created. If they are passed, the code performs further checks to form sink particles. Some of those criteria checks can be disabled, as explained below. The next set of parameters deals with the sampling of the IMF and the representation of star particles: -* size of the calibration sample used to determine the probabilities to form stellar particles with mass ``stellar_particle_mass``: ``size_of_calibration_sample``, -* minimal mass of stars represented by discrete particles: ``minimal_discrete_mass``, -* mass of the stellar particle representing the continuous part of the IMF: ``stellar_particle_mass``, -* minimal mass of the first stars represented by discrete particles: ``minimal_discrete_mass_first_stars``, -* mass of the stellar particle (first stars) representing the continuous part of the IMF:: ``stellar_particle_mass_first_stars``. +* minimal mass of stars represented by discrete particles: ``minimal_discrete_mass_Msun``, +* mass of the stellar particle representing the continuous part of the IMF: ``stellar_particle_mass_Msun``, +* minimal mass of the first stars represented by discrete particles: ``minimal_discrete_mass_first_stars_Msun``, +* mass of the stellar particle (first stars) representing the continuous part of the IMF:: ``stellar_particle_mass_first_stars_Msun``. -With sink particles, star particles can represent either a single star or a population of stars in the low mass part of the IMF (continuous IMF sampling). The stars in the continuous part of the IMF are put together in a particle of mass ``stellar_particle_mass`` or ``stellar_particle_mass_first_stars``, while individual stars in the discrete part have their mass sampled from the IMF. The limit between the continuous and discrete sampling of the IMF is controlled by ``minimal_discrete_mass`` and ``minimal_discrete_mass_first_stars``. +With sink particles, star particles can represent either a single star or a population of stars in the low mass part of the IMF (continuous IMF sampling). The stars in the continuous part of the IMF are put together in a particle of mass ``stellar_particle_mass_Msun`` or ``stellar_particle_mass_first_stars_Msun``, while individual stars in the discrete part have their mass sampled from the IMF. The limit between the continuous and discrete sampling of the IMF is controlled by ``minimal_discrete_mass_Msun`` and ``minimal_discrete_mass_first_stars_Msun``. The next set of parameters controls the sink formation scheme. More details are provided in the GEAR documentation. Here is a brief overview: @@ -57,12 +53,12 @@ The full section is: cut_off_radius: 1e-3 # Cut off radius of the sink particles (in internal units). f_acc: 0.8 # (Optional) Fraction of the cut_off_radius that determines if a gas particle should be swallowed wihtout additional check. (Default: 0.8) maximal_temperature: 3e3 # Maximal gas temperature for forming a star (in K) - density_threshold: 1.67e-21 # Minimal gas density for forming a star (in g/cm3 (1.67e-24 =1acc)) - size_of_calibration_sample: 100000 # Size of the calibration sample used to determine the probabilities to form stellar particles with mass stellar_particle_mass - stellar_particle_mass: 20 # Mass of the stellar particle representing the low mass stars (continuous IMF sampling) (in solar mass) - minimal_discrete_mass: 8 # Minimal mass of stars represented by discrete particles (in solar mass) - stellar_particle_mass_first_stars: 20 # Mass of the stellar particle representing the low mass stars (continuous IMF sampling) (in solar mass). First stars - minimal_discrete_mass_first_stars: 8 # Minimal mass of stars represented by discrete particles (in solar mass). First stars + density_threshold_g_per_cm3: 1.67e-21 # Minimal gas density for forming a star (in g/cm3 (1.67e-24 =1acc)) + stellar_particle_mass_Msun: 20 # Mass of the stellar particle representing the low mass stars (continuous IMF sampling) (in solar mass) + minimal_discrete_mass_Msun: 8 # Minimal mass of stars represented by discrete particles (in solar mass) + stellar_particle_mass_first_stars_Msun: 20 # Mass of the stellar particle representing the low mass stars (continuous IMF sampling) (in solar mass). First stars + minimal_discrete_mass_first_stars_Msun: 8 # Minimal mass of stars represented by discrete particles (in solar mass). First stars + star_spawning_sigma_factor: 0.2 # Factor to rescale the velocity dispersion of the stars when they are spawned. (Default: 0.2) sink_formation_contracting_gas_criterion: 1 # (Optional) Activate the contracting gas criterion for sink formation. (Default: 1) sink_formation_smoothing_length_criterion: 1 # (Optional) Activate the smoothing length criterion for sink formation. (Default: 1) sink_formation_jeans_instability_criterion: 1 # (Optional) Activate the two Jeans instability criteria for sink formation. (Default: 1) @@ -73,17 +69,39 @@ The full section is: .. warning:: Some parameter choices can greatly impact the outcome of your simulations. Think twice when choosing them. -The most critical parameter is ``cut_off_radius``. As explained in the theory, to form a sink, the gas smoothing length `h` must be smaller than ``cut_off_radius / 2`` (if this criterion is enabled). Therefore, the cut-off radius strongly depends on the resolution of your simulations. Moreover, if you use a minimal gas smoothing length `h`, and plan to use sink particles, consider whether the cut-off radius will meet the smoothing length criterion. If `h` never meets the aforementioned criterion, you will never form sinks and thus never have stars. +Sink accretion radius +~~~~~~~~~~~~~~~~~~~~~ + +The most critical parameter is ``cut_off_radius``. As explained in the theory, to form a sink, the gas smoothing kernel edge :math:`\gamma_k h` (:math:`\gamma_k` is a kernel dependent constant) must be smaller than ``cut_off_radius`` (if this criterion is enabled). Therefore, the cut-off radius strongly depends on the resolution of your simulations. Moreover, if you use a minimal gas smoothing length `h`, and plan to use sink particles, consider whether the cut-off radius will meet the smoothing length criterion. If `h` never meets the aforementioned criterion, you will never form sinks and thus never have stars. On the contrary, if you set a too high cut-off radius, then sinks will accrete a lot of gas particles and spawn a lot of stars in the same cell, which the code might not like and crash with the error: ``runner_others.c:runner_do_star_formation_sink():274: Too many stars in the cell tree leaf! The sorting task will not be able to perform its duties. Possible solutions: (1) The code need to be run with different star formation parameters to reduce the number of star particles created. OR (2) The size of the sorting stack must be increased in runner_sort.c.`` -This problem can be mitigated by choosing a higher value of ``stellar_particle_mass`` and ``stellar_particle_mass_first_stars``, or higher values of ``minimal_discrete_mass`` and ``minimal_discrete_mass_first_stars``. Of course, this comes at the price of having fewer individual stars. Finally, all parameters will depend on your needs. +This problem can be mitigated by choosing a higher value of ``stellar_particle_mass_Msun`` and ``stellar_particle_mass_first_stars_Msun``, or higher values of ``minimal_discrete_mass_Msun`` and ``minimal_discrete_mass_first_stars_Msun``. Of course, this comes at the price of having fewer individual stars. Finally, all parameters will depend on your needs. *If you do not want to change your parameters*, you can increase the ``sort_stack_size`` variable at the beginning ``runner_sort.c``. The default value is 10 in powers of 2 (so the stack size is 1024 particles). Increase it to the desired value. Be careful to not overestimate this. +Guide to choose the the accretion radius or the density threshold +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We provide some advice to help you set up the sink accretion radius or the threshold density appropriately. + +First, you must choose either the sink accretion radius or the threshold density. Choosing the density might be easier based on your previous work or if you have an expected star formation density. Once you fix the density or the accretion radius, you can use the following formula to estimate the remaining parameter. In the code, the gas smoothing length is determined with: + +.. math:: + h = \eta \left( \frac{X_{\text{H}} m_B}{m_{\text{H}} n_{\text{H}}} \right)^{1/3} \, , + +where :math:`\eta` is a constant related to the number of neighbours in the kernel, :math:`X_{\text{H}}` is the hydrogen mass fraction, :math:`m_B` the gas particle's mass, :math:`m_{\text{H}}` the hydrogen particle mass and :math:`n_{\text{H}}` the hydrogen number density. + +Let us provide an example. In GEAR, we do not model physical processes below the parsec scale. Hence, let us take :math:`h \sim 1` pc. In zoom-in simulations we have :math:`m_B \simeq 95 \; M_{\odot}`. The remaining parameters are :math:`\eta = 1.2348` and :math:`X_{\text{H}} = 0.76`. So, after inverting the formula, we find :math:`n_H \simeq 5500 \text{ hydrogen atoms/cm}^3`. In practice, we use :math:`n_H = 1000 \text{ hydrogen atoms/cm}^3`, close to the estimation, and an accretion radius :math:`r_{\text{acc}} = 10` pc. These values are slightly different for safety reasons, but they are consistent. + +Remember that this was a way, among others, to determine good accretion radius and threshold density. It can help you with your first runs with sink particles. + +Comment on star formation efficiency +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Notice that this model does not have parameters to control the star formation rate of the sink. The SFR is self-regulated by the gas/sink accretion and other feedback mechanisms. Supernovae tend to create bubbles of lower density at the site of star formation, removing the gas and preventing further gas accretion. However, the sink might run into this stack size problem by the time the first supernovae explode. Other pre-stellar feedback mechanisms could do the job earlier, though they are not implemented in GEAR. .. note:: - We provide a piece of general advice: do some calibration on low-resolution simulations. This will help to see what works and what does not work. Keep in mind that you might want to put a higher ``stellar_particle_mass_X`` at the beginning to avoid spawning too many stars. For the high-resolution simulations, you then can lower the particle's mass. + We provide a piece of general advice: do some calibration on low-resolution simulations. This will help to see what works and what does not work. Keep in mind that you might want to put a higher ``stellar_particle_mass_X_Msun`` at the beginning to avoid spawning too many stars. For the high-resolution simulations, you then can lower the particle's mass. diff --git a/doc/RTD/source/SubgridModels/GEAR/sinks/sink_imf.png b/doc/RTD/source/SubgridModels/GEAR/sinks/sink_imf.png index e9c0fa70f13a89a73dd9fed299307d6682022cea..e59f0d770681a35666bfaa3224b9a4f3e5e25289 100644 Binary files a/doc/RTD/source/SubgridModels/GEAR/sinks/sink_imf.png and b/doc/RTD/source/SubgridModels/GEAR/sinks/sink_imf.png differ diff --git a/doc/RTD/source/SubgridModels/GEAR/sinks/sink_scheme.png b/doc/RTD/source/SubgridModels/GEAR/sinks/sink_scheme.png new file mode 100644 index 0000000000000000000000000000000000000000..8353c6a34a92dbbb161e9442724be73c89fa06d3 Binary files /dev/null and b/doc/RTD/source/SubgridModels/GEAR/sinks/sink_scheme.png differ diff --git a/doc/RTD/source/SubgridModels/GEAR/sinks/theory.rst b/doc/RTD/source/SubgridModels/GEAR/sinks/theory.rst index f3be48da6275b77b4f691034a41d4b7399c9c6a5..df9c5619a76d2d6b7a7ba24a2961aa534ee23f3c 100644 --- a/doc/RTD/source/SubgridModels/GEAR/sinks/theory.rst +++ b/doc/RTD/source/SubgridModels/GEAR/sinks/theory.rst @@ -1,21 +1,29 @@ .. Sink particles in GEAR model Darwin Roduit, 17 April 2024 -.. sink_GEAR_model: - -.. warning:: - This page is under construction. It may lack information. +.. _sink_GEAR_model_summary: Model summary ------------- +.. figure:: sink_scheme.png + :width: 400px + :align: center + :figclass: align-center + :alt: Illustration of the sink scheme. + + This figure illustrates the sink scheme. Eligible gas particles (blue) are converted to sink particles (orange). Then, the sink searches for eligible gas/sink particles (red edges) to swallow and finally accretes them. The final step is to spawn star particles (yellow) by sampling an IMF. These stars represent a continuous portion of the IMF or an individual star (the figure does not distinguish the two types of stars). + Here, we provide a comprehensive summary of the model. Sink particles are an alternative to the current model of star formation that transforms gas particles into sink particles under some criteria explained below. Then, the sink can accrete gas and spawn stars. Sink particles are collisionless particles, i.e. they interact with other particles only through gravity. They can be seen as particles representing unresolved regions of collapse. -To spawn stars, an IMF is sampled. Details explanation of the IMF sampling are explained below. In short, the IMF is split into two parts In the lower part, star particles represent a continuous stellar population in a similar way to what is currently implemented in common models. In the second upper part, star particles represent individual stars. Then, the feedback is improved to take into account both types of stars. Currently, only supernovae feedback is implemented. The sink particle method allows thus to track the effect of the supernovae of individual stars in the simulation. +We sample an IMF to draw the stars' mass and spawn them stochastically. Below, we provide a detailed explanation of the IMF sampling. In short, we split the IMF into two parts. In the lower part, star particles represent a continuous stellar population, similar to what is currently implemented in standard models. In the second upper part, star particles represent individual stars. Then, the feedback is improved to take into account both types of stars. Currently, only supernovae feedback is implemented. Thus, the sink particle method allows us to track the effects of individual stars' supernovae in the simulation. -The current model includes sink formation, gas accretion, sink merging, IMF sampling, star spawning and finally supernovae feedback (type Ia and II). +The current model includes sink formation, gas accretion, sink merging, IMF sampling, star spawning and finally supernovae feedback (type Ia and II). The figures below illustrates the scheme and the associated tasks. -Our main references are the following papers `Bate et al. <https://ui.adsabs.harvard.edu/abs/1995MNRAS.277..362B/abstract>`_, `Price et al. <https://ui.adsabs.harvard.edu/abs/2018PASA...35...31P/abstract>`_ and `Federrath et al. <https://ui.adsabs.harvard.edu/abs/2010ApJ...713..269F/abstract>`_ +Our main references are the following papers: `Bate et al. <https://ui.adsabs.harvard.edu/abs/1995MNRAS.277..362B/abstract>`_, `Price et al. <https://ui.adsabs.harvard.edu/abs/2018PASA...35...31P/abstract>`_ and `Federrath et al. <https://ui.adsabs.harvard.edu/abs/2010ApJ...713..269F/abstract>`_ + +.. note:: + Sink examples are available in ``examples/SinkParticles/``. They include self-gravity, cooling and feedback effects. .. figure:: ../../../Task/sink.png :width: 400px @@ -34,16 +42,16 @@ Our main references are the following papers `Bate et al. <https://ui.adsabs.har Conversion from comoving to physical space ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In the following, we always refer to physical quantities. In non-cosmological simulations, there is no ambiguity between comoving and physical quantities, since the universe is not expanding and thus the scale factor is :math:`a(t)=1`. However, in cosmological simulation, we need to take care to convert from comoving quantities to physical ones when needed, e.g. to compute energies. Here is a recap: +In the following, we always refer to physical quantities. In non-cosmological simulations, there is no ambiguity between comoving and physical quantities since the universe is not expanding, and thus, the scale factor is :math:`a(t)=1`. However, in cosmological simulation, we need to convert from comoving quantities to physical ones when needed, e.g. to compute energies. We denote physical quantities by the subscript `p` and comoving ones by `c`. Here is a recap: -* :math:`\mathbf{x}_p = \mathbf{x}_ca` +* :math:`\mathbf{x}_p = \mathbf{x}_c a` * :math:`\mathbf{v}_p = \mathbf{v}_c/a + a H \mathbf{x}_c` * :math:`\rho_p = \rho_c/a^3` * :math:`\Phi_p = \Phi_c/a + c(a)` +* :math:`u_p = u_c/a^{3(\gamma -1)}` -Here, the subscript `p` stands for physical and `c` for comoving. -Notice that the potential normalization constant has been chosen to be :math:`c(a) = 0`. +Here, :math:`H` is the Hubble constant at any redshift, :math:`c(a)` is the potential normalization constant and :math:`\gamma` the gas adiabatic index. Notice that the potential normalization constant has been chosen to be :math:`c(a) = 0`. Sink formation @@ -63,9 +71,9 @@ The primary criteria required to transform a gas particle into a sink are: 1. the density of a given particle :math:`i` is bigger than a user-defined threshold density: :math:`\rho_i > \rho_{\text{threshold}}` ; 2. the temperature of a given particle is smaller than a user-defined threshold temperature: :math:`T_i < T_{\text{threshold}}`. -The first criterion is common but not the second one. This is checked to ensure that sink particles, and thus stars, are not generated in hot regions. The parameters for those threshold quantities are respectively called ``density_threshold`` and ``maximal_temperature``. +The first criterion is common but not the second one. This is checked to ensure that sink particles, and thus stars, are not generated in hot regions. The parameters for those threshold quantities are respectively called ``density_threshold_g_per_cm3`` and ``maximal_temperature``. -Then, further criteria are checked. They are always checked for gas particles within the accretion radius :math:`r_{\text{acc}}` (also called the cut-off radius) of a given gas particle :math:`i`. Such gas particles are called *neighbours*. +Then, further criteria are checked. They are always checked for gas particles within the accretion radius :math:`r_{\text{acc}}` (called the ``cut_off_radius`` in the parameter file) of a given gas particle :math:`i`. Such gas particles are called *neighbours*. .. note:: Notice that in the current implementation, the accretion radius is kept *fixed and the same* for all sinks. However, for the sake of generality, the mathematical expressions are given as if the accretion radii could be different. @@ -76,7 +84,7 @@ So, the other criteria are the following: 4. Gas surrounding the particle is at rest or collapsing: :math:`\nabla \cdot \mathbf{v}_{i, p} \leq 0`. (Optional) 5. The smoothing kernel's edge of the particle is less than the accretion radius: :math:`\gamma_k h_i < r_{\text{acc}}`, where :math:`\gamma_k` is kernel dependent. (Optional) 6. All neighbours are currently active. -7. The sum of the thermal of the neighbours satisfies: :math:`E_{\text{therm}} < |E_{\text{pot}}|/2`. (Optional, together with criterion 8.) +7. The thermal energy of the neighbours satisfies: :math:`E_{\text{therm}} < |E_{\text{pot}}|/2`. (Optional, together with criterion 8.) 8. The sum of thermal energy and rotational energy satisfies: :math:`E_{\text{therm}} + E_{\text{rot}} < | E_{\text{pot}}|`. (Optional, together with criterion 7.) 9. The total energy of the neighbours is negative, i.e. the clump is bound to the sink: :math:`E_{\text{tot}} < 0`. (Optional) 10. Forming a sink here will not overlap an existing sink :math:`s`: :math:`\left| \mathbf{x}_i - \mathbf{x}_s \right| > r_{\text{acc}, i} + r_{\text{acc}, s}`. (Optional) @@ -106,7 +114,7 @@ Some comments about the criteria: The third criterion is mainly here to prevent two sink particles from forming at a distance smaller than the sink accretion radius. Since we allow sinks to merge, such a situation raises the question of which sink should swallow the other. This can depend on the order of the tasks, which is not a desirable property. As a result, this criterion is enforced. -The tenth criterion prevents the formation of spurious sinks. Experiences have shown that removing gas within the accretion radius biases the hydro density estimates: the gas feels a force toward the sink. At some point, there is an equilibrium and gas particles accumulate at the edge of the accretion radius, which can then spawn sink particles that do not fall onto the primary sink and never merge. *This criterion can be disabled*. +The tenth criterion prevents the formation of spurious sinks. Experiences have shown that removing gas within the accretion radius biases the hydro density estimates: the gas feels a force toward the sink. At some point, there is an equilibrium and gas particles accumulate at the edge of the accretion radius, which can then spawn sink particles that do not fall onto the primary sink and never merge. Moreover, the physical reason behind this criterion is that a sink represents a region of collapse. As a result, there is no need to have many sinks occupying the same space volume. They would compete for gas accretion without necessarily merging. This criterion is particularly meaningful in cosmological simulations to ensure proper sampling of the IMF. *This criterion can be disabled*. .. note:: However, notice that contrary to `Bate et al. <https://ui.adsabs.harvard.edu/abs/1995MNRAS.277..362B/abstract>`_, no boundary conditions for sink particles are introduced in the hydrodynamics calculations. @@ -131,19 +139,19 @@ The physical specific angular momenta and the total energy are given by: * :math:`\mathbf{L}_{si} = ( \mathbf{x}_{s, p} - \mathbf{x}_{i, p}) \times ( \mathbf{v}_{s, p} - \mathbf{x}_{i, p})`, * :math:`|\mathbf{L}_{\text{Kepler}}| = r_{\text{acc}, p} \cdot \sqrt{G_N m_s / |\mathbf{x}_{s, p} - \mathbf{x}_{i, p}|^3}`. -* :math:`E_{\text{tot}} = \frac{1}{2} (\mathbf{v}_{s, p} - \mathbf{x}_{i, p})^2 - G_N \Phi(|\mathbf{x}_{s, p} - \mathbf{x}_{i, p}|)`. +* :math:`E_{\text{tot}} = \frac{1}{2} (\mathbf{v}_{s, p} - \mathbf{x}_{i, p})^2 - G_N \Phi(|\mathbf{x}_{s, p} - \mathbf{x}_{i, p}|) + m_i u_{i, p}`. .. note:: Here the potential is the softened potential of Swift. -Those criteria are similar to `Price et al. <https://ui.adsabs.harvard.edu/abs/2018PASA...35...31P/abstract>`_. +Those criteria are similar to `Price et al. <https://ui.adsabs.harvard.edu/abs/2018PASA...35...31P/abstract>`_, with the addition of the internal energy. This term ensures that the gas is cold enough to be accreted. Its main purpose is to avoid gas accretion and star spawning in hot regions far from sink/star-forming regions, which can happen, e.g., if a sink leaves a galaxy. Once a gas is eligible for accretion, its properties are assigned to the sink. The sink accretes the *entire* gas particle mass and its properties are updated in the following way: * :math:`\displaystyle \mathbf{v}_{s, c} = \frac{m_s \mathbf{v}_{s, c} + m_i \mathbf{v}_{i, c}}{m_s + m_i}`, * Swallowed physical angular momentum: :math:`\mathbf{L}_{\text{acc}} = \mathbf{L}_{\text{acc}} + m_i( \mathbf{x}_{s, p} - \mathbf{x}_{i, p}) \times ( \mathbf{v}_{s, p} - \mathbf{x}_{i, p})`, -* The chemistry data are transferred from the gas to the sink. -* :math:`m_s = m_s + m_i`, +* :math:`X_{Z, s} = \dfrac{X_{Z,i} m_i + X_{Z,s} m_s}{m_s + m_i}`, the metal mass fraction for each element, +* :math:`m_s = m_s + m_i`. .. figure:: sink_overlapping.png :width: 400px @@ -156,11 +164,16 @@ Once a gas is eligible for accretion, its properties are assigned to the sink. T Sink merging ~~~~~~~~~~~~ -Sinks are allowed to merge if they enter the accretion radius. Two sink particles can be merged if: +Sinks are allowed to merge if they enter one's accretion radius. We merge two sink particles if they respect a set of criteria. The criteria are similar to the gas particles, namely: -* One of the sink particles must be bound to the other. +#. If one of the sinks falls within the other's inner accretion radius, :math:`f_{\text{acc}} r_{\text{acc}}` (:math:`0 \leq f_{\text{acc}} \leq 1`), the sinks are merged without further check. +#. In the region :math:`f_{\text{acc}} r_{\text{acc}} \leq |\mathbf{x}_i| \leq r_{\text{acc}}`, then, we check: + + #. One sink is gravitationally bound to the other: :math:`E_{\text{mec}, ss'} < 0` or :math:`E_{\text{mec}, s's} < 0`. -In this case, the sink with the smallest mass is merged with the sink with the largest. If the two sinks have the same mass, we check the sink ID number and accrete the smallest ID onto the biggest one. +We compute the total energies in the same manner as gas particles, with the difference that we do not use internal energy. Notice that we have two energies: each sink has a different potential energy since their mass can differ. + +When sinks merge, the sink with the smallest mass merges with the sink with the largest. If the two sinks have the same mass, we check the sink ID number and add the smallest ID to the biggest one. IMF sampling ~~~~~~~~~~~~ @@ -171,17 +184,17 @@ IMF sampling :figclass: align-center :alt: Initial mass function split into the continuous and discrete part. - This figure shows an IMF split into two parts: the continuous (orange) and the discrete (blue) part. + This figure shows an IMF split into two parts by :math:`m_t`: the continuous (orange) and the discrete (blue) part. Now remains one critical question: how are stars formed in this scheme? Simply, by sampling an IMF. In our scheme, population III stars and population II have two different IMFs. For the sake of simplicity, in the following presentation, we consider only the case of population II stars. However, this can be easily generalized to population III. -Consider an IMF such as the one above. We split it into two parts at ``minimal_discrete_masss``. The reason behind this is that we want to spawn star particles that represent *individual* (massive) stars, i.e. they are "discrete". However, for computational reasons, we cannot afford to spawn every star of the IMF as a single particle. Since the IMF is dominated by low-mass stars (< 8 :math:`M_\odot` and even smaller) that do not end up in supernovae, we would have lots of "passive" stars. +Consider an IMF such as the one above. We split it into two parts at ``minimal_discrete_mass_Msun`` (called :math:`m_t` on the illustration). The reason behind this is that we want to spawn star particles that represent *individual* (massive) stars, i.e. they are "discrete". However, for computational reasons, we cannot afford to spawn every star of the IMF as a single particle. Since the IMF is dominated by low-mass stars (< 8 :math:`M_\odot` and even smaller) that do not end up in supernovae, we would have lots of "passive" stars. .. note:: - Recall that currently (April 2024), GEAR only implements SNIa and SNII as stellar feedback. Stars that do not undergo supernovae phases are "passive" in the current implementation. + Recall that currently (July 2024), GEAR only implements SNIa and SNII as stellar feedback. Stars that do not undergo supernovae phases are "passive" in the current implementation. -As a result, we group all those low-mass stars in one stellar particle of mass ``stellar_particle_mass``. Such star particles are called "continuous", contrary to the "discrete" individual stars. With all that information, we can compute the number of stars in the continuous part of the IMF (called :math:`N_c`) and in the discrete part (called :math:`N_d`). Finally, we can compute the probabilities of each part, respectively called :math:`P_c` and :math:`P_d`. Notice that the mathematical derivation is given in the theory latex files. +As a result, we group all those low-mass stars in one stellar particle of mass ``stellar_particle_mass_Msun``. Such star particles are called "continuous", contrary to the "discrete" individual stars. With all that information, we can compute the number of stars in the continuous part of the IMF (called :math:`N_c`) and in the discrete part (called :math:`N_d`). Finally, we can compute the probabilities of each part, respectively called :math:`P_c` and :math:`P_d`. Notice that the mathematical derivation is given in the theory latex files. Thus, the algorithm to sample the IMF and five the sink their ``target_mass`` is the following : @@ -191,12 +204,19 @@ Thus, the algorithm to sample the IMF and five the sink their ``target_mass`` is We have assumed that we have a function ``sample_IMF_high()`` that correctly samples the IMF in the discrete part. -Now, what happens to the sink? After a first sink forms, we give it a target mass with the algorithm outlined above. The sink then swallows gas particles (see the task graph at the top of the page) and finally spawns stars. While the sink possesses enough mass, we can continue to choose a new target mass. When the sink does have enough mass, the algorithm stops for this timestep. The next timestep, the sink may accrete gas and spawn stars again. If the sink never reaches the target mass, then it cannot spawn stars. In practice, however, sink particles could accumulate enough pass to spawn individual (Pop III) stars with masses 240 :math:`M_\odot` and more! +Now, what happens to the sink? After a first sink forms, we give it a target mass with the algorithm outlined above. The sink then swallows gas particles (see the task graph at the top of the page) and finally spawns stars. While the sink possesses enough mass, we can continue to choose a new target mass. When the sink does have enough mass, the algorithm stops for this timestep. The next timestep, the sink may accrete gas and spawn stars again. If the sink never reaches the target mass, then it cannot spawn stars. In practice, however, sink particles could accumulate enough pass to spawn individual (Pop III) stars with masses 240 :math:`M_\odot` and more! + +As explained at the beginning of this section, GEAR uses two IMFs for the population of II and III stars. The latter are called the first stars in the code. How does a sink decide which IMF to draw the target mass from? We define a threshold metallicity, ``GEARFeedback:imf_transition_metallicity`` that determines the first stars' maximal metallicity. When the sink particle's metallicity exceeds this threshold, it uses the population II IMF, defined in ``GEARFeedback:yields_table``. Star spawning ~~~~~~~~~~~~~ -Once the sink spawns a star particle, we need to give properties to the star. From the sink, the star inherits the chemistry properties. Concerning position, the star is currently put at the same location as the sink and the sink is moved by a small distance (randomly chosen) to avoid the two particles from overlapping. The star’s velocity is the same as the sink’s one. This model will be improved in a future update. +Once the sink spawns a star particle, we need to give properties to the star. From the sink, the star inherits the chemistry properties. The star is placed randomly within the sink's accretion radius. We draw the star's velocity components from a Gaussian distribution with mean :math:`\mu = 0` and standard deviation :math:`\sigma` determined as follows: + +.. math:: + \sigma = f \cdot \sqrt{\frac{G_N M_s}{r_{\text{acc}}}} \; , + +where :math:`G_N` is Newton's gravitational constant, math:`M_s` is the sink's mass before starting to spawn stars, and :math:`f` is a user-defined scaling factor. The latter corresponds to the ``star_spawning_sigma_factor`` parameter. Stellar feedback @@ -206,6 +226,8 @@ Stellar feedback *per se* is not in the sink module but in the feedback one. How All details and explanations about GEAR stellar feedback are provided in the GEAR :ref:`gear_feedback` section. Here, we only provide the changes from the previous model. -In the previous model, star particles represented a population of stars with a defined IMF. Now, we have two kinds of star particles: particles representing a *continuous* portion of the IMF (see the image above) and particles representing a *single* (discrete) star. This requires updating the feedback model such that stars eligible for SN feedback can realise this feedback. In practice, this means that now we have individual SNII feedback for individual stars with a mass larger than 8 :math:`M_\odot`. +In the previous model, star particles represented a population of stars with a defined IMF. Now, we have two kinds of star particles: particles representing a *continuous* portion of the IMF (see the image above) and particles representing a *single* (discrete) star. This new model requires updating the feedback model so that stars eligible for SN feedback can realise this feedback. + +**Discrete star particles:** Since we now have individual star particles, we can easily track SNII feedback for stars with a mass larger than 8 :math:`M_\odot`. When a star's age reaches its lifetime, it undergoes SNII feedback. -SNIa feedback is not yet implemented for the continuous star particle, but it will be in a future update. +**Continuous star particles**: In this case, we implemented SNII and SNIa as in the previous model. At each timestep, we determine the number of SN explosions occurring. In practice, this means that we can set the ``minimal_discrete_masss`` to any value, and the code takes care of the rest. diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR/README b/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR/README index b85555ada3cd7732b083bbe64312eb7f7bd27153..3a9be3bc2242fe6a8ec497fdf87615249dff90a0 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR/README +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR/README @@ -16,3 +16,10 @@ To start the simulation with the GEAR model: ./run.sh +# Sink particles + +To configure this example with GEAR model + GEAR sink particles, add --with-sink=GEAR. + +To start the simulation with the sink particles, type: + +./run_sink.sh diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR/params.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR/params.yml index 4d74c7f1026cce870d2f21471fb0d428a897109c..51d7da0147d06a500f6b00bc01534046019ba36a 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR/params.yml +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR/params.yml @@ -8,7 +8,10 @@ InternalUnitSystem: Scheduler: max_top_level_cells: 16 - cell_extra_gparts: 100 # (Optional) Number of spare gparts per top-level allocated at rebuild time for on-the-fly creation. + cell_extra_gparts: 1000 # (Optional) Number of spare gparts per top-level allocated at rebuild time for on-the-fly creation. + cell_extra_sinks: 1000 # (Optional) Number of spare sinks per top-level allocated at rebuild time for on-the-fly creation. + cell_extra_sparts: 1000 # (Optional) Number of spare sparts per top-level allocated at rebuild time for on-the-fly creation. + # Parameters governing the time integration TimeIntegration: @@ -74,6 +77,24 @@ GEARStarFormation: min_mass_frac: 0.5 density_threshold: 1.67e-25 # Density threashold in g/cm3 +GEARSink: + cut_off_radius: 5e-3 # Cut off radius of all the sinks in internal units. + f_acc: 0.1 + maximal_temperature: 1e4 # Upper limit to the temperature of a star forming particle + density_threshold_g_per_cm3: 1.67e-24 # Density threashold in g/cm3 (1.67e-24 =1acc) + stellar_particle_mass_Msun: 1e5 # Mass of the stellar particle representing the low mass stars, in solar mass + minimal_discrete_mass_Msun: 30 # Minimal mass of stars represented by discrete particles, in solar mass + stellar_particle_mass_first_stars_Msun: 1e5 # Mass of the stellar particle representing the low mass stars, in solar mass + minimal_discrete_mass_first_stars_Msun: 30 # Minimal mass of stars represented by discrete particles, in solar mass + star_spawning_sigma_factor: 0.5 # Factor to rescale the velocity dispersion of the stars when they are spawned. (Default: 0.2) + sink_formation_contracting_gas_criterion: 1 # (Optional) Activate the contracting gas check for sink formation. (Default: 1) + sink_formation_smoothing_length_criterion: 0 # (Optional) Activate the smoothing length check for sink formation. (Default: 1) + sink_formation_jeans_instability_criterion: 1 # (Optional) Activate the two Jeans instability checks for sink formation. (Default: 1) + sink_formation_bound_state_criterion: 1 # (Optional) Activate the bound state check for sink formation. (Default: 1) + sink_formation_overlapping_sink_criterion: 1 # (Optional) Activate the overlapping sink check for sink formation. (Default: 1) + disable_sink_formation: 0 # (Optional) Disable sink formation. (Default: 0) + + GEARPressureFloor: jeans_factor: 10 diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR/run_sink.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR/run_sink.sh new file mode 100755 index 0000000000000000000000000000000000000000..f3b7b9fa73582e73ad499029eddb53cb46453617 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR/run_sink.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# make run.sh fail if a subcommand fails +set -e + +if [ ! -e galaxy_multi_component.hdf5 ] +then + echo "Fetching initial conditions to run the example..." + wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IsolatedGalaxies/galaxy_multi_component.hdf5 +fi + + +# Get the Grackle cooling table +if [ ! -e CloudyData_UVB=HM2012.h5 ] +then + echo "Fetching the Cloudy tables required by Grackle..." + ./getGrackleCoolingTable.sh +fi + + +if [ ! -e POPIIsw.h5 ] +then + echo "Fetching the chemistry tables..." + ./getChemistryTable.sh +fi + +printf "Running simulation with sink particles..." + +../../../../swift --hydro --stars --sinks --self-gravity --feedback --cooling --threads=14 params.yml 2>&1 | tee output.log diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/makeDisk.py b/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/makeDisk.py index e18ae362356d0e13ce38ef1f38e914bfc3edd8c5..cec23105e59edc4e1a6c551a80b722b4eb580d05 100755 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/makeDisk.py +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_multi_component/makeDisk.py @@ -586,3 +586,16 @@ if hydro: # save model nb.rename("galaxy_multi_component.hdf5") nb.write() + +#%% +#Add the StellarParticleType attribute to the dataset +import h5py as h5 +import numpy as np + +nb_star = nb.select("stars") +N_star = np.sum(nb_star.npart) +star_tpe = 2 # Single population stars +star_type = np.ones(N_star)*star_tpe + +with h5.File("galaxy_multi_component.hdf5", "r+") as f: + f["PartType4"].create_dataset("StellarParticleType", data=star_type) diff --git a/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/plotSolution.py b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/plotSolution.py index fb6a962976e3e94f1fa37fb9dbe88d889f6eabd8..6485c420bca090c037491714c426ff07458e5dfd 100755 --- a/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/plotSolution.py +++ b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/plotSolution.py @@ -69,7 +69,7 @@ except IndexError: def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -98,9 +98,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. + energy_boundaries: list of [E_min, E_max] for each photon group. If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. + flux_boundaries: list of [F_min, F_max] for each photon group. If none, limits are set automatically. """ @@ -112,7 +112,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) boxsize = meta.boxsize[0] - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) for g in range(ngroups): # workaround to access named columns data with swiftsimio visualisaiton diff --git a/examples/RadiativeTransferTests/Advection_1D/README b/examples/RadiativeTransferTests/Advection_1D/README index 13dd7ce4caeeb00bd17b4998dffdbd92eacaee9c..9c14089e4b81c8acd2c3b8aa68ba56ad90b93da8 100644 --- a/examples/RadiativeTransferTests/Advection_1D/README +++ b/examples/RadiativeTransferTests/Advection_1D/README @@ -14,10 +14,14 @@ There are no stars to act as sources. Also make sure that you choose your photon frequencies in a way that doesn't interact with gas! The ICs are created to be compatible with GEAR_RT and SPHM1RT. Recommended configuration: -GEAR_RT: +GEAR_RT (with gizmo-mfv solver): --with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro-dimension=1 --with-hydro=gizmo-mfv \ --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT +GEAR_RT (with sphenix SPH solver): + --with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro-dimension=1 --with-hydro=sphenix + --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT + SPHM1RT: --with-rt=SPHM1RT_4 --with-hydro-dimension=1 --with-stars=basic --with-sundials=$SUNDIALS_ROOT diff --git a/examples/RadiativeTransferTests/Advection_1D/plotSolution.py b/examples/RadiativeTransferTests/Advection_1D/plotSolution.py index 47e94a669a6502a16daa0b0345e2944c1f1093b6..99eae9b20fe0278fd8636804242f43fa8d7184ce 100755 --- a/examples/RadiativeTransferTests/Advection_1D/plotSolution.py +++ b/examples/RadiativeTransferTests/Advection_1D/plotSolution.py @@ -70,7 +70,7 @@ except IndexError: def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -99,9 +99,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. + energy_boundaries: list of [E_min, E_max] for each photon group. If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. + flux_boundaries: list of [F_min, F_max] for each photon group. If none, limits are set automatically. """ @@ -114,7 +114,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): boxsize = meta.boxsize[0] - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) # Currently, SPHM1RT only works for frequency group = 4 in the code # However, we only plot 3 frequency groups here, so # we set ngroups = 3: @@ -319,7 +319,7 @@ def get_minmax_vals(snaplist): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) emin_group = [] emax_group = [] fluxmin_group = [] diff --git a/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml b/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml index a528dbeb1624e60679f23aecbde2de7a8376c296..e7bece757404d0bbd082e2de3622443a3e26828c 100644 --- a/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml +++ b/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml @@ -28,6 +28,9 @@ Statistics: time_first: 0. delta_time: 4.e-2 # Time between statistics output +Scheduler: + tasks_per_cell: 100 + # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). diff --git a/examples/RadiativeTransferTests/Advection_1D/run.sh b/examples/RadiativeTransferTests/Advection_1D/run.sh index 0b64a9b773f87b40c1237b91960bd09311da6c38..b277965b1be3b1e5392c31c067fae135c7191ffa 100755 --- a/examples/RadiativeTransferTests/Advection_1D/run.sh +++ b/examples/RadiativeTransferTests/Advection_1D/run.sh @@ -10,6 +10,7 @@ if [ ! -f advection_1D.hdf5 ]; then fi # Run SWIFT with RT +# mpirun -n 4 ../../../swift_mpi \ ../../../swift \ --hydro \ --threads=4 \ @@ -18,7 +19,6 @@ fi --stars \ --feedback \ --external-gravity \ - --fpe \ ./rt_advection1D.yml 2>&1 | tee output.log python3 ./plotSolution.py diff --git a/examples/RadiativeTransferTests/Advection_2D/README b/examples/RadiativeTransferTests/Advection_2D/README index 5914f149225aa447a5bf3d651cf88a408de30722..c7eba421c52b218fab397b5489ab6046b2625c80 100644 --- a/examples/RadiativeTransferTests/Advection_2D/README +++ b/examples/RadiativeTransferTests/Advection_2D/README @@ -16,9 +16,15 @@ There are no stars to act as sources. Also make sure that you choose your photon frequencies in a way that doesn't interact with gas! The ICs are created to be compatible with GEAR_RT. Recommended configuration: + +GEAR_RT (with gizmo-mfv solver): --with-rt=GEAR_4 --with-rt-riemann-solver=GLF --with-hydro-dimension=2 --with-hydro=gizmo-mfv \ --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT +GEAR_RT (with sphenix SPH solver): + --with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro-dimension=2 --with-hydro=sphenix + --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT + SPHM1RT: --with-rt=SPHM1RT_4 --with-hydro-dimension=2 --with-stars=basic --with-sundials=$SUNDIALS_ROOT diff --git a/examples/RadiativeTransferTests/Advection_2D/plotSolution.py b/examples/RadiativeTransferTests/Advection_2D/plotSolution.py index d0ebe4bf62817d989a72471246965cd461245d2e..89da6740c770b10efaaf2d60d03b4cd81bd85ade 100755 --- a/examples/RadiativeTransferTests/Advection_2D/plotSolution.py +++ b/examples/RadiativeTransferTests/Advection_2D/plotSolution.py @@ -60,7 +60,7 @@ mpl.rcParams["text.usetex"] = True def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -96,9 +96,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. + energy_boundaries: list of [E_min, E_max] for each photon group. If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. + flux_boundaries: list of [F_min, F_max] for each photon group. If none, limits are set automatically. """ @@ -108,7 +108,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) xlabel_units_str = meta.boxsize.units.latex_representation() global imshow_kwargs @@ -257,7 +257,7 @@ def get_minmax_vals(snaplist): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) emin_group = [] emax_group = [] fluxmin_group = [] diff --git a/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py b/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py index 3a4c37b3027720eb270283ef834f5296d9777c84..23c812f153d5854b20302bb93f8d81bf614dd5d6 100755 --- a/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py +++ b/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py @@ -63,7 +63,7 @@ mpl.rcParams["text.usetex"] = True def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -100,7 +100,7 @@ def plot_photons(filename): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) xlabel_units_str = meta.boxsize.units.latex_representation() x_coordinates = data.gas.coordinates[:, 0] diff --git a/examples/RadiativeTransferTests/Advection_2D/run.sh b/examples/RadiativeTransferTests/Advection_2D/run.sh index 56851dab7bec3104c985b77dde565557dc6ec995..99cbac0f89265ec0306d11d3dbfb568a84a3a2df 100755 --- a/examples/RadiativeTransferTests/Advection_2D/run.sh +++ b/examples/RadiativeTransferTests/Advection_2D/run.sh @@ -17,6 +17,7 @@ then fi # Run SWIFT with RT +# mpirun -n 4 ../../../swift_mpi \ ../../../swift \ --hydro \ --threads=4 \ @@ -25,7 +26,6 @@ fi --stars \ --feedback \ --external-gravity \ - --fpe \ ./rt_advection2D.yml 2>&1 | tee output.log python3 ./plotSolution.py diff --git a/examples/RadiativeTransferTests/CollidingBeams_1D/plotEnergies.py b/examples/RadiativeTransferTests/CollidingBeams_1D/plotEnergies.py index 4d5285467455e775e225a02a05378060070599c0..fbcba68658be79085c4678f1b4ab4f6da3d93d86 100755 --- a/examples/RadiativeTransferTests/CollidingBeams_1D/plotEnergies.py +++ b/examples/RadiativeTransferTests/CollidingBeams_1D/plotEnergies.py @@ -51,7 +51,7 @@ except IndexError: def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ snaplist = [] @@ -119,13 +119,13 @@ def get_photon_energies(snaplist): returns: snap_nrs : list of integers of snapshot numbers - energy_sums: np.array(shape=(len snaplist, ngroups)) of + energy_sums: np.array(shape=(len snaplist, ngroups)) of total photon energies per group per snapshot """ data = swiftsimio.load(snaplist[0]) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) energy_sums = np.zeros((len(snaplist), ngroups)) snap_nrs = np.zeros(len(snaplist), dtype=int) diff --git a/examples/RadiativeTransferTests/CollidingBeams_1D/plotSolution.py b/examples/RadiativeTransferTests/CollidingBeams_1D/plotSolution.py index 5e372d67976336086dac3579a524790242d238e8..d81a3ed3d7cc4e81823f029c75f4bc5f4ec8c5ad 100755 --- a/examples/RadiativeTransferTests/CollidingBeams_1D/plotSolution.py +++ b/examples/RadiativeTransferTests/CollidingBeams_1D/plotSolution.py @@ -61,7 +61,7 @@ except IndexError: def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -90,9 +90,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. + energy_boundaries: list of [E_min, E_max] for each photon group. If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. + flux_boundaries: list of [F_min, F_max] for each photon group. If none, limits are set automatically. """ @@ -103,7 +103,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): meta = data.metadata scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) for g in range(ngroups): # workaround to access named columns data with swiftsimio visualisaiton @@ -219,7 +219,7 @@ def get_minmax_vals(snaplist): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) emin_group = [] emax_group = [] fluxmin_group = [] diff --git a/examples/RadiativeTransferTests/CollidingBeams_2D/plotSolution.py b/examples/RadiativeTransferTests/CollidingBeams_2D/plotSolution.py index 8f100e5fae99c15fb18d63b3f572db6f045d802f..e834317237080fb4e1dfbfd4a6df74183a8aa50d 100755 --- a/examples/RadiativeTransferTests/CollidingBeams_2D/plotSolution.py +++ b/examples/RadiativeTransferTests/CollidingBeams_2D/plotSolution.py @@ -60,7 +60,7 @@ mpl.rcParams["text.usetex"] = True def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -96,9 +96,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. + energy_boundaries: list of [E_min, E_max] for each photon group. If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. + flux_boundaries: list of [F_min, F_max] for each photon group. If none, limits are set automatically. """ @@ -108,7 +108,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) xlabel_units_str = meta.boxsize.units.latex_representation() global imshow_kwargs @@ -257,7 +257,7 @@ def get_minmax_vals(snaplist): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) emin_group = [] emax_group = [] fluxmin_group = [] diff --git a/examples/RadiativeTransferTests/CoolingTest/plotSolution.py b/examples/RadiativeTransferTests/CoolingTest/plotSolution.py index 5ea851990a6d0284839b723ccb4467da1e52a8db..dd1a511dd4a04e52139878ebe6b37e1ec28203e0 100755 --- a/examples/RadiativeTransferTests/CoolingTest/plotSolution.py +++ b/examples/RadiativeTransferTests/CoolingTest/plotSolution.py @@ -184,7 +184,7 @@ def get_snapshot_data(snaplist): firstdata = swiftsimio.load(snaplist[0]) with_rt = True try: - ngroups = int(firstdata.metadata.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(firstdata.metadata.subgrid_scheme["PhotonGroupNumber"][0]) except KeyError: # allow to read in solutions with only cooling, without RT with_rt = False diff --git a/examples/RadiativeTransferTests/CosmoAdvection_1D/plotSolution.py b/examples/RadiativeTransferTests/CosmoAdvection_1D/plotSolution.py index cfd051d812245ec6318ae5dad324bd65f12a779c..797f34d9c2401397205f237fa092ffb54116c544 100755 --- a/examples/RadiativeTransferTests/CosmoAdvection_1D/plotSolution.py +++ b/examples/RadiativeTransferTests/CosmoAdvection_1D/plotSolution.py @@ -85,7 +85,7 @@ def parse_args(): def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -117,9 +117,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. + energy_boundaries: list of [E_min, E_max] for each photon group. If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. + flux_boundaries: list of [F_min, F_max] for each photon group. If none, limits are set automatically. """ global time_first @@ -358,7 +358,7 @@ def get_minmax_vals(snaplist): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) emin_group = [] emax_group = [] fluxmin_group = [] diff --git a/examples/RadiativeTransferTests/CosmoAdvection_1D/run.sh b/examples/RadiativeTransferTests/CosmoAdvection_1D/run.sh index 964d3e80e2c665b45228f98fe3552bbd4a380e04..c1a6a43cc6cd754e86b66d1dcd3f15dfe0766aee 100755 --- a/examples/RadiativeTransferTests/CosmoAdvection_1D/run.sh +++ b/examples/RadiativeTransferTests/CosmoAdvection_1D/run.sh @@ -15,7 +15,7 @@ zdomain="h" # Do we have a cmdline argument provided? if [ $# -gt 0 ]; then - case "$1" in + case "$1" in -l | -low | --l | --low | l | ./rt_advection1D_low_redshift.yml | rt_advection1D_low_redshift | rt_advection1D_low_redshift.yml ) ymlfile=rt_advection1D_low_redshift.yml zdomain="l" diff --git a/examples/RadiativeTransferTests/CosmoAdvection_2D/plotSolution.py b/examples/RadiativeTransferTests/CosmoAdvection_2D/plotSolution.py index 6b7eb6a3918937cdc29badf72973f7fa51b2e18e..a30195718674ba79cbc857a93c742802272df154 100755 --- a/examples/RadiativeTransferTests/CosmoAdvection_2D/plotSolution.py +++ b/examples/RadiativeTransferTests/CosmoAdvection_2D/plotSolution.py @@ -75,7 +75,7 @@ def parse_args(): def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -114,9 +114,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. + energy_boundaries: list of [E_min, E_max] for each photon group. If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. + flux_boundaries: list of [F_min, F_max] for each photon group. If none, limits are set automatically. """ diff --git a/examples/RadiativeTransferTests/CosmoCoolingTest/plotSolution.py b/examples/RadiativeTransferTests/CosmoCoolingTest/plotSolution.py index fb4e103ef12d1c68ebff3c6a23874ba086d565a1..0b74d8605f2a94401847adc1b2ea1ed5c56145ca 100755 --- a/examples/RadiativeTransferTests/CosmoCoolingTest/plotSolution.py +++ b/examples/RadiativeTransferTests/CosmoCoolingTest/plotSolution.py @@ -189,7 +189,7 @@ def get_snapshot_data(snaplist): firstdata = swiftsimio.load(snaplist[0]) with_rt = True try: - ngroups = int(firstdata.metadata.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(firstdata.metadata.subgrid_scheme["PhotonGroupNumber"][0]) except KeyError: # allow to read in solutions with only cooling, without RT with_rt = False diff --git a/examples/RadiativeTransferTests/HeatingTest/plotSolution.py b/examples/RadiativeTransferTests/HeatingTest/plotSolution.py index 2c6cee439cde01eed6161f026582e74d95f99373..95e3338e37b16ee0fe6f75965fe0928cf2533ee2 100755 --- a/examples/RadiativeTransferTests/HeatingTest/plotSolution.py +++ b/examples/RadiativeTransferTests/HeatingTest/plotSolution.py @@ -49,7 +49,7 @@ mass_units = unyt.Msun def mean_molecular_weight(XH0, XHp, XHe0, XHep, XHepp): """ - Determines the mean molecular weight for given + Determines the mean molecular weight for given mass fractions of hydrogen: XH0 H+: XHp @@ -76,7 +76,7 @@ def mean_molecular_weight(XH0, XHp, XHe0, XHep, XHepp): def gas_temperature(u, mu, gamma): """ - Compute the gas temperature given the specific internal + Compute the gas temperature given the specific internal energy u and the mean molecular weight mu """ @@ -90,7 +90,7 @@ def gas_temperature(u, mu, gamma): def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -155,14 +155,14 @@ def get_snapshot_data(snaplist): Returns: numpy arrays of: time - temperatures + temperatures mean molecular weights mass fractions """ nsnaps = len(snaplist) firstdata = swiftsimio.load(snaplist[0]) - ngroups = int(firstdata.metadata.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(firstdata.metadata.subgrid_scheme["PhotonGroupNumber"][0]) times = np.zeros(nsnaps) * unyt.Myr temperatures = np.zeros(nsnaps) * unyt.K diff --git a/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py b/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py index 6837842e09302839460955eaa855d8c16ecc50fb..aa190f93d516a32081bf55d88cec971a700d2f10 100755 --- a/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py +++ b/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py @@ -76,7 +76,7 @@ mpl.rcParams["text.usetex"] = True def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -116,9 +116,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. + energy_boundaries: list of [E_min, E_max] for each photon group. If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. + flux_boundaries: list of [F_min, F_max] for each photon group. If none, limits are set automatically. """ @@ -128,7 +128,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) xlabel_units_str = meta.boxsize.units.latex_representation() global imshow_kwargs @@ -324,7 +324,7 @@ def get_minmax_vals(snaplist): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) emin_group = [] emax_group = [] fluxmin_group = [] diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/README b/examples/RadiativeTransferTests/StromgrenSphere_2D/README index d291adc607e54c1ab20e561f725fe54a535e0416..603fb463f555828f6ad11406c849e806f7d80fbc 100644 --- a/examples/RadiativeTransferTests/StromgrenSphere_2D/README +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/README @@ -21,9 +21,13 @@ Additional scripts: 'snapshot_base' variable at the top of the script depending on which solutions you want to plot. -To use the GEAR RT model, compile with : +To use the GEAR RT (with gizmo-mfv solver) model, compile with : --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-hydro-dimension=2 --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT +To use the GEAR RT (with sphenix SPH solver) model, compile with : + --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-hydro-dimension=2 --with-hydro=sphenix --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT + + To use the SPHM1 RT model, compile with : --with-rt=SPHM1RT_4 --with-hydro-dimension=2 --with-stars=basic --with-feedback=none --with-sundials=$SUNDIALS_ROOT diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py index 06a6e95e32900d3fcde3748fefe74d8825093075..5feac7486df09a301036db8a40685b74506861ae 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py @@ -108,7 +108,7 @@ def get_units(scheme, unit_system="cgs_units"): def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -152,9 +152,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. + energy_boundaries: list of [E_min, E_max] for each photon group. If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. + flux_boundaries: list of [F_min, F_max] for each photon group. If none, limits are set automatically. """ @@ -173,7 +173,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): scheme, unit_system="cgs_units" ) - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) xlabel_units_str = meta.boxsize.units.latex_representation() global imshow_kwargs @@ -352,7 +352,7 @@ def get_minmax_vals(snaplist): scheme, unit_system="cgs_units" ) - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) emin_group = [] emax_group = [] fluxmin_group = [] diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py index 09e78f504acf75ef86e923e7d04c7eb11d6b1268..3ce7646691687f18c6ec55ecc38d0b123242ae15 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py @@ -108,7 +108,7 @@ def get_units(scheme, unit_system="cgs_units"): def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -152,9 +152,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. + energy_boundaries: list of [E_min, E_max] for each photon group. If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. + flux_boundaries: list of [F_min, F_max] for each photon group. If none, limits are set automatically. """ @@ -174,7 +174,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): scheme, unit_system="cgs_units" ) - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) xlabel_units_str = meta.boxsize.units.latex_representation() global imshow_kwargs @@ -378,7 +378,7 @@ def get_minmax_vals(snaplist): scheme, unit_system="cgs_units" ) - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) emin_group = [] emax_group = [] fluxmin_group = [] diff --git a/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py b/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py index 746c1e10ff297e7986382f1e1c5bcbf97a053326..34ad10a7f8be4cc67b9f9741bc849d43cc08c578 100755 --- a/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py +++ b/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py @@ -74,7 +74,7 @@ mpl.rcParams["text.usetex"] = True def get_snapshot_list(snapshot_basename="output"): """ - Find the snapshot(s) that are to be plotted + Find the snapshot(s) that are to be plotted and return their names as list """ @@ -114,9 +114,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. + energy_boundaries: list of [E_min, E_max] for each photon group. If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. + flux_boundaries: list of [F_min, F_max] for each photon group. If none, limits are set automatically. """ @@ -126,7 +126,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) xlabel_units_str = meta.boxsize.units.latex_representation() global imshow_kwargs @@ -320,7 +320,7 @@ def get_minmax_vals(snaplist): data = swiftsimio.load(filename) meta = data.metadata - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"][0]) emin_group = [] emax_group = [] fluxmin_group = [] diff --git a/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py b/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py index 6fe1efdd00fec032d93a892d81d98619a26cfa58..cf4b8474ec23ded81414bf211b14d18cf7350a85 100644 --- a/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py +++ b/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py @@ -171,7 +171,7 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False): "Compile swift --with-rt=GEAR_N", ) - ngroups = int(firstfile.metadata.subgrid_scheme["PhotonGroupNumber"]) + ngroups = int(firstfile.metadata.subgrid_scheme["PhotonGroupNumber"][0]) rundata.ngroups = ngroups luminosity_model = firstfile.metadata.parameters["GEARRT:stellar_luminosity_model"] diff --git a/examples/SinkParticles/HomogeneousBox/params.yml b/examples/SinkParticles/HomogeneousBox/params.yml index 2366ff82120406dd96ca727c2f54e7e082408076..d47ec29112f66c20b2b771036a489842b57e5eb3 100755 --- a/examples/SinkParticles/HomogeneousBox/params.yml +++ b/examples/SinkParticles/HomogeneousBox/params.yml @@ -84,15 +84,14 @@ GEARFeedback: GEARSink: - cut_off_radius: 5e-3 # Cut off radius of all the sinks in internal units. + cut_off_radius: 5e-3 # Cut off radius of all the sinks in internal units. f_acc: 0.1 - maximal_temperature: 100 # Upper limit to the temperature of a star forming particle - density_threshold: 1.67e-23 # Density threashold in g/cm3 (1.67e-24 =1acc) - size_of_calibration_sample: 100000 # Size of the calibration sample - stellar_particle_mass: 50 # Mass of the stellar particle representing the low mass stars, in solar mass - minimal_discrete_mass: 8 # Minimal mass of stars represented by discrete particles, in solar mass - stellar_particle_mass_first_stars: 50 # Mass of the stellar particle representing the low mass stars, in solar mass - minimal_discrete_mass_first_stars: 8 # Minimal mass of stars represented by discrete particles, in solar mass + maximal_temperature: 100 # Upper limit to the temperature of a star forming particle + density_threshold_g_per_cm3: 1.67e-23 # Density threashold in g/cm3 (1.67e-24 =1acc) + stellar_particle_mass_Msun: 50 # Mass of the stellar particle representing the low mass stars, in solar mass + minimal_discrete_mass_Msun: 8 # Minimal mass of stars represented by discrete particles, in solar mass + stellar_particle_mass_first_stars_Msun: 50 # Mass of the stellar particle representing the low mass stars, in solar mass + minimal_discrete_mass_first_stars_Msun: 8 # Minimal mass of stars represented by discrete particles, in solar mass star_spawning_sigma_factor: 0.5 # Factor to rescale the velocity dispersion of the stars when they are spawned. (Default: 0.2) sink_formation_contracting_gas_criterion: 1 # (Optional) Activate the contracting gas check for sink formation. (Default: 1) sink_formation_smoothing_length_criterion: 1 # (Optional) Activate the smoothing length check for sink formation. (Default: 1) diff --git a/examples/SinkParticles/IsolatedGalaxy_multi_component_GEAR b/examples/SinkParticles/IsolatedGalaxy_multi_component_GEAR new file mode 120000 index 0000000000000000000000000000000000000000..8abfe91f453616995753e2edf39a265452bfaf84 --- /dev/null +++ b/examples/SinkParticles/IsolatedGalaxy_multi_component_GEAR @@ -0,0 +1 @@ +../IsolatedGalaxy/IsolatedGalaxy_multi_component/GEAR \ No newline at end of file diff --git a/examples/SinkParticles/PlummerSphere/params.yml b/examples/SinkParticles/PlummerSphere/params.yml index 76ba1081e312a134bfe5b975eaac71885dc1de1b..729c0571a955f9b657cededb673cfc93c4812058 100755 --- a/examples/SinkParticles/PlummerSphere/params.yml +++ b/examples/SinkParticles/PlummerSphere/params.yml @@ -1,7 +1,7 @@ # Define the system of units to use internally. InternalUnitSystem: - UnitMass_in_cgs: 1.988409870698051e+43 # 10^10 solar masses - UnitLength_in_cgs: 3.0856775814913673e+21 # 1 kpc + UnitMass_in_cgs: 1.988409870698051e+43 # 10^10 solar masses + UnitLength_in_cgs: 3.0856775814913673e+21 # 1 kpc UnitVelocity_in_cgs: 1e5 # km/s UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin @@ -18,23 +18,23 @@ Gravity: # Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) TimeIntegration: - time_begin: 0. # The starting time of the simulation (in internal units). - time_end: 1.2e-02 # The end time of the simulation (in internal units). + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 12e-3 # The end time of the simulation (in internal units). dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). # Parameters governing the snapshots Snapshots: subdir: snap basename: snapshot # Common part of the name of output files - time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) - delta_time: 1e-3 # Time difference between consecutive outputs (in internal units) + time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) + delta_time: 1e-3 # Time difference between consecutive outputs (in internal units) Scheduler: cell_extra_gparts: 10000 # (Optional) Number of spare sparts per top-level allocated at rebuild time for on-the-fly creation. cell_extra_sinks: 10000 # (Optional) Number of spare sparts per top-level allocated at rebuild time for on-the-fly creation. cell_extra_sparts: 10000 # (Optional) Number of spare sparts per top-level allocated at rebuild time for on-the-fly creation. - max_top_level_cells: 3 # + max_top_level_cells: 3 # Parameters governing the conserved quantities statistics Statistics: @@ -85,18 +85,18 @@ GEARFeedback: GEARSink: - cut_off_radius: 1e-2 # Cut off radius of all the sinks in internal units. + cut_off_radius: 1e-2 # Cut off radius of all the sinks in internal units. f_acc: 0.4 - maximal_temperature: 3e4 # Upper limit to the temperature of a star forming particle - density_threshold: 1.67e-24 # Density threashold in g/cm3 (1.67e-24 =1acc) - size_of_calibration_sample: 100000 # Size of the calibration sample - stellar_particle_mass: 8 # Mass of the stellar particle representing the low mass stars, in solar mass - minimal_discrete_mass: 8 # Minimal mass of stars represented by discrete particles, in solar mass - stellar_particle_mass_first_stars: 13 # Mass of the stellar particle representing the low mass stars, in solar mass - minimal_discrete_mass_first_stars: 13 # Minimal mass of stars represented by discrete particles, in solar mass - sink_formation_contracting_gas_check: 0 - sink_formation_smoothing_length_check: 0 - sink_formation_jeans_instability_check: 0 - sink_formation_bound_state_check: 0 - sink_formation_overlapping_sink_criterion: 0 - + maximal_temperature: 3e4 # Upper limit to the temperature of a star forming particle + density_threshold_g_per_cm3: 1.67e-24 # Density threashold in g/cm3 (1.67e-24 =1acc) + stellar_particle_mass_Msun: 21 # Mass of the stellar particle representing the low mass stars, in solar mass + minimal_discrete_mass_Msun: 8 # Minimal mass of stars represented by discrete particles, in solar mass + stellar_particle_mass_first_stars_Msun: 20 # Mass of the stellar particle representing the low mass stars, in solar mass + minimal_discrete_mass_first_stars_Msun: 8 # Minimal mass of stars represented by discrete particles, in solar mass + star_spawning_sigma_factor: 0.1 # Factor to rescale the velocity dispersion of the stars when they are spawned. (Default: 0.2) + sink_formation_contracting_gas_criterion: 0 # (Optional) Activate the contracting gas check for sink formation. (Default: 1) + sink_formation_smoothing_length_criterion: 0 # (Optional) Activate the smoothing length check for sink formation. (Default: 1) + sink_formation_jeans_instability_criterion: 0 # (Optional) Activate the two Jeans instability checks for sink formation. (Default: 1) + sink_formation_bound_state_criterion: 0 # (Optional) Activate the bound state check for sink formation. (Default: 1) + sink_formation_overlapping_sink_criterion: 0 # (Optional) Activate the overlapping sink check for sink formation. (Default: 1) + disable_sink_formation: 0 # (Optional) Disable sink formation. (Default: 0) diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/README b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/README index 14a289cf4a1d638c18f421f23ca8bcf0ced68d1b..c1bbf64e21599eba5c499b3d0798a080998897b7 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/README +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/README @@ -7,3 +7,12 @@ on the options to cancel the h-factors and a-factors at reading time. MD5 checksum of the ICs: 08736c3101fd738e22f5159f78e6022b small_cosmo_volume.hdf5 + +# How to run with CSDS + +To enable the CSDS, add --enable-csds --enable-python when +configuring swift. + +In `run_csds.sh`, update `velociraptor_path` to the location +of velociraptor on your system. Then, use `./run_csds.sh` and +let's go! diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/csds_analysis.py b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/csds_analysis.py new file mode 100644 index 0000000000000000000000000000000000000000..1ae897fc46a0b23ea2e3d8ff41ec8dcaf6e862fb --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/csds_analysis.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Plots the evolution of the DM particles with the CSDS. +""" + +import sys +import os +import argparse +import numpy as np +import matplotlib.pyplot as plt +from velociraptor import load +from velociraptor.particles import load_groups + +# Include the CSDS library to the path before importing the CSDS python module +sys.path.append("../../../csds/src/.libs/") + +# Now we can load the csds +import libcsds as csds + +#%% +def get_redshift(a): + return 1 / a - 1 + + +def get_scale_factor(z): + return 1 / (z + 1) + + +#%% Argparse options +def parse_option(): + description = """"Plots the evolution of the DM particles with the CSDS. """ + epilog = """ +Examples: +-------- + +python3 csds_analysis.py csds_index_0000.dump +python3 csds_analysis.py csds_index_0000.dump --halo 2 +python3 csds_analysis.py csds_index_0000.dump --halo 2 -n 250 +""" + parser = argparse.ArgumentParser(description=description, epilog=epilog) + + parser.add_argument("csds_file", type=str, help="CSDS .dump file name.") + + parser.add_argument( + "--halo", + action="store", + type=int, + dest="halo_index", + default=1, + help="Halo id of which we want trace the evolution. The halo id are given by velociraptor.", + ) + + parser.add_argument( + "-n", + action="store", + type=int, + default=500, + help="Number of timestamps to fetch the CSDS.", + ) + + parser.add_argument( + "--stf_file", + action="store", + type=str, + dest="stf_file", + default="stf_output/snap_0031", + help="stf file basename.", + ) + + args = parser.parse_args() + + return args + + +#%% Parse the arguments +args = parse_option() + +stf_file = args.stf_file +halo_index = args.halo_index +filename = args.csds_file +n_files = 1 # Number of csds files +n = args.n # Number of timestamps to fetch the CSDS +dt = 1e-3 # small timestep for csds start (starts at t0+dt, see below) + +# These are the number of timestamps we want for z > 2 (n_high_z) and z <=2 (n_low_z). This allows to provide more images outputs for 0 < z < 0 for the movie. +n_high_z = int(n / 10) +n_low_z = int(9 / 10 * n) + +print("\n-------------------------------------") +print("Welcome to csds_anlysis.py !\n") + +#%% Extract particles from halo +# Load Velociraptor data +print("Extracting halo data...") + +catalogue = load(stf_file + ".properties.0") +groups = load_groups(stf_file + ".catalog_groups.0", catalogue=catalogue) + +# Get the particles in halo_index +particles, unbound_particles = groups.extract_halo(halo_index=halo_index) + +# Get the IDs +IDs = particles.particle_ids +N_particles = len(IDs) + +#%% Now, we can open the CSDS, get the evolution of the particles though time with their IDs +cm_positions = np.zeros((n_files, n, 3)) +positions = np.empty((n, N_particles, 3)) +velocities = np.empty((n, N_particles, 3)) +n_particles = np.zeros((n_files, n), dtype=int) +m_tot = np.zeros((n_files, n)) + +file_index = 0 + +if filename.endswith(".dump"): + filename = filename[:-5] + +print("Openening the CSDS...") + +#%% +with csds.Reader( + filename, verbose=0, number_index=10, restart_init=False, use_cache=True +) as reader: + + print("The CSDS is opened.") + + # Check the time limits (scale-factors in this example) + t0, t1 = reader.get_time_limits() + + print("Scale factor limits: [{:e}, {:e}]".format(t0, t1)) + print(np.log10(t0), np.log10(t1)) + print("Redshift limits: [{:e}, {:e}]".format(get_redshift(t0), get_redshift(t1))) + + # Ensure that the fields are present + fields = ["Coordinates", "Masses", "ParticleIDs", "Velocities"] + missing = set(fields).difference( + set(reader.get_list_fields(part_type=csds.particle_types.gas)) + ) + + if missing: + raise Exception("Fields %s not found in the logfile." % missing) + + # Create the list of IDs for *all* particle types + IDs_list = [None] * csds.particle_types.count + IDs_list[csds.particle_types.dark_matter] = IDs + + # Read the particles by IDs + out = reader.get_data(fields=fields, time=t0, filter_by_ids=IDs_list) + + # Print the missing ids + dm_ids, ids_found = set(IDs), set(out["ParticleIDs"]) + diff_ids = list(dm_ids.difference(ids_found)) + diff_found = list(ids_found.difference(dm_ids)) + print("The following ids were not found: ", np.array(diff_ids)) + print("The following ids are wrongly missing: ", np.array(diff_found)) + + # Reverse the time: it is better to start from the end and rewind to the beginning + high_z = np.linspace(t0 + dt, get_scale_factor(2), num=n_high_z, endpoint=False) + low_z = np.linspace(get_scale_factor(2), t1, num=n_low_z) + times = np.concatenate((high_z, low_z))[::-1] + + print("Starting to move through time") + for i, t in enumerate(times): + out = reader.get_data(fields=fields, time=t, filter_by_ids=IDs_list) + + # Get positions and velocities + positions[i] = out["Coordinates"] + velocities[i] = out["Velocities"] + + # Compute CM of halo + n_particles[file_index, i] = len(out["ParticleIDs"]) + m_tot[file_index, i] = np.sum(out["Masses"]) + pos_cm = np.sum(out["Masses"][:, np.newaxis] * out["Coordinates"], axis=0) + pos_cm /= m_tot[file_index, i] + cm_positions[file_index, i, :] = pos_cm + +print("The CSDS is now closed.") +#%% Do a movie showing the evolution of the halo (positions) +from matplotlib.animation import FuncAnimation + +fig, ax = plt.subplots(num=1, figsize=(5, 5), dpi=300) +fig.tight_layout() + + +def animate(index): + # Read the positions in the right order + i = -index - 1 + x = positions[i, :, 0] + y = positions[i, :, 1] + scale_factor = times[i] + redshift = 1 / scale_factor - 1 + + # Clear the previous data + ax.cla() + ax.text( + 0.975, + 0.975, + "z = {:.2f}".format(redshift), + ha="right", + va="top", + transform=ax.transAxes, + color="k", + ) + ax.scatter(x, y, c="k", zorder=1, marker=".") + ax.set_aspect("equal", "box") + return ax + + +print("Making movie...") +animation = FuncAnimation( + fig, animate, range(positions.shape[0]), fargs=[], interval=n / 24 +) +animation.save("halo_evolution.mp4") +print("End :)") diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/run_csds.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/run_csds.sh new file mode 100755 index 0000000000000000000000000000000000000000..30b414eab1be7eabde93d14283e180ea9dac06f6 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/run_csds.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +#Path to velociraptor executable +velociraptor_path=/path/to/velociraptor + + # Generate the initial conditions if they are not present. +if [ ! -e small_cosmo_volume.hdf5 ] +then + echo "Fetching initial conditions for the small cosmological volume example..." + ./getIC.sh +fi + +# Run SWIFT +../../../swift --cosmology --self-gravity --csds --threads=12 small_cosmo_volume_dm.yml 2>&1 | tee output.log + +if [ ! -d "stf_output" ] +then + mkdir stf_output +fi + +# Run velociraptor +$velociraptor_path/stf -I 2 -C vrconfig_3dfof_subhalos_SO_hydro.cfg -i snap_0031 -o stf_output/snap_0031 + +# Make a movie of the evolution of the DM particles +python3 csds_analysis.py csds_index_0000.dump --halo 1 diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml index f469b972e5d6e93f71c51a277e8549e9b2ab9d47..135500246d868e37bd2048ed51a57530a2e097bb 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml @@ -67,3 +67,11 @@ PowerSpectrum: fold_factor: 2 window_order: 2 requested_spectra: ["matter-matter"] + +# Parameters governing the CSDS snapshot system +CSDS: + delta_step: 10 # Update the particle log every this many updates + basename: csds_index # Common part of the filenames + initial_buffer_size: 0.3 # (Optional) Buffer size in GB + buffer_scale: 2 # (Optional) When buffer size is too small, update it with required memory times buffer_scale + diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/vrconfig_3dfof_subhalos_SO_hydro.cfg b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/vrconfig_3dfof_subhalos_SO_hydro.cfg index 8590cbf5bc77e8d7a956d210339cced4bbdc692c..8ecc372e3fac2cf377d97955aa2139b4f74ca541 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/vrconfig_3dfof_subhalos_SO_hydro.cfg +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/vrconfig_3dfof_subhalos_SO_hydro.cfg @@ -9,9 +9,9 @@ ################################ HDF_name_convention=6 #HDF SWIFT naming convention Input_includes_dm_particle=1 #include dark matter particles in hydro input -Input_includes_gas_particle=1 #include gas particles in hydro input -Input_includes_star_particle=1 #include star particles in hydro input -Input_includes_bh_particle=1 #include bh particles in hydro input +Input_includes_gas_particle=0 #include gas particles in hydro input +Input_includes_star_particle=0 #include star particles in hydro input +Input_includes_bh_particle=0 #include bh particles in hydro input Input_includes_wind_particle=0 #include wind particles in hydro input (used by Illustris and moves particle type 0 to particle type 3 when decoupled from hydro forces). Here shown as example Input_includes_tracer_particle=0 #include tracer particles in hydro input (used by Illustris). Here shown as example Input_includes_extradm_particle=0 #include extra dm particles stored in particle type 2 and type 3, useful for zooms diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index e5ba0f8dc830371ee4d17f6b853dc770a35ea824..f6ca3395a98ff805e38c10e89ce4647ee5f5df3f 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -34,29 +34,30 @@ Cosmology: # Parameters for the hydrodynamics scheme SPH: - resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). - CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. - use_mass_weighted_num_ngb: 0 # (Optional) Are we using the mass-weighted definition of the number of neighbours? - h_tolerance: 1e-4 # (Optional) Relative accuracy of the Netwon-Raphson scheme for the smoothing lengths. - h_max: 10. # (Optional) Maximal allowed smoothing length in internal units. Defaults to FLT_MAX if unspecified. - h_min_ratio: 0. # (Optional) Minimal allowed smoothing length in units of the softening. Defaults to 0 if unspecified. - max_volume_change: 1.4 # (Optional) Maximal allowed change of kernel volume over one time-step. - max_ghost_iterations: 30 # (Optional) Maximal number of iterations allowed to converge towards the smoothing length. - particle_splitting: 1 # (Optional) Are we splitting particles that are too massive (default: 0) - particle_splitting_mass_threshold: 7e-4 # (Optional) Mass threshold for particle splitting (in internal units) - generate_random_ids: 0 # (Optional) When creating new particles via splitting, generate ids at random (1) or use new IDs beyond the current range (0) (default: 0) - initial_temperature: 0 # (Optional) Initial temperature (in internal units) to set the gas particles at start-up. Value is ignored if set to 0. - minimal_temperature: 0 # (Optional) Minimal temperature (in internal units) allowed for the gas particles. Value is ignored if set to 0. - H_mass_fraction: 0.755 # (Optional) Hydrogen mass fraction used for initial conversion from temp to internal energy. Default value is derived from the physical constants. - H_ionization_temperature: 1e4 # (Optional) Temperature of the transition from neutral to ionized Hydrogen for primoridal gas. - viscosity_alpha: 0.8 # (Optional) Override for the initial value of the artificial viscosity. In schemes that have a fixed AV, this remains as alpha throughout the run. - viscosity_alpha_max: 2.0 # (Optional) Maximal value for the artificial viscosity in schemes that allow alpha to vary. - viscosity_alpha_min: 0.1 # (Optional) Minimal value for the artificial viscosity in schemes that allow alpha to vary. - viscosity_length: 0.1 # (Optional) Decay length for the artificial viscosity in schemes that allow alpha to vary. - diffusion_alpha: 0.0 # (Optional) Override the initial value for the thermal diffusion coefficient in schemes with thermal diffusion. - diffusion_beta: 0.01 # (Optional) Override the decay/rise rate tuning parameter for the thermal diffusion. - diffusion_alpha_max: 1.0 # (Optional) Override the maximal thermal diffusion coefficient that is allowed for a given particle. - diffusion_alpha_min: 0.0 # (Optional) Override the minimal thermal diffusion coefficient that is allowed for a given particle. + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + use_mass_weighted_num_ngb: 0 # (Optional) Are we using the mass-weighted definition of the number of neighbours? + h_tolerance: 1e-4 # (Optional) Relative accuracy of the Netwon-Raphson scheme for the smoothing lengths. + h_max: 10. # (Optional) Maximal allowed smoothing length in internal units. Defaults to FLT_MAX if unspecified. + h_min_ratio: 0. # (Optional) Minimal allowed smoothing length in units of the softening. Defaults to 0 if unspecified. + max_volume_change: 1.4 # (Optional) Maximal allowed change of kernel volume over one time-step. + max_ghost_iterations: 30 # (Optional) Maximal number of iterations allowed to converge towards the smoothing length. + particle_splitting: 1 # (Optional) Are we splitting particles that are too massive (default: 0) + particle_splitting_mass_threshold: 7e-4 # (Optional) Mass threshold for particle splitting (in internal units) + particle_splitting_log_extra_splits: 0 # (Optional) Are we logging the splits beyond the maximal allowed into files? (default: 0) + generate_random_ids: 0 # (Optional) When creating new particles via splitting, generate ids at random (1) or use new IDs beyond the current range (0) (default: 0) + initial_temperature: 0 # (Optional) Initial temperature (in internal units) to set the gas particles at start-up. Value is ignored if set to 0. + minimal_temperature: 0 # (Optional) Minimal temperature (in internal units) allowed for the gas particles. Value is ignored if set to 0. + H_mass_fraction: 0.755 # (Optional) Hydrogen mass fraction used for initial conversion from temp to internal energy. Default value is derived from the physical constants. + H_ionization_temperature: 1e4 # (Optional) Temperature of the transition from neutral to ionized Hydrogen for primoridal gas. + viscosity_alpha: 0.8 # (Optional) Override for the initial value of the artificial viscosity. In schemes that have a fixed AV, this remains as alpha throughout the run. + viscosity_alpha_max: 2.0 # (Optional) Maximal value for the artificial viscosity in schemes that allow alpha to vary. + viscosity_alpha_min: 0.1 # (Optional) Minimal value for the artificial viscosity in schemes that allow alpha to vary. + viscosity_length: 0.1 # (Optional) Decay length for the artificial viscosity in schemes that allow alpha to vary. + diffusion_alpha: 0.0 # (Optional) Override the initial value for the thermal diffusion coefficient in schemes with thermal diffusion. + diffusion_beta: 0.01 # (Optional) Override the decay/rise rate tuning parameter for the thermal diffusion. + diffusion_alpha_max: 1.0 # (Optional) Override the maximal thermal diffusion coefficient that is allowed for a given particle. + diffusion_alpha_min: 0.0 # (Optional) Override the minimal thermal diffusion coefficient that is allowed for a given particle. # Parameters of the stars Stars: @@ -137,6 +138,7 @@ Scheduler: cell_sub_size_pair_grav: 256000000 # (Optional) Maximal number of interactions per sub-pair gravity task (this is the default value). cell_sub_size_self_grav: 32000 # (Optional) Maximal number of interactions per sub-self gravity task (this is the default value). cell_split_size: 400 # (Optional) Maximal number of particles per cell (this is the default value). + grid_split_threshold: 400 # (Optional) Maximal number of particles per cell at construction level of Voronoi grid (this is the default value). cell_subdepth_diff_grav: 4 # (Optional) Maximal depth difference between leaves and a cell that gravity tasks can be pushed down to (this is the default value). cell_extra_parts: 0 # (Optional) Number of spare parts per top-level allocated at rebuild time for on-the-fly creation. cell_extra_gparts: 0 # (Optional) Number of spare gparts per top-level allocated at rebuild time for on-the-fly creation. @@ -690,11 +692,14 @@ EAGLEFeedback: # GEAR feedback model GEARFeedback: supernovae_energy_erg: 0.1e51 # Energy released by a single supernovae. + supernovae_efficiency: 0.1 # Supernovae energy efficiency, used for both SNIa and SNII. The energy released effectively is E_sn = supernovae_efficiency*E_sn yields_table: chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5 # Table containing the yields. yields_table_first_stars: chemistry-PopIII.hdf5 # Table containing the yields of the first stars. metallicity_max_first_stars: -1 # Maximal metallicity (in mass fraction) for a first star (-1 to deactivate). + imf_transition_metallicity: -5 # Maximal metallicity ([Fe/H]) for a first star (0 to deactivate). discrete_yields: 0 # Should we use discrete yields or the IMF integrated one? elements: [Fe, Mg, O, S, Zn, Sr, Y, Ba, Eu] # Elements to read in the yields table. The number of element should be one less than the number of elements (N) requested during the configuration (--with-chemistry=GEAR_N). + discrete_star_minimal_gravity_mass_Msun: 0.1 # Minimal gravity mass after a discrete star completely explodes. In M_sun. (Default: 0.1) # AGORA feedback model AGORAFeedback: @@ -875,12 +880,12 @@ GEARSink: cut_off_radius: 1e-3 # Cut off radius of the sink particles (in internal units). This parameter should be adapted with the resolution. f_acc: 0.8 # (Optional) Fraction of the cut_off_radius that determines if a gas particle should be swallowed wihtout additional check. It has to respect 0 <= f_acc <= 1. (Default: 0.8) maximal_temperature: 3e10 # Maximal gas temperature for forming a star (in K) - density_threshold: 1.67e-23 # Minimal gas density for forming a star (in g/cm3 (1.67e-24 =1acc)) - size_of_calibration_sample: 100000 # Size of the calibration sample used to determine the probabilities to form stellar particles with mass stellar_particle_mass - stellar_particle_mass: 20 # Mass of the stellar particle representing the low mass stars (continuous IMF sampling) (in solar mass) - minimal_discrete_mass: 8 # Minimal mass of stars represented by discrete particles (in solar mass) - stellar_particle_mass_first_stars: 20 # Mass of the stellar particle representing the low mass stars (continuous IMF sampling) (in solar mass). First stars - minimal_discrete_mass_first_stars: 8 # Minimal mass of stars represented by discrete particles (in solar mass). First stars + density_threshold_g_per_cm3: 1.67e-23 # Minimal gas density for forming a star (in g/cm3 (1.67e-24 =1acc)) + stellar_particle_mass_Msun: 20 # Mass of the stellar particle representing the low mass stars (continuous IMF sampling) (in solar mass) + minimal_discrete_mass_Msun: 8 # Minimal mass of stars represented by discrete particles (in solar mass) + stellar_particle_mass_first_stars_Msun: 20 # Mass of the stellar particle representing the low mass stars (continuous IMF sampling) (in solar mass). First stars + minimal_discrete_mass_first_stars_Msun: 8 # Minimal mass of stars represented by discrete particles (in solar mass). First stars + star_spawning_sigma_factor: 0.2 # Factor to rescale the velocity dispersion of the stars when they are spawned. (Default: 0.2) sink_formation_contracting_gas_criterion: 1 # (Optional) Activate the contracting gas check for sink formation. (Default: 1) sink_formation_smoothing_length_criterion: 1 # (Optional) Activate the smoothing length check for sink formation. (Default: 1) sink_formation_jeans_instability_criterion: 1 # (Optional) Activate the two Jeans instability checks for sink formation. (Default: 1) diff --git a/format.sh b/format.sh index dcb35209ce9231a1bb8e16c422d9586ece401fda..b25a073c70f38dd8e87b842cac61621aa64b2c5a 100755 --- a/format.sh +++ b/format.sh @@ -16,7 +16,7 @@ if test "$?" != "0"; then fi # Formatting command -cmd="$clang -style=file $(git ls-files | grep '\.[ch]$')" +cmd="$clang -style=file $(git ls-files | grep '\.[ch]$' | grep -v csds_io\.h | grep -v argparse\.h | grep -v vector\.h)" # Test if `clang-format-18` works command -v $clang > /dev/null diff --git a/src/Makefile.am b/src/Makefile.am index 2082763c8bc9f5f6d0953e78a7d2ba29f27edafd..8099524651d8b3bb0a2765f1c0e7f05ef570af08 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,7 +25,7 @@ AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) GIT_CMD = @GIT_CMD@ # Additional dependencies for shared libraries. -EXTRA_LIBS = $(GSL_LIBS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(SUNDIALS_LIBS) $(CHEALPIX_LIBS) +EXTRA_LIBS = $(GSL_LIBS) $(GMP_LIBS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(SUNDIALS_LIBS) $(CHEALPIX_LIBS) # MPI libraries. MPI_LIBS = $(PARMETIS_LIBS) $(METIS_LIBS) $(MPI_THREAD_LIBS) @@ -42,12 +42,12 @@ endif # List required headers include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h -include_HEADERS += cell_hydro.h cell_stars.h cell_grav.h cell_sinks.h cell_black_holes.h cell_rt.h +include_HEADERS += cell_hydro.h cell_stars.h cell_grav.h cell_sinks.h cell_black_holes.h cell_rt.h cell_grid.h include_HEADERS += engine.h swift.h serial_io.h timers.h debug.h scheduler.h proxy.h parallel_io.h include_HEADERS += common_io.h single_io.h distributed_io.h map.h tools.h partition_fixed_costs.h include_HEADERS += partition.h clocks.h parser.h physical_constants.h physical_constants_cgs.h potential.h version.h include_HEADERS += hydro_properties.h riemann.h threadpool.h cooling_io.h cooling.h cooling_struct.h cooling_properties.h cooling_debug.h -include_HEADERS += statistics.h memswap.h cache.h runner_doiact_hydro_vec.h runner_doiact_undef.h profiler.h entropy_floor.h +include_HEADERS += statistics.h memswap.h cache.h runner_doiact_hydro_vec.h runner_doiact_undef.h profiler.h entropy_floor.h include_HEADERS += csds.h active.h timeline.h xmf.h gravity_properties.h gravity_derivatives.h include_HEADERS += gravity_softened_derivatives.h vector_power.h collectgroup.h hydro_space.h sort_part.h include_HEADERS += chemistry.h chemistry_additions.h chemistry_io.h chemistry_struct.h chemistry_debug.h @@ -161,14 +161,14 @@ endif AM_SOURCES = space.c space_rebuild.c space_regrid.c space_unique_id.c AM_SOURCES += space_sort.c space_split.c space_extras.c space_first_init.c space_init.c AM_SOURCES += space_cell_index.c space_recycle.c -AM_SOURCES += runner_main.c runner_doiact_hydro.c runner_doiact_limiter.c +AM_SOURCES += runner_main.c runner_doiact_hydro.c runner_doiact_limiter.c AM_SOURCES += runner_doiact_stars.c runner_doiact_black_holes.c runner_ghost.c AM_SOURCES += runner_recv.c runner_pack.c AM_SOURCES += runner_sort.c runner_drift.c runner_black_holes.c runner_time_integration.c AM_SOURCES += runner_doiact_hydro_vec.c runner_others.c AM_SOURCES += runner_sinks.c -AM_SOURCES += cell.c cell_convert_part.c cell_drift.c cell_lock.c cell_pack.c cell_split.c -AM_SOURCES += cell_unskip.c +AM_SOURCES += cell.c cell_convert_part.c cell_drift.c cell_lock.c cell_pack.c cell_split.c +AM_SOURCES += cell_unskip.c cell_grid.c AM_SOURCES += engine.c engine_maketasks.c engine_split_particles.c engine_strays.c AM_SOURCES += engine_marktasks.c engine_drift.c engine_unskip.c engine_collect_end_of_step.c AM_SOURCES += engine_redistribute.c engine_fof.c engine_proxy.c engine_io.c engine_config.c @@ -210,11 +210,11 @@ AM_SOURCES += $(GEAR_RT_SOURCES) # Include files for distribution, not installation. nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h nobase_noinst_HEADERS += gravity_iact.h kernel_long_gravity.h vector.h accumulate.h cache.h exp.h log.h -nobase_noinst_HEADERS += runner_doiact_nosort.h runner_doiact_hydro.h runner_doiact_stars.h runner_doiact_black_holes.h runner_doiact_grav.h +nobase_noinst_HEADERS += runner_doiact_nosort.h runner_doiact_hydro.h runner_doiact_stars.h runner_doiact_black_holes.h runner_doiact_grav.h nobase_noinst_HEADERS += runner_doiact_functions_hydro.h runner_doiact_functions_stars.h runner_doiact_functions_black_holes.h nobase_noinst_HEADERS += runner_doiact_functions_limiter.h runner_doiact_limiter.h units.h intrinsics.h minmax.h nobase_noinst_HEADERS += runner_doiact_sinks.h -nobase_noinst_HEADERS += kick.h timestep.h drift.h adiabatic_index.h io_properties.h dimension.h part_type.h periodic.h memswap.h +nobase_noinst_HEADERS += kick.h timestep.h drift.h adiabatic_index.h io_properties.h dimension.h part_type.h periodic.h memswap.h nobase_noinst_HEADERS += timestep_limiter.h timestep_limiter_iact.h timestep_sync.h timestep_sync_part.h timestep_limiter_struct.h nobase_noinst_HEADERS += csds.h sign.h csds_io.h hashmap.h gravity.h gravity_io.h gravity_csds.h gravity_cache.h output_options.h nobase_noinst_HEADERS += gravity/Default/gravity.h gravity/Default/gravity_iact.h gravity/Default/gravity_io.h @@ -281,21 +281,10 @@ nobase_noinst_HEADERS += hydro/Gizmo/MFM/hydro_part.h nobase_noinst_HEADERS += hydro/Gizmo/MFM/hydro_flux.h nobase_noinst_HEADERS += hydro/Gizmo/MFM/hydro_velocities.h nobase_noinst_HEADERS += hydro/Shadowswift/hydro_debug.h -nobase_noinst_HEADERS += hydro/Shadowswift/hydro_gradients.h hydro/Shadowswift/hydro.h +nobase_noinst_HEADERS += hydro/Shadowswift/hydro.h nobase_noinst_HEADERS += hydro/Shadowswift/hydro_iact.h nobase_noinst_HEADERS += hydro/Shadowswift/hydro_io.h -nobase_noinst_HEADERS += hydro/Shadowswift/hydro_part.h -nobase_noinst_HEADERS += hydro/Shadowswift/hydro_slope_limiters_cell.h -nobase_noinst_HEADERS += hydro/Shadowswift/hydro_slope_limiters_face.h -nobase_noinst_HEADERS += hydro/Shadowswift/hydro_slope_limiters.h -nobase_noinst_HEADERS += hydro/Shadowswift/voronoi1d_algorithm.h -nobase_noinst_HEADERS += hydro/Shadowswift/voronoi1d_cell.h -nobase_noinst_HEADERS += hydro/Shadowswift/voronoi2d_algorithm.h -nobase_noinst_HEADERS += hydro/Shadowswift/voronoi2d_cell.h -nobase_noinst_HEADERS += hydro/Shadowswift/voronoi3d_algorithm.h -nobase_noinst_HEADERS += hydro/Shadowswift/voronoi3d_cell.h -nobase_noinst_HEADERS += hydro/Shadowswift/voronoi_algorithm.h -nobase_noinst_HEADERS += hydro/Shadowswift/voronoi_cell.h +nobase_noinst_HEADERS += hydro/Shadowswift/hydro_part.h nobase_noinst_HEADERS += hydro/Shadowswift/hydro_parameters.h nobase_noinst_HEADERS += mhd.h mhd_struct.h mhd_io.h nobase_noinst_HEADERS += mhd/None/mhd.h mhd/None/mhd_iact.h mhd/None/mhd_struct.h mhd/None/mhd_io.h mhd/None/mhd_debug.h mhd/None/mhd_parameters.h @@ -329,31 +318,31 @@ nobase_noinst_HEADERS += rt/GEAR/rt_blackbody.h nobase_noinst_HEADERS += rt/GEAR/rt_debugging.h nobase_noinst_HEADERS += rt/GEAR/rt_flux.h nobase_noinst_HEADERS += rt/GEAR/rt_getters.h -nobase_noinst_HEADERS += rt/GEAR/rt_grackle_utils.h -nobase_noinst_HEADERS += rt/GEAR/rt_gradients.h -nobase_noinst_HEADERS += rt/GEAR/rt.h -nobase_noinst_HEADERS += rt/GEAR/rt_iact.h -nobase_noinst_HEADERS += rt/GEAR/rt_interaction_cross_sections.h -nobase_noinst_HEADERS += rt/GEAR/rt_interaction_rates.h +nobase_noinst_HEADERS += rt/GEAR/rt_grackle_utils.h +nobase_noinst_HEADERS += rt/GEAR/rt_gradients.h +nobase_noinst_HEADERS += rt/GEAR/rt.h +nobase_noinst_HEADERS += rt/GEAR/rt_iact.h +nobase_noinst_HEADERS += rt/GEAR/rt_interaction_cross_sections.h +nobase_noinst_HEADERS += rt/GEAR/rt_interaction_rates.h nobase_noinst_HEADERS += rt/GEAR/rt_io.h nobase_noinst_HEADERS += rt/GEAR/rt_ionization_equilibrium.h nobase_noinst_HEADERS += rt/GEAR/rt_parameters.h nobase_noinst_HEADERS += rt/GEAR/rt_properties.h nobase_noinst_HEADERS += rt/GEAR/rt_riemann_GLF.h -nobase_noinst_HEADERS += rt/GEAR/rt_riemann_HLL_eigenvalues.h -nobase_noinst_HEADERS += rt/GEAR/rt_riemann_HLL.h +nobase_noinst_HEADERS += rt/GEAR/rt_riemann_HLL_eigenvalues.h +nobase_noinst_HEADERS += rt/GEAR/rt_riemann_HLL.h nobase_noinst_HEADERS += rt/GEAR/rt_slope_limiters_cell.h nobase_noinst_HEADERS += rt/GEAR/rt_slope_limiters_face.h nobase_noinst_HEADERS += rt/GEAR/rt_species.h -nobase_noinst_HEADERS += rt/GEAR/rt_stellar_emission_model.h +nobase_noinst_HEADERS += rt/GEAR/rt_stellar_emission_model.h nobase_noinst_HEADERS += rt/GEAR/rt_struct.h nobase_noinst_HEADERS += rt/GEAR/rt_thermochemistry.h nobase_noinst_HEADERS += rt/GEAR/rt_thermochemistry_utils.h nobase_noinst_HEADERS += rt/GEAR/rt_unphysical.h nobase_noinst_HEADERS += rt/SPHM1RT/rt.h -nobase_noinst_HEADERS += rt/SPHM1RT/rt_getters.h -nobase_noinst_HEADERS += rt/SPHM1RT/rt_setters.h -nobase_noinst_HEADERS += rt/SPHM1RT/rt_iact.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_getters.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_setters.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_iact.h nobase_noinst_HEADERS += rt/SPHM1RT/rt_io.h nobase_noinst_HEADERS += rt/SPHM1RT/rt_parameters.h nobase_noinst_HEADERS += rt/SPHM1RT/rt_properties.h @@ -363,9 +352,10 @@ nobase_noinst_HEADERS += rt/SPHM1RT/rt_additions.h nobase_noinst_HEADERS += rt/SPHM1RT/rt_cooling.h nobase_noinst_HEADERS += rt/SPHM1RT/rt_cooling_rates.h nobase_noinst_HEADERS += rt/SPHM1RT/rt_unphysical.h -nobase_noinst_HEADERS += rt/SPHM1RT/rt_species_and_elements.h -nobase_noinst_HEADERS += rt/SPHM1RT/rt_stellar_emission_rate.h -nobase_noinst_HEADERS += stars.h stars_io.h stars_csds.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_species_and_elements.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_stellar_emission_rate.h +nobase_noinst_HEADERS += shadowswift/voronoi.h +nobase_noinst_HEADERS += stars.h stars_io.h stars_csds.h nobase_noinst_HEADERS += stars/None/stars.h stars/None/stars_iact.h stars/None/stars_io.h nobase_noinst_HEADERS += stars/None/stars_debug.h stars/None/stars_part.h nobase_noinst_HEADERS += stars/Basic/stars.h stars/Basic/stars_iact.h stars/Basic/stars_io.h @@ -374,6 +364,7 @@ nobase_noinst_HEADERS += stars/EAGLE/stars.h stars/EAGLE/stars_iact.h stars/EAGL nobase_noinst_HEADERS += stars/EAGLE/stars_debug.h stars/EAGLE/stars_part.h nobase_noinst_HEADERS += stars/GEAR/stars.h stars/GEAR/stars_iact.h stars/GEAR/stars_io.h nobase_noinst_HEADERS += stars/GEAR/stars_debug.h stars/GEAR/stars_csds.h stars/GEAR/stars_part.h +nobase_noinst_HEADERS += stars/GEAR/stars_stellar_type.h nobase_noinst_HEADERS += forcing/none/forcing.h forcing/roberts_flow/forcing.h forcing/roberts_flow_acceleration/forcing.h nobase_noinst_HEADERS += forcing/ABC_flow/forcing.h nobase_noinst_HEADERS += potential/none/potential.h potential/point_mass/potential.h @@ -387,12 +378,13 @@ nobase_noinst_HEADERS += star_formation/none/star_formation_csds.h star_formatio nobase_noinst_HEADERS += star_formation/QLA/star_formation.h star_formation/QLA/star_formation_struct.h nobase_noinst_HEADERS += star_formation/QLA/star_formation_io.h star_formation/QLA/star_formation_iact.h nobase_noinst_HEADERS += star_formation/QLA/star_formation_debug.h -nobase_noinst_HEADERS += star_formation/EAGLE/star_formation.h star_formation/EAGLE/star_formation_struct.h +nobase_noinst_HEADERS += star_formation/EAGLE/star_formation.h star_formation/EAGLE/star_formation_struct.h nobase_noinst_HEADERS += star_formation/EAGLE/star_formation_io.h star_formation/EAGLE/star_formation_iact.h nobase_noinst_HEADERS += star_formation/EAGLE/star_formation_debug.h -nobase_noinst_HEADERS += star_formation/GEAR/star_formation.h star_formation/GEAR/star_formation_struct.h +nobase_noinst_HEADERS += star_formation/GEAR/star_formation.h star_formation/GEAR/star_formation_struct.h nobase_noinst_HEADERS += star_formation/GEAR/star_formation_io.h star_formation/GEAR/star_formation_iact.h nobase_noinst_HEADERS += star_formation/GEAR/star_formation_csds.h star_formation/GEAR/star_formation_debug.h +nobase_noinst_HEADERS += star_formation/GEAR/star_formation_setters.h nobase_noinst_HEADERS += star_formation/EAGLE/star_formation_logger.h star_formation/EAGLE/star_formation_logger_struct.h nobase_noinst_HEADERS += star_formation/GEAR/star_formation_logger.h star_formation/GEAR/star_formation_logger_struct.h nobase_noinst_HEADERS += star_formation/none/star_formation_logger.h star_formation/none/star_formation_logger_struct.h @@ -413,8 +405,8 @@ nobase_noinst_HEADERS += cooling/QLA_EAGLE/cooling_properties.h cooling/QLA_EAGL nobase_noinst_HEADERS += cooling/QLA/cooling.h cooling/QLA/cooling_struct.h cooling/QLA/cooling_tables.h nobase_noinst_HEADERS += cooling/QLA/cooling_io.h cooling/QLA/interpolate.h cooling/QLA/cooling_rates.h nobase_noinst_HEADERS += cooling/QLA/cooling_properties.h cooling/QLA/cooling_debug.h -nobase_noinst_HEADERS += cooling/PS2020/cooling.h cooling/PS2020/cooling_struct.h cooling/PS2020/cooling_subgrid.h -nobase_noinst_HEADERS += cooling/PS2020/cooling_io.h cooling/PS2020/interpolate.h cooling/PS2020/cooling_rates.h +nobase_noinst_HEADERS += cooling/PS2020/cooling.h cooling/PS2020/cooling_struct.h cooling/PS2020/cooling_subgrid.h +nobase_noinst_HEADERS += cooling/PS2020/cooling_io.h cooling/PS2020/interpolate.h cooling/PS2020/cooling_rates.h nobase_noinst_HEADERS += cooling/PS2020/cooling_tables.h cooling/PS2020/cooling_subgrid.h nobase_noinst_HEADERS += cooling/PS2020/cooling_properties.h cooling/PS2020/cooling_debug.h nobase_noinst_HEADERS += chemistry/none/chemistry.h @@ -429,7 +421,7 @@ nobase_noinst_HEADERS += chemistry/GEAR/chemistry_io.h nobase_noinst_HEADERS += chemistry/GEAR/chemistry_csds.h nobase_noinst_HEADERS += chemistry/GEAR/chemistry_struct.h nobase_noinst_HEADERS += chemistry/GEAR/chemistry_iact.h -nobase_noinst_HEADERS += chemistry/GEAR/chemistry_debug.h +nobase_noinst_HEADERS += chemistry/GEAR/chemistry_debug.h nobase_noinst_HEADERS += chemistry/GEAR_DIFFUSION/chemistry.h nobase_noinst_HEADERS += chemistry/GEAR_DIFFUSION/chemistry_io.h nobase_noinst_HEADERS += chemistry/GEAR_DIFFUSION/chemistry_struct.h @@ -451,8 +443,8 @@ nobase_noinst_HEADERS += chemistry/QLA/chemistry.h nobase_noinst_HEADERS += chemistry/QLA/chemistry_io.h nobase_noinst_HEADERS += chemistry/QLA/chemistry_struct.h nobase_noinst_HEADERS += chemistry/QLA/chemistry_iact.h -nobase_noinst_HEADERS += chemistry/QLA/chemistry_debug.h -nobase_noinst_HEADERS += entropy_floor/none/entropy_floor.h +nobase_noinst_HEADERS += chemistry/QLA/chemistry_debug.h +nobase_noinst_HEADERS += entropy_floor/none/entropy_floor.h nobase_noinst_HEADERS += entropy_floor/EAGLE/entropy_floor.h nobase_noinst_HEADERS += entropy_floor/QLA/entropy_floor.h nobase_noinst_HEADERS += tracers/none/tracers.h tracers/none/tracers_struct.h @@ -463,13 +455,13 @@ nobase_noinst_HEADERS += extra_io/EAGLE/extra_io.h extra_io/EAGLE/extra.h nobase_noinst_HEADERS += feedback/none/feedback.h feedback/none/feedback_struct.h feedback/none/feedback_iact.h nobase_noinst_HEADERS += feedback/none/feedback_properties.h feedback/none/feedback.h nobase_noinst_HEADERS += feedback/none/feedback_debug.h -nobase_noinst_HEADERS += feedback/AGORA/feedback.h feedback/AGORA/feedback_struct.h feedback/AGORA/feedback_iact.h +nobase_noinst_HEADERS += feedback/AGORA/feedback.h feedback/AGORA/feedback_struct.h feedback/AGORA/feedback_iact.h nobase_noinst_HEADERS += feedback/AGORA/feedback_properties.h feedback/AGORA/feedback.h nobase_noinst_HEADERS += feedback/AGORA/feedback_debug.h -nobase_noinst_HEADERS += feedback/EAGLE_kinetic/feedback.h feedback/EAGLE_kinetic/feedback_struct.h +nobase_noinst_HEADERS += feedback/EAGLE_kinetic/feedback.h feedback/EAGLE_kinetic/feedback_struct.h nobase_noinst_HEADERS += feedback/EAGLE_kinetic/feedback_properties.h feedback/EAGLE_kinetic/feedback_iact.h nobase_noinst_HEADERS += feedback/EAGLE_kinetic/feedback_debug.h -nobase_noinst_HEADERS += feedback/EAGLE_thermal/feedback.h feedback/EAGLE_thermal/feedback_struct.h +nobase_noinst_HEADERS += feedback/EAGLE_thermal/feedback.h feedback/EAGLE_thermal/feedback_struct.h nobase_noinst_HEADERS += feedback/EAGLE_thermal/feedback_properties.h feedback/EAGLE_thermal/feedback_iact.h nobase_noinst_HEADERS += feedback/EAGLE_thermal/feedback_debug.h nobase_noinst_HEADERS += feedback/EAGLE/yield_tables.h feedback/EAGLE/imf.h feedback/EAGLE/interpolate.h @@ -480,7 +472,7 @@ nobase_noinst_HEADERS += feedback/GEAR/feedback_properties.h feedback/GEAR/feedb nobase_noinst_HEADERS += feedback/GEAR/initial_mass_function.h feedback/GEAR/supernovae_ia.h feedback/GEAR/supernovae_ii.h nobase_noinst_HEADERS += feedback/GEAR/lifetime.h feedback/GEAR/hdf5_functions.h feedback/GEAR/interpolation.h nobase_noinst_HEADERS += feedback/GEAR/feedback_debug.h -nobase_noinst_HEADERS += black_holes/Default/black_holes.h black_holes/Default/black_holes_io.h +nobase_noinst_HEADERS += black_holes/Default/black_holes.h black_holes/Default/black_holes_io.h nobase_noinst_HEADERS += black_holes/Default/black_holes_part.h black_holes/Default/black_holes_iact.h nobase_noinst_HEADERS += black_holes/Default/black_holes_properties.h nobase_noinst_HEADERS += black_holes/Default/black_holes_struct.h @@ -489,23 +481,27 @@ nobase_noinst_HEADERS += black_holes/EAGLE/black_holes.h black_holes/EAGLE/black nobase_noinst_HEADERS += black_holes/EAGLE/black_holes_part.h black_holes/EAGLE/black_holes_iact.h nobase_noinst_HEADERS += black_holes/EAGLE/black_holes_properties.h black_holes/EAGLE/black_holes_parameters.h nobase_noinst_HEADERS += black_holes/EAGLE/black_holes_struct.h black_holes/EAGLE/black_holes_debug.h -nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes.h black_holes/SPIN_JET/black_holes_io.h -nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_part.h black_holes/SPIN_JET/black_holes_iact.h +nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes.h black_holes/SPIN_JET/black_holes_io.h +nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_part.h black_holes/SPIN_JET/black_holes_iact.h nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_properties.h black_holes/SPIN_JET/black_holes_parameters.h -nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_spin.h black_holes/SPIN_JET/black_holes_struct.h -nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_debug.h -nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor.h pressure_floor/none/pressure_floor.h +nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_spin.h black_holes/SPIN_JET/black_holes_struct.h +nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_debug.h +nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor.h pressure_floor/none/pressure_floor.h nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor_iact.h pressure_floor/none/pressure_floor_iact.h nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor_struct.h pressure_floor/none/pressure_floor_struct.h nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor_debug.h pressure_floor/none/pressure_floor_debug.h -nobase_noinst_HEADERS += sink/Default/sink.h sink/Default/sink_io.h sink/Default/sink_part.h sink/Default/sink_properties.h +nobase_noinst_HEADERS += sink/Default/sink.h sink/Default/sink_io.h sink/Default/sink_part.h sink/Default/sink_properties.h nobase_noinst_HEADERS += sink/Default/sink_iact.h sink/Default/sink_struct.h sink/Default/sink_debug.h -nobase_noinst_HEADERS += sink/GEAR/sink.h sink/GEAR/sink_io.h sink/GEAR/sink_part.h sink/GEAR/sink_properties.h +nobase_noinst_HEADERS += sink/GEAR/sink.h sink/GEAR/sink_io.h sink/GEAR/sink_part.h sink/GEAR/sink_properties.h nobase_noinst_HEADERS += sink/GEAR/sink_iact.h sink/GEAR/sink_struct.h sink/GEAR/sink_debug.h nobase_noinst_HEADERS += neutrino.h neutrino_properties.h neutrino_io.h nobase_noinst_HEADERS += neutrino/Default/neutrino.h neutrino/Default/relativity.h neutrino/Default/fermi_dirac.h nobase_noinst_HEADERS += neutrino/Default/neutrino_properties.h neutrino/Default/neutrino_io.h nobase_noinst_HEADERS += neutrino/Default/neutrino_response.h +nobase_noinst_HEADERS += fvpm_geometry.h fvpm_geometry_struct.h +nobase_noinst_HEADERS += fvpm_geometry/None/fvpm_geometry.h fvpm_geometry/None/fvpm_geometry_struct.h +nobase_noinst_HEADERS += fvpm_geometry/Gizmo/fvpm_geometry.h fvpm_geometry/Gizmo/fvpm_geometry_struct.h +nobase_noinst_HEADERS += fvpm_geometry/Gizmo/MFM/fvpm_geometry.h fvpm_geometry/Gizmo/MFV/fvpm_geometry.h # Sources and special flags for the gravity library libgrav_la_SOURCES = runner_doiact_grav.c diff --git a/src/black_holes/SPIN_JET/black_holes_io.h b/src/black_holes/SPIN_JET/black_holes_io.h index 2a256c9f378b3567c2b1e7b49ad247253330eeca..b19579c7ec7f30db6ef807ba335cd39016aa9162 100644 --- a/src/black_holes/SPIN_JET/black_holes_io.h +++ b/src/black_holes/SPIN_JET/black_holes_io.h @@ -227,7 +227,8 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, list[9] = io_make_output_field( "EnergyReservoirs", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts, energy_reservoir, - "Physcial energy contained in the feedback reservoir of the particles"); + "Physcial energy contained in the thermal feedback reservoir of the " + "particles"); list[10] = io_make_output_field( "AccretionRates", FLOAT, 1, UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, bparts, @@ -393,14 +394,15 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, list[32] = io_make_physical_output_field( "NumberOfAGNEvents", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, AGN_number_of_AGN_events, /*can convert to comoving=*/1, - "Integer number of AGN events the black hole has had so far" - " (the number of time steps the BH did AGN feedback)"); + "Integer number of heating AGN events the black hole has had so far" + " (the number of time steps the BH did thermal AGN feedback)"); if (with_cosmology) { list[33] = io_make_physical_output_field( "LastAGNFeedbackScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, last_AGN_event_scale_factor, /*can convert to comoving=*/0, - "Scale-factors at which the black holes last had an AGN event."); + "Scale-factors at which the black holes last had a thermal AGN " + "event."); } else { list[33] = io_make_output_field( "LastAGNFeedbackTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, @@ -417,8 +419,9 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, list[35] = io_make_output_field( "AGNTotalInjectedEnergies", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts, AGN_cumulative_energy, - "Total (cumulative) physical energies injected into gas particles " - "in AGN feedback, including the effects of both radiation and winds."); + "Total (cumulative) physical thermal energies injected into gas " + "particles in thermal AGN feedback, including the effects of both " + "radiation and winds."); list[36] = io_make_output_field_convert_bpart( "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, bparts, @@ -440,7 +443,9 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, list[40] = io_make_output_field( "RadiativeEfficiencies", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, - radiative_efficiency, "AGN luminosity divided by accretion rate."); + radiative_efficiency, + "The radiative efficiencies of the BHs, i.e. the " + "AGN luminosity divided by accretion rate."); list[41] = io_make_output_field("CosAccretionDiskAngle", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, diff --git a/src/black_holes/SPIN_JET/black_holes_part.h b/src/black_holes/SPIN_JET/black_holes_part.h index 3699a51ce50ef1d7faa6edce0da882f90e49cacf..ee01202cb81037c5c7bf7b273b3c9362a49d91b2 100644 --- a/src/black_holes/SPIN_JET/black_holes_part.h +++ b/src/black_holes/SPIN_JET/black_holes_part.h @@ -170,18 +170,18 @@ struct bpart { /*! Total (physical) angular momentum accumulated from subgrid accretion */ float accreted_angular_momentum[3]; - /*! Integer (cumulative) number of energy injections in AGN feedback. At a - * given time-step, an AGN-active BH may produce multiple energy injections. - * The number of energy injections is equal to or more than the number of - * particles heated by the BH during this time-step. */ + /*! Integer (cumulative) number of thermal energy injections in AGN feedback. + * At a given time-step, an AGN-active BH may produce multiple energy + * injections. The number of energy injections is equal to or more than the + * number of particles heated by the BH during this time-step. */ int AGN_number_of_energy_injections; - /*! Integer (cumulative) number of AGN events. If a BH does feedback at a - * given time-step, the number of its AGN events is incremented by 1. Each - * AGN event may have multiple energy injections. */ + /*! Integer (cumulative) number of thermal AGN events. If a BH does feedback + * at a given time-step, the number of its AGN events is incremented by 1. + * Each AGN event may have multiple energy injections. */ int AGN_number_of_AGN_events; - /* Total energy injected into the gas in AGN feedback by this BH */ + /* Total thermal energy injected into the gas in AGN feedback by this BH */ float AGN_cumulative_energy; /*! Union for the last AGN event time and the last AGN event scale factor */ @@ -311,16 +311,17 @@ struct bpart { /*! Properties used in the feedback loop to distribute to gas neighbours. */ struct { - /*! Energy per unit mass in a single AGN energy-injection event */ + /*! Thermal energy per unit mass in a single thermal AGN */ + /* energy-injection event. */ float AGN_delta_u; - /*! Number of energy injections per time-step */ + /*! Number of thermal energy injections per time-step */ int AGN_number_of_energy_injections; - /*! Number of energy injections per time-step */ + /*! Number of thermal energy injections per time-step */ int AGN_number_of_jet_injections; - /*! Change in energy from SNII feedback energy injection */ + /*! Change in energy from thermal AGN energy injection */ float AGN_delta_u_jet; } to_distribute; diff --git a/src/cell.c b/src/cell.c index 11a2ed229a0c2148229075d37165696da4fb65a2..929a2dd00de9f5aa506495ffcfbcc62a1bf150ad 100644 --- a/src/cell.c +++ b/src/cell.c @@ -1068,6 +1068,9 @@ void cell_clean(struct cell *c) { /* Stars */ cell_free_stars_sorts(c); + /* Grid */ + cell_free_grid(c); + /* Recurse */ for (int k = 0; k < 8; k++) if (c->progeny[k]) cell_clean(c->progeny[k]); diff --git a/src/cell.h b/src/cell.h index 8c779b5758a17eb49f75228c0d4dd9d366a7ee22..386b51e28616ec9f5bbd280d9ec080b827058dd3 100644 --- a/src/cell.h +++ b/src/cell.h @@ -35,6 +35,7 @@ #include "align.h" #include "cell_black_holes.h" #include "cell_grav.h" +#include "cell_grid.h" #include "cell_hydro.h" #include "cell_rt.h" #include "cell_sinks.h" @@ -211,6 +212,12 @@ struct pcell { } sinks; + /*! Grid variables */ + struct { + /*! self complete flag */ + enum grid_completeness self_completeness; + } grid; + /*! RT variables */ struct { @@ -435,6 +442,9 @@ struct cell { /*! Sink particles variables */ struct cell_sinks sinks; + /*! The grid variables */ + struct cell_grid grid; + /*! Radiative transfer variables */ struct cell_rt rt; @@ -583,6 +593,10 @@ void cell_unpack_sink_swallow(struct cell *c, const struct sink_sink_data *data); int cell_pack_tags(const struct cell *c, int *tags); int cell_unpack_tags(const int *tags, struct cell *c); +int cell_pack_grid_extra(const struct cell *c, + enum grid_construction_level *info); +int cell_unpack_grid_extra(const enum grid_construction_level *info, + struct cell *c, struct cell *construction_level); int cell_pack_end_step(const struct cell *c, struct pcell_step *pcell); int cell_unpack_end_step(struct cell *c, const struct pcell_step *pcell); void cell_pack_timebin(const struct cell *const c, timebin_t *const t); @@ -683,6 +697,13 @@ void cell_activate_limiter(struct cell *c, struct scheduler *s); void cell_clear_drift_flags(struct cell *c, void *data); void cell_clear_limiter_flags(struct cell *c, void *data); void cell_set_super_mapper(void *map_data, int num_elements, void *extra_data); +void cell_grid_update_self_completeness(struct cell *c, int force); +void cell_set_grid_completeness_mapper(void *map_data, int num_elements, + void *extra_data); +void cell_set_grid_construction_level_mapper(void *map_data, int num_elements, + void *extra_data); +void cell_grid_set_self_completeness_mapper(void *map_data, int num_elements, + void *extra_data); void cell_check_spart_pos(const struct cell *c, const struct spart *global_sparts); void cell_check_sort_flags(const struct cell *c); @@ -1044,6 +1065,45 @@ cell_need_rebuild_for_hydro_pair(const struct cell *ci, const struct cell *cj) { return 0; } +/** + * @brief Have gas particles in a pair of cells moved too much, invalidating the + * completeness criterion for #ci? + * + * This function returns true the grid completeness criterion from the + * perspective of ci (the #cell for which the grid will be constructed) is no + * longer valid. + * + * NOTE: This function assumes that the self_completeness flags are up to date. + * NOTE: This whether a cell needs a rebuild for a grid construction pair is an + * asymmetric property, so it should always be checked both ways! + * + * @param ci The first #cell. This is the cell for which the grid will be + * constructed. + * @param cj The second #cell. This is the neighbouring cell whose particles are + * used as ghost particles. + * @return Whether completeness of #ci is invalidated by the pair (#ci, #cj). + */ +__attribute__((always_inline, nonnull)) INLINE static int +cell_grid_pair_invalidates_completeness(struct cell *ci, struct cell *cj) { + + /* Check completeness criteria */ + /* NOTE: Both completeness flags should already be updated at this point */ + const int ci_self_complete = ci->grid.self_completeness == grid_complete; + const int cj_self_complete = cj->grid.self_completeness == grid_complete; + if (!ci_self_complete) return 1; +#ifdef SHADOWSWIFT_RELAXED_COMPLETENESS + /* If if ci is self-complete and cj is not, but its maximal search radius is + * sufficiently small, we still consider the pair (ci, cj) complete. + * NOTE: ci->dmin == cj->dmin */ + if (!cj_self_complete && kernel_gamma * ci->hydro.h_max > 0.5 * cj->dmin) + return 1; +#else + if (!ci_self_complete || !cj_self_complete) return 1; +#endif + + return 0; +} + /** * @brief Have star particles in a pair of cells moved too much and require a * rebuild? @@ -1365,6 +1425,32 @@ cell_get_stars_sorts(const struct cell *c, const int sid) { return &c->stars.sort[j * (c->stars.count + 1)]; } +/** + * @brief Free grid memory for cell. + * + * @param c The #cell. + */ +__attribute__((always_inline)) INLINE static void cell_free_grid( + struct cell *c) { + +#ifndef MOVING_MESH + /* Nothing to do as we have no tessellations */ +#else +#ifdef SWIFT_DEBUG_CHECKS + if (c->grid.construction_level != c && c->grid.voronoi != NULL) + error("Grid allocated, but not on grid construction level!"); +#endif + if (c->grid.construction_level == c) { + if (c->grid.voronoi != NULL) { + voronoi_destroy(c->grid.voronoi); + c->grid.voronoi = NULL; + } + } +#endif +} + +void cell_free_grid_rec(struct cell *c); + /** * @brief Set the given flag for the given cell. */ diff --git a/src/cell_grid.c b/src/cell_grid.c new file mode 100644 index 0000000000000000000000000000000000000000..313d3843a30d1ffc31a9ee4fd8365b37fdc25207 --- /dev/null +++ b/src/cell_grid.c @@ -0,0 +1,442 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2024 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Yolan Uyttenhove (Yolan.Uyttenhove@UGent.be) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include <config.h> + +/* Corresponding header */ +#include "cell_grid.h" + +/* Local headers */ +#include "engine.h" +#include "error.h" +#include "space_getsid.h" + +/** + * @brief Recursively free grid memory for cell. + * + * @param c The #cell. + */ +void cell_free_grid_rec(struct cell *c) { + +#ifndef MOVING_MESH + /* Nothing to do as we have no tessellations */ +#else +#ifdef SWIFT_DEBUG_CHECKS + if (c->grid.construction_level != c && c->grid.voronoi != NULL) + error("Grid allocated, but not on grid construction level!"); +#endif + if (c->grid.construction_level == NULL) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) cell_free_grid_rec(c->progeny[k]); + + } else if (c->grid.construction_level == c) { + cell_free_grid(c); + } else if (c->grid.construction_level != c) { + error("Somehow ended up below grid construction level!"); + } +#endif +} + +/** + * @brief Updates the grid.self_completeness flag on this cell and its + * sub-cells. + * + * This cell satisfies the local completeness criterion for the Voronoi grid. + * + * A cell is defined as locally complete if, when we would split that cell in + * thirds along each dimension (i.e. in 27 smaller cells), every small cube + * would contain at least one particle. + * + * If a given cell and its direct neighbours on the same level in the AMR tree + * are self-complete, the Voronoi grid of that cell can completely be + * constructed using only particles of this cell and its direct neighbours, + * i.e. by doing a normal SWIFT neighbour loop. + * + * @param c The #cell to be checked + * @param force Whether to forcefully recompute the completeness if it was not + * invalidated. + * */ +void cell_grid_update_self_completeness(struct cell *c, int force) { + if (c == NULL) return; + if (!force && (c->grid.self_completeness != grid_invalidated_completeness)) + return; + + if (c->split) { + int all_complete = 1; + + /* recurse */ + for (int i = 0; all_complete && i < 8; i++) { + if (c->progeny[i] != NULL) { + cell_grid_update_self_completeness(c->progeny[i], force); + /* As long as all progeny is complete, this cell can safely be split for + * the grid construction (when not considering neighbouring cells) */ + all_complete &= + (c->progeny[i]->grid.self_completeness == grid_complete); + } + } + + /* If all sub-cells are complete, this cell is also complete. */ + if (all_complete) { + c->grid.self_completeness = grid_complete; + /* We set complete to true for now */ + c->grid.complete = 1; + /* We are done here */ + return; + } + } + + /* If this cell is not split, or not all subcells are complete, we need to + * check if this cell is complete by looping over all the particles. */ + + /* criterion = 0b111_111_111_111_111_111_111_111_111*/ +#ifdef HYDRO_DIMENSION_1D + const int criterion = (1 << 3) - 1; +#elif defined(HYDRO_DIMENSION_2D) + const int criterion = (1 << 9) - 1; +#elif defined(HYDRO_DIMENSION_3D) + const int criterion = (1 << 27) - 1; +#else +#error "Unknown hydro dimension" +#endif + int flags = 0; + for (int i = 0; flags != criterion && i < c->hydro.count; i++) { + struct part *p = &c->hydro.parts[i]; + int x_bin = (int)(3. * (p->x[0] - c->loc[0]) / c->width[0]); + int y_bin = (int)(3. * (p->x[1] - c->loc[1]) / c->width[1]); + int z_bin = (int)(3. * (p->x[2] - c->loc[2]) / c->width[2]); + if (x_bin >= 0 && x_bin < 3 && y_bin >= 0 && y_bin < 3 && z_bin >= 0 && + z_bin < 3) { + flags |= 1 << (x_bin + 3 * y_bin + 9 * z_bin); + } + } + + /* Set completeness flags accordingly */ + if (flags == criterion) { + c->grid.self_completeness = grid_complete; + c->grid.complete = 1; + } else { + c->grid.self_completeness = grid_incomplete; + c->grid.complete = 0; + } +} + +/** + * @brief (mapper) Updates the grid.self_completeness flag on this cell and its + * sub-cells. + * + * This function recomputes the self_completeness flag for all cells containing + * particles. + */ +void cell_grid_set_self_completeness_mapper(void *map_data, int num_elements, + void *extra_data) { + /* Extract the engine pointer. */ + struct engine *e = (struct engine *)extra_data; + + struct space *s = e->s; + const int nodeID = e->nodeID; + struct cell *cells = s->cells_top; + + /* Loop through the elements, which are just byte offsets from NULL. */ + for (int ind = 0; ind < num_elements; ind++) { + /* Get the cell index. */ + const int cid = (size_t)(map_data) + ind; + + /* Get the cell */ + struct cell *c = &cells[cid]; + + /* A top level cell can be empty in 1D and 2D simulations, just skip it */ + if (c->hydro.count == 0) { + continue; +#ifdef SWIFT_DEBUG_CHECKS + if (hydro_dimension == 3) + error("Found empty top-level cell while running in 3D!"); +#endif + } + if (c->nodeID != nodeID) continue; + + /* Set the splittable attribute for the moving mesh */ + cell_grid_update_self_completeness(c, /*force*/ 1); + } +} + +/** + * @brief Updates the grid.completeness flag for the given pair of cells and all + * recursive pairs of sub-cells. + * + * If one of the cells of the pair is not self-complete, or requires a rebuild, + * we mark the other cell in the pair as incomplete (it cannot construct its + * Voronoi grid on that level). + * + * + * + * @param ci The first #cell of the pair to be checked. + * @param cj The second #cell of the pair to be checked. If NULL, only check + * pairs of subcells from #ci. + * @param sid The sort_id (direction) of the pair. + * @param e The #engine. + * */ +void cell_grid_set_pair_completeness(struct cell *restrict ci, + struct cell *restrict cj, int sid, + const struct engine *e) { + + int ci_local = ci->nodeID == e->nodeID; + int cj_local = cj != NULL ? cj->nodeID == e->nodeID : 0; + /* Anything to do here? */ + if (!ci_local && !cj_local) return; + + /* Self or pair? */ + if (cj == NULL) { + /* Self: Here we just need to recurse to hit all the pairs of sub-cells */ + if (ci->split) { + /* recurse */ + for (int k = 0; k < 7; k++) { + if (ci->progeny[k] != NULL) { + /* Self: Recurse for pairs of sub-cells of this sub-cell */ + cell_grid_set_pair_completeness(ci->progeny[k], NULL, 0, e); + + /* Recurse for pairs of sub-cells */ + for (int l = k + 1; l < 8; l++) { + if (ci->progeny[l] != NULL) { + /* Get sid for pair */ + int sid_sub = sub_sid_flag[k][l]; + /* Pair: Recurse for pairs of sub-cells of this pair of sub-cells + */ + cell_grid_set_pair_completeness(ci->progeny[k], ci->progeny[l], + sid_sub, e); + } + } + } + } + } + } else { + /* pair: Here we need to recurse further AND check whether one of the + * neighbouring cells invalidates the completeness of the other. */ + struct cell_split_pair pairs = cell_split_pairs[sid]; + if (ci->split && cj->split) { + /* recurse */ + for (int i = 0; i < pairs.count; i++) { + struct cell *ci_sub = ci->progeny[pairs.pairs[i].pid]; + struct cell *cj_sub = cj->progeny[pairs.pairs[i].pjd]; + if (ci_sub == NULL || cj_sub == NULL) continue; + double shift[3]; + int sid_sub = + space_getsid_and_swap_cells(e->s, &ci_sub, &cj_sub, shift); +#ifdef SWIFT_DEBUG_CHECKS + assert(sid_sub == pairs.pairs[i].sid); +#endif + cell_grid_set_pair_completeness(ci_sub, cj_sub, sid_sub, e); + } + } else if (!ci->split && cj->split) { + /* Set the completeness for the sub-cells of cj for this sid to 0 (they + * have no neighbouring cell on the same level for this SID) */ + for (int i = 0; i < pairs.count; i++) { + int l = pairs.pairs[i].pjd; + if (cj->progeny[l] != NULL) cj->progeny[l]->grid.complete = 0; + } + } else if (!cj->split && ci->split) { + /* Set the completeness for the sub-cells of ci for this sid to 0 (they + * have no neighbouring cell on the same level for this SID) */ + for (int i = 0; i < pairs.count; i++) { + int k = pairs.pairs[i].pid; + if (ci->progeny[k] != NULL) ci->progeny[k]->grid.complete = 0; + } + } + + /* Update these cells' completeness flags (i.e. check whether the + * neighbouring cell invalidates completeness) + * We need to use atomics here, since multiple threads may change this at + * the same time. */ + if (ci_local) { + atomic_and(&ci->grid.complete, + !cell_grid_pair_invalidates_completeness(ci, cj)); + } + if (cj_local) { + atomic_and(&cj->grid.complete, + !cell_grid_pair_invalidates_completeness(cj, ci)); + } + } +} + +/** + * @brief (mapper) Sets the grid.completeness flag for all cells, by looping + * aggregating the self_completeness flags of all neighbours of each cell. + * + * A cell is considered complete if it and all its neighbours are + * self_complete. The Voronoi grid may be constructed at any level in the AMR + * tree as long as the cell at that level is complete. + * */ +void cell_set_grid_completeness_mapper(void *map_data, int num_elements, + void *extra_data) { + /* Extract the engine pointer. */ + struct engine *e = (struct engine *)extra_data; + const int periodic = e->s->periodic; + + struct space *s = e->s; + const int nodeID = e->nodeID; + const int *cdim = s->cdim; + struct cell *cells = s->cells_top; + + /* Loop through the elements, which are just byte offsets from NULL, to set + * the neighbour flags. */ + for (int ind = 0; ind < num_elements; ind++) { + /* Get the cell index. */ + const int cid = (size_t)(map_data) + ind; + + /* Integer indices of the cell in the top-level grid */ + const int i = cid / (cdim[1] * cdim[2]); + const int j = (cid / cdim[2]) % cdim[1]; + const int k = cid % cdim[2]; + + /* Get the cell */ + struct cell *ci = &cells[cid]; + + /* Anything to do here? */ + if (ci->hydro.count == 0) continue; + + const int ci_local = ci->nodeID == nodeID; + + /* Update completeness for all the pairs of sub cells of this cell */ + if (ci_local) cell_grid_set_pair_completeness(ci, NULL, 0, e); + + /* Now loop over all the neighbours of this cell to also update the + * completeness for pairs with this cell and all pairs of sub-cells */ + for (int ii = -1; ii < 2; ii++) { + int iii = i + ii; + if (!periodic && (iii < 0 || iii >= cdim[0])) continue; + iii = (iii + cdim[0]) % cdim[0]; + for (int jj = -1; jj < 2; jj++) { + int jjj = j + jj; + if (!periodic && (jjj < 0 || jjj >= cdim[1])) continue; + jjj = (jjj + cdim[1]) % cdim[1]; + for (int kk = -1; kk < 2; kk++) { + int kkk = k + kk; + if (!periodic && (kkk < 0 || kkk >= cdim[2])) continue; + kkk = (kkk + cdim[2]) % cdim[2]; + + /* Get the neighbouring cell */ + const int cjd = cell_getid(cdim, iii, jjj, kkk); + struct cell *cj = &cells[cjd]; + + /* Treat pairs only once. */ + const int cj_local = cj->nodeID == nodeID; + if (cid >= cjd || cj->hydro.count == 0 || (!ci_local && !cj_local)) + continue; + + /* Update the completeness flag for this pair of cells and all pair of + * sub-cells */ + int sid = (kk + 1) + 3 * ((jj + 1) + 3 * (ii + 1)); + const int flip = runner_flip[sid]; + sid = sortlistID[sid]; + if (flip) { + cell_grid_set_pair_completeness(cj, ci, sid, e); + } else { + cell_grid_set_pair_completeness(ci, cj, sid, e); + } + } + } + } /* Now loop over all the neighbours of this cell */ + } /* Loop through the elements, which are just byte offsets from NULL. */ +} + +/** + * @brief Select a suitable construction level for the Voronoi grid. + * + * The Voronoi grid is constructed at the lowest level for which the cell + * has more than #space_grid_split_threshold hydro particles *and* is marked + * as complete for the Voronoi construction. + * + * Cells below the construction level store a pointer to its higher level cell + * at the construction level. + * + * @param c The #cell + * @param construction_level NULL, if we are yet to encounter the suitable + * construction level, or a pointer to the parent-cell of #c at the construction + * level. + */ +void cell_set_grid_construction_level(struct cell *c, + struct cell *construction_level) { + + /* Above construction level? */ + if (construction_level == NULL) { + /* Check if we can split this cell (i.e. all sub-cells are complete) */ + int splittable = c->split && c->hydro.count > space_grid_split_threshold; + if (!c->grid.complete) { + /* Are we on the top level? */ + if (c->top == c) { + warning("Found incomplete top level cell!"); + splittable = 0; + } else { + error("Found incomplete cell above construction level!"); + } + } + for (int k = 0; splittable && k < 8; k++) { + if (c->progeny[k] != NULL) splittable &= c->progeny[k]->grid.complete; + } + + if (!splittable) { + /* This is the first time we encounter an unsplittable cell, meaning that + * it has too few particles to be split further or one of its progenitors + * is not complete. I.e. we have arrived at the construction level! */ + construction_level = c; + } + } + + /* Set the construction level of this cell */ + c->grid.construction_level = construction_level; + + /* Recurse. */ + if (c->split) + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) + cell_set_grid_construction_level(c->progeny[k], construction_level); + } +} + +void cell_set_grid_construction_level_mapper(void *map_data, int num_elements, + void *extra_data) { + /* Extract the engine pointer. */ + struct engine *e = (struct engine *)extra_data; + + struct space *s = e->s; + const int nodeID = e->nodeID; + struct cell *cells = s->cells_top; + + /* Loop through the elements, which are just byte offsets from NULL, to set + * the neighbour flags. */ + for (int ind = 0; ind < num_elements; ind++) { + + /* Get the cell index. */ + const int cid = (size_t)(map_data) + ind; + + /* Get the cell */ + struct cell *ci = &cells[cid]; + + /* Anything to do here? */ + if (ci->hydro.count == 0) continue; + const int ci_local = ci->nodeID == nodeID; + + /* This cell's completeness flags are now set all the way down the cell + * hierarchy. We can now set the construction level. */ + if (ci_local) { + cell_set_grid_construction_level(ci, NULL); + } + } +} \ No newline at end of file diff --git a/src/cell_grid.h b/src/cell_grid.h new file mode 100644 index 0000000000000000000000000000000000000000..87e8ec7c9fc8d15af19ec6dc72643374bce02fbc --- /dev/null +++ b/src/cell_grid.h @@ -0,0 +1,104 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2024 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Yolan Uyttenhove (Yolan.Uyttenhove@UGent.be) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +#ifndef SWIFTSIM_CELL_GRID_H +#define SWIFTSIM_CELL_GRID_H + +/* Config parameters. */ +#include <config.h> + +/* Includes. */ +#include <stddef.h> + +/* Local includes */ +#include "const.h" +#include "shadowswift/voronoi.h" +#include "timeline.h" + +/*! @brief Enum indicating the completeness for the Voronoi mesh of this cell. + * + * A cell is considered complete when it and its neighbours on the same level in + * the AMR have at least one particle in every 1/27th cube of the cell (obtained + * by dividing cells in three along all axes). + * + * The Voronoi grid can safely be constructed on any level where the cell is + * complete. */ +enum grid_completeness { + grid_invalidated_completeness = 0, + grid_complete, + grid_incomplete, +}; + +struct cell_grid { + /*! Pointer to parent where the grid is constructed. */ + struct cell *construction_level; + + /*! Pointer to shallowest parent of this cell used in any pair construction + * task. Can be above the construction level of this cell. We need to drift at + * this level. */ + struct cell *super; + + /*! Whether this cell is complete (at least one particle in all 27 sub-cells + * if this cell is divided in thirds along each axis). */ + enum grid_completeness self_completeness; + + /*! Whether this cell is itself complete and has directly neighbouring cell + * on the same level in all directions which are also complete. */ + int complete; + +#ifdef WITH_MPI + /*! Flags indicating whether we should send the faces for the corresponding + * SIDs over MPI */ + int send_flags; +#endif + + /*! Pointer to the voronoi struct of this cell (if any) */ + struct voronoi *voronoi; + + /*! Pointer to this cells construction task. */ + struct task *construction; + + /*! Linked list of this cells outgoing construction synchronization tasks + * (i.e. for cells that need this cell for their construction task) */ + struct link *sync_out; + + /*! Linked list of this cells incoming construction synchronization tasks + * (i.e. cells needed for this cell's construction task) */ + struct link *sync_in; + + /*! Time of last construction */ + integertime_t ti_old; +}; + +struct pcell_faces { + size_t counts[27]; + + struct voronoi_pair faces[]; +}; + +/*! @brief Enum used to indicate whether a cell is above, below or on the + * construction level. Only used in the packed cell representation */ +enum grid_construction_level { + grid_above_construction_level, + grid_on_construction_level, + grid_below_construction_level +}; + +#endif // SWIFTSIM_CELL_GRID_H diff --git a/src/cell_pack.c b/src/cell_pack.c index f647291d917c710401022c33cc835b01ee5d984c..8101ad8fbe352bd6ba93744beee696000236959c 100644 --- a/src/cell_pack.c +++ b/src/cell_pack.c @@ -68,6 +68,8 @@ int cell_pack(struct cell *restrict c, struct pcell *restrict pc, pc->black_holes.count = c->black_holes.count; pc->maxdepth = c->maxdepth; + pc->grid.self_completeness = c->grid.self_completeness; + /* Copy the Multipole related information */ if (with_gravity) { const struct gravity_tensors *mp = c->grav.multipole; @@ -140,6 +142,47 @@ int cell_pack_tags(const struct cell *c, int *tags) { #endif } +/** + * @brief Pack the extra grid info of the given cell and all it's sub-cells. + * + * @param c The #cell. + * @param info Pointer to an array of packed extra grid info (construction + * levels). + * + * @return The number of packed tags. + */ +int cell_pack_grid_extra(const struct cell *c, + enum grid_construction_level *info) { +#ifdef WITH_MPI + + /* Start by packing the construction level of the current cell. */ + if (c->grid.construction_level == NULL) { + info[0] = grid_above_construction_level; + } else if (c->grid.construction_level == c) { + info[0] = grid_on_construction_level; + } else { + info[0] = grid_below_construction_level; + } + + /* Fill in the progeny, depth-first recursion. */ + int count = 1; + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) + count += cell_pack_grid_extra(c->progeny[k], &info[count]); + +#ifdef SWIFT_DEBUG_CHECKS + if (c->mpi.pcell_size != count) error("Inconsistent info and pcell count!"); +#endif // SWIFT_DEBUG_CHECKS + + /* Return the number of packed tags used. */ + return count; + +#else + error("SWIFT was not compiled with MPI support."); + return 0; +#endif +} + void cell_pack_part_swallow(const struct cell *c, struct black_holes_part_data *data) { @@ -271,6 +314,8 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c, c->black_holes.count = pc->black_holes.count; c->maxdepth = pc->maxdepth; + c->grid.self_completeness = pc->grid.self_completeness; + #ifdef SWIFT_DEBUG_CHECKS c->cellID = pc->cellID; #endif @@ -375,6 +420,62 @@ int cell_unpack_tags(const int *tags, struct cell *restrict c) { #endif } +/** + * @brief Unpack the extra grid info of a given cell and its sub-cells. + * + * @param tags An array of extra grid info (construction levels). + * @param c The #cell in which to unpack the tags. + * + * @return The number of tags created. + */ +int cell_unpack_grid_extra(const enum grid_construction_level *info, + struct cell *c, struct cell *construction_level) { +#ifdef WITH_MPI + + /* Unpack the current pcell. */ + if (info[0] == grid_on_construction_level) { + construction_level = c; + } else if (info[0] == grid_above_construction_level) { +#ifdef SWIFT_DEBUG_CHECKS + if (construction_level != NULL) + error( + "Above construction level, but construction level cell pointer is " + "not NULL!"); +#endif + } else { +#ifdef SWIFT_DEBUG_CHECKS + if (info[0] != grid_below_construction_level) + error("Invalid construction level!"); + if (construction_level == NULL || construction_level == c) + error("Invalid construction level cell pointer!"); +#endif + } + + c->grid.construction_level = construction_level; + + /* Number of new cells created. */ + int count = 1; + + /* Fill the progeny recursively, depth-first. */ + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) { + count += cell_unpack_grid_extra(&info[count], c->progeny[k], + construction_level); + } + +#ifdef SWIFT_DEBUG_CHECKS + if (c->mpi.pcell_size != count) error("Inconsistent info and pcell count!"); +#endif // SWIFT_DEBUG_CHECKS + + /* Return the total number of unpacked tags. */ + return count; + +#else + error("SWIFT was not compiled with MPI support."); + return 0; +#endif +} + /** * @brief Pack the cell information about time-step sizes and displacements * of a cell hierarchy. diff --git a/src/cell_unskip.c b/src/cell_unskip.c index 15ae2f3ffd660a66ae8c1b92395180b89f1ea919..26422815a74a171cfcb8c5286d2600efc4d12c5f 100644 --- a/src/cell_unskip.c +++ b/src/cell_unskip.c @@ -3302,7 +3302,7 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s, scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rt_transport); } } else if (ci_active) { -#ifdef MPI_SYMMETRIC_FORCE_INTERACTION +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION_RT /* If the local cell is inactive and the remote cell is active, we * still need to receive stuff to be able to do the force interaction * on this node as well. @@ -3326,7 +3326,7 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s, ci_nodeID); } } else if (cj_active) { -#ifdef MPI_SYMMETRIC_FORCE_INTERACTION +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION_RT /* If the foreign cell is inactive, but the local cell is active, * we still need to send stuff to be able to do the force interaction * on both nodes. @@ -3355,7 +3355,7 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s, scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rt_transport); } } else if (cj_active) { -#ifdef MPI_SYMMETRIC_FORCE_INTERACTION +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION_RT /* If the local cell is inactive and the remote cell is active, we * still need to receive stuff to be able to do the force interaction * on this node as well. @@ -3379,7 +3379,7 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s, cj_nodeID); } } else if (ci_active) { -#ifdef MPI_SYMMETRIC_FORCE_INTERACTION +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION_RT /* If the foreign cell is inactive, but the local cell is active, * we still need to send stuff to be able to do the force interaction * on both nodes @@ -3440,7 +3440,7 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s, if (c->rt.rt_tchem != NULL) scheduler_activate(s, c->rt.rt_tchem); if (c->rt.rt_out != NULL) scheduler_activate(s, c->rt.rt_out); } else { -#if defined(MPI_SYMMETRIC_FORCE_INTERACTION) && defined(WITH_MPI) +#if defined(MPI_SYMMETRIC_FORCE_INTERACTION_RT) && defined(WITH_MPI) /* Additionally unskip force interactions between inactive local cell and * active remote cell. (The cell unskip will only be called for active * cells, so, we have to do this now, from the active remote cell). */ diff --git a/src/chemistry/GEAR/chemistry.h b/src/chemistry/GEAR/chemistry.h index a1818ff61e85b3fcf798ba08152351c4d00f58a8..304809fe0b44fc2bd39fdd4bea00495fd4f24431 100644 --- a/src/chemistry/GEAR/chemistry.h +++ b/src/chemistry/GEAR/chemistry.h @@ -502,7 +502,7 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_sink( * * @param si_data The black hole data to add to. * @param sj_data The gas data to use. - * @param gas_mass The mass of the gas particle. + * @param mi_old The mass of the #sink i before accreting the #part p. */ __attribute__((always_inline)) INLINE static void chemistry_add_sink_to_sink( struct sink* si, const struct sink* sj, const double mi_old) { @@ -520,7 +520,7 @@ __attribute__((always_inline)) INLINE static void chemistry_add_sink_to_sink( * * @param sp_data The sink data to add to. * @param p_data The gas data to use. - * @param gas_mass The mass of the gas particle. + * @param ms_old The mass of the #sink before accreting the #part p. */ __attribute__((always_inline)) INLINE static void chemistry_add_part_to_sink( struct sink* s, const struct part* p, const double ms_old) { diff --git a/src/const.h b/src/const.h index 9cf25776e40b324dc2399bf7ce7ca58b41aa00fa..75be9963f5a87182a7689ba43fb923fe79783075 100644 --- a/src/const.h +++ b/src/const.h @@ -81,22 +81,10 @@ gradient matrix and use SPH gradients instead. */ #define const_gizmo_min_wcorr 0.5f -/* Types of gradients to use for SHADOWFAX_SPH */ -/* If no option is chosen, no gradients are used (first order scheme) */ -#define SHADOWFAX_GRADIENTS - -/* SHADOWFAX_SPH slope limiters */ -#define SHADOWFAX_SLOPE_LIMITER_PER_FACE -#define SHADOWFAX_SLOPE_LIMITER_CELL_WIDE - -/* Options to control SHADOWFAX_SPH */ -/* This option disables cell movement */ -// #define SHADOWFAX_FIX_CELLS -/* This option enables cell steering, i.e. trying to keep the cells regular by - adding a correction to the cell velocities.*/ -#define SHADOWFAX_STEER_CELL_MOTION -/* This option evolves the total energy instead of the thermal energy */ -// #define SHADOWFAX_TOTAL_ENERGY +/* Options controlling ShadowSWIFT */ +/* Options controlling acceleration strategies*/ +/*! @brief Option enabling a more relaxed completeness criterion */ +#define SHADOWSWIFT_RELAXED_COMPLETENESS /* Source terms */ #define SOURCETERMS_NONE diff --git a/src/cooling/grackle/cooling.c b/src/cooling/grackle/cooling.c index 1526fd65003241c9cb7d7c41612c307c322b1ab4..205a7b4f96ca663fbf0ee540febb38d4f5479488 100644 --- a/src/cooling/grackle/cooling.c +++ b/src/cooling/grackle/cooling.c @@ -218,11 +218,17 @@ void cooling_first_init_part(const struct phys_const* phys_const, /* Compute nH (formally divided by the gas density and assuming the proton * mass to be one) */ - double nH = grackle_data->HydrogenFractionByMass; + double nH = 1.0; + if (grackle_data != NULL) { + nH = grackle_data->HydrogenFractionByMass; + } /* Compute nHe (formally divided by the gas density and assuming the proton * mass to be one) */ - double nHe = (1.f - grackle_data->HydrogenFractionByMass) / 4.f; + double nHe = 0.0; + if (grackle_data != NULL) { + nHe = (1.f - grackle_data->HydrogenFractionByMass) / 4.f; + } /* Electron density */ double ne = zero; @@ -330,9 +336,12 @@ void cooling_first_init_part(const struct phys_const* phys_const, if (cooling->initial_nDI_to_nH_ratio >= 0.f) { double nDI = nH * cooling->initial_nDI_to_nH_ratio; xp->cooling_data.DI_frac = nDI * 2.f; - } else - xp->cooling_data.DI_frac = grackle_data->DeuteriumToHydrogenRatio * - grackle_data->HydrogenFractionByMass; + } else { + if (grackle_data != NULL) { + xp->cooling_data.DI_frac = grackle_data->DeuteriumToHydrogenRatio * + grackle_data->HydrogenFractionByMass; + } + } Xtot += xp->cooling_data.DI_frac; diff --git a/src/cosmology.c b/src/cosmology.c index 269c808e07d46b38e5b7364823973e7b4aaf5f79..ef98cdb815890a7401870b3520bcca0bf8196668 100644 --- a/src/cosmology.c +++ b/src/cosmology.c @@ -832,11 +832,10 @@ void cosmology_init_tables(struct cosmology *c) { if (frac_error > max_error_distance) max_error_distance = frac_error; } - message("Max fractional error in a to age of universe round trip = %16.8e\n", + message("Max fractional error in a to age of universe round trip = %16.8e", max_error_time); - message( - "Max fractional error in a to comoving distance round trip = %16.8e\n", - max_error_distance); + message("Max fractional error in a to comoving distance round trip = %16.8e", + max_error_distance); #endif /* SWIFT_DEBUG_CHECKS */ diff --git a/src/debug.c b/src/debug.c index 978da76415b20caf0724f7c4328a8d431bf16aa8..a82363365c9004d27ca6b8fbb007e46d9fa89e64 100644 --- a/src/debug.c +++ b/src/debug.c @@ -70,7 +70,7 @@ #include "./hydro/Phantom/hydro_debug.h" #elif defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) #include "./hydro/Gizmo/hydro_debug.h" -#elif defined(SHADOWFAX_SPH) +#elif defined(SHADOWSWIFT) #include "./hydro/Shadowswift/hydro_debug.h" #elif defined(PLANETARY_SPH) #include "./hydro/Planetary/hydro_debug.h" diff --git a/src/engine.c b/src/engine.c index 134775dc9673bba3347bcbbf82a6741137ffda8c..e25c11e366906a6516138adf60ee135bf7c2330d 100644 --- a/src/engine.c +++ b/src/engine.c @@ -134,7 +134,9 @@ const char *engine_policy_names[] = {"none", "line of sight", "sink", "rt", - "power spectra"}; + "power spectra", + "moving mesh", + "moving mesh hydro"}; const int engine_default_snapshot_subsample[swift_type_count] = {0}; @@ -495,6 +497,34 @@ void engine_exchange_cells(struct engine *e) { #endif } +/** + * @brief Exchange extra information for the grid construction with other nodes. + * + * @param e The #engine. + */ +void engine_exchange_grid_extra(struct engine *e) { + +#ifdef WITH_MPI + +#ifdef SWIFT_DEBUG_CHECKS + if (!(e->policy & engine_policy_grid)) + error("Not running with grid, but trying to exchange grid information!"); +#endif + + const ticks tic = getticks(); + + /* Exchange the grid info with neighbouring ranks. */ + proxy_grid_extra_exchange(e->proxies, e->nr_proxies, e->s); + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); + +#else + error("SWIFT was not compiled with MPI support."); +#endif +} + /** * @brief Exchanges the top-level multipoles between all the nodes * such that every node has a multipole for each top-level cell. @@ -751,7 +781,8 @@ void engine_allocate_foreign_particles(struct engine *e, const int fof) { #ifdef WITH_MPI const int nr_proxies = e->nr_proxies; - const int with_hydro = e->policy & engine_policy_hydro; + const int with_hydro = + e->policy & (engine_policy_hydro | engine_policy_grid_hydro); const int with_stars = e->policy & engine_policy_stars; const int with_black_holes = e->policy & engine_policy_black_holes; const int with_sinks = e->policy & engine_policy_sinks; @@ -1191,6 +1222,35 @@ int engine_estimate_nr_tasks(const struct engine *e) { n1 += 39; #ifdef WITH_MPI n1 += 2; +#endif + } + if (e->policy & engine_policy_grid) { + /* Grid construction: 1 self + 26 (asymmetric) pairs + 1 ghost + 1 sort */ + n1 += 29; + n2 += 3; +#ifdef WITH_MPI + n1 += 3; +#endif + } + if (e->policy & engine_policy_grid_hydro) { + /* slope estimate: 1 self + 13 pairs (on average) | 14 + * others: 1 ghosts, 2 kicks, 1 drift, 1 timestep | + 7 + * Total: = 21 */ + n1 += 21; + n2 += 2; +#ifdef EXTRA_HYDRO_LOOP + /* slope limiter: 1 self + 13 pairs | + 14 + * flux: 1 self + 13 pairs | + 14 + * others: 2 ghost. | + 2 + * Total: = 30 */ + n1 += 30; + n2 += 3; +#endif +#ifdef WITH_MPI + n1 += 1; +#ifdef EXTRA_HYDRO_LOOP + n1 += 1; +#endif #endif } @@ -1369,6 +1429,12 @@ void engine_rebuild(struct engine *e, const int repartitioned, /* Initial cleaning up session ? */ if (clean_smoothing_length_values) space_sanitize(e->s); + /* Set the initial completeness flag for the moving mesh (before exchange) */ + if (e->policy & engine_policy_grid) { + cell_grid_set_self_completeness_mapper(e->s->cells_top, e->s->nr_cells, + NULL); + } + /* If in parallel, exchange the cell structure, top-level and neighbouring * multipoles. To achieve this, free the foreign particle buffers first. */ #ifdef WITH_MPI @@ -1392,8 +1458,27 @@ void engine_rebuild(struct engine *e, const int repartitioned, if (counter != e->total_nr_gparts) error("Total particles in multipoles inconsistent with engine"); } + if (e->policy & engine_policy_grid) { + for (int i = 0; i < e->s->nr_cells; i++) { + const struct cell *ci = &e->s->cells_top[i]; + if (ci->hydro.count > 0 && !(ci->grid.self_completeness == grid_complete)) + error("Encountered incomplete top level cell!"); + } + } #endif + /* Set the grid construction level, is needed before splitting the tasks */ + if (e->policy & engine_policy_grid) { + /* Set the completeness and construction level */ + threadpool_map(&e->threadpool, cell_set_grid_completeness_mapper, NULL, + e->s->nr_cells, 1, threadpool_auto_chunk_size, e); + threadpool_map(&e->threadpool, cell_set_grid_construction_level_mapper, + NULL, e->s->nr_cells, 1, threadpool_auto_chunk_size, e); +#ifdef WITH_MPI + engine_exchange_grid_extra(e); +#endif + } + /* Re-build the tasks. */ engine_maketasks(e); @@ -2108,6 +2193,10 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, if (e->nodeID == 0) message("Setting particles to a valid state..."); engine_first_init_particles(e); + /* Initialise the particle splitting mechanism */ + if (e->hydro_properties->particle_splitting) + engine_init_split_gas_particles(e); + if (e->nodeID == 0) message("Computing initial gas densities and approximate gravity."); @@ -3712,6 +3801,9 @@ void engine_recompute_displacement_constraint(struct engine *e) { /* Apply the dimensionless factor */ e->dt_max_RMS_displacement = dt * e->max_RMS_displacement_factor; + if (e->dt_max_RMS_displacement == 0.f) { + error("Setting dt_max_RMS_displacement to 0!"); + } if (e->verbose) message("max_dt_RMS_displacement = %e", e->dt_max_RMS_displacement); diff --git a/src/engine.h b/src/engine.h index f02832cfe6ffcebf065b576c066f7965d7986abe..4d1c128b293816b06ac6cd5b8e53dd3681a0c69e 100644 --- a/src/engine.h +++ b/src/engine.h @@ -89,8 +89,10 @@ enum engine_policy { engine_policy_sinks = (1 << 25), engine_policy_rt = (1 << 26), engine_policy_power_spectra = (1 << 27), + engine_policy_grid = (1 << 28), + engine_policy_grid_hydro = (1 << 29), }; -#define engine_maxpolicy 28 +#define engine_maxpolicy 30 extern const char *engine_policy_names[engine_maxpolicy + 1]; /** @@ -778,6 +780,7 @@ int engine_marktasks(struct engine *e); /* Function prototypes, engine_split_particles.c. */ void engine_split_gas_particles(struct engine *e); +void engine_init_split_gas_particles(struct engine *e); #ifdef HAVE_SETAFFINITY cpu_set_t *engine_entry_affinity(void); diff --git a/src/engine_maketasks.c b/src/engine_maketasks.c index 4984f6888a3fc71a13df43f6e110fded92f1dd96..1531cc6b6a8c0c192f0bf199d4467fef6e0018fe 100644 --- a/src/engine_maketasks.c +++ b/src/engine_maketasks.c @@ -1814,8 +1814,10 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c, if ((c->nodeID == e->nodeID) && (star_resort_cell == NULL) && (c->depth == engine_star_resort_task_depth || c->hydro.super == c)) { - /* Star formation */ - if (with_feedback && c->hydro.count > 0 && with_star_formation) { + /* If star formation from gas or sinks has happened, we need to resort */ + if (with_feedback && ((c->hydro.count > 0 && with_star_formation) || + ((c->hydro.count > 0 || c->sinks.count > 0) && + with_star_formation_sink))) { /* Record this is the level where we re-sort */ star_resort_cell = c; @@ -1823,22 +1825,20 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c, c->hydro.stars_resort = scheduler_addtask( s, task_type_stars_resort, task_subtype_none, 0, 0, c, NULL); - scheduler_addunlock(s, c->top->hydro.star_formation, - c->hydro.stars_resort); - } - - /* Star formation from sinks */ - if (with_feedback && with_star_formation_sink && - (c->hydro.count > 0 || c->sinks.count > 0)) { - - /* Record this is the level where we re-sort */ - star_resort_cell = c; - - c->hydro.stars_resort = scheduler_addtask( - s, task_type_stars_resort, task_subtype_none, 0, 0, c, NULL); - - scheduler_addunlock(s, c->top->sinks.star_formation_sink, - c->hydro.stars_resort); + /* Now add the relevant unlocks */ + /* If we can make stars, we should wait until SF is done before resorting + */ + if (with_star_formation && c->hydro.count > 0) { + scheduler_addunlock(s, c->top->hydro.star_formation, + c->hydro.stars_resort); + } + /* If we can make sinks or spawn from existing ones, we should wait until + SF is done before resorting */ + if (with_star_formation_sink && + (c->hydro.count > 0 || c->sinks.count > 0)) { + scheduler_addunlock(s, c->top->sinks.star_formation_sink, + c->hydro.stars_resort); + } } } @@ -2004,13 +2004,9 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c, scheduler_addunlock(s, c->stars.stars_out, c->super->timestep); /* Star formation*/ - if (with_feedback && c->hydro.count > 0 && with_star_formation) { - scheduler_addunlock(s, star_resort_cell->hydro.stars_resort, - c->stars.stars_in); - } - /* Star formation from sinks */ - if (with_feedback && with_star_formation_sink && - (c->hydro.count > 0 || c->sinks.count > 0)) { + if (with_feedback && ((c->hydro.count > 0 && with_star_formation) || + ((c->hydro.count > 0 || c->sinks.count > 0) && + with_star_formation_sink))) { scheduler_addunlock(s, star_resort_cell->hydro.stars_resort, c->stars.stars_in); } diff --git a/src/engine_marktasks.c b/src/engine_marktasks.c index 547415489e91a0553dbd269e2933e2e7fdfce089..1e597bac655ace879fb2fed55e98cc79955b610f 100644 --- a/src/engine_marktasks.c +++ b/src/engine_marktasks.c @@ -863,7 +863,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, (ci_nodeID == nodeID && cj_nodeID != nodeID && !ci_active_rt && cj_active_rt)) { -#if defined(WITH_MPI) && defined(MPI_SYMMETRIC_FORCE_INTERACTION) +#if defined(WITH_MPI) && defined(MPI_SYMMETRIC_FORCE_INTERACTION_RT) if (t_subtype == task_subtype_rt_transport) { scheduler_activate(s, t); @@ -1503,7 +1503,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, task_subtype_rt_transport); } } else if (ci_active_rt) { -#ifdef MPI_SYMMETRIC_FORCE_INTERACTION +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION_RT /* If the local cell is inactive and the remote cell is active, we * still need to receive stuff to be able to do the force * interaction on this node as well. */ @@ -1523,7 +1523,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, task_subtype_rt_transport, ci_nodeID); } } else if (cj_active_rt) { -#ifdef MPI_SYMMETRIC_FORCE_INTERACTION +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION_RT /* If the foreign cell is inactive, but the local cell is active, * we still need to send stuff to be able to do the force * interaction on both nodes */ @@ -1548,7 +1548,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, task_subtype_rt_transport); } } else if (cj_active_rt) { -#ifdef MPI_SYMMETRIC_FORCE_INTERACTION +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION_RT /* If the local cell is inactive and the remote cell is active, we * still need to receive stuff to be able to do the force * interaction on this node as well. */ @@ -1570,7 +1570,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, task_subtype_rt_transport, cj_nodeID); } } else if (ci_active_rt) { -#ifdef MPI_SYMMETRIC_FORCE_INTERACTION +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION_RT /* If the foreign cell is inactive, but the local cell is active, * we still need to send stuff to be able to do the force * interaction on both nodes */ diff --git a/src/engine_split_particles.c b/src/engine_split_particles.c index 5a2c8d0b683a528af87b845b63e9f75f586c4b74..26a6fa20371d3f9830225c043151cd84f0ba4506 100644 --- a/src/engine_split_particles.c +++ b/src/engine_split_particles.c @@ -33,6 +33,7 @@ #include "random.h" #include "rt.h" #include "star_formation.h" +#include "tools.h" #include "tracers.h" const int particle_split_factor = 2; @@ -60,6 +61,7 @@ struct data_split { long long offset_id; long long *count_id; swift_lock_type lock; + FILE *extra_split_logger; }; /** @@ -172,10 +174,6 @@ void engine_split_gas_particle_split_mapper(void *restrict map_data, int count, memcpy(&global_gparts[k_gparts], gp, sizeof(struct gpart)); } - /* Update splitting tree */ - particle_splitting_update_binary_tree(&xp->split_data, - &global_xparts[k_parts].split_data); - /* Update the IDs. */ if (generate_random_ids) { /* The gas IDs are always odd, so we multiply by two here to @@ -185,6 +183,11 @@ void engine_split_gas_particle_split_mapper(void *restrict map_data, int count, global_parts[k_parts].id = offset_id + 2 * atomic_inc(count_id); } + /* Update splitting tree */ + particle_splitting_update_binary_tree( + &xp->split_data, &global_xparts[k_parts].split_data, p->id, + global_parts[k_parts].id, data->extra_split_logger, &data->lock); + /* Re-link everything */ if (with_gravity) { global_parts[k_parts].gpart = &global_gparts[k_gparts]; @@ -312,7 +315,10 @@ void engine_split_gas_particles(struct engine *e) { /* Verify that nothing wrong happened with the IDs */ if (data_count.max_id > e->max_parts_id) { - error("Found a gas particle with an ID larger than the current max!"); + error( + "Found a gas particle with an ID (%lld) larger than the current max " + "(%lld)!", + data_count.max_id, e->max_parts_id); } /* Be verbose about this. This is an important event */ @@ -432,12 +438,22 @@ void engine_split_gas_particles(struct engine *e) { size_t k_parts = s->nr_parts; size_t k_gparts = s->nr_gparts; + FILE *extra_split_logger = NULL; + if (e->hydro_properties->log_extra_splits_in_file) { + char extra_split_logger_filename[256]; + sprintf(extra_split_logger_filename, "splits/splits_%04d.txt", engine_rank); + extra_split_logger = fopen(extra_split_logger_filename, "a"); + if (extra_split_logger == NULL) error("Error opening split logger file!"); + } + /* Loop over the particles again to split them */ long long local_count_id = 0; struct data_split data_split = { - e, mass_threshold, generate_random_ids, &k_parts, - &k_gparts, offset_id, &local_count_id, 0}; + e, mass_threshold, generate_random_ids, &k_parts, + &k_gparts, offset_id, &local_count_id, + /*lock=*/0, extra_split_logger}; lock_init(&data_split.lock); + threadpool_map(&e->threadpool, engine_split_gas_particle_split_mapper, s->parts, nr_parts_old, sizeof(struct part), 0, &data_split); if (lock_destroy(&data_split.lock) != 0) error("Error destroying lock"); @@ -459,7 +475,33 @@ void engine_split_gas_particles(struct engine *e) { } #endif + /* Close the logger file */ + if (e->hydro_properties->log_extra_splits_in_file) fclose(extra_split_logger); + if (e->verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); } + +void engine_init_split_gas_particles(struct engine *e) { + + if (e->hydro_properties->log_extra_splits_in_file) { + + /* Create the directory to host the logs */ + if (engine_rank == 0) safe_checkdir("splits", /*create=*/1); + +#ifdef WITH_MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + + /* Create the logger files and add a header */ + char extra_split_logger_filename[256]; + sprintf(extra_split_logger_filename, "splits/splits_%04d.txt", engine_rank); + FILE *extra_split_logger = fopen(extra_split_logger_filename, "w"); + fprintf(extra_split_logger, "# %12s %20s %20s %20s %20s\n", "Step", "ID", + "Progenitor", "Count", "Tree"); + + /* Close everything for now */ + fclose(extra_split_logger); + } +} diff --git a/src/feedback/AGORA/feedback.c b/src/feedback/AGORA/feedback.c index 4e662b551a806b98fdfc2211f0c5a8d631457b6a..60c6d64039cadfd2f2cdfea4ce52d37f954824e5 100644 --- a/src/feedback/AGORA/feedback.c +++ b/src/feedback/AGORA/feedback.c @@ -255,7 +255,7 @@ void feedback_init_spart(struct spart* sp) { */ void feedback_init_after_star_formation( struct spart* sp, const struct feedback_props* feedback_props, - enum star_feedback_type star_type) { + const enum stellar_type star_type) { /* Zero the energy of supernovae */ sp->feedback_data.energy_ejected = 0; @@ -268,7 +268,7 @@ void feedback_init_after_star_formation( /* Give to the star its appropriate type: single star, continuous IMF star or single population star */ - sp->feedback_data.star_type = star_type; + sp->star_type = star_type; } /** diff --git a/src/feedback/AGORA/feedback.h b/src/feedback/AGORA/feedback.h index a5b1aed331c09e5b1803282207fcd137e412c564..d90be8f842eff30c18975cd57606c02d269d8a8e 100644 --- a/src/feedback/AGORA/feedback.h +++ b/src/feedback/AGORA/feedback.h @@ -24,6 +24,7 @@ #include "feedback_properties.h" #include "hydro_properties.h" #include "part.h" +#include "stars.h" #include "units.h" /** @@ -60,7 +61,7 @@ void feedback_init_spart(struct spart* sp); void feedback_init_after_star_formation( struct spart* sp, const struct feedback_props* feedback_props, - enum star_feedback_type star_type); + enum stellar_type star_type); /** * @brief Should we do feedback for this star? diff --git a/src/feedback/AGORA/feedback_struct.h b/src/feedback/AGORA/feedback_struct.h index 1d8a4de3a11a52d31395351e1b70eb16be2d557a..c668aa8eb06654f8d0e0c1b8148ced59be8f212f 100644 --- a/src/feedback/AGORA/feedback_struct.h +++ b/src/feedback/AGORA/feedback_struct.h @@ -21,19 +21,6 @@ #include "chemistry_struct.h" -/** - * @brief The stellar feedback type for each star type. - * - * Now, star particles can represent a single star ("single_star"), a stellar - * population from a continuous IMF or a stellar population from a whole IMF. - */ -enum star_feedback_type { - single_star, /* particle representing a single star */ - star_population_continuous_IMF, /* particle representing a population of the - continuous part of the IMF */ - star_population /* particle representing a population with the whole IMF */ -}; - /** * @brief Feedback fields carried by each hydro particles */ @@ -77,9 +64,6 @@ struct feedback_spart_data { /*! Set the stellar particle to idle regarding the feedback */ char idle; - - /* Feedback type in function of the star particle type */ - enum star_feedback_type star_type; }; #endif /* SWIFT_FEEDBACK_STRUCT_AGORA_H */ diff --git a/src/feedback/GEAR/feedback.c b/src/feedback/GEAR/feedback.c index a61be623f27d2e025c4a98459ee0d7b6475e1578..46dfc6dea1ced3d51dc7eea992f1ea3c3657ad98 100644 --- a/src/feedback/GEAR/feedback.c +++ b/src/feedback/GEAR/feedback.c @@ -208,7 +208,17 @@ void feedback_will_do_feedback( star_age_beg_step < 0 ? 0 : star_age_beg_step; /* A single star */ - if (sp->feedback_data.star_type == single_star) { + if (sp->star_type == single_star) { + /* If the star has completely exploded, do not continue. This will also + avoid NaN values in the liftetime if the mass is set to 0. Correction + (28.04.2024): A bug fix in the mass of the star (see stellar_evolution.c + in stellar_evolution_compute_X_feedback_properties, X=discrete, + continuous) has changed the mass of the star from 0 to + discrete_star_minimal_gravity_mass. Hence the fix is propagated here. */ + if (sp->mass <= model->discrete_star_minimal_gravity_mass) { + return; + } + /* Now, compute the stellar evolution state for individual star particles. */ stellar_evolution_evolve_individual_star(sp, model, cosmo, us, phys_const, @@ -287,7 +297,7 @@ void feedback_init_spart(struct spart* sp) { */ void feedback_init_after_star_formation( struct spart* sp, const struct feedback_props* feedback_props, - enum star_feedback_type star_type) { + const enum stellar_type star_type) { feedback_init_spart(sp); @@ -299,7 +309,7 @@ void feedback_init_after_star_formation( /* Give to the star its appropriate type: single star, continuous IMF star or single population star */ - sp->feedback_data.star_type = star_type; + sp->star_type = star_type; } /** diff --git a/src/feedback/GEAR/feedback.h b/src/feedback/GEAR/feedback.h index 18b0a940202b5d99678404e41b634807b2701ff9..a27408a460b9ae66ec89bdab70a72f5b5bce834b 100644 --- a/src/feedback/GEAR/feedback.h +++ b/src/feedback/GEAR/feedback.h @@ -24,6 +24,7 @@ #include "feedback_properties.h" #include "hydro_properties.h" #include "part.h" +#include "stars.h" #include "stellar_evolution.h" #include "units.h" @@ -51,7 +52,7 @@ void feedback_init_spart(struct spart* sp); void feedback_init_after_star_formation( struct spart* sp, const struct feedback_props* feedback_props, - enum star_feedback_type star_type); + enum stellar_type star_type); void feedback_reset_feedback(struct spart* sp, const struct feedback_props* feedback_props); void feedback_first_init_spart(struct spart* sp, diff --git a/src/feedback/GEAR/feedback_struct.h b/src/feedback/GEAR/feedback_struct.h index 00f90b1f8f8f839fa5c740ceb32995de71d64028..ac24862ebf3328a7e9a6a7e531b4333833047356 100644 --- a/src/feedback/GEAR/feedback_struct.h +++ b/src/feedback/GEAR/feedback_struct.h @@ -21,19 +21,6 @@ #include "chemistry_struct.h" -/** - * @brief The stellar feedback type for each star type. - * - * Now, star particles can represent a single star ("single_star"), a stellar - * population from a continuous IMF or a stellar population from a whole IMF. - */ -enum star_feedback_type { - single_star, /* particle representing a single star */ - star_population_continuous_IMF, /* particle representing a population of the - continuous part of the IMF */ - star_population /* particle representing a population with the whole IMF */ -}; - /** * @brief Feedback fields carried by each hydro particles */ @@ -78,14 +65,6 @@ struct feedback_spart_data { /*! Does the particle needs the feedback loop? */ char will_do_feedback; - - /* Feedback type in function of the star particle type */ - /* Note for Darwin (July 2024): Ideally, I'd like to move this to the star - module and not the feedback. The type of the stellar particle is more related - to the star than the feedback. Furthermore, this type may be used in other - modules, e.g. RT, star formation, etc. This type is already used by the sink - module. */ - enum star_feedback_type star_type; }; #endif /* SWIFT_FEEDBACK_STRUCT_GEAR_H */ diff --git a/src/feedback/GEAR/hdf5_functions.h b/src/feedback/GEAR/hdf5_functions.h index 9219e7eebf7ec5a2164aed40ecc89f26ef017548..1f2c06a22cf4b83a2f8f13669042cc34268a3566 100644 --- a/src/feedback/GEAR/hdf5_functions.h +++ b/src/feedback/GEAR/hdf5_functions.h @@ -50,9 +50,9 @@ io_read_string_array_attribute(hid_t grp, const char *name, char *data, /* Check if correct number of element */ if (count != number_element) { error( - "Error found a different number of elements than expected (%lu != " - "%lu) in attribute %s", - count, number_element, name); + "Error found a different number of elements than expected (%lli != " + "%lli) in attribute %s", + (long long)count, (long long)number_element, name); } /* Get the string length */ @@ -63,7 +63,8 @@ io_read_string_array_attribute(hid_t grp, const char *name, char *data, /* Check if the size is correct */ if (sdim > size_per_element) { - error("Cannot read string longer than %lu in %s", size_per_element, name); + error("Cannot read string longer than %lli in %s", + (long long)size_per_element, name); } /* Allocate the temporary array */ diff --git a/src/feedback/GEAR/initial_mass_function.c b/src/feedback/GEAR/initial_mass_function.c index 96fcba34ee3f0093f4bcbf41653512f009d2f93d..5c4f1f8838943dcca832575af03bfa88c820f0b5 100644 --- a/src/feedback/GEAR/initial_mass_function.c +++ b/src/feedback/GEAR/initial_mass_function.c @@ -32,8 +32,13 @@ float initial_mass_function_get_exponent( #ifdef SWIFT_DEBUG_CHECKS if (mass_max > imf->mass_max) error("Cannot have mass larger than the largest one in the IMF"); - if (mass_min < imf->mass_min) - error("Cannot have mass smaller than the smallest one in the IMF"); + + /* 18.05.2024: This check is ill-defined. It needs to be improved. + For population II stars, no problem. + For population III stars, the snia->mass_min_progenitor can be smaller + than the minimal mass of the IMF, which causes the code to crash here. */ + /* if (mass_min < imf->mass_min) */ + /* error("Cannot have mass smaller than the smallest one in the IMF"); */ if (mass_max < mass_min) error("Cannot have mass_min larger than mass_max"); #endif @@ -626,3 +631,43 @@ INLINE double initial_mass_function_sample_power_law(double min_mass, double pmax = pow(max_mass, exp); return pow(x * (pmax - pmin) + pmin, 1. / exp); } + +/** @brief Compute the mass of the continuous and discrete part of the + * IMF. Also compute the total mass of the IMF. + * + * This function is used when we deal with an IMF split into two parts, + * e.g. with the sink particles or the stellar feedback. + * + * Note: This function does not verify wheter it computes the masses for the + * first stars or not. You need to verify this before this function and pass the + * correct values to 'minimal_discrete_mass_Msun' and 'stellar_particle_mass'. + * + * Note 2: This function implicitiely assumes M_sun since the IMF data + * structures handles the masses in M_sun. + * + * @param imf The #initial_mass_function. + * @param (return) M_continuous Mass of the continous part of the IMF. + * @param (return) M_discrete Mass of the discrete part of the IMF. + * @param (return) M_tot Total mass of the IMF. + */ +void initial_mass_function_compute_Mc_Md_Mtot( + const struct initial_mass_function *imf, double *M_continuous, + double *M_discrete, double *M_tot) { + + /* f_continuous is the imf mass fraction of the continuous part (of the IMF). + */ + const double f_continuous = initial_mass_function_get_imf_mass_fraction( + imf, imf->mass_min, imf->minimal_discrete_mass_Msun); + + /* Determine Mc and Md the masses of the continuous and discrete parts of the + IMF, as well as Mtot the total mass of the IMF. */ + if (f_continuous > 0) { + *M_tot = imf->stellar_particle_mass_Msun / f_continuous; + *M_discrete = *M_tot - imf->stellar_particle_mass_Msun; + *M_continuous = imf->stellar_particle_mass_Msun; + } else { + *M_tot = imf->stellar_particle_mass_Msun; + *M_discrete = *M_tot; + *M_continuous = 0; + } +} diff --git a/src/feedback/GEAR/initial_mass_function.h b/src/feedback/GEAR/initial_mass_function.h index 27a950b46f2c07540f36cb5ce7d936c2249d033e..302f1a344b2447d9bb72cf906750bcad9732ef1a 100644 --- a/src/feedback/GEAR/initial_mass_function.h +++ b/src/feedback/GEAR/initial_mass_function.h @@ -67,4 +67,8 @@ void initial_mass_function_clean(struct initial_mass_function *imf); double initial_mass_function_sample_power_law(double min_mass, double max_mass, double exp, double x); +void initial_mass_function_compute_Mc_Md_Mtot( + const struct initial_mass_function *imf, double *M_continuous, + double *M_discrete, double *M_tot); + #endif // SWIFT_INITIAL_MASS_FUNCTION_GEAR_H diff --git a/src/feedback/GEAR/stellar_evolution.c b/src/feedback/GEAR/stellar_evolution.c index a2b4985363d4daf2da642267e3922c2192cb91c7..563870f0b1cb84cb3b247d27c4ce70721b862d9e 100644 --- a/src/feedback/GEAR/stellar_evolution.c +++ b/src/feedback/GEAR/stellar_evolution.c @@ -32,6 +32,8 @@ #include <math.h> #include <stddef.h> +#define DEFAULT_STAR_MINIMAL_GRAVITY_MASS_MSUN 1e-1 + /** * @brief Print the stellar model. * @@ -80,6 +82,81 @@ int stellar_evolution_compute_integer_number_supernovae( return number_supernovae_i + (rand_sn < frac_sn); } +/** + * @brief Update the #spart mass from the supernovae ejected mass. + * + * This function deals with each star_type. + * + * Note: This function is called by + * stellar_evolution_compute_discrete_feedback_properties() and + * stellar_evolution_compute_continuous_feedback_properties(). + * + * @param sp The particle to act upon + * @param sm The #stellar_model structure. + */ +void stellar_evolution_sn_apply_ejected_mass(struct spart* restrict sp, + const struct stellar_model* sm) { + /* If a star is a discrete star */ + if (sp->star_type == single_star) { + const int null_mass = (sp->mass == sp->feedback_data.mass_ejected); + const int negative_mass = (sp->mass < sp->feedback_data.mass_ejected); + + if (null_mass) { + message("Star %lld (m_star = %e, m_ej = %e) completely exploded!", sp->id, + sp->mass, sp->feedback_data.mass_ejected); + /* If the star ejects all its mass (for very massive stars), give it a + zero mass so that we know it has exploded. + We do not remove the star from the simulation to keep track of its + properties, e.g. to check the IMF sampling (with sinks). + + Bug fix (28.04.2024): The mass of the star should not be set to + 0 because of gravity. So, we give some minimal value. */ + sp->mass = sm->discrete_star_minimal_gravity_mass; + + /* If somehow the star has a negative mass, we have a problem. */ + } else if (negative_mass) { + error( + "(Discrete star) Negative mass (m_star = %e, m_ej = %e), skipping " + "current star: %lli", + sp->mass, sp->feedback_data.mass_ejected, sp->id); + /* Reset everything */ + sp->feedback_data.number_snia = 0; + sp->feedback_data.number_snii = 0; + sp->feedback_data.mass_ejected = 0; + + /* Reset energy to avoid injecting anything in the + runner_iact_nonsym_feedback_apply() */ + sp->feedback_data.energy_ejected = 0; + return; + } else { + /* Update the mass */ + sp->mass -= sp->feedback_data.mass_ejected; + } + + /* If the star is the continuous part of the IMF or the enteire IMF */ + } else { + /* Check if we can ejected the required amount of elements. */ + const int negative_mass = (sp->mass <= sp->feedback_data.mass_ejected); + if (negative_mass) { + warning( + "(Continuous star) Negative mass (m_star = %e, m_ej = %e), skipping " + "current star: %lli", + sp->mass, sp->feedback_data.mass_ejected, sp->id); + /* Reset everything */ + sp->feedback_data.number_snia = 0; + sp->feedback_data.number_snii = 0; + sp->feedback_data.mass_ejected = 0; + + /* Reset energy to avoid injecting anything in the + runner_iact_nonsym_feedback_apply() */ + sp->feedback_data.energy_ejected = 0; + return; + } + /* Update the mass */ + sp->mass -= sp->feedback_data.mass_ejected; + } +} + /** * @brief Compute the feedback properties. * @@ -121,19 +198,8 @@ void stellar_evolution_compute_continuous_feedback_properties( sp->feedback_data.mass_ejected = mass_frac_snii * sp->sf_data.birth_mass + mass_snia * phys_const->const_solar_mass; - /* Check if we can ejected the required amount of elements. */ - const int negative_mass = sp->mass <= sp->feedback_data.mass_ejected; - if (negative_mass) { - message("Negative mass, skipping current star: %lli", sp->id); - /* Reset everything */ - sp->feedback_data.number_snia = 0; - sp->feedback_data.number_snii = 0; - sp->feedback_data.mass_ejected = 0; - return; - } - - /* Update the mass */ - sp->mass -= sp->feedback_data.mass_ejected; + /* Removes the ejected mass from the star */ + stellar_evolution_sn_apply_ejected_mass(sp, sm); /* Now deal with the metals */ @@ -214,19 +280,8 @@ void stellar_evolution_compute_discrete_feedback_properties( /* Transform into internal units */ sp->feedback_data.mass_ejected *= phys_const->const_solar_mass; - /* Check if we can ejected the required amount of elements. */ - const int negative_mass = sp->mass <= sp->feedback_data.mass_ejected; - if (negative_mass) { - message("Negative mass, skipping current star: %lli", sp->id); - /* Reset everything */ - sp->feedback_data.number_snia = 0; - sp->feedback_data.number_snii = 0; - sp->feedback_data.mass_ejected = 0; - return; - } - - /* Update the mass */ - sp->mass -= sp->feedback_data.mass_ejected; + /* Removes the ejected mass from the star */ + stellar_evolution_sn_apply_ejected_mass(sp, sm); /* Get the SNIa yields */ const float* snia_yields = supernovae_ia_get_yields(&sm->snia); @@ -273,6 +328,8 @@ void stellar_evolution_compute_discrete_feedback_properties( * Here I am using Myr-solar mass units internally in order to * avoid numerical errors. * + * Note: This function treats the case of single/individual stars. + * * @param sp The particle to act upon * @param sm The #stellar_model structure. * @param cosmo The current cosmological model. @@ -289,6 +346,11 @@ void stellar_evolution_evolve_individual_star( const struct phys_const* phys_const, const integertime_t ti_begin, const double star_age_beg_step, const double dt) { + /* Check that this function is called for single_star only. */ + if (sp->star_type != single_star) { + error("This function can only be called for single/individual star!"); + } + /* Convert the inputs */ const double conversion_to_myr = phys_const->const_year * 1e6; const double star_age_end_step_myr = @@ -315,7 +377,7 @@ void stellar_evolution_evolve_individual_star( star_age_beg_step_myr, star_age_end_step_myr, sp->mass / phys_const->const_solar_mass); - /* This is need by stellar_evolution_compute_discrete_feedback_properties(), + /* This is needed by stellar_evolution_compute_discrete_feedback_properties(), but this is not used inside the function. */ const float m_init = 0; @@ -370,6 +432,10 @@ void stellar_evolution_evolve_individual_star( * Here I am using Myr-solar mass units internally in order to * avoid numerical errors. * + * Note: This function treats the case of particles representing the whole IMF + * (star_type = star_population) and the particles representing only the + * continuous part of the IMF (star_type = star_population_continuous_IMF). + * * @param sp The particle to act upon * @param sm The #stellar_model structure. * @param cosmo The current cosmological model. @@ -386,6 +452,14 @@ void stellar_evolution_evolve_spart( const struct phys_const* phys_const, const integertime_t ti_begin, const double star_age_beg_step, const double dt) { + /* Check that this function is called for populations of stars and not + individual stars. */ + if (sp->star_type == single_star) { + error( + "This function can only be called for sparts representing stars " + "populations!"); + } + /* Convert the inputs */ const double conversion_to_myr = phys_const->const_year * 1e6; const double star_age_beg_step_myr = star_age_beg_step / conversion_to_myr; @@ -415,6 +489,21 @@ void stellar_evolution_evolve_spart( */ if (m_end_step >= m_beg_step) return; + /* Star particles representing only the continuous part of the IMF need a + special treatment. They do not contain stars above the mass that separate the + IMF into two parts (variable called minimal_discrete_mass_Msun in the sink + module). So, if m_beg_step > minimal_discrete_mass_Msun, you don't do + feedback. Note that the sm structure contains different information for the + 'first stars' and the 'late stars'. The right sm data is passed to this + function so we do not need any special treatment here. */ + if (sp->star_type == star_population_continuous_IMF) { + /* If it's not time yet for feedback, exit. Notice that both masses are in + solar mass. */ + if (m_beg_step > sm->imf.minimal_discrete_mass_Msun) { + return; + } + } + /* Check if the star can produce a supernovae */ const int can_produce_snia = supernovae_ia_can_explode(&sm->snia, m_end_step, m_beg_step); @@ -424,8 +513,14 @@ void stellar_evolution_evolve_spart( /* Is it possible to generate a supernovae? */ if (!can_produce_snia && !can_produce_snii) return; - /* Compute the initial mass */ - const float m_init = sp->sf_data.birth_mass / phys_const->const_solar_mass; + /* Compute the initial mass. The initial mass is different if the star + particle is of type 'star_population' or + 'star_population_continuous_IMF'. The function call treats both cases. */ + const float m_init = + stellar_evolution_compute_initial_mass(sp, sm, phys_const); + + /* Then, for 'star_population_continuous_IMF', everything remain the same as + with the "old" 'star_population'! */ /* Compute number of SNIa */ float number_snia_f = 0; @@ -618,6 +713,17 @@ void stellar_evolution_props_init(struct stellar_model* sm, /* Initialize the supernovae II model */ supernovae_ii_init(&sm->snii, params, sm, us); + + /* Initialize the minimal gravity mass for the stars */ + /* const float default_star_minimal_gravity_mass_Msun = 1e-1; */ + sm->discrete_star_minimal_gravity_mass = parser_get_opt_param_float( + params, "GEARFeedback:discrete_star_minimal_gravity_mass_Msun", + DEFAULT_STAR_MINIMAL_GRAVITY_MASS_MSUN); + + /* Convert from M_sun to internal units */ + sm->discrete_star_minimal_gravity_mass *= phys_const->const_solar_mass; + message("discrete_star_minimal_gravity_mass: (internal units) %e", + sm->discrete_star_minimal_gravity_mass); } /** @@ -682,3 +788,38 @@ void stellar_evolution_clean(struct stellar_model* sm) { supernovae_ia_clean(&sm->snia); supernovae_ii_clean(&sm->snii); } + +/** + * @brief Computes the initial mass of a #spart. This function distinguishes + * between the stellar particle representing a whole IMF and the stellar + * particles representing only the continuous part. + * + * @param sp The particle for which we compute the initial mass. + * @param sm The #stellar_model structure. + * @param phys_const the physical constants in internal units. + * @param (return) m_init Initial mass of the star particle (in M_sun). + */ +float stellar_evolution_compute_initial_mass( + const struct spart* restrict sp, const struct stellar_model* sm, + const struct phys_const* phys_const) { + + const struct initial_mass_function* imf = &sm->imf; + switch (sp->star_type) { + case star_population: + return sp->sf_data.birth_mass / phys_const->const_solar_mass; + case star_population_continuous_IMF: { + double M_IMF_tot, M_d_dummy, M_c_dummy; + initial_mass_function_compute_Mc_Md_Mtot(imf, &M_c_dummy, &M_d_dummy, + &M_IMF_tot); + /* No need to convert from internal units to M_sun because the masses are + already in solar masses (to avoid numerical errors) */ + return M_IMF_tot; + } + case single_star: + return sp->sf_data.birth_mass / phys_const->const_solar_mass; + default: { + error("This star_type (%d) is not implemented!", sp->star_type); + return -1.0; + } + } +} diff --git a/src/feedback/GEAR/stellar_evolution.h b/src/feedback/GEAR/stellar_evolution.h index 656f145b485eb47ba58b38f43256f6c8091c8c43..b2229b2c42040fd651eccbe28e3c25600d8fa015 100644 --- a/src/feedback/GEAR/stellar_evolution.h +++ b/src/feedback/GEAR/stellar_evolution.h @@ -76,4 +76,8 @@ void stellar_evolution_restore(struct stellar_model* sm, FILE* stream); void stellar_evolution_clean(struct stellar_model* sm); +float stellar_evolution_compute_initial_mass( + const struct spart* restrict sp, const struct stellar_model* sm, + const struct phys_const* phys_consts); + #endif // SWIFT_STELLAR_EVOLUTION_GEAR_H diff --git a/src/feedback/GEAR/stellar_evolution_struct.h b/src/feedback/GEAR/stellar_evolution_struct.h index 6671bb6b2736b581a9f3d3e612f00da51dc3e5d3..d3de0f1aa215c181bcd3f93ce3f7247f82d70c6e 100644 --- a/src/feedback/GEAR/stellar_evolution_struct.h +++ b/src/feedback/GEAR/stellar_evolution_struct.h @@ -65,7 +65,11 @@ struct initial_mass_function { float sink_Pc; /*! Stellar mass of the continous part of the IMF (in solar mass). */ - float stellar_particle_mass; + float stellar_particle_mass_Msun; + + /*! Minimal mass of stars represented by discrete particles (in solar mass). + */ + float minimal_discrete_mass_Msun; }; /** @@ -204,6 +208,19 @@ struct stellar_model { /* Filename of the yields table */ char yields_table[FILENAME_BUFFER_SIZE]; + + /* Minimal gravity mass after a discrete star has completely exploded. + + This will be the mass of the gpart's friend of the star. The mass of the + star will be 0 after it losses all its mass. + + The purpose of this is to avoid zero mass for the gravitsy + computations. We keep the star so that we know it *existed* and we can + extract its properties at the end of a run. If we remove the star, then + we do not have any information about its existence. + However, since the star is dead/inexistent, the gravity mass must be small + so that it does not drastically alter the dynamics of the systems. */ + float discrete_star_minimal_gravity_mass; }; #endif // SWIFT_STELLAR_EVOLUTION_STRUCT_GEAR_H diff --git a/src/hydro/Shadowswift/voronoi_cell.h b/src/fvpm_geometry.h similarity index 67% rename from src/hydro/Shadowswift/voronoi_cell.h rename to src/fvpm_geometry.h index 30d3e17fdfa76448773c0e45834ddf732989a3a4..847e241501fae64f5f001d9a0b936de8487059d2 100644 --- a/src/hydro/Shadowswift/voronoi_cell.h +++ b/src/fvpm_geometry.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). + * Copyright (c) 2024 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -16,18 +16,17 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_FVPM_GEOMETRY_H +#define SWIFT_FVPM_GEOMETRY_H -#ifndef SWIFT_VORONOI_CELL_H -#define SWIFT_VORONOI_CELL_H +/* Config parameters. */ +#include <config.h> -#if defined(HYDRO_DIMENSION_1D) -#include "voronoi1d_cell.h" -#elif defined(HYDRO_DIMENSION_2D) -#include "voronoi2d_cell.h" -#elif defined(HYDRO_DIMENSION_3D) -#include "voronoi3d_cell.h" +/* Import the right FVPM geometry functions */ +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(RT_GEAR) +#include "./fvpm_geometry/Gizmo/fvpm_geometry.h" #else -#error "You have to select a dimension for the hydro!" +#include "./fvpm_geometry/None/fvpm_geometry.h" #endif -#endif // SWIFT_VORONOI_CELL_H +#endif /* SWIFT_FVPM_GEOMETRY_H */ diff --git a/src/fvpm_geometry/Gizmo/MFM/fvpm_geometry.h b/src/fvpm_geometry/Gizmo/MFM/fvpm_geometry.h new file mode 100644 index 0000000000000000000000000000000000000000..8dedc8b88bebc99684de4568390ff281f0787b07 --- /dev/null +++ b/src/fvpm_geometry/Gizmo/MFM/fvpm_geometry.h @@ -0,0 +1,46 @@ +#ifndef SWIFT_FVPM_GEOMETRY_GIZMO_MFM_H +#define SWIFT_FVPM_GEOMETRY_GIZMO_MFM_H + +/** + * @brief Reset the variables used to store the centroid; used for the velocity + * correction. + */ +__attribute__((always_inline)) INLINE static void fvpm_reset_centroids( + struct part* restrict p) {} + +/** + * @brief Normalise the centroids after the density loop. + * + * @param p Particle. + * @param wcount Wcount for the particle. This is an explicit argument, so that + * it is clear from the code that wcount needs to be normalised by the time it + * is used here. + */ +__attribute__((always_inline)) INLINE static void fvpm_normalise_centroid( + struct part* restrict p, const float wcount) {} + +/** + * @brief Update the centroid with the given contribution, assuming the particle + * acts as the left particle in the neighbour interaction. + * + * @param p Particle (pi). + * @param dx Distance vector between the particle and its neighbour (dx = pi->x + * - pj->x). + * @param w Kernel value at position pj->x. + */ +__attribute__((always_inline)) INLINE static void fvpm_update_centroid_left( + struct part* restrict p, const float* dx, const float w) {} + +/** + * @brief Update the centroid with the given contribution, assuming the particle + * acts as the right particle in the neighbour interaction. + * + * @param p Particle (pj). + * @param dx Distance vector between the particle and its neighbour (dx = pi->x + * - pj->x). + * @param w Kernel value at position pi->x. + */ +__attribute__((always_inline)) INLINE static void fvpm_update_centroid_right( + struct part* restrict p, const float* dx, const float w) {} + +#endif /* SWIFT_FVPM_GEOMETRY_GIZMO_MFM_H */ diff --git a/src/fvpm_geometry/Gizmo/MFV/fvpm_geometry.h b/src/fvpm_geometry/Gizmo/MFV/fvpm_geometry.h new file mode 100644 index 0000000000000000000000000000000000000000..f5976bd201683735ac63a99be0baa7d98eb17bc1 --- /dev/null +++ b/src/fvpm_geometry/Gizmo/MFV/fvpm_geometry.h @@ -0,0 +1,69 @@ +#ifndef SWIFT_FVPM_GEOMETRY_GIZMO_MFV_H +#define SWIFT_FVPM_GEOMETRY_GIZMO_MFV_H + +#include "const.h" + +/** + * @brief Reset the variables used to store the centroid; used for the velocity + * correction. + */ +__attribute__((always_inline)) INLINE static void fvpm_reset_centroids( + struct part* restrict p) { + + p->geometry.centroid[0] = 0.0f; + p->geometry.centroid[1] = 0.0f; + p->geometry.centroid[2] = 0.0f; +} + +/** + * @brief Normalise the centroids after the density loop. + * + * @param p Particle. + * @param wcount Wcount for the particle. This is an explicit argument, so that + * it is clear from the code that wcount needs to be normalised by the time it + * is used here. + */ +__attribute__((always_inline)) INLINE static void fvpm_normalise_centroid( + struct part* restrict p, const float wcount) { + + const float norm = kernel_norm / wcount; + p->geometry.centroid[0] *= norm; + p->geometry.centroid[1] *= norm; + p->geometry.centroid[2] *= norm; +} + +/** + * @brief Update the centroid with the given contribution, assuming the particle + * acts as the left particle in the neighbour interaction. + * + * @param p Particle (pi). + * @param dx Distance vector between the particle and its neighbour (dx = pi->x + * - pj->x). + * @param w Kernel value at position pj->x. + */ +__attribute__((always_inline)) INLINE static void fvpm_update_centroid_left( + struct part* restrict p, const float* dx, const float w) { + + p->geometry.centroid[0] -= dx[0] * w; + p->geometry.centroid[1] -= dx[1] * w; + p->geometry.centroid[2] -= dx[2] * w; +} + +/** + * @brief Update the centroid with the given contribution, assuming the particle + * acts as the right particle in the neighbour interaction. + * + * @param p Particle (pj). + * @param dx Distance vector between the particle and its neighbour (dx = pi->x + * - pj->x). + * @param w Kernel value at position pi->x. + */ +__attribute__((always_inline)) INLINE static void fvpm_update_centroid_right( + struct part* restrict p, const float* dx, const float w) { + + p->geometry.centroid[0] += dx[0] * w; + p->geometry.centroid[1] += dx[1] * w; + p->geometry.centroid[2] += dx[2] * w; +} + +#endif /* SWIFT_FVPM_GEOMETRY_GIZMO_MFV_H */ diff --git a/src/fvpm_geometry/Gizmo/fvpm_geometry.h b/src/fvpm_geometry/Gizmo/fvpm_geometry.h new file mode 100644 index 0000000000000000000000000000000000000000..951a3c7fb2ec5185034a4a3cdac83436f7456b53 --- /dev/null +++ b/src/fvpm_geometry/Gizmo/fvpm_geometry.h @@ -0,0 +1,153 @@ +#ifndef SWIFT_FVPM_GEOMETRY_GIZMO_H +#define SWIFT_FVPM_GEOMETRY_GIZMO_H + +#include "const.h" +#include "part.h" + +#include <config.h> + +/** + * @file Gizmo/fvpm_geometry.h + * @brief Functions related to the Gizmo FVPM geometry struct collection, + * in particular the collection of the data required for the matrix needed + * for gradients. + * This was moved here so we can cleanly couple GEAR-RT on top of SPH + * hydrodynamics while avoiding code replication. + */ + +#if defined(RT_GEAR) && defined(GIZMO_MFM_SPH) +/* Some functions clash here. MFM resets and does some geometry centroid + * stuff, while GEAR-RT, which uses MFV, doesn't. So we'd need to split the + * functions for RT and for hydro use. + * However, it is very unlikely we'll ever actually use that combination, + * so leaving it as-is for now. */ +#error "Combining GIZMO MFM and GEAR-RT not implemented yet." +#endif + +#if defined(GIZMO_MFV_SPH) || defined(RT_GEAR) +#include "./MFV/fvpm_geometry.h" +#elif defined(GIZMO_MFM_SPH) +#include "./MFM/fvpm_geometry.h" +#endif + +/** + * @brief Check if the gradient matrix for this particle is well behaved. + * + * @param p Particle. + * @return 1 if the gradient matrix is well behaved, 0 otherwise. + */ +__attribute__((always_inline)) INLINE static int +fvpm_part_geometry_well_behaved(const struct part* restrict p) { + + return p->geometry.wcorr > const_gizmo_min_wcorr; +} + +/** + * @brief Collect the data needed for the matrix construction. + */ +__attribute__((always_inline)) INLINE static void +fvpm_accumulate_geometry_and_matrix(struct part* restrict pi, const float wi, + const float dx[3]) { + /* these are eqns. (1) and (2) in the Gizmo theory summary */ + pi->geometry.volume += wi; + for (int k = 0; k < 3; k++) + for (int l = 0; l < 3; l++) + pi->geometry.matrix_E[k][l] += dx[k] * dx[l] * wi; +} + +__attribute__((always_inline)) INLINE static void fvpm_geometry_init( + struct part* restrict p) { + + p->geometry.volume = 0.0f; + p->geometry.matrix_E[0][0] = 0.0f; + p->geometry.matrix_E[0][1] = 0.0f; + p->geometry.matrix_E[0][2] = 0.0f; + p->geometry.matrix_E[1][0] = 0.0f; + p->geometry.matrix_E[1][1] = 0.0f; + p->geometry.matrix_E[1][2] = 0.0f; + p->geometry.matrix_E[2][0] = 0.0f; + p->geometry.matrix_E[2][1] = 0.0f; + p->geometry.matrix_E[2][2] = 0.0f; + + /* reset the centroid variables used for the velocity correction in MFV */ + fvpm_reset_centroids(p); +} + +/** + * @brief Finish the computation of the matrix. + * + * @param p the particle to work on + * @param ihdim 1/h^{dim} + */ +__attribute__((always_inline)) INLINE static void +fvpm_compute_volume_and_matrix(struct part* restrict p, const float ihdim) { + + /* Final operation on the geometry. */ + /* we multiply with the smoothing kernel normalization ih3 and calculate the + * volume */ + const float volume_inv = ihdim * (p->geometry.volume + kernel_root); + const float volume = 1.0f / volume_inv; + p->geometry.volume = volume; + + /* we multiply with the smoothing kernel normalization */ + p->geometry.matrix_E[0][0] *= ihdim; + p->geometry.matrix_E[0][1] *= ihdim; + p->geometry.matrix_E[0][2] *= ihdim; + p->geometry.matrix_E[1][0] *= ihdim; + p->geometry.matrix_E[1][1] *= ihdim; + p->geometry.matrix_E[1][2] *= ihdim; + p->geometry.matrix_E[2][0] *= ihdim; + p->geometry.matrix_E[2][1] *= ihdim; + p->geometry.matrix_E[2][2] *= ihdim; + + /* normalise the centroids for MFV */ + fvpm_normalise_centroid(p, p->density.wcount); + + /* Check the condition number to see if we have a stable geometry. */ + const float condition_number_E = + p->geometry.matrix_E[0][0] * p->geometry.matrix_E[0][0] + + p->geometry.matrix_E[0][1] * p->geometry.matrix_E[0][1] + + p->geometry.matrix_E[0][2] * p->geometry.matrix_E[0][2] + + p->geometry.matrix_E[1][0] * p->geometry.matrix_E[1][0] + + p->geometry.matrix_E[1][1] * p->geometry.matrix_E[1][1] + + p->geometry.matrix_E[1][2] * p->geometry.matrix_E[1][2] + + p->geometry.matrix_E[2][0] * p->geometry.matrix_E[2][0] + + p->geometry.matrix_E[2][1] * p->geometry.matrix_E[2][1] + + p->geometry.matrix_E[2][2] * p->geometry.matrix_E[2][2]; + + float condition_number = 0.0f; + if (invert_dimension_by_dimension_matrix(p->geometry.matrix_E) != 0) { + /* something went wrong in the inversion; force bad condition number */ + condition_number = const_gizmo_max_condition_number + 1.0f; + } else { + const float condition_number_Einv = + p->geometry.matrix_E[0][0] * p->geometry.matrix_E[0][0] + + p->geometry.matrix_E[0][1] * p->geometry.matrix_E[0][1] + + p->geometry.matrix_E[0][2] * p->geometry.matrix_E[0][2] + + p->geometry.matrix_E[1][0] * p->geometry.matrix_E[1][0] + + p->geometry.matrix_E[1][1] * p->geometry.matrix_E[1][1] + + p->geometry.matrix_E[1][2] * p->geometry.matrix_E[1][2] + + p->geometry.matrix_E[2][0] * p->geometry.matrix_E[2][0] + + p->geometry.matrix_E[2][1] * p->geometry.matrix_E[2][1] + + p->geometry.matrix_E[2][2] * p->geometry.matrix_E[2][2]; + + condition_number = + hydro_dimension_inv * sqrtf(condition_number_E * condition_number_Einv); + } + + if (condition_number > const_gizmo_max_condition_number && + p->geometry.wcorr > const_gizmo_min_wcorr) { +#ifdef GIZMO_PATHOLOGICAL_ERROR + error("Condition number larger than %g (%g)!", + const_gizmo_max_condition_number, condition_number); +#endif +#ifdef GIZMO_PATHOLOGICAL_WARNING + message("Condition number too large: %g (> %g, p->id: %llu)!", + condition_number, const_gizmo_max_condition_number, p->id); +#endif + /* add a correction to the number of neighbours for this particle */ + p->geometry.wcorr = const_gizmo_w_correction_factor * p->geometry.wcorr; + } +} + +#endif /* SWIFT_FVPM_GEOMETRY_GIZMO_H */ diff --git a/src/fvpm_geometry/Gizmo/fvpm_geometry_struct.h b/src/fvpm_geometry/Gizmo/fvpm_geometry_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..7f3e8cb2cabc8a355d2d8690eefe1f7b53147eee --- /dev/null +++ b/src/fvpm_geometry/Gizmo/fvpm_geometry_struct.h @@ -0,0 +1,30 @@ +#ifndef SWIFT_FVPM_GEOMETRY_STRUCT_GIZMO_H +#define SWIFT_FVPM_GEOMETRY_STRUCT_GIZMO_H + +/** + * @file Gizmo/fvpm_geometry_struct.h + * @brief Struct related to the Gizmo FVPM geometry particle data collection, + * in particular the collection of the data required for the matrix needed + * for gradients. + * This was moved here so we can cleanly couple GEAR-RT on top of SPH + * hydrodynamics while avoiding code replication. + */ + +/* Geometrical quantities used for hydro. */ +struct fvpm_geometry_struct { + + /* Volume of the particle. */ + float volume; + + /* Geometrical shear matrix used to calculate second order accurate + gradients */ + float matrix_E[3][3]; + + /* Centroid of the "cell". */ + float centroid[3]; + + /* Correction factor for wcount. */ + float wcorr; +}; + +#endif /* SWIFT_FVPM_GEOMETRY_STRUCT_GIZMO_H */ diff --git a/src/fvpm_geometry/None/fvpm_geometry.h b/src/fvpm_geometry/None/fvpm_geometry.h new file mode 100644 index 0000000000000000000000000000000000000000..eabc7edee719eb677cdaaa8eb77f16a5ebb9a545 --- /dev/null +++ b/src/fvpm_geometry/None/fvpm_geometry.h @@ -0,0 +1,83 @@ +#ifndef SWIFT_FVPM_GEOMETRY_NONE_H +#define SWIFT_FVPM_GEOMETRY_NONE_H + +/** + * @file None/fvpm_geometry.h + * @brief Functions related to the Gizmo FVPM geometry struct collection, + * in particular the collection of the data required for the matrix needed + * for gradients. Empty definitions for when FVPM are unused. + */ + +/** + * @brief Reset the variables used to store the centroid; used for the velocity + * correction. + */ +__attribute__((always_inline)) INLINE static void fvpm_reset_centroids( + struct part* restrict p) {} + +/** + * @brief Normalise the centroids after the density loop. + * + * @param p Particle. + * @param wcount Wcount for the particle. This is an explicit argument, so that + * it is clear from the code that wcount needs to be normalised by the time it + * is used here. + */ +__attribute__((always_inline)) INLINE static void fvpm_normalise_centroid( + struct part* restrict p, const float wcount) {} + +/** + * @brief Update the centroid with the given contribution, assuming the particle + * acts as the left particle in the neighbour interaction. + * + * @param p Particle (pi). + * @param dx Distance vector between the particle and its neighbour (dx = pi->x + * - pj->x). + * @param w Kernel value at position pj->x. + */ +__attribute__((always_inline)) INLINE static void fvpm_update_centroid_left( + struct part* restrict p, const float* dx, const float w) {} + +/** + * @brief Update the centroid with the given contribution, assuming the particle + * acts as the right particle in the neighbour interaction. + * + * @param p Particle (pj). + * @param dx Distance vector between the particle and its neighbour (dx = pi->x + * - pj->x). + * @param w Kernel value at position pi->x. + */ +__attribute__((always_inline)) INLINE static void fvpm_update_centroid_right( + struct part* restrict p, const float* dx, const float w) {} + +/** + * @brief Check if the gradient matrix for this particle is well behaved. + * + * @param p Particle. + * @return 1 if the gradient matrix is well behaved, 0 otherwise. + */ +__attribute__((always_inline)) INLINE static int +fvpm_part_geometry_well_behaved(const struct part* restrict p) { + return 0; +} + +/** + * @brief Collect the data needed for the matrix construction. + */ +__attribute__((always_inline)) INLINE static void +fvpm_accumulate_geometry_and_matrix(struct part* restrict pi, const float wi, + const float dx[3]) {} + +__attribute__((always_inline)) INLINE static void fvpm_geometry_init( + struct part* restrict p) {} + +/** + * @brief Finish the computation of the matrix. + * + * @param p the particle to work on + * @param ihdim 1/h^{dim} + */ +__attribute__((always_inline)) INLINE static void +fvpm_compute_volume_and_matrix(struct part* restrict p, const float ihdim) {} + +#endif /* SWIFT_FVPM_GEOMETRY_NONE_H */ diff --git a/src/fvpm_geometry/None/fvpm_geometry_struct.h b/src/fvpm_geometry/None/fvpm_geometry_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..db00534b56b6517962198906359a06056c8cfd7a --- /dev/null +++ b/src/fvpm_geometry/None/fvpm_geometry_struct.h @@ -0,0 +1,14 @@ +#ifndef SWIFT_FVPM_GEOMETRY_STRUCT_NONE_H +#define SWIFT_FVPM_GEOMETRY_STRUCT_NONE_H + +/** + * @file fvpm_geometry_struct.h + * @brief Struct related to the Gizmo FVPM geometry particle data collection, + * in particular the collection of the data required for the matrix needed + * for gradients. Empty definitions for when FVPM are unused. + */ + +/* Geometrical quantities used for hydro. */ +struct fvpm_geometry_struct {}; + +#endif /* SWIFT_FVPM_GEOMETRY_STRUCT_NONE_H */ diff --git a/src/hydro/Shadowswift/voronoi_algorithm.h b/src/fvpm_geometry_struct.h similarity index 65% rename from src/hydro/Shadowswift/voronoi_algorithm.h rename to src/fvpm_geometry_struct.h index 19ecc723741e6c91b19e85f6457311a80c6faf34..9cd32b7846423fa5e80abb20c3e9a825d8db183c 100644 --- a/src/hydro/Shadowswift/voronoi_algorithm.h +++ b/src/fvpm_geometry_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). + * Copyright (c) 2024 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -16,18 +16,17 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_FVPM_GEOMETRY_STRUCT_H +#define SWIFT_FVPM_GEOMETRY_STRUCT_H -#ifndef SWIFT_VORONOI_ALGORITHM_H -#define SWIFT_VORONOI_ALGORITHM_H +/* Config parameters. */ +#include <config.h> -#if defined(HYDRO_DIMENSION_1D) -#include "voronoi1d_algorithm.h" -#elif defined(HYDRO_DIMENSION_2D) -#include "voronoi2d_algorithm.h" -#elif defined(HYDRO_DIMENSION_3D) -#include "voronoi3d_algorithm.h" +/* Import the right geometry struct definition */ +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(RT_GEAR) +#include "./fvpm_geometry/Gizmo/fvpm_geometry_struct.h" #else -#error "You have to select a dimension for the hydro!" +#include "./fvpm_geometry/None/fvpm_geometry_struct.h" #endif -#endif // SWIFT_VORONOI_ALGORITHM_H +#endif /* SWIFT_FVPM_GEOMETRY_STRUCT_H */ diff --git a/src/hydro.h b/src/hydro.h index 1f96d1f3031033c85d5915ecb3ca1d0959738d70..048be9f314a22f14246cd39434dfcbb66ec610bf 100644 --- a/src/hydro.h +++ b/src/hydro.h @@ -62,11 +62,10 @@ #elif defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) #include "./hydro/Gizmo/hydro.h" #include "./hydro/Gizmo/hydro_iact.h" -#elif defined(SHADOWFAX_SPH) +#elif defined(SHADOWSWIFT) #include "./hydro/Shadowswift/hydro.h" #include "./hydro/Shadowswift/hydro_iact.h" -#define SPH_IMPLEMENTATION \ - "Shadowfax moving mesh (Vandenbroucke and De Rijcke 2016)" +#define SPH_IMPLEMENTATION "ShadowSWIFT moving mesh" #elif defined(PLANETARY_SPH) #include "./hydro/Planetary/hydro.h" #include "./hydro/Planetary/hydro_iact.h" diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h index 4ac0b4c6fc0de6ab381beddd1c178758071e615d..13662e7745b6b9ed2be51c94ebb15e308fd25f49 100644 --- a/src/hydro/Gadget2/hydro_iact.h +++ b/src/hydro/Gadget2/hydro_iact.h @@ -579,7 +579,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Adaptive softening acceleration term */ const float adapt_soft_acc_term = - adaptive_softening_get_acc_term(pi, pj, wi_dr, wj_dr, f_ij, f_ji, r_inv); + adaptive_softening_get_acc_term(pi, pj, wi_dr, wj_dr, f_i, f_j, r_inv); /* Eventually got the acceleration */ const float acc = visc_term + sph_term + adapt_soft_acc_term; @@ -708,7 +708,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Adaptive softening acceleration term */ const float adapt_soft_acc_term = - adaptive_softening_get_acc_term(pi, pj, wi_dr, wj_dr, f_ij, f_ji, r_inv); + adaptive_softening_get_acc_term(pi, pj, wi_dr, wj_dr, f_i, f_j, r_inv); /* Eventually got the acceleration */ const float acc = visc_term + sph_term + adapt_soft_acc_term; diff --git a/src/hydro/Gizmo/MFM/hydro_part.h b/src/hydro/Gizmo/MFM/hydro_part.h index f476b860eaaca8d54386fe3f2502db3b58c67df5..f114dd2b9f9ed4ea81f9cfe86830c2849c0fe81f 100644 --- a/src/hydro/Gizmo/MFM/hydro_part.h +++ b/src/hydro/Gizmo/MFM/hydro_part.h @@ -22,13 +22,13 @@ /* Data of a single particle. */ struct part { - /* Particle ID. */ + /*! Particle ID. */ long long id; - /* Associated gravitas. */ + /*! Associated gravitas. */ struct gpart *gpart; - /* Particle position. */ + /*! Particle position. */ double x[3]; /* In MFM, the particle and fluid velocities are the same. @@ -42,16 +42,16 @@ struct part { float fluid_v[3]; }; - /* Particle acceleration. */ + /*! Particle acceleration. */ float a_hydro[3]; - /* Particle smoothing length. */ + /*! Particle smoothing length. */ float h; - /* Density. */ + /*! Density. */ float rho; - /* Pressure. */ + /*! Pressure. */ float P; union { @@ -94,7 +94,7 @@ struct part { }; }; - /* Fluxes. */ + /*! Fluxes. */ struct { /* No mass flux, since it is always zero. */ @@ -109,7 +109,7 @@ struct part { } flux; - /* Gradients of the primitive variables. */ + /*! Gradients of the primitive variables. */ struct { /* Density gradients. */ @@ -123,7 +123,7 @@ struct part { } gradients; - /* The conserved hydrodynamical variables. */ + /*! The conserved hydrodynamical variables. */ struct { /* Fluid mass */ @@ -137,22 +137,10 @@ struct part { } conserved; - /* Geometrical quantities used for hydro. */ - struct { - - /* Volume of the particle. */ - float volume; - - /* Geometrical shear matrix used to calculate second order accurate - gradients */ - float matrix_E[3][3]; - - /* Correction factor for wcount. */ - float wcorr; - - } geometry; + /*! Geometrical quantities used for hydro. */ + struct fvpm_geometry_struct geometry; - /* Variables used for timestep calculation. */ + /*! Variables used for timestep calculation. */ struct { /* Maximum signal velocity among all the neighbours of the particle. The diff --git a/src/hydro/Gizmo/MFM/hydro_velocities.h b/src/hydro/Gizmo/MFM/hydro_velocities.h index c3929cf2f3e4b864397cebed5490c15ba80c5332..84da17bf4b8e30524ba76f01013b8cbde1645f6a 100644 --- a/src/hydro/Gizmo/MFM/hydro_velocities.h +++ b/src/hydro/Gizmo/MFM/hydro_velocities.h @@ -101,51 +101,4 @@ __attribute__((always_inline)) INLINE static void hydro_velocities_set( } } -/** - * @brief Reset the variables used to store the centroid; used for the velocity - * correction. - * - * @param p Particle. - */ -__attribute__((always_inline)) INLINE static void -hydro_velocities_reset_centroids(struct part* restrict p) {} - -/** - * @brief Normalise the centroids after the density loop. - * - * @param p Particle. - * @param wcount Wcount for the particle. This is an explicit argument, so that - * it is clear from the code that wcount needs to be normalised by the time it - * is used here. - */ -__attribute__((always_inline)) INLINE static void -hydro_velocities_normalise_centroid(struct part* restrict p, - const float wcount) {} - -/** - * @brief Update the centroid with the given contribution, assuming the particle - * acts as the left particle in the neighbour interaction. - * - * @param p Particle (pi). - * @param dx Distance vector between the particle and its neighbour (dx = pi->x - * - pj->x). - * @param w Kernel value at position pj->x. - */ -__attribute__((always_inline)) INLINE static void -hydro_velocities_update_centroid_left(struct part* restrict p, const float* dx, - const float w) {} - -/** - * @brief Update the centroid with the given contribution, assuming the particle - * acts as the right particle in the neighbour interaction. - * - * @param p Particle (pj). - * @param dx Distance vector between the particle and its neighbour (dx = pi->x - * - pj->x). - * @param w Kernel value at position pi->x. - */ -__attribute__((always_inline)) INLINE static void -hydro_velocities_update_centroid_right(struct part* restrict p, const float* dx, - const float w) {} - #endif /* SWIFT_GIZMO_MFM_HYDRO_VELOCITIES_H */ diff --git a/src/hydro/Gizmo/MFV/hydro_part.h b/src/hydro/Gizmo/MFV/hydro_part.h index e64b605f2d240a5dbda0266dcdc5c8277f994d55..7b0e22d1a860e009e61a79a732724aa7f0e8c0df 100644 --- a/src/hydro/Gizmo/MFV/hydro_part.h +++ b/src/hydro/Gizmo/MFV/hydro_part.h @@ -22,34 +22,34 @@ /* Data of a single particle. */ struct part { - /* Particle ID. */ + /*! Particle ID. */ long long id; - /* Associated gravitas. */ + /*! Associated gravitas. */ struct gpart *gpart; - /* Particle position. */ + /*! Particle position. */ double x[3]; - /* Particle predicted velocity. */ + /*! Particle predicted velocity. */ float v[3]; - /* Particle acceleration. */ + /*! Particle acceleration. */ float a_hydro[3]; - /* Particle smoothing length. */ + /*! Particle smoothing length. */ float h; - /* Density. */ + /*! Density. */ float rho; - /* Fluid velocity. */ + /*! Fluid velocity. */ float fluid_v[3]; - /* Pressure. */ + /*! Pressure. */ float P; - /* Gradients of the primitive variables. */ + /*! Gradients of the primitive variables. */ struct { /* Density gradients. */ @@ -63,7 +63,7 @@ struct part { } gradients; - /* Quantities needed by the slope limiter. */ + /*! Quantities needed by the slope limiter. */ struct { /* Extreme values of the density among the neighbours. */ @@ -80,7 +80,7 @@ struct part { } limiter; - /* The conserved hydrodynamical variables. */ + /*! The conserved hydrodynamical variables. */ struct { /* Fluid mass */ @@ -94,7 +94,7 @@ struct part { } conserved; - /* Fluxes. */ + /*! Fluxes. */ struct { /* Mass flux. */ @@ -111,25 +111,10 @@ struct part { } flux; - /* Geometrical quantities used for hydro. */ - struct { - - /* Volume of the particle. */ - float volume; - - /* Geometrical shear matrix used to calculate second order accurate - gradients */ - float matrix_E[3][3]; - - /* Centroid of the "cell". */ - float centroid[3]; - - /* Correction factor for wcount. */ - float wcorr; - - } geometry; + /*! Geometrical quantities used for hydro. */ + struct fvpm_geometry_struct geometry; - /* Variables used for timestep calculation. */ + /*! Variables used for timestep calculation. */ struct { /* Maximum signal velocity among all the neighbours of the particle. The @@ -140,7 +125,7 @@ struct part { } timestepvars; - /* Quantities used during the volume (=density) loop. */ + /*! Quantities used during the volume (=density) loop. */ struct { /* Derivative of particle number density. */ @@ -151,7 +136,7 @@ struct part { } density; - /* Quantities used during the force loop. */ + /*! Quantities used during the force loop. */ struct { /* Needed to drift the primitive variables. */ @@ -159,7 +144,7 @@ struct part { } force; - /* Specific stuff for the gravity-hydro coupling. */ + /*! Specific stuff for the gravity-hydro coupling. */ struct { /* Current value of the mass flux vector. */ diff --git a/src/hydro/Gizmo/MFV/hydro_velocities.h b/src/hydro/Gizmo/MFV/hydro_velocities.h index faba82652bc9cfbc32563fb5566508ed05fa101c..3120c780d81d79362cdc8c47efb1370d759a6c2d 100644 --- a/src/hydro/Gizmo/MFV/hydro_velocities.h +++ b/src/hydro/Gizmo/MFV/hydro_velocities.h @@ -152,70 +152,4 @@ __attribute__((always_inline)) INLINE static void hydro_velocities_set( } } -/** - * @brief Reset the variables used to store the centroid; used for the velocity - * correction. - */ -__attribute__((always_inline)) INLINE static void -hydro_velocities_reset_centroids(struct part* restrict p) { - - p->geometry.centroid[0] = 0.0f; - p->geometry.centroid[1] = 0.0f; - p->geometry.centroid[2] = 0.0f; -} - -/** - * @brief Normalise the centroids after the density loop. - * - * @param p Particle. - * @param wcount Wcount for the particle. This is an explicit argument, so that - * it is clear from the code that wcount needs to be normalised by the time it - * is used here. - */ -__attribute__((always_inline)) INLINE static void -hydro_velocities_normalise_centroid(struct part* restrict p, - const float wcount) { - - const float norm = kernel_norm / wcount; - p->geometry.centroid[0] *= norm; - p->geometry.centroid[1] *= norm; - p->geometry.centroid[2] *= norm; -} - -/** - * @brief Update the centroid with the given contribution, assuming the particle - * acts as the left particle in the neighbour interaction. - * - * @param p Particle (pi). - * @param dx Distance vector between the particle and its neighbour (dx = pi->x - * - pj->x). - * @param w Kernel value at position pj->x. - */ -__attribute__((always_inline)) INLINE static void -hydro_velocities_update_centroid_left(struct part* restrict p, const float* dx, - const float w) { - - p->geometry.centroid[0] -= dx[0] * w; - p->geometry.centroid[1] -= dx[1] * w; - p->geometry.centroid[2] -= dx[2] * w; -} - -/** - * @brief Update the centroid with the given contribution, assuming the particle - * acts as the right particle in the neighbour interaction. - * - * @param p Particle (pj). - * @param dx Distance vector between the particle and its neighbour (dx = pi->x - * - pj->x). - * @param w Kernel value at position pi->x. - */ -__attribute__((always_inline)) INLINE static void -hydro_velocities_update_centroid_right(struct part* restrict p, const float* dx, - const float w) { - - p->geometry.centroid[0] += dx[0] * w; - p->geometry.centroid[1] += dx[1] * w; - p->geometry.centroid[2] += dx[2] * w; -} - #endif /* SWIFT_GIZMO_MFV_HYDRO_VELOCITIES_H */ diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/Gizmo/hydro.h index 85a16a4a64cccc4cb5745c766822a8f4ccfedfdc..5a843c7144a9c12f32b4815bbcab7bfb52f11669 100644 --- a/src/hydro/Gizmo/hydro.h +++ b/src/hydro/Gizmo/hydro.h @@ -29,6 +29,7 @@ #include "approx_math.h" #include "entropy_floor.h" +#include "fvpm_geometry.h" #include "hydro_flux.h" #include "hydro_getters.h" #include "hydro_gradients.h" @@ -190,19 +191,7 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->density.wcount = 0.0f; p->density.wcount_dh = 0.0f; - p->geometry.volume = 0.0f; - p->geometry.matrix_E[0][0] = 0.0f; - p->geometry.matrix_E[0][1] = 0.0f; - p->geometry.matrix_E[0][2] = 0.0f; - p->geometry.matrix_E[1][0] = 0.0f; - p->geometry.matrix_E[1][1] = 0.0f; - p->geometry.matrix_E[1][2] = 0.0f; - p->geometry.matrix_E[2][0] = 0.0f; - p->geometry.matrix_E[2][1] = 0.0f; - p->geometry.matrix_E[2][2] = 0.0f; - - /* reset the centroid variables used for the velocity correction in MFV */ - hydro_velocities_reset_centroids(p); + fvpm_geometry_init(p); } /** @@ -241,72 +230,10 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.wcount_dh -= hydro_dimension * kernel_root; p->density.wcount_dh *= ihdim_plus_one; - /* Final operation on the geometry. */ - /* we multiply with the smoothing kernel normalization ih3 and calculate the - * volume */ - const float volume_inv = ihdim * (p->geometry.volume + kernel_root); - const float volume = 1.0f / volume_inv; - p->geometry.volume = volume; - - /* we multiply with the smoothing kernel normalization */ - p->geometry.matrix_E[0][0] *= ihdim; - p->geometry.matrix_E[0][1] *= ihdim; - p->geometry.matrix_E[0][2] *= ihdim; - p->geometry.matrix_E[1][0] *= ihdim; - p->geometry.matrix_E[1][1] *= ihdim; - p->geometry.matrix_E[1][2] *= ihdim; - p->geometry.matrix_E[2][0] *= ihdim; - p->geometry.matrix_E[2][1] *= ihdim; - p->geometry.matrix_E[2][2] *= ihdim; - - /* normalise the centroids for MFV */ - hydro_velocities_normalise_centroid(p, p->density.wcount); - - /* Check the condition number to see if we have a stable geometry. */ - const float condition_number_E = - p->geometry.matrix_E[0][0] * p->geometry.matrix_E[0][0] + - p->geometry.matrix_E[0][1] * p->geometry.matrix_E[0][1] + - p->geometry.matrix_E[0][2] * p->geometry.matrix_E[0][2] + - p->geometry.matrix_E[1][0] * p->geometry.matrix_E[1][0] + - p->geometry.matrix_E[1][1] * p->geometry.matrix_E[1][1] + - p->geometry.matrix_E[1][2] * p->geometry.matrix_E[1][2] + - p->geometry.matrix_E[2][0] * p->geometry.matrix_E[2][0] + - p->geometry.matrix_E[2][1] * p->geometry.matrix_E[2][1] + - p->geometry.matrix_E[2][2] * p->geometry.matrix_E[2][2]; - - float condition_number = 0.0f; - if (invert_dimension_by_dimension_matrix(p->geometry.matrix_E) != 0) { - /* something went wrong in the inversion; force bad condition number */ - condition_number = const_gizmo_max_condition_number + 1.0f; - } else { - const float condition_number_Einv = - p->geometry.matrix_E[0][0] * p->geometry.matrix_E[0][0] + - p->geometry.matrix_E[0][1] * p->geometry.matrix_E[0][1] + - p->geometry.matrix_E[0][2] * p->geometry.matrix_E[0][2] + - p->geometry.matrix_E[1][0] * p->geometry.matrix_E[1][0] + - p->geometry.matrix_E[1][1] * p->geometry.matrix_E[1][1] + - p->geometry.matrix_E[1][2] * p->geometry.matrix_E[1][2] + - p->geometry.matrix_E[2][0] * p->geometry.matrix_E[2][0] + - p->geometry.matrix_E[2][1] * p->geometry.matrix_E[2][1] + - p->geometry.matrix_E[2][2] * p->geometry.matrix_E[2][2]; - - condition_number = - hydro_dimension_inv * sqrtf(condition_number_E * condition_number_Einv); - } - - if (condition_number > const_gizmo_max_condition_number && - p->geometry.wcorr > const_gizmo_min_wcorr) { -#ifdef GIZMO_PATHOLOGICAL_ERROR - error("Condition number larger than %g (%g)!", - const_gizmo_max_condition_number, condition_number); -#endif -#ifdef GIZMO_PATHOLOGICAL_WARNING - message("Condition number too large: %g (> %g, p->id: %llu)!", - condition_number, const_gizmo_max_condition_number, p->id); -#endif - /* add a correction to the number of neighbours for this particle */ - p->geometry.wcorr = const_gizmo_w_correction_factor * p->geometry.wcorr; - } + /* Finish operation on particle volume and matrix. */ + fvpm_compute_volume_and_matrix(p, ihdim); + const float volume = p->geometry.volume; + const float volume_inv = 1.f / volume; /* compute primitive variables */ /* eqns (3)-(5) */ @@ -399,7 +326,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( p->geometry.matrix_E[2][2] = 1.0f; /* reset the centroid to disable MFV velocity corrections for this particle */ - hydro_velocities_reset_centroids(p); + fvpm_reset_centroids(p); } /** diff --git a/src/hydro/Gizmo/hydro_getters.h b/src/hydro/Gizmo/hydro_getters.h index 775e548e561e3735e39e0a630e5e424304468dde..2c06da3c35ea199fa8990a4de24f000fa49227de 100644 --- a/src/hydro/Gizmo/hydro_getters.h +++ b/src/hydro/Gizmo/hydro_getters.h @@ -383,16 +383,4 @@ hydro_get_physical_internal_energy_dt(const struct part* restrict p, cosmo->a_factor_internal_energy; } -/** - * @brief Check if the gradient matrix for this particle is well behaved. - * - * @param p Particle. - * @return 1 if the gradient matrix is well behaved, 0 otherwise. - */ -__attribute__((always_inline)) INLINE static int -hydro_part_geometry_well_behaved(const struct part* restrict p) { - - return p->geometry.wcorr > const_gizmo_min_wcorr; -} - #endif /* SWIFT_GIZMO_HYDRO_GETTERS_H */ diff --git a/src/hydro/Gizmo/hydro_gradients_gizmo.h b/src/hydro/Gizmo/hydro_gradients_gizmo.h index 7d55eea38dd562dab1cfb6b091f66bca646284d5..16e349a0912386c501a246412a743e9a67df39ed 100644 --- a/src/hydro/Gizmo/hydro_gradients_gizmo.h +++ b/src/hydro/Gizmo/hydro_gradients_gizmo.h @@ -25,6 +25,7 @@ #ifndef SWIFT_GIZMO_MFM_HYDRO_GRADIENTS_H #define SWIFT_GIZMO_MFM_HYDRO_GRADIENTS_H +#include "fvpm_geometry.h" #include "hydro_getters.h" #include "hydro_setters.h" @@ -78,7 +79,7 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_collect( Wi[3] - Wj[3], Wi[4] - Wj[4]}; float wiBidx[3]; - if (hydro_part_geometry_well_behaved(pi)) { + if (fvpm_part_geometry_well_behaved(pi)) { wiBidx[0] = wi * (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]); wiBidx[1] = wi * (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]); wiBidx[2] = wi * (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]); @@ -123,7 +124,7 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_collect( kernel_deval(xj, &wj, &wj_dx); float wjBjdx[3]; - if (hydro_part_geometry_well_behaved(pj)) { + if (fvpm_part_geometry_well_behaved(pj)) { wjBjdx[0] = wj * (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); wjBjdx[1] = wj * (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); @@ -205,7 +206,7 @@ hydro_gradients_nonsym_collect(float r2, const float *dx, float hi, float hj, Wi[3] - Wj[3], Wi[4] - Wj[4]}; float wiBidx[3]; - if (hydro_part_geometry_well_behaved(pi)) { + if (fvpm_part_geometry_well_behaved(pi)) { wiBidx[0] = wi * (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]); wiBidx[1] = wi * (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]); wiBidx[2] = wi * (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]); @@ -259,7 +260,7 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_finalize( const float ihdim = pow_dimension(h_inv); float norm; - if (hydro_part_geometry_well_behaved(p)) { + if (fvpm_part_geometry_well_behaved(p)) { norm = ihdim; } else { const float ihdimp1 = pow_dimension_plus_one(h_inv); diff --git a/src/hydro/Gizmo/hydro_iact.h b/src/hydro/Gizmo/hydro_iact.h index 87ae75e73b4a3df72b271559b4ea1db5888cd648..088fcb1414f14bfcbf59a4f2b4a2d8b56e942d33 100644 --- a/src/hydro/Gizmo/hydro_iact.h +++ b/src/hydro/Gizmo/hydro_iact.h @@ -20,6 +20,7 @@ #define SWIFT_GIZMO_HYDRO_IACT_H #include "chemistry_additions.h" +#include "fvpm_geometry.h" #include "hydro_flux.h" #include "hydro_getters.h" #include "hydro_gradients.h" @@ -67,13 +68,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( pi->density.wcount += wi; pi->density.wcount_dh -= (hydro_dimension * wi + xi * wi_dx); - /* these are eqns. (1) and (2) in the summary */ - pi->geometry.volume += wi; - for (int k = 0; k < 3; k++) - for (int l = 0; l < 3; l++) - pi->geometry.matrix_E[k][l] += dx[k] * dx[l] * wi; - - hydro_velocities_update_centroid_left(pi, dx, wi); + /* Collect data for matrix construction */ + fvpm_accumulate_geometry_and_matrix(pi, wi, dx); + fvpm_update_centroid_left(pi, dx, wi); /* Compute density of pj. */ const float hj_inv = 1.0f / hj; @@ -83,13 +80,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( pj->density.wcount += wj; pj->density.wcount_dh -= (hydro_dimension * wj + xj * wj_dx); - /* these are eqns. (1) and (2) in the summary */ - pj->geometry.volume += wj; - for (int k = 0; k < 3; k++) - for (int l = 0; l < 3; l++) - pj->geometry.matrix_E[k][l] += dx[k] * dx[l] * wj; - - hydro_velocities_update_centroid_right(pj, dx, wj); + /* Collect data for matrix construction */ + fvpm_accumulate_geometry_and_matrix(pj, wj, dx); + fvpm_update_centroid_right(pj, dx, wj); } /** @@ -130,13 +123,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( pi->density.wcount += wi; pi->density.wcount_dh -= (hydro_dimension * wi + xi * wi_dx); - /* these are eqns. (1) and (2) in the summary */ - pi->geometry.volume += wi; - for (int k = 0; k < 3; k++) - for (int l = 0; l < 3; l++) - pi->geometry.matrix_E[k][l] += dx[k] * dx[l] * wi; - - hydro_velocities_update_centroid_left(pi, dx, wi); + fvpm_accumulate_geometry_and_matrix(pi, wi, dx); + fvpm_update_centroid_left(pi, dx, wi); } /** @@ -306,8 +294,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common( /* eqn. (7) */ float Anorm2 = 0.0f; float A[3]; - if (hydro_part_geometry_well_behaved(pi) && - hydro_part_geometry_well_behaved(pj)) { + if (fvpm_part_geometry_well_behaved(pi) && + fvpm_part_geometry_well_behaved(pj)) { /* in principle, we use Vi and Vj as weights for the left and right contributions to the generalized surface vector. However, if Vi and Vj are very different (because they have very diff --git a/src/hydro/Gizmo/hydro_part.h b/src/hydro/Gizmo/hydro_part.h index 731efd00ec50cda71331cbe372fbc146d6f853df..1d591ecdd79ec082478ea8c3ccd83cb5b0985539 100644 --- a/src/hydro/Gizmo/hydro_part.h +++ b/src/hydro/Gizmo/hydro_part.h @@ -23,6 +23,7 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "feedback_struct.h" +#include "fvpm_geometry_struct.h" #include "particle_splitting_struct.h" #include "rt_struct.h" #include "sink_struct.h" diff --git a/src/hydro/SPHENIX/hydro.h b/src/hydro/SPHENIX/hydro.h index f04c95abb3665bc6f20b7fc9d70f96c3ba32ebdd..26c5bdeeff4087273ab96f1922a0de48e640bed9 100644 --- a/src/hydro/SPHENIX/hydro.h +++ b/src/hydro/SPHENIX/hydro.h @@ -33,6 +33,7 @@ #include "dimension.h" #include "entropy_floor.h" #include "equation_of_state.h" +#include "fvpm_geometry.h" #include "hydro_parameters.h" #include "hydro_properties.h" #include "hydro_space.h" @@ -364,6 +365,16 @@ hydro_set_physical_internal_energy(struct part *p, struct xpart *xp, xp->u_full = u / cosmo->a_factor_internal_energy; } +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy_TESTING_SPH_RT(struct part *p, + const struct cosmology *cosmo, + const float u) { + + // TODO: This might be a problem. Hacky version to get code to compile. + // TODO: Cosmology might need attention + p->u = u / cosmo->a_factor_internal_energy; +} + /** * @brief Sets the drifted physical internal energy of a particle * @@ -581,6 +592,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->inhibited_exact = 0; p->limited_part = 0; #endif + + /* Init geometry for FVPM Radiative Transfer */ + fvpm_geometry_init(p); } /** @@ -629,6 +643,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->viscosity.div_v *= h_inv_dim_plus_one * rho_inv * a_inv2; p->viscosity.div_v += cosmo->H * hydro_dimension; + /* Finish matrix and volume computations for FVPM Radiative Transfer */ + fvpm_compute_volume_and_matrix(p, h_inv_dim); + #ifdef SWIFT_HYDRO_DENSITY_CHECKS p->n_density += kernel_root; p->n_density *= h_inv_dim; diff --git a/src/hydro/SPHENIX/hydro_iact.h b/src/hydro/SPHENIX/hydro_iact.h index 966414d52770866dbaf3e720639444a739d63304..478e77f22f0d3b56de9e1f68f626777a279496af 100644 --- a/src/hydro/SPHENIX/hydro_iact.h +++ b/src/hydro/SPHENIX/hydro_iact.h @@ -28,6 +28,7 @@ #include "adaptive_softening_iact.h" #include "adiabatic_index.h" +#include "fvpm_geometry.h" #include "hydro_parameters.h" #include "minmax.h" #include "signal_velocity.h" @@ -70,6 +71,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); adaptive_softening_add_correction_term(pi, ui, hi_inv, mj); + /* Collect data for FVPM matrix construction */ + fvpm_accumulate_geometry_and_matrix(pi, wi, dx); + fvpm_update_centroid_left(pi, dx, wi); + /* Compute density of pj. */ const float hj_inv = 1.f / hj; const float uj = r * hj_inv; @@ -81,6 +86,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( pj->density.wcount_dh -= (hydro_dimension * wj + uj * wj_dx); adaptive_softening_add_correction_term(pj, uj, hj_inv, mi); + /* Collect data for FVPM matrix construction */ + fvpm_accumulate_geometry_and_matrix(pj, wj, dx); + fvpm_update_centroid_right(pj, dx, wj); + /* Now we need to compute the div terms */ const float r_inv = r ? 1.0f / r : 0.0f; const float faci = mj * wi_dx * r_inv; @@ -153,6 +162,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); adaptive_softening_add_correction_term(pi, ui, h_inv, mj); + /* Collect data for FVPM matrix construction */ + fvpm_accumulate_geometry_and_matrix(pi, wi, dx); + fvpm_update_centroid_left(pi, dx, wi); + const float r_inv = r ? 1.0f / r : 0.0f; const float faci = mj * wi_dx * r_inv; diff --git a/src/hydro/SPHENIX/hydro_part.h b/src/hydro/SPHENIX/hydro_part.h index 41d8c8b9bb65761d9f838c2ef12064e4d52ee6ea..e6e4fafe5f45a1a11a338cc4d1544b91d7b0a3a5 100644 --- a/src/hydro/SPHENIX/hydro_part.h +++ b/src/hydro/SPHENIX/hydro_part.h @@ -32,6 +32,7 @@ #include "cooling_struct.h" #include "csds.h" #include "feedback_struct.h" +#include "fvpm_geometry_struct.h" #include "mhd_struct.h" #include "particle_splitting_struct.h" #include "pressure_floor_struct.h" @@ -161,8 +162,8 @@ struct part { } diffusion; /* Store density/force specific stuff. */ - union { + union { /** * @brief Structure for the variables only used in the density loop over * neighbours. @@ -310,6 +311,9 @@ struct part { char limited_part; #endif + /*! Geometrical quantities used for Finite Volume Particle Method RT. */ + struct fvpm_geometry_struct geometry; + } SWIFT_STRUCT_ALIGN; #endif /* SWIFT_SPHENIX_HYDRO_PART_H */ diff --git a/src/hydro/Shadowswift/hydro.h b/src/hydro/Shadowswift/hydro.h index 2343d7706103b598a186aa1a092c423d1156ecc5..6c52adb15ecf8c3c5aa8489fca9e65bd5e509db6 100644 --- a/src/hydro/Shadowswift/hydro.h +++ b/src/hydro/Shadowswift/hydro.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). + * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leideuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -16,602 +16,249 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#ifndef SWIFT_SHADOWSWIFT_HYDRO_H -#define SWIFT_SHADOWSWIFT_HYDRO_H +#ifndef SWIFT_NONE_HYDRO_H +#define SWIFT_NONE_HYDRO_H + +/** + * @file Shadowswift/hydro.h + * @brief Empty implementation + */ #include "adiabatic_index.h" #include "approx_math.h" #include "cosmology.h" +#include "dimension.h" #include "entropy_floor.h" #include "equation_of_state.h" -#include "hydro_gradients.h" +#include "hydro_parameters.h" #include "hydro_properties.h" #include "hydro_space.h" +#include "kernel_hydro.h" +#include "minmax.h" #include "pressure_floor.h" -#include "voronoi_algorithm.h" #include <float.h> /** - * @brief Computes the hydro time-step of a given particle - * - * @param p Pointer to the particle data. - * @param xp Pointer to the extended particle data. - * @param hydro_properties Pointer to the hydro parameters. - */ -__attribute__((always_inline)) INLINE static float hydro_compute_timestep( - const struct part* restrict p, const struct xpart* restrict xp, - const struct hydro_props* restrict hydro_properties, - const struct cosmology* restrict cosmo) { - - const float CFL_condition = hydro_properties->CFL_condition; - - float vrel[3]; - vrel[0] = p->primitives.v[0] - xp->v_full[0]; - vrel[1] = p->primitives.v[1] - xp->v_full[1]; - vrel[2] = p->primitives.v[2] - xp->v_full[2]; - float vmax = - sqrtf(vrel[0] * vrel[0] + vrel[1] * vrel[1] + vrel[2] * vrel[2]) + - sqrtf(hydro_gamma * p->primitives.P / p->primitives.rho); - vmax = max(vmax, p->timestepvars.vmax); - - const float psize = - cosmo->a * - powf(p->cell.volume / hydro_dimension_unit_sphere, hydro_dimension_inv); - float dt = FLT_MAX; - if (vmax > 0.) { - dt = psize / vmax; - } - return CFL_condition * dt; -} - -/** - * @brief Does some extra hydro operations once the actual physical time step - * for the particle is known. - * - * We use this to store the physical time step, since it is used for the flux - * exchange during the force loop. + * @brief Returns the comoving internal energy of a particle at the last + * time the particle was kicked. * - * We also set the active flag of the particle to inactive. It will be set to - * active in hydro_init_part, which is called the next time the particle becomes - * active. - * - * @param p The particle to act upon. - * @param dt Physical time step of the particle during the next step. + * @param p The particle of interest + * @param xp The extended data of the particle of interest. */ -__attribute__((always_inline)) INLINE static void hydro_timestep_extra( - struct part* p, float dt) { +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy(const struct part *restrict p, + const struct xpart *restrict xp) { - p->force.dt = dt; - p->force.active = 0; + error("Empty implementation"); + return -1.f; } /** - * @brief Initialises the particles for the first time + * @brief Returns the physical internal energy of a particle at the last + * time the particle was kicked. * - * This function is called only once just after the ICs have been - * read in to do some conversions. - * - * In this case, we copy the particle velocities into the corresponding - * primitive variable field. We do this because the particle velocities in GIZMO - * can be independent of the actual fluid velocity. The latter is stored as a - * primitive variable and integrated using the linear momentum, a conserved - * variable. - * - * @param p The particle to act upon - * @param xp The extended particle data to act upon + * @param p The particle of interest. + * @param xp The extended data of the particle of interest. + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static void hydro_first_init_part( - struct part* p, struct xpart* xp) { - - const float mass = p->conserved.mass; - - p->time_bin = 0; - - p->primitives.v[0] = p->v[0]; - p->primitives.v[1] = p->v[1]; - p->primitives.v[2] = p->v[2]; - - p->conserved.momentum[0] = mass * p->primitives.v[0]; - p->conserved.momentum[1] = mass * p->primitives.v[1]; - p->conserved.momentum[2] = mass * p->primitives.v[2]; - -#ifdef EOS_ISOTHERMAL_GAS - p->conserved.energy = mass * gas_internal_energy_from_entropy(0.f, 0.f); -#else - p->conserved.energy *= mass; -#endif - -#ifdef SHADOWFAX_TOTAL_ENERGY - p->conserved.energy += 0.5f * (p->conserved.momentum[0] * p->primitives.v[0] + - p->conserved.momentum[1] * p->primitives.v[1] + - p->conserved.momentum[2] * p->primitives.v[2]); -#endif - -#if defined(SHADOWFAX_FIX_CELLS) - p->v[0] = 0.; - p->v[1] = 0.; - p->v[2] = 0.; -#else - p->v[0] = p->primitives.v[0]; - p->v[1] = p->primitives.v[1]; - p->v[2] = p->primitives.v[2]; -#endif - - /* set the initial velocity of the cells */ - xp->v_full[0] = p->v[0]; - xp->v_full[1] = p->v[1]; - xp->v_full[2] = p->v[2]; +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy(const struct part *restrict p, + const struct xpart *restrict xp, + const struct cosmology *cosmo) { - /* ignore accelerations present in the initial condition */ - p->a_hydro[0] = 0.0f; - p->a_hydro[1] = 0.0f; - p->a_hydro[2] = 0.0f; + error("Empty implementation"); + return -1.f; } /** - * @brief Prepares a particle for the volume calculation. + * @brief Returns the comoving internal energy of a particle drifted to the + * current time. * - * Simply makes sure all necessary variables are initialized to zero. - * Initializes the Voronoi cell. - * - * @param p The particle to act upon - * @param hs #hydro_space containing extra information about the space. + * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static void hydro_init_part( - struct part* p, const struct hydro_space* hs) { - - /* make sure we don't enter the no neighbour case in runner.c */ - p->density.wcount = 1.0f; - p->density.wcount_dh = 0.0f; - - voronoi_cell_init(&p->cell, p->x, hs->anchor, hs->side); +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_comoving_internal_energy(const struct part *restrict p) { - /* Set the active flag to active. */ - p->force.active = 1; + error("Empty implementation"); + return -1.f; } /** - * @brief Finishes the volume calculation. + * @brief Returns the physical internal energy of a particle drifted to the + * current time. * - * Calls the finalize method on the Voronoi cell, which calculates the volume - * and centroid of the cell. We use the return value of this function to set - * a new value for the smoothing length and possibly force another iteration - * of the volume calculation for this particle. We then use the volume to - * convert conserved variables into primitive variables. - * - * This method also initializes the gradient variables (if gradients are used). - * - * @param p The particle to act upon. + * @param p The particle of interest. + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static void hydro_end_density( - struct part* restrict p, const struct cosmology* cosmo) { - - float volume; - float m, momentum[3], energy; - - hydro_gradients_init(p); - - float hnew = voronoi_cell_finalize(&p->cell); - /* Enforce hnew as new smoothing length in the iteration - This is annoyingly difficult, as we do not have access to the variables - that govern the loop... - So here's an idea: let's force in some method somewhere that makes sure - r->e->hydro_properties->target_neighbours is 1, and - r->e->hydro_properties->delta_neighbours is 0. - This way, we can accept an iteration by setting p->density.wcount to 1. - To get the right correction for h, we set wcount to something else - (say 0), and then set p->density.wcount_dh to 1/(hnew-h). */ - if (hnew < p->h) { - /* Iteration succesful: we accept, but manually set h to a smaller value - for the next time step */ - const float hinvdim = pow_dimension(1.0f / p->h); - p->density.wcount = hinvdim; - p->h = 1.1f * hnew; - } else { - /* Iteration not succesful: we force h to become 1.1*hnew */ - p->density.wcount = 0.0f; - p->density.wcount_dh = p->h / (1.1f * hnew - p->h); - return; - } - volume = p->cell.volume; - -#ifdef SWIFT_DEBUG_CHECKS - /* the last condition checks for NaN: a NaN value always evaluates to false, - even when checked against itself */ - if (volume == 0. || volume == INFINITY || volume != volume) { - error("Invalid value for cell volume (%g)!", volume); - } -#endif - - /* compute primitive variables */ - /* eqns (3)-(5) */ - m = p->conserved.mass; - if (m > 0.) { - momentum[0] = p->conserved.momentum[0]; - momentum[1] = p->conserved.momentum[1]; - momentum[2] = p->conserved.momentum[2]; - p->primitives.rho = m / volume; - p->primitives.v[0] = momentum[0] / m; - p->primitives.v[1] = momentum[1] / m; - p->primitives.v[2] = momentum[2] / m; - - energy = p->conserved.energy; - -#ifdef SHADOWFAX_TOTAL_ENERGY - energy -= 0.5f * (momentum[0] * p->primitives.v[0] + - momentum[1] * p->primitives.v[1] + - momentum[2] * p->primitives.v[2]); -#endif - - energy /= m; - - p->primitives.P = - gas_pressure_from_internal_energy(p->primitives.rho, energy); - } else { - p->primitives.rho = 0.; - p->primitives.v[0] = 0.; - p->primitives.v[1] = 0.; - p->primitives.v[2] = 0.; - p->primitives.P = 0.; - } - -#ifdef SWIFT_DEBUG_CHECKS - if (p->primitives.rho < 0.) { - error("Negative density!"); - } - - if (p->primitives.P < 0.) { - error("Negative pressure!"); - } -#endif +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_physical_internal_energy(const struct part *restrict p, + const struct cosmology *cosmo) { + error("Empty implementation"); + return -1.f; } /** - * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. + * @brief Returns the comoving pressure of a particle * - * @param p The particle to act upon - * @param xp The extended particle data to act upon + * Computes the pressure based on the particle's properties. + * + * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( - struct part* restrict p, struct xpart* restrict xp, - const struct cosmology* cosmo) { - - /* Some smoothing length multiples. */ - const float h = p->h; - const float h_inv = 1.0f / h; /* 1/h */ - const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure( + const struct part *restrict p) { - /* Re-set problematic values */ - p->density.wcount = kernel_root * h_inv_dim; - p->density.wcount_dh = 0.f; + error("Empty implementation"); + return -1.f; } /** - * @brief Prepare a particle for the force calculation. + * @brief Returns the physical pressure of a particle * - * This function is called in the ghost task to convert some quantities coming - * from the density loop over neighbours into quantities ready to be used in the - * force loop over neighbours. Quantities are typically read from the density - * sub-structure and written to the force sub-structure. - * Examples of calculations done here include the calculation of viscosity term - * constants, thermal conduction terms, hydro conversions, etc. + * Computes the pressure based on the particle's properties and + * convert it to physical coordinates. * - * @param p The particle to act upon - * @param xp The extended particle data to act upon - * @param cosmo The current cosmological model. - * @param hydro_props Hydrodynamic properties. - * @param dt_alpha The time-step used to evolve non-cosmological quantities such - * as the artificial viscosity. - * @param dt_therm The time-step used to evolve hydrodynamical quantities. + * @param p The particle of interest + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static void hydro_prepare_force( - struct part* restrict p, struct xpart* restrict xp, - const struct cosmology* cosmo, const struct hydro_props* hydro_props, - const struct pressure_floor_props* pressure_floor, const float dt_alpha, - const float dt_therm) { - - /* Initialize time step criterion variables */ - p->timestepvars.vmax = 0.0f; - - /* Set the actual velocity of the particle */ - p->force.v_full[0] = xp->v_full[0]; - p->force.v_full[1] = xp->v_full[1]; - p->force.v_full[2] = xp->v_full[2]; - - p->conserved.flux.mass = 0.0f; - p->conserved.flux.momentum[0] = 0.0f; - p->conserved.flux.momentum[1] = 0.0f; - p->conserved.flux.momentum[2] = 0.0f; - p->conserved.flux.energy = 0.0f; +__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure( + const struct part *restrict p, const struct cosmology *cosmo) { + + error("Empty implementation"); + return -1.f; } /** - * @brief Prepare a particle for the gradient calculation. - * - * This function is called after the density loop and before the gradient loop. + * @brief Returns the comoving entropy of a particle at the last + * time the particle was kicked. * - * We use it to set the physical timestep for the particle and to copy the - * actual velocities, which we need to boost our interfaces during the flux - * calculation. We also initialize the variables used for the time step - * calculation. - * - * @param p The particle to act upon. - * @param xp The extended particle data to act upon. - * @param cosmo The cosmological model. - * @param hydro_props Hydrodynamic properties. + * @param p The particle of interest. + * @param xp The extended data of the particle of interest. */ -__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( - struct part* restrict p, struct xpart* restrict xp, - const struct cosmology* cosmo, const struct hydro_props* hydro_props, - const struct pressure_floor_props* pressure_floor) { +__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( + const struct part *restrict p, const struct xpart *restrict xp) { - /* Initialize time step criterion variables */ - p->timestepvars.vmax = 0.; + error("Empty implementation"); + return -1.f; } /** - * @brief Resets the variables that are required for a gradient calculation. + * @brief Returns the physical entropy of a particle at the last + * time the particle was kicked. * - * This function is called after hydro_prepare_gradient. - * - * @param p The particle to act upon. - * @param xp The extended particle data to act upon. + * @param p The particle of interest. + * @param xp The extended data of the particle of interest. * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static void hydro_reset_gradient( - struct part* restrict p) {} - -/** - * @brief Finishes the gradient calculation. - * - * Just a wrapper around hydro_gradients_finalize, which can be an empty method, - * in which case no gradients are used. - * - * @param p The particle to act upon. - */ -__attribute__((always_inline)) INLINE static void hydro_end_gradient( - struct part* p) { +__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( + const struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { - hydro_gradients_finalize(p); + error("Empty implementation"); + return -1.f; } /** - * @brief Reset acceleration fields of a particle - * - * This is actually not necessary for Shadowswift, since we just set the - * accelerations after the flux calculation. + * @brief Returns the comoving entropy of a particle drifted to the + * current time. * - * @param p The particle to act upon. + * @param p The particle of interest. */ -__attribute__((always_inline)) INLINE static void hydro_reset_acceleration( - struct part* p) { - - /* Reset the acceleration. */ - p->a_hydro[0] = 0.0f; - p->a_hydro[1] = 0.0f; - p->a_hydro[2] = 0.0f; +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_comoving_entropy(const struct part *restrict p) { - /* Reset the time derivatives. */ - p->force.h_dt = 0.0f; + error("Empty implementation"); + return -1.f; } /** - * @brief Sets the values to be predicted in the drifts to their values at a - * kick time + * @brief Returns the physical entropy of a particle drifted to the + * current time. * - * @param p The particle. - * @param xp The extended data of this particle. + * @param p The particle of interest. * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part* restrict p, const struct xpart* restrict xp, - const struct cosmology* cosmo, - const struct pressure_floor_props* pressure_floor) {} - -/** - * @brief Converts the hydrodynamic variables from the initial condition file to - * conserved variables that can be used during the integration - * - * Requires the volume to be known. - * - * The initial condition file contains a mixture of primitive and conserved - * variables. Mass is a conserved variable, and we just copy the particle - * mass into the corresponding conserved quantity. We need the volume to - * also derive a density, which is then used to convert the internal energy - * to a pressure. However, we do not actually use these variables anymore. - * We do need to initialize the linear momentum, based on the mass and the - * velocity of the particle. - * - * @param p The particle to act upon. - * @param xp The extended particle data to act upon. - */ -__attribute__((always_inline)) INLINE static void hydro_convert_quantities( - struct part* p, struct xpart* xp, const struct cosmology* cosmo, - const struct hydro_props* hydro_props, - const struct pressure_floor_props* pressure_floor) {} - -/** - * @brief Extra operations to be done during the drift - * - * Not used for Shadowswift. - * - * @param p Particle to act upon. - * @param xp The extended particle data to act upon. - * @param dt The drift time-step. - */ -__attribute__((always_inline)) INLINE static void hydro_predict_extra( - struct part* p, struct xpart* xp, float dt_drift, float dt_therm, - float dt_kick_grav, const struct cosmology* cosmo, - const struct hydro_props* hydro_props, - const struct entropy_floor_properties* floor_props, - const struct pressure_floor_props* pressure_floor) {} +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_physical_entropy(const struct part *restrict p, + const struct cosmology *cosmo) { -/** - * @brief Set the particle acceleration after the flux loop. - * - * @param p Particle to act upon. - */ -__attribute__((always_inline)) INLINE static void hydro_end_force( - struct part* p, const struct cosmology* cosmo) {} + error("Empty implementation"); + return -1.f; +} /** - * @brief Extra operations done during the kick - * - * Not used for Shadowswift. + * @brief Returns the comoving sound speed of a particle * - * @param p Particle to act upon. - * @param xp Extended particle data to act upon. - * @param dt Physical time step. + * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static void hydro_kick_extra( - struct part* p, struct xpart* xp, float dt, float dt_grav, - float dt_grav_mesh, float dt_hydro, float dt_kick_corr, - const struct cosmology* cosmo, const struct hydro_props* hydro_props, - const struct entropy_floor_properties* floor_props) { - - /* Update the conserved variables. We do this here and not in the kick, - since we need the updated variables below. */ - p->conserved.mass += p->conserved.flux.mass * dt; - p->conserved.momentum[0] += p->conserved.flux.momentum[0] * dt; - p->conserved.momentum[1] += p->conserved.flux.momentum[1] * dt; - p->conserved.momentum[2] += p->conserved.flux.momentum[2] * dt; - -#ifdef EOS_ISOTHERMAL_GAS - /* reset the thermal energy */ - p->conserved.energy = - p->conserved.mass * gas_internal_energy_from_entropy(0.f, 0.f); -#else - p->conserved.energy += p->conserved.flux.energy * dt; -#endif - -#if defined(SHADOWFAX_FIX_CELLS) - p->v[0] = 0.0f; - p->v[1] = 0.0f; - p->v[2] = 0.0f; -#else - if (p->conserved.mass > 0.0f && p->primitives.rho > 0.0f) { - - const float inverse_mass = 1.f / p->conserved.mass; - - /* Normal case: set particle velocity to fluid velocity. */ - p->v[0] = p->conserved.momentum[0] * inverse_mass; - p->v[1] = p->conserved.momentum[1] * inverse_mass; - p->v[2] = p->conserved.momentum[2] * inverse_mass; - -#ifdef SHADOWFAX_STEER_CELL_MOTION - float centroid[3], d[3]; - float volume, csnd, R, vfac, fac, dnrm; - voronoi_get_centroid(&p->cell, centroid); - d[0] = centroid[0] - p->x[0]; - d[1] = centroid[1] - p->x[1]; - d[2] = centroid[2] - p->x[2]; - dnrm = sqrtf(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); - csnd = sqrtf(hydro_gamma * p->primitives.P / p->primitives.rho); - volume = p->cell.volume; - R = get_radius_dimension_sphere(volume); - fac = 4.0f * dnrm / R; - if (fac > 0.9f) { - if (fac < 1.1f) { - vfac = csnd * (dnrm - 0.225f * R) / dnrm / (0.05f * R); - } else { - vfac = csnd / dnrm; - } - p->v[0] += vfac * d[0]; - p->v[1] += vfac * d[1]; - p->v[2] += vfac * d[2]; - } -#endif - - } else { - p->v[0] = 0.; - p->v[1] = 0.; - p->v[2] = 0.; - } -#endif - - /* Now make sure all velocity variables are up to date. */ - xp->v_full[0] = p->v[0]; - xp->v_full[1] = p->v[1]; - xp->v_full[2] = p->v[2]; +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_soundspeed(const struct part *restrict p) { - if (p->gpart) { - p->gpart->v_full[0] = p->v[0]; - p->gpart->v_full[1] = p->v[1]; - p->gpart->v_full[2] = p->v[2]; - } + error("Empty implementation"); + return -1.f; } /** - * @brief Returns the internal energy of a particle + * @brief Returns the physical sound speed of a particle * - * @param p The particle of interest. - * @return Internal energy of the particle. + * @param p The particle of interest + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float hydro_get_internal_energy( - const struct part* restrict p) { +__attribute__((always_inline)) INLINE static float +hydro_get_physical_soundspeed(const struct part *restrict p, + const struct cosmology *cosmo) { - if (p->primitives.rho > 0.) { - return gas_internal_energy_from_pressure(p->primitives.rho, - p->primitives.P); - } else { - return 0.; - } + error("Empty implementation"); + return -1.f; } /** - * @brief Returns the entropy of a particle + * @brief Returns the physical density of a particle * - * @param p The particle of interest. - * @return Entropy of the particle. + * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_entropy( - const struct part* restrict p) { +__attribute__((always_inline)) INLINE static float hydro_get_comoving_density( + const struct part *restrict p) { - if (p->primitives.rho > 0.) { - return gas_entropy_from_pressure(p->primitives.rho, p->primitives.P); - } else { - return 0.; - } + error("Empty implementation"); + return -1.f; } /** - * @brief Returns the sound speed of a particle + * @brief Returns the comoving density of a particle. * - * @param p The particle of interest. - * @param Sound speed of the particle. + * @param p The particle of interest + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float hydro_get_soundspeed( - const struct part* restrict p) { +__attribute__((always_inline)) INLINE static float hydro_get_physical_density( + const struct part *restrict p, const struct cosmology *cosmo) { - if (p->primitives.rho > 0.) { - return gas_soundspeed_from_pressure(p->primitives.rho, p->primitives.P); - } else { - return 0.; - } + error("Empty implementation"); + return -1.f; } /** - * @brief Returns the pressure of a particle + * @brief Returns the mass of a particle * * @param p The particle of interest - * @param Pressure of the particle. */ -__attribute__((always_inline)) INLINE static float hydro_get_pressure( - const struct part* restrict p) { +__attribute__((always_inline)) INLINE static float hydro_get_mass( + const struct part *restrict p) { - return p->primitives.P; + error("Empty implementation"); + return -1.f; } /** - * @brief Returns the mass of a particle + * @brief Sets the mass of a particle * * @param p The particle of interest + * @param m The mass to set. */ -__attribute__((always_inline)) INLINE static float hydro_get_mass( - const struct part* restrict p) { +__attribute__((always_inline)) INLINE static void hydro_set_mass( + struct part *restrict p, float m) { - return p->conserved.mass; + error("Empty implementation"); } /** @@ -619,349 +266,478 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( * * @param p The particle of interest * @param xp The extended data of the particle. - * @param dt The time since the last kick. + * @param dt_kick_hydro The time (for hydro accelerations) since the last kick. + * @param dt_kick_grav The time (for gravity accelerations) since the last kick. * @param v (return) The velocities at the current time. */ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( - const struct part* restrict p, const struct xpart* xp, float dt_kick_hydro, + const struct part *restrict p, const struct xpart *xp, float dt_kick_hydro, float dt_kick_grav, float v[3]) { - v[0] = p->v[0]; - v[1] = p->v[1]; - v[2] = p->v[2]; + v[0] = xp->v_full[0] + p->a_hydro[0] * dt_kick_hydro + + xp->a_grav[0] * dt_kick_grav; + v[1] = xp->v_full[1] + p->a_hydro[1] * dt_kick_hydro + + xp->a_grav[1] * dt_kick_grav; + v[2] = xp->v_full[2] + p->a_hydro[2] * dt_kick_hydro + + xp->a_grav[2] * dt_kick_grav; } /** - * @brief Modifies the thermal state of a particle to the imposed internal - * energy + * @brief Returns the time derivative of co-moving internal energy of a particle * - * This overrides the current state of the particle but does *not* change its - * time-derivatives + * We assume a constant density. * - * @param p The particle - * @param u The new internal energy + * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static void hydro_set_internal_energy( - struct part* restrict p, float u) { - - if (p->primitives.rho > 0.) { - p->conserved.energy = u * p->conserved.mass; - -#ifdef SHADOWFAX_TOTAL_ENERGY - p->conserved.energy += - 0.5f * (p->conserved.momentum[0] * p->primitives.v[0] + - p->conserved.momentum[1] * p->primitives.v[1] + - p->conserved.momentum[2] * p->primitives.v[2]); -#endif +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy_dt(const struct part *restrict p) { - p->primitives.P = gas_pressure_from_internal_energy(p->primitives.rho, u); - } + error("Empty implementation"); + return -1.f; } /** - * @brief Modifies the thermal state of a particle to the imposed entropy + * @brief Returns the time derivative of internal energy of a particle * - * This overrides the current state of the particle but does *not* change its - * time-derivatives + * We assume a constant density. * - * @param p The particle - * @param S The new entropy + * @param p The particle of interest + * @param cosmo Cosmology data structure */ -__attribute__((always_inline)) INLINE static void hydro_set_entropy( - struct part* restrict p, float S) { - - if (p->primitives.rho > 0.) { - p->conserved.energy = - gas_internal_energy_from_entropy(p->primitives.rho, S) * - p->conserved.mass; - -#ifdef SHADOWFAX_TOTAL_ENERGY - p->conserved.energy += - 0.5f * (p->conserved.momentum[0] * p->primitives.v[0] + - p->conserved.momentum[1] * p->primitives.v[1] + - p->conserved.momentum[2] * p->primitives.v[2]); -#endif +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy_dt(const struct part *restrict p, + const struct cosmology *cosmo) { - p->primitives.P = gas_pressure_from_entropy(p->primitives.rho, S); - } + error("Empty implementation"); + return -1.f; } /** - * @brief Sets the mass of a particle + * @brief Sets the time derivative of the co-moving internal energy of a + * particle * - * @param p The particle of interest - * @param m The mass to set. + * We assume a constant density for the conversion to entropy. + * + * @param p The particle of interest. + * @param du_dt The new time derivative of the internal energy. */ -__attribute__((always_inline)) INLINE static void hydro_set_mass( - struct part* restrict p, float m) { +__attribute__((always_inline)) INLINE static void +hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) { - p->conserved.mass = m; + error("Empty implementation"); } /** - * @brief Overwrite the initial internal energy of a particle. + * @brief Returns the time derivative of internal energy of a particle * - * Note that in the cases where the thermodynamic variable is not - * internal energy but gets converted later, we must overwrite that - * field. The conversion to the actual variable happens later after - * the initial fake time-step. + * We assume a constant density. * - * @param p The #part to write to. - * @param u_init The new initial internal energy. + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param du_dt The new time derivative of the internal energy. */ __attribute__((always_inline)) INLINE static void -hydro_set_init_internal_energy(struct part* p, float u_init) { - - p->conserved.energy = u_init * p->conserved.mass; -#ifdef GIZMO_TOTAL_ENERGY - /* add the kinetic energy */ - p->conserved.energy += 0.5f * p->conserved.mass * - (p->conserved.momentum[0] * p->primitives.v[0] + - p->conserved.momentum[1] * p->primitives.v[1] + - p->conserved.momentum[2] * p->primitives.v[2]); -#endif - p->primitives.P = hydro_gamma_minus_one * p->primitives.rho * u_init; +hydro_set_physical_internal_energy_dt(struct part *restrict p, + const struct cosmology *cosmo, + float du_dt) { + + error("Empty implementation"); } /** - * @brief Returns the comoving internal energy of a particle + * @brief Sets the physical entropy of a particle * * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy */ -__attribute__((always_inline)) INLINE static float -hydro_get_comoving_internal_energy(const struct part* restrict p) { +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const float entropy) { - if (p->primitives.rho > 0.) - return gas_internal_energy_from_pressure(p->primitives.rho, - p->primitives.P); - else - return 0.; + error("Empty implementation"); } /** - * @brief Returns the comoving entropy of a particle + * @brief Sets the physical internal energy of a particle * * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy */ -__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( - const struct part* restrict p) { +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy(struct part *p, struct xpart *xp, + const struct cosmology *cosmo, + const float u) { - if (p->primitives.rho > 0.) { - return gas_entropy_from_pressure(p->primitives.rho, p->primitives.P); - } else { - return 0.; - } + error("Empty implementation"); } /** - * @brief Returns the sound speed of a particle + * @brief Sets the drifted physical internal energy of a particle * * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param u The physical internal energy */ -__attribute__((always_inline)) INLINE static float -hydro_get_comoving_soundspeed(const struct part* restrict p) { - - if (p->primitives.rho > 0.) - return gas_soundspeed_from_pressure(p->primitives.rho, p->primitives.P); - else - return 0.; +__attribute__((always_inline)) INLINE static void +hydro_set_drifted_physical_internal_energy( + struct part *p, const struct cosmology *cosmo, + const struct pressure_floor_props *pressure_floor, const float u) { + error("Empty implementation"); } /** - * @brief Returns the comoving pressure of a particle + * @brief Correct the signal velocity of the particle partaking in + * supernova (kinetic) feedback based on the velocity kick the particle receives * - * @param p The particle of interest + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param dv_phys The velocity kick received by the particle expressed in + * physical units (note that dv_phys must be positive or equal to zero) */ -__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure( - const struct part* restrict p) { +__attribute__((always_inline)) INLINE static void +hydro_set_v_sig_based_on_velocity_kick(struct part *p, + const struct cosmology *cosmo, + const float dv_phys) { - return p->primitives.P; + error("Empty implementation"); } /** - * @brief Returns the comoving density of a particle + * @brief Update the value of the viscosity alpha for the scheme. * - * @param p The particle of interest + * @param p the particle of interest + * @param alpha the new value for the viscosity coefficient. */ -__attribute__((always_inline)) INLINE static float hydro_get_comoving_density( - const struct part* restrict p) { - - return p->primitives.rho; +__attribute__((always_inline)) INLINE static void hydro_set_viscosity_alpha( + struct part *restrict p, float alpha) { + error("Empty implementation"); } /** - * @brief Returns the physical internal energy of a particle + * @brief Update the value of the diffusive coefficients to the + * feedback reset value for the scheme. * - * @param p The particle of interest. - * @param cosmo The cosmological model. + * @param p the particle of interest */ -__attribute__((always_inline)) INLINE static float -hydro_get_physical_internal_energy(const struct part* restrict p, - const struct xpart* restrict xp, - const struct cosmology* cosmo) { - - return cosmo->a_factor_internal_energy * - hydro_get_comoving_internal_energy(p); +__attribute__((always_inline)) INLINE static void +hydro_diffusive_feedback_reset(struct part *restrict p) { + error("Empty implementation"); } /** - * @brief Returns the physical internal energy of a particle + * @brief Computes the hydro time-step of a given particle * - * @param p The particle of interest. + * This function returns the time-step of a particle given its hydro-dynamical + * state. A typical time-step calculation would be the use of the CFL condition. + * + * @param p Pointer to the particle data + * @param xp Pointer to the extended particle data + * @param hydro_properties The SPH parameters * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( - const struct part* restrict p, const struct cosmology* cosmo) { +__attribute__((always_inline)) INLINE static float hydro_compute_timestep( + const struct part *restrict p, const struct xpart *restrict xp, + const struct hydro_props *restrict hydro_properties, + const struct cosmology *restrict cosmo) { - /* Note: no cosmological conversion required here with our choice of - * coordinates. */ - return hydro_get_comoving_entropy(p); + return FLT_MAX; } /** - * @brief Returns the physical sound speed of a particle + * @brief Compute the signal velocity between two gas particles * - * @param p The particle of interest. - * @param cosmo The cosmological model. + * Just return -1 in this empty implementation. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. */ -__attribute__((always_inline)) INLINE static float -hydro_get_physical_soundspeed(const struct part* restrict p, - const struct cosmology* cosmo) { +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { - return cosmo->a_factor_sound_speed * hydro_get_comoving_soundspeed(p); + return -1.; } /** - * @brief Sets the physical entropy of a particle + * @brief Does some extra hydro operations once the actual physical time step + * for the particle is known. * - * @param p The particle of interest. - * @param xp The extended particle data. - * @param cosmo Cosmology data structure - * @param entropy The physical entropy + * @param p The particle to act upon. + * @param dt Physical time step of the particle during the next step. */ -__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( - struct part* p, struct xpart* xp, const struct cosmology* cosmo, - const float entropy) { +__attribute__((always_inline)) INLINE static void hydro_timestep_extra( + struct part *p, float dt) {} - error("Needs implementing"); +/** + * @brief Prepares a particle for the density calculation. + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the various density loop over neighbours. Typically, all fields of the + * density sub-structure of a particle get zeroed in here. + * + * @param p The particle to act upon + * @param hs #hydro_space containing hydro specific space information. + */ +__attribute__((always_inline)) INLINE static void hydro_init_part( + struct part *restrict p, const struct hydro_space *hs) { + + p->density.wcount = 0.f; + p->density.wcount_dh = 0.f; } /** - * @brief Sets the physical internal energy of a particle + * @brief Finishes the density calculation. * - * @param p The particle of interest. - * @param xp The extended particle data. - * @param cosmo Cosmology data structure - * @param u The physical internal energy + * Multiplies the density and number of neighbours by the appropiate constants + * and add the self-contribution term. + * Additional quantities such as velocity gradients will also get the final + * terms added to them here. + * + * Also adds/multiplies the cosmological terms if need be. + * + * @param p The particle to act upon + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static void -hydro_set_physical_internal_energy(struct part* p, struct xpart* xp, - const struct cosmology* cosmo, - const float u) { - error("Need implementing"); -} +__attribute__((always_inline)) INLINE static void hydro_end_density( + struct part *restrict p, const struct cosmology *cosmo) {} /** - * @brief Sets the drifted physical internal energy of a particle + * @brief Prepare a particle for the gradient calculation. * - * @param p The particle of interest. - * @param cosmo Cosmology data structure - * @param u The physical internal energy + * This function is called after the density loop and before the gradient loop. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + * @param hydro_props Hydrodynamic properties. */ -__attribute__((always_inline)) INLINE static void -hydro_set_drifted_physical_internal_energy(struct part* p, - const struct cosmology* cosmo, - const float u) { - error("Need implementing"); -} +__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct pressure_floor_props *pressure_floor) {} /** - * @brief Gets the drifted physical internal energy of a particle + * @brief Resets the variables that are required for a gradient calculation. * - * @param p The particle of interest. - * @param cosmo Cosmology data structure + * This function is called after hydro_prepare_gradient. + * Nothing to do in this scheme as the gradient loop is not used. * - * @return The physical internal energy + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float -hydro_get_drifted_physical_internal_energy(const struct part* p, - const struct cosmology* cosmo) { - error("Need implementing"); +__attribute__((always_inline)) INLINE static void hydro_reset_gradient( + struct part *restrict p) {} - return 0; -} +/** + * @brief Finishes the gradient calculation. + * + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + */ +__attribute__((always_inline)) INLINE static void hydro_end_gradient( + struct part *p) {} /** - * @brief Gets the drifted physical entropy of a particle + * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. * - * @param p The particle of interest. - * @param cosmo Cosmology data structure + * In the desperate case where a particle has no neighbours (likely because + * of the h_max ceiling), set the particle fields to something sensible to avoid + * NaNs in the next calculations. * - * @return The physical entropy + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float -hydro_get_drifted_physical_entropy(const struct part* p, - const struct cosmology* cosmo) { - error("Need implementing"); +__attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) {} - return 0; -} +/** + * @brief Prepare a particle for the force calculation. + * + * This function is called in the ghost task to convert some quantities coming + * from the density loop over neighbours into quantities ready to be used in the + * force loop over neighbours. Quantities are typically read from the density + * sub-structure and written to the force sub-structure. + * Examples of calculations done here include the calculation of viscosity term + * constants, thermal conduction terms, hydro conversions, etc. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. + * @param hydro_props Hydrodynamic properties. + * @param dt_alpha The time-step used to evolve non-cosmological quantities such + * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_force( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct pressure_floor_props *pressure_floor, const float dt_alpha, + const float dt_therm) {} /** - * @brief Update the value of the viscosity alpha for the scheme. + * @brief Reset acceleration fields of a particle * - * @param p the particle of interest - * @param alpha the new value for the viscosity coefficient. + * Resets all hydro acceleration and time derivative fields in preparation + * for the sums taking place in the various force tasks. + * + * @param p The particle to act upon */ -__attribute__((always_inline)) INLINE static void hydro_set_viscosity_alpha( - struct part* restrict p, float alpha) { - /* Purposefully left empty */ -} +__attribute__((always_inline)) INLINE static void hydro_reset_acceleration( + struct part *restrict p) {} /** - * @brief Update the value of the viscosity alpha to the - * feedback reset value for the scheme. + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time * - * @param p the particle of interest + * @param p The particle. + * @param xp The extended data of this particle. + * @param cosmo The cosmological model */ -__attribute__((always_inline)) INLINE static void -hydro_diffusive_feedback_reset(struct part* restrict p) { - /* Purposefully left empty */ -} +__attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( + struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo, + const struct pressure_floor_props *pressure_floor) {} /** - * @brief Returns the comoving pressure of a particle + * @brief Predict additional particle fields forward in time when drifting * - * @param p The particle of interest. + * Additional hydrodynamic quantites are drifted forward in time here. These + * include thermal quantities (thermal energy or total energy or entropy, ...). + * + * Note the different time-step sizes used for the different quantities as they + * include cosmological factors. + * + * @param p The particle. + * @param xp The extended data of the particle. + * @param dt_drift The drift time-step for positions. + * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. + * @param hydro_props The properties of the hydro scheme. + * @param floor_props The properties of the entropy floor. */ -__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure( - const struct part* restrict p, const struct cosmology* cosmo) { +__attribute__((always_inline)) INLINE static void hydro_predict_extra( + struct part *restrict p, const struct xpart *restrict xp, float dt_drift, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, + const struct hydro_props *hydro_props, + const struct entropy_floor_properties *floor_props, + const struct pressure_floor_props *pressure_floor) {} - return cosmo->a_factor_pressure * p->primitives.P; -} +/** + * @brief Finishes the force calculation. + * + * Multiplies the force and accelerations by the appropiate constants + * and add the self-contribution term. In most cases, there is little + * to do here. + * + * Cosmological terms are also added/multiplied here. + * + * @param p The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_end_force( + struct part *restrict p, const struct cosmology *cosmo) {} /** - * @brief Returns the physical density of a particle + * @brief Kick the additional variables * - * @param p The particle of interest + * Additional hydrodynamic quantites are kicked forward in time here. These + * include thermal quantities (thermal energy or total energy or entropy, ...). + * + * @param p The particle to act upon. + * @param xp The particle extended data to act upon. + * @param dt_therm The time-step for this kick (for thermodynamic quantities). + * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). + * @param dt_hydro The time-step for this kick (for hydro quantities). + * @param dt_kick_corr The time-step for this kick (for gravity corrections). * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme. + * @param floor_props The properties of the entropy floor. */ -__attribute__((always_inline)) INLINE static float hydro_get_physical_density( - const struct part* restrict p, const struct cosmology* cosmo) { +__attribute__((always_inline)) INLINE static void hydro_kick_extra( + struct part *restrict p, struct xpart *restrict xp, float dt_therm, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct entropy_floor_properties *floor_props) {} - return cosmo->a3_inv * p->primitives.rho; +/** + * @brief Converts hydro quantity of a particle at the start of a run + * + * This function is called once at the end of the engine_init_particle() + * routine (at the start of a calculation) after the densities of + * particles have been computed. + * This can be used to convert internal energy into entropy for instance. + * + * @param p The particle to act upon + * @param xp The extended particle to act upon + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme. + */ +__attribute__((always_inline)) INLINE static void hydro_convert_quantities( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct pressure_floor_props *pressure_floor) {} + +/** + * @brief Initialises the particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions or assignments between the particle + * and extended particle fields. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_first_init_part( + struct part *restrict p, struct xpart *restrict xp) { + + p->time_bin = 0; + xp->v_full[0] = p->v[0]; + xp->v_full[1] = p->v[1]; + xp->v_full[2] = p->v[2]; + xp->a_grav[0] = 0.f; + xp->a_grav[1] = 0.f; + xp->a_grav[2] = 0.f; + + hydro_init_part(p, NULL); } +/** + * @brief Overwrite the initial internal energy of a particle. + * + * Note that in the cases where the thermodynamic variable is not + * internal energy but gets converted later, we must overwrite that + * field. The conversion to the actual variable happens later after + * the initial fake time-step. + * + * @param p The #part to write to. + * @param u_init The new initial internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_init_internal_energy(struct part *p, float u_init) {} + /** * @brief Operations performed when a particle gets removed from the * simulation volume. * * @param p The particle. * @param xp The extended particle data. - * @param time The simulation time. */ __attribute__((always_inline)) INLINE static void hydro_remove_part( - const struct part* p, const struct xpart* xp, const double time) {} + const struct part *p, const struct xpart *xp, const double time) {} -#endif /* SWIFT_SHADOWSWIFT_HYDRO_H */ +#endif /* SWIFT_MINIMAL_HYDRO_H */ diff --git a/src/hydro/Shadowswift/hydro_debug.h b/src/hydro/Shadowswift/hydro_debug.h index 91ea90ef82880e51e0947090e48f2cb3c6927225..0ca04085fc465445ae1d4c4ec6c9d5fefb3fa701 100644 --- a/src/hydro/Shadowswift/hydro_debug.h +++ b/src/hydro/Shadowswift/hydro_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leideuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -16,56 +16,17 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_NONE_HYDRO_DEBUG_H +#define SWIFT_NONE_HYDRO_DEBUG_H + +/** + * @file Shadowswift/hydro_debug.h + * @brief Empty implementation. + */ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - warning( - "[PID%lld] x=[%.16e,%.16e,%.16e], " - "v=[%.3e,%.3e,%.3e], " - "a=[%.3e,%.3e,%.3e], " - "time_bin=%d, " - "wakeup=%d, " - "h=%.3e, " - "primitives={" - "v=[%.3e,%.3e,%.3e], " - "rho=%.3e, " - "P=%.3e, " - "gradients={" - "rho=[%.3e,%.3e,%.3e], " - "v=[[%.3e,%.3e,%.3e],[%.3e,%.3e,%.3e],[%.3e,%.3e,%.3e]], " - "P=[%.3e,%.3e,%.3e]}, " - "limiter={" - "rho=[%.3e,%.3e], " - "v=[[%.3e,%.3e],[%.3e,%.3e],[%.3e,%.3e]], " - "P=[%.3e,%.3e], " - "maxr=%.3e}}, " - "conserved={" - "momentum=[%.3e,%.3e,%.3e], " - "mass=%.3e, " - "energy=%.3e}, " - "timestepvars={" - "vmax=%.3e}, " - "density={" - "wcount_dh=%.3e, " - "wcount=%.3e}", - p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], - p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->time_bin, - p->limiter_data.wakeup, p->h, p->primitives.v[0], p->primitives.v[1], - p->primitives.v[2], p->primitives.rho, p->primitives.P, - p->primitives.gradients.rho[0], p->primitives.gradients.rho[1], - p->primitives.gradients.rho[2], p->primitives.gradients.v[0][0], - p->primitives.gradients.v[0][1], p->primitives.gradients.v[0][2], - p->primitives.gradients.v[1][0], p->primitives.gradients.v[1][1], - p->primitives.gradients.v[1][2], p->primitives.gradients.v[2][0], - p->primitives.gradients.v[2][1], p->primitives.gradients.v[2][2], - p->primitives.gradients.P[0], p->primitives.gradients.P[1], - p->primitives.gradients.P[2], p->primitives.limiter.rho[0], - p->primitives.limiter.rho[1], p->primitives.limiter.v[0][0], - p->primitives.limiter.v[0][1], p->primitives.limiter.v[1][0], - p->primitives.limiter.v[1][1], p->primitives.limiter.v[2][0], - p->primitives.limiter.v[2][1], p->primitives.limiter.P[0], - p->primitives.limiter.P[1], p->primitives.limiter.maxr, - p->conserved.momentum[0], p->conserved.momentum[1], - p->conserved.momentum[2], p->conserved.mass, p->conserved.energy, - p->timestepvars.vmax, p->density.wcount_dh, p->density.wcount); + error("Empty implementation"); } + +#endif /* SWIFT_NONE_HYDRO_DEBUG_H */ diff --git a/src/hydro/Shadowswift/hydro_gradients.h b/src/hydro/Shadowswift/hydro_gradients.h deleted file mode 100644 index 4e7a9911d8d4fc586fe7a56687dd4c4ae9ec8de2..0000000000000000000000000000000000000000 --- a/src/hydro/Shadowswift/hydro_gradients.h +++ /dev/null @@ -1,163 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -#ifndef SWIFT_HYDRO_GRADIENTS_H -#define SWIFT_HYDRO_GRADIENTS_H - -#include "hydro_slope_limiters.h" - -#if defined(SHADOWFAX_GRADIENTS) - -#define HYDRO_GRADIENT_IMPLEMENTATION "Shadowfax gradients (Springel 2010)" -#include "hydro_gradients_shadowfax.h" - -#else - -/* No gradients. Perfectly acceptable, but we have to provide empty functions */ -#define HYDRO_GRADIENT_IMPLEMENTATION "No gradients (first order scheme)" - -/** - * @brief Initialize gradient variables - * - * @param p Particle. - */ -__attribute__((always_inline)) INLINE static void hydro_gradients_init( - struct part* p) {} - -/** - * @brief Gradient calculations done during the neighbour loop - * - * @param r2 Squared distance between the two particles. - * @param dx Distance vector (pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. - * @param pi Particle i. - * @param pj Particle j. - */ -__attribute__((always_inline)) INLINE static void hydro_gradients_collect( - float r2, const float* dx, float hi, float hj, struct part* restrict pi, - struct part* restrict pj) {} - -/** - * @brief Gradient calculations done during the neighbour loop: non-symmetric - * version - * - * @param r2 Squared distance between the two particles. - * @param dx Distance vector (pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. - * @param pi Particle i. - * @param pj Particle j. - */ -__attribute__((always_inline)) INLINE static void -hydro_gradients_nonsym_collect(float r2, const float* dx, float hi, float hj, - struct part* restrict pi, - const struct part* restrict pj) {} - -/** - * @brief Finalize the gradient variables after all data have been collected - * - * @param p Particle. - */ -__attribute__((always_inline)) INLINE static void hydro_gradients_finalize( - struct part* p) {} - -#endif - -/** - * @brief Gradients reconstruction. Is the same for all gradient types (although - * gradients_none does nothing, since all gradients are zero -- are they?). - */ -__attribute__((always_inline)) INLINE static void hydro_gradients_predict( - struct part* pi, struct part* pj, float hi, float hj, const float* dx, - float r, float* xij_i, float* Wi, float* Wj) { - - float dWi[5], dWj[5]; - float xij_j[3]; - - /* xij_j = real_midpoint - pj->x - = xij_i + pi->x - pj->x - = xij_i + dx */ - xij_j[0] = xij_i[0] + dx[0]; - xij_j[1] = xij_i[1] + dx[1]; - xij_j[2] = xij_i[2] + dx[2]; - - dWi[0] = pi->primitives.gradients.rho[0] * xij_i[0] + - pi->primitives.gradients.rho[1] * xij_i[1] + - pi->primitives.gradients.rho[2] * xij_i[2]; - dWi[1] = pi->primitives.gradients.v[0][0] * xij_i[0] + - pi->primitives.gradients.v[0][1] * xij_i[1] + - pi->primitives.gradients.v[0][2] * xij_i[2]; - dWi[2] = pi->primitives.gradients.v[1][0] * xij_i[0] + - pi->primitives.gradients.v[1][1] * xij_i[1] + - pi->primitives.gradients.v[1][2] * xij_i[2]; - dWi[3] = pi->primitives.gradients.v[2][0] * xij_i[0] + - pi->primitives.gradients.v[2][1] * xij_i[1] + - pi->primitives.gradients.v[2][2] * xij_i[2]; - dWi[4] = pi->primitives.gradients.P[0] * xij_i[0] + - pi->primitives.gradients.P[1] * xij_i[1] + - pi->primitives.gradients.P[2] * xij_i[2]; - - dWj[0] = pj->primitives.gradients.rho[0] * xij_j[0] + - pj->primitives.gradients.rho[1] * xij_j[1] + - pj->primitives.gradients.rho[2] * xij_j[2]; - dWj[1] = pj->primitives.gradients.v[0][0] * xij_j[0] + - pj->primitives.gradients.v[0][1] * xij_j[1] + - pj->primitives.gradients.v[0][2] * xij_j[2]; - dWj[2] = pj->primitives.gradients.v[1][0] * xij_j[0] + - pj->primitives.gradients.v[1][1] * xij_j[1] + - pj->primitives.gradients.v[1][2] * xij_j[2]; - dWj[3] = pj->primitives.gradients.v[2][0] * xij_j[0] + - pj->primitives.gradients.v[2][1] * xij_j[1] + - pj->primitives.gradients.v[2][2] * xij_j[2]; - dWj[4] = pj->primitives.gradients.P[0] * xij_j[0] + - pj->primitives.gradients.P[1] * xij_j[1] + - pj->primitives.gradients.P[2] * xij_j[2]; - - hydro_slope_limit_face(Wi, Wj, dWi, dWj, xij_i, xij_j, r); - - Wi[0] += dWi[0]; - Wi[1] += dWi[1]; - Wi[2] += dWi[2]; - Wi[3] += dWi[3]; - Wi[4] += dWi[4]; - - Wj[0] += dWj[0]; - Wj[1] += dWj[1]; - Wj[2] += dWj[2]; - Wj[3] += dWj[3]; - Wj[4] += dWj[4]; - - /* Sanity check: if density or pressure becomes negative after the - interpolation, just reset them */ - if (Wi[0] < 0.0f) { - Wi[0] -= dWi[0]; - } - if (Wi[4] < 0.0f) { - Wi[4] -= dWi[4]; - } - if (Wj[0] < 0.0f) { - Wj[0] -= dWj[0]; - } - if (Wj[4] < 0.0f) { - Wj[4] -= dWj[4]; - } -} - -#endif // SWIFT_HYDRO_GRADIENTS_H diff --git a/src/hydro/Shadowswift/hydro_gradients_shadowfax.h b/src/hydro/Shadowswift/hydro_gradients_shadowfax.h deleted file mode 100644 index d131731907806536e86a03921b1c701f287077f1..0000000000000000000000000000000000000000 --- a/src/hydro/Shadowswift/hydro_gradients_shadowfax.h +++ /dev/null @@ -1,218 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -#include "voronoi_algorithm.h" - -/** - * @brief Initialize gradient variables - * - * @param p Particle. - */ -__attribute__((always_inline)) INLINE static void hydro_gradients_init( - struct part *p) { - - p->primitives.gradients.rho[0] = 0.0f; - p->primitives.gradients.rho[1] = 0.0f; - p->primitives.gradients.rho[2] = 0.0f; - - p->primitives.gradients.v[0][0] = 0.0f; - p->primitives.gradients.v[0][1] = 0.0f; - p->primitives.gradients.v[0][2] = 0.0f; - - p->primitives.gradients.v[1][0] = 0.0f; - p->primitives.gradients.v[1][1] = 0.0f; - p->primitives.gradients.v[1][2] = 0.0f; - - p->primitives.gradients.v[2][0] = 0.0f; - p->primitives.gradients.v[2][1] = 0.0f; - p->primitives.gradients.v[2][2] = 0.0f; - - p->primitives.gradients.P[0] = 0.0f; - p->primitives.gradients.P[1] = 0.0f; - p->primitives.gradients.P[2] = 0.0f; - - hydro_slope_limit_cell_init(p); -} - -/** - * @brief Add the gradient estimate for a single quantity due to a particle pair - * to the total gradient for that quantity - * - * This corresponds to one term of equation (21) in Springel (2010). - * - * @param qL Value of the quantity on the left. - * @param qR Value of the quantity on the right. - * @param cLR Vector pointing from the midpoint of the particle pair to the - * geometrical centroid of the face in between the particles. - * @param xLR Vector pointing from the right particle to the left particle. - * @param A Surface area of the face in between the particles. - * @param grad Current value of the gradient for the quantity (is updated). - */ -__attribute__((always_inline)) INLINE void hydro_gradients_single_quantity( - float qL, float qR, float *cLR, const float *xLR, float rLR, float A, - float *grad) { - - grad[0] += A * ((qR - qL) * cLR[0] / rLR - 0.5f * (qL + qR) * xLR[0] / rLR); - grad[1] += A * ((qR - qL) * cLR[1] / rLR - 0.5f * (qL + qR) * xLR[1] / rLR); - grad[2] += A * ((qR - qL) * cLR[2] / rLR - 0.5f * (qL + qR) * xLR[2] / rLR); -} - -/** - * @brief Gradient calculations done during the neighbour loop - * - * @param r2 Squared distance between the two particles. - * @param dx Distance vector (pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. - * @param pi Particle i. - * @param pj Particle j. - */ -__attribute__((always_inline)) INLINE static void hydro_gradients_collect( - float r2, const float *dx, float hi, float hj, struct part *pi, - struct part *pj) { - - float A, midpoint[3]; - - A = voronoi_get_face(&pi->cell, pj->id, midpoint); - if (!A) { - /* particle is not a cell neighbour: do nothing */ - return; - } - - float c[3]; - /* midpoint is relative w.r.t. pi->x, as is dx */ - /* c is supposed to be the vector pointing from the midpoint of pi and pj to - the midpoint of the face between pi and pj: - c = real_midpoint - 0.5*(pi+pj) - = midpoint + pi - 0.5*(2*pi - dx) - = midpoint + 0.5*dx */ - c[0] = midpoint[0] + 0.5f * dx[0]; - c[1] = midpoint[1] + 0.5f * dx[1]; - c[2] = midpoint[2] + 0.5f * dx[2]; - - float r = sqrtf(r2); - hydro_gradients_single_quantity(pi->primitives.rho, pj->primitives.rho, c, dx, - r, A, pi->primitives.gradients.rho); - hydro_gradients_single_quantity(pi->primitives.v[0], pj->primitives.v[0], c, - dx, r, A, pi->primitives.gradients.v[0]); - hydro_gradients_single_quantity(pi->primitives.v[1], pj->primitives.v[1], c, - dx, r, A, pi->primitives.gradients.v[1]); - hydro_gradients_single_quantity(pi->primitives.v[2], pj->primitives.v[2], c, - dx, r, A, pi->primitives.gradients.v[2]); - hydro_gradients_single_quantity(pi->primitives.P, pj->primitives.P, c, dx, r, - A, pi->primitives.gradients.P); - - hydro_slope_limit_cell_collect(pi, pj, r); - - float mindx[3]; - mindx[0] = -dx[0]; - mindx[1] = -dx[1]; - mindx[2] = -dx[2]; - hydro_gradients_single_quantity(pj->primitives.rho, pi->primitives.rho, c, - mindx, r, A, pj->primitives.gradients.rho); - hydro_gradients_single_quantity(pj->primitives.v[0], pi->primitives.v[0], c, - mindx, r, A, pj->primitives.gradients.v[0]); - hydro_gradients_single_quantity(pj->primitives.v[1], pi->primitives.v[1], c, - mindx, r, A, pj->primitives.gradients.v[1]); - hydro_gradients_single_quantity(pj->primitives.v[2], pi->primitives.v[2], c, - mindx, r, A, pj->primitives.gradients.v[2]); - hydro_gradients_single_quantity(pj->primitives.P, pi->primitives.P, c, mindx, - r, A, pj->primitives.gradients.P); - - hydro_slope_limit_cell_collect(pj, pi, r); -} - -/** - * @brief Gradient calculations done during the neighbour loop - * - * @param r2 Squared distance between the two particles. - * @param dx Distance vector (pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. - * @param pi Particle i. - * @param pj Particle j. - */ -__attribute__((always_inline)) INLINE static void -hydro_gradients_nonsym_collect(float r2, const float *dx, float hi, float hj, - struct part *pi, const struct part *pj) { - - float A, midpoint[3]; - - A = voronoi_get_face(&pi->cell, pj->id, midpoint); - if (!A) { - /* particle is not a cell neighbour: do nothing */ - return; - } - - float c[3]; - /* midpoint is relative w.r.t. pi->x, as is dx */ - /* c is supposed to be the vector pointing from the midpoint of pi and pj to - the midpoint of the face between pi and pj: - c = real_midpoint - 0.5*(pi+pj) - = midpoint + pi - 0.5*(2*pi - dx) - = midpoint + 0.5*dx */ - c[0] = midpoint[0] + 0.5f * dx[0]; - c[1] = midpoint[1] + 0.5f * dx[1]; - c[2] = midpoint[2] + 0.5f * dx[2]; - - float r = sqrtf(r2); - hydro_gradients_single_quantity(pi->primitives.rho, pj->primitives.rho, c, dx, - r, A, pi->primitives.gradients.rho); - hydro_gradients_single_quantity(pi->primitives.v[0], pj->primitives.v[0], c, - dx, r, A, pi->primitives.gradients.v[0]); - hydro_gradients_single_quantity(pi->primitives.v[1], pj->primitives.v[1], c, - dx, r, A, pi->primitives.gradients.v[1]); - hydro_gradients_single_quantity(pi->primitives.v[2], pj->primitives.v[2], c, - dx, r, A, pi->primitives.gradients.v[2]); - hydro_gradients_single_quantity(pi->primitives.P, pj->primitives.P, c, dx, r, - A, pi->primitives.gradients.P); - - hydro_slope_limit_cell_collect(pi, pj, r); -} - -/** - * @brief Finalize the gradient variables after all data have been collected - * - * @param p Particle. - */ -__attribute__((always_inline)) INLINE static void hydro_gradients_finalize( - struct part *p) { - - float volume = p->cell.volume; - - p->primitives.gradients.rho[0] /= volume; - p->primitives.gradients.rho[1] /= volume; - p->primitives.gradients.rho[2] /= volume; - - p->primitives.gradients.v[0][0] /= volume; - p->primitives.gradients.v[0][1] /= volume; - p->primitives.gradients.v[0][2] /= volume; - p->primitives.gradients.v[1][0] /= volume; - p->primitives.gradients.v[1][1] /= volume; - p->primitives.gradients.v[1][2] /= volume; - p->primitives.gradients.v[2][0] /= volume; - p->primitives.gradients.v[2][1] /= volume; - p->primitives.gradients.v[2][2] /= volume; - - p->primitives.gradients.P[0] /= volume; - p->primitives.gradients.P[1] /= volume; - p->primitives.gradients.P[2] /= volume; - - hydro_slope_limit_cell(p); -} diff --git a/src/hydro/Shadowswift/hydro_iact.h b/src/hydro/Shadowswift/hydro_iact.h index e7b93fd9a6b7154e83131dff9ababf938c6ce9f9..d4459b9dfbc0db04055db068f137c38ded0026e7 100644 --- a/src/hydro/Shadowswift/hydro_iact.h +++ b/src/hydro/Shadowswift/hydro_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leideuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -16,336 +16,125 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_NONE_HYDRO_IACT_H +#define SWIFT_NONE_HYDRO_IACT_H + +/** + * @file Shadowswift/hydro_iact.h + * @brief Empty implementation + */ #include "adiabatic_index.h" -#include "hydro_gradients.h" -#include "riemann.h" -#include "voronoi_algorithm.h" +#include "hydro_parameters.h" +#include "minmax.h" /** - * @brief Calculate the Voronoi cell by interacting particle pi and pj - * - * This method wraps around voronoi_cell_interact(). - * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. - * @param pi Particle i. - * @param pj Particle j. + * @brief Density interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_density( const float r2, const float dx[3], const float hi, const float hj, struct part *restrict pi, struct part *restrict pj, const float a, - const float H) { - - float mindx[3]; - - voronoi_cell_interact(&pi->cell, dx, pj->id); - mindx[0] = -dx[0]; - mindx[1] = -dx[1]; - mindx[2] = -dx[2]; - voronoi_cell_interact(&pj->cell, mindx, pi->id); -} + const float H) {} /** - * @brief Calculate the Voronoi cell by interacting particle pi with pj - * - * This method wraps around voronoi_cell_interact(). - * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. - * @param pi Particle i. - * @param pj Particle j. + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( const float r2, const float dx[3], const float hi, const float hj, struct part *restrict pi, const struct part *restrict pj, const float a, - const float H) { - - voronoi_cell_interact(&pi->cell, dx, pj->id); -} + const float H) {} /** * @brief Calculate the gradient interaction between particle i and particle j * - * This method wraps around hydro_gradients_collect, which can be an empty - * method, in which case no gradients are used. + * Nothing to do here in this scheme. * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. * @param pi Particle i. * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float r2, const float dx[3], const float hi, const float hj, struct part *restrict pi, struct part *restrict pj, const float a, - const float H) { - - hydro_gradients_collect(r2, dx, hi, hj, pi, pj); -} + const float H) {} /** * @brief Calculate the gradient interaction between particle i and particle j: * non-symmetric version * - * This method wraps around hydro_gradients_nonsym_collect, which can be an - * empty method, in which case no gradients are used. + * Nothing to do here in this scheme. * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. * @param pi Particle i. * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float r2, const float dx[3], const float hi, const float hj, struct part *restrict pi, struct part *restrict pj, const float a, - const float H) { - - hydro_gradients_nonsym_collect(r2, dx, hi, hj, pi, pj); -} + const float H) {} /** - * @brief Common part of the flux calculation between particle i and j - * - * Since the only difference between the symmetric and non-symmetric version - * of the flux calculation is in the update of the conserved variables at the - * very end (which is not done for particle j if mode is 0 and particle j is - * active), both runner_iact_force and runner_iact_nonsym_force call this - * method, with an appropriate mode. - * - * This method retrieves the oriented surface area and face midpoint for the - * Voronoi face between pi and pj (if it exists). It uses the midpoint position - * to reconstruct the primitive quantities (if gradients are used) at the face - * and then uses the face quantities to estimate a flux through the face using - * a Riemann solver. - * - * This method also calculates the maximal velocity used to calculate the time - * step. - * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. - * @param pi Particle i. - * @param pj Particle j. - */ -__attribute__((always_inline)) INLINE static void runner_iact_fluxes_common( - const float r2, const float dx[3], const float hi, const float hj, - struct part *restrict pi, struct part *restrict pj, int mode, const float a, - const float H) { - - float r = sqrtf(r2); - int k; - float A; - float xij_i[3]; - float vmax, dvdotdx; - float vi[3], vj[3], vij[3]; - float Wi[5], Wj[5]; - float n_unit[3]; - - A = voronoi_get_face(&pi->cell, pj->id, xij_i); - if (A == 0.0f) { - /* this neighbour does not share a face with the cell, return */ - return; - } - - /* Initialize local variables */ - for (k = 0; k < 3; k++) { - vi[k] = pi->force.v_full[k]; /* particle velocities */ - vj[k] = pj->force.v_full[k]; - } - Wi[0] = pi->primitives.rho; - Wi[1] = pi->primitives.v[0]; - Wi[2] = pi->primitives.v[1]; - Wi[3] = pi->primitives.v[2]; - Wi[4] = pi->primitives.P; - Wj[0] = pj->primitives.rho; - Wj[1] = pj->primitives.v[0]; - Wj[2] = pj->primitives.v[1]; - Wj[3] = pj->primitives.v[2]; - Wj[4] = pj->primitives.P; - - /* calculate the maximal signal velocity */ - vmax = 0.0f; - if (Wi[0] > 0.) { - vmax += gas_soundspeed_from_pressure(Wi[0], Wi[4]); - } - - if (Wj[0] > 0.) { - vmax += gas_soundspeed_from_pressure(Wj[0], Wj[4]); - } - - dvdotdx = (Wi[1] - Wj[1]) * dx[0] + (Wi[2] - Wj[2]) * dx[1] + - (Wi[3] - Wj[3]) * dx[2]; - if (dvdotdx > 0.) { - vmax -= dvdotdx / r; - } - - pi->timestepvars.vmax = fmaxf(pi->timestepvars.vmax, vmax); - if (mode == 1) { - pj->timestepvars.vmax = fmaxf(pj->timestepvars.vmax, vmax); - } - - /* compute the normal vector of the interface */ - for (k = 0; k < 3; ++k) { - n_unit[k] = -dx[k] / r; - } - - /* Compute interface velocity */ - float fac = (vi[0] - vj[0]) * (xij_i[0] + 0.5f * dx[0]) + - (vi[1] - vj[1]) * (xij_i[1] + 0.5f * dx[1]) + - (vi[2] - vj[2]) * (xij_i[2] + 0.5f * dx[2]); - fac /= r; - vij[0] = 0.5f * (vi[0] + vj[0]) - fac * dx[0]; - vij[1] = 0.5f * (vi[1] + vj[1]) - fac * dx[1]; - vij[2] = 0.5f * (vi[2] + vj[2]) - fac * dx[2]; - - /* Boost the primitive variables to the frame of reference of the interface */ - /* Note that velocities are indices 1-3 in W */ - Wi[1] -= vij[0]; - Wi[2] -= vij[1]; - Wi[3] -= vij[2]; - Wj[1] -= vij[0]; - Wj[2] -= vij[1]; - Wj[3] -= vij[2]; - - hydro_gradients_predict(pi, pj, hi, hj, dx, r, xij_i, Wi, Wj); - - /* we don't need to rotate, we can use the unit vector in the Riemann problem - * itself (see GIZMO) */ - - if (Wi[0] < 0.0f || Wj[0] < 0.0f || Wi[4] < 0.0f || Wj[4] < 0.0f) { - printf("WL: %g %g %g %g %g\n", pi->primitives.rho, pi->primitives.v[0], - pi->primitives.v[1], pi->primitives.v[2], pi->primitives.P); -#ifdef USE_GRADIENTS - printf("dWL: %g %g %g %g %g\n", dWi[0], dWi[1], dWi[2], dWi[3], dWi[4]); -#endif - printf("gradWL[0]: %g %g %g\n", pi->primitives.gradients.rho[0], - pi->primitives.gradients.rho[1], pi->primitives.gradients.rho[2]); - printf("gradWL[1]: %g %g %g\n", pi->primitives.gradients.v[0][0], - pi->primitives.gradients.v[0][1], pi->primitives.gradients.v[0][2]); - printf("gradWL[2]: %g %g %g\n", pi->primitives.gradients.v[1][0], - pi->primitives.gradients.v[1][1], pi->primitives.gradients.v[1][2]); - printf("gradWL[3]: %g %g %g\n", pi->primitives.gradients.v[2][0], - pi->primitives.gradients.v[2][1], pi->primitives.gradients.v[2][2]); - printf("gradWL[4]: %g %g %g\n", pi->primitives.gradients.P[0], - pi->primitives.gradients.P[1], pi->primitives.gradients.P[2]); - printf("WL': %g %g %g %g %g\n", Wi[0], Wi[1], Wi[2], Wi[3], Wi[4]); - printf("WR: %g %g %g %g %g\n", pj->primitives.rho, pj->primitives.v[0], - pj->primitives.v[1], pj->primitives.v[2], pj->primitives.P); -#ifdef USE_GRADIENTS - printf("dWR: %g %g %g %g %g\n", dWj[0], dWj[1], dWj[2], dWj[3], dWj[4]); -#endif - printf("gradWR[0]: %g %g %g\n", pj->primitives.gradients.rho[0], - pj->primitives.gradients.rho[1], pj->primitives.gradients.rho[2]); - printf("gradWR[1]: %g %g %g\n", pj->primitives.gradients.v[0][0], - pj->primitives.gradients.v[0][1], pj->primitives.gradients.v[0][2]); - printf("gradWR[2]: %g %g %g\n", pj->primitives.gradients.v[1][0], - pj->primitives.gradients.v[1][1], pj->primitives.gradients.v[1][2]); - printf("gradWR[3]: %g %g %g\n", pj->primitives.gradients.v[2][0], - pj->primitives.gradients.v[2][1], pj->primitives.gradients.v[2][2]); - printf("gradWR[4]: %g %g %g\n", pj->primitives.gradients.P[0], - pj->primitives.gradients.P[1], pj->primitives.gradients.P[2]); - printf("WR': %g %g %g %g %g\n", Wj[0], Wj[1], Wj[2], Wj[3], Wj[4]); - error("Negative density or pressure!\n"); - } - - float totflux[5]; - riemann_solve_for_flux(Wi, Wj, n_unit, vij, totflux); - - /* Update conserved variables */ - /* eqn. (16) */ - pi->conserved.flux.mass -= A * totflux[0]; - pi->conserved.flux.momentum[0] -= A * totflux[1]; - pi->conserved.flux.momentum[1] -= A * totflux[2]; - pi->conserved.flux.momentum[2] -= A * totflux[3]; - pi->conserved.flux.energy -= A * totflux[4]; - -#ifndef SHADOWFAX_TOTAL_ENERGY - float ekin = 0.5f * (pi->primitives.v[0] * pi->primitives.v[0] + - pi->primitives.v[1] * pi->primitives.v[1] + - pi->primitives.v[2] * pi->primitives.v[2]); - pi->conserved.flux.energy += A * totflux[1] * pi->primitives.v[0]; - pi->conserved.flux.energy += A * totflux[2] * pi->primitives.v[1]; - pi->conserved.flux.energy += A * totflux[3] * pi->primitives.v[2]; - pi->conserved.flux.energy -= A * totflux[0] * ekin; -#endif - - /* here is how it works: - Mode will only be 1 if both particles are ACTIVE and they are in the same - cell. In this case, this method IS the flux calculation for particle j, and - we HAVE TO UPDATE it. - Mode 0 can mean several things: it can mean that particle j is INACTIVE, in - which case we NEED TO UPDATE it, since otherwise the flux is lost from the - system and the conserved variable is not conserved. - It can also mean that particle j sits in another cell and is ACTIVE. In - this case, the flux exchange for particle j is done TWICE and we SHOULD NOT - UPDATE particle j. - ==> we update particle j if (MODE IS 1) OR (j IS INACTIVE) - */ - if (mode == 1 || pj->force.active == 0) { - pj->conserved.flux.mass += A * totflux[0]; - pj->conserved.flux.momentum[0] += A * totflux[1]; - pj->conserved.flux.momentum[1] += A * totflux[2]; - pj->conserved.flux.momentum[2] += A * totflux[3]; - pj->conserved.flux.energy += A * totflux[4]; - -#ifndef SHADOWFAX_TOTAL_ENERGY - ekin = 0.5f * (pj->primitives.v[0] * pj->primitives.v[0] + - pj->primitives.v[1] * pj->primitives.v[1] + - pj->primitives.v[2] * pj->primitives.v[2]); - pj->conserved.flux.energy -= A * totflux[1] * pj->primitives.v[0]; - pj->conserved.flux.energy -= A * totflux[2] * pj->primitives.v[1]; - pj->conserved.flux.energy -= A * totflux[3] * pj->primitives.v[2]; - pj->conserved.flux.energy += A * totflux[0] * ekin; -#endif - } -} - -/** - * @brief Flux calculation between particle i and particle j - * - * This method calls runner_iact_fluxes_common with mode 1. - * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. - * @param pi Particle i. - * @param pj Particle j. + * @brief Force interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_force( const float r2, const float dx[3], const float hi, const float hj, struct part *restrict pi, struct part *restrict pj, const float a, - const float H) { - - runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 1, a, H); -} + const float H) {} /** - * @brief Flux calculation between particle i and particle j: non-symmetric - * version - * - * This method calls runner_iact_fluxes_common with mode 0. - * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. - * @param pi Particle i. - * @param pj Particle j. + * @brief Force interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float r2, const float dx[3], const float hi, const float hj, - struct part *restrict pi, struct part *restrict pj, const float a, - const float H) { + struct part *restrict pi, const struct part *restrict pj, const float a, + const float H) {} - runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0, a, H); -} +#endif /* SWIFT_NONE_HYDRO_IACT_H */ diff --git a/src/hydro/Shadowswift/hydro_io.h b/src/hydro/Shadowswift/hydro_io.h index 3a588d2fd848a37a75c01e84106294e71e52552f..3ffb842f372d7fa127663c93718169c3c2e2637c 100644 --- a/src/hydro/Shadowswift/hydro_io.h +++ b/src/hydro/Shadowswift/hydro_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leideuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -16,14 +16,19 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_NONE_HYDRO_IO_H +#define SWIFT_NONE_HYDRO_IO_H + +/** + * @file Shadowswift/hydro_io.h + * @brief Empty implementation. + */ #include "adiabatic_index.h" -#include "equation_of_state.h" #include "hydro.h" -#include "hydro_gradients.h" -#include "hydro_slope_limiters.h" +#include "hydro_parameters.h" #include "io_properties.h" -#include "riemann.h" +#include "kernel_hydro.h" /** * @brief Specifies which particle fields to read from a dataset @@ -36,137 +41,32 @@ INLINE static void hydro_read_particles(struct part* parts, struct io_props* list, int* num_fields) { - *num_fields = 8; - - /* List what we want to read */ - list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, - UNIT_CONV_LENGTH, parts, x); - list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, - UNIT_CONV_SPEED, parts, v); - list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, - parts, conserved.mass); - list[3] = io_make_input_field("SmoothingLength", FLOAT, 1, COMPULSORY, - UNIT_CONV_LENGTH, parts, h); - list[4] = io_make_input_field("InternalEnergy", FLOAT, 1, COMPULSORY, - UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, - conserved.energy); - list[5] = io_make_input_field("ParticleIDs", ULONGLONG, 1, COMPULSORY, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = io_make_input_field("Accelerations", FLOAT, 3, OPTIONAL, - UNIT_CONV_ACCELERATION, parts, a_hydro); - list[7] = io_make_input_field("Density", FLOAT, 1, OPTIONAL, - UNIT_CONV_DENSITY, parts, primitives.rho); -} - -/** - * @brief Get the internal energy of a particle - * - * @param e #engine. - * @param p Particle. - * @return Internal energy of the particle - */ -INLINE static void convert_u(const struct engine* e, const struct part* p, - const struct xpart* xp, float* ret) { - ret[0] = hydro_get_internal_energy(p); -} - -/** - * @brief Get the entropic function of a particle - * - * @param e #engine. - * @param p Particle. - * @return Entropic function of the particle - */ -INLINE static void convert_A(const struct engine* e, const struct part* p, - const struct xpart* xp, float* ret) { - ret[0] = hydro_get_entropy(p); -} - -/** - * @brief Get the total energy of a particle - * - * @param e #engine. - * @param p Particle. - * @return Total energy of the particle - */ -INLINE static void convert_Etot(const struct engine* e, const struct part* p, - const struct xpart* xp, float* ret) { -#ifdef SHADOWFAX_TOTAL_ENERGY - return p->conserved.energy; -#else - if (p->conserved.mass > 0.) { - float momentum2; - - momentum2 = p->conserved.momentum[0] * p->conserved.momentum[0] + - p->conserved.momentum[1] * p->conserved.momentum[1] + - p->conserved.momentum[2] * p->conserved.momentum[2]; - - ret[0] = p->conserved.energy + 0.5f * momentum2 / p->conserved.mass; - } else { - ret[0] = 0.; - } -#endif + *num_fields = 0; } INLINE static void convert_part_pos(const struct engine* e, const struct part* p, const struct xpart* xp, double* ret) { - const struct space* s = e->s; - if (s->periodic) { - ret[0] = box_wrap(p->x[0], 0.0, s->dim[0]); - ret[1] = box_wrap(p->x[1], 0.0, s->dim[1]); - ret[2] = box_wrap(p->x[2], 0.0, s->dim[2]); - } else { - ret[0] = p->x[0]; - ret[1] = p->x[1]; - ret[2] = p->x[2]; - } - if (e->snapshot_use_delta_from_edge) { - ret[0] = min(ret[0], s->dim[0] - e->snapshot_delta_from_edge); - ret[1] = min(ret[1], s->dim[1] - e->snapshot_delta_from_edge); - ret[2] = min(ret[2], s->dim[2] - e->snapshot_delta_from_edge); - } + ret[0] = 0.; + ret[1] = 0.; + ret[2] = 0.; + error("Not implemented in the 'None' hydro scheme."); } INLINE static void convert_part_vel(const struct engine* e, const struct part* p, const struct xpart* xp, float* ret) { - - const int with_cosmology = (e->policy & engine_policy_cosmology); - const struct cosmology* cosmo = e->cosmology; - const integertime_t ti_current = e->ti_current; - const double time_base = e->time_base; - - const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin); - const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin); - - /* Get time-step since the last kick */ - float dt_kick_grav, dt_kick_hydro; - if (with_cosmology) { - dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); - dt_kick_grav -= - cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); - dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current); - dt_kick_hydro -= - cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); - } else { - dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; - dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; - } - - /* Extrapolate the velocites to the current time */ - hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret); - - /* Conversion from internal units to peculiar velocities */ - ret[0] *= cosmo->a_inv; - ret[1] *= cosmo->a_inv; - ret[2] *= cosmo->a_inv; + ret[0] = 0.f; + ret[1] = 0.f; + ret[2] = 0.f; + error("Not implemented in the 'None' hydro scheme."); } /** * @brief Specifies which particle fields to write to a dataset * * @param parts The particle array. + * @param xparts The extended particle array. * @param list The list of i/o properties to write. * @param num_fields The number of i/o fields to write. */ @@ -175,84 +75,14 @@ INLINE static void hydro_write_particles(const struct part* parts, struct io_props* list, int* num_fields) { - *num_fields = 13; - - /* List what we want to write */ - list[0] = io_make_output_field_convert_part( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, - convert_part_pos, "Co-moving positions of the particles"); - - list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, - convert_part_vel, - "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " - "co-moving positions of the particles"); - - list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, - conserved.mass, "Masses of the particles"); - - list[3] = io_make_output_field( - "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, - "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); - - list[4] = io_make_output_field_convert_part( - "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, - -3.f * hydro_gamma_minus_one, parts, xparts, convert_u, - "Co-moving thermal energies per unit mass of the particles"); - - list[5] = io_make_physical_output_field( - "ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, parts, id, - /*can convert to comoving=*/0, "Unique IDs of the particles"); - - list[6] = io_make_output_field("Accelerations", FLOAT, 3, - UNIT_CONV_ACCELERATION, 1.f, parts, a_hydro, - "Accelerations of the particles(does not " - "work in non-cosmological runs)."); - - list[7] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, - parts, primitives.rho, - "Co-moving mass densities of the particles"); - - list[8] = - io_make_output_field("Volumes", FLOAT, 1, UNIT_CONV_VOLUME, -3.f, parts, - cell.volume, "Co-moving volumes of the particles"); - - list[9] = io_make_output_field("GradDensities", FLOAT, 3, UNIT_CONV_DENSITY, - 1.f, parts, primitives.gradients.rho, - "Gradient densities of the particles"); - - list[10] = io_make_output_field_convert_part( - "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY, 1.f, parts, xparts, convert_A, - "Co-moving entropies of the particles"); - - list[11] = io_make_output_field("Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, - -3.f * hydro_gamma, parts, primitives.P, - "Co-moving pressures of the particles"); - - list[12] = io_make_output_field_convert_part( - "TotalEnergies", FLOAT, 1, UNIT_CONV_ENERGY, -3.f * hydro_gamma_minus_one, - parts, xparts, convert_Etot, "Total (co-moving) energy of the particles"); + *num_fields = 0; } /** * @brief Writes the current model of SPH to the file * @param h_grpsph The HDF5 group in which to write */ -INLINE static void hydro_write_flavour(hid_t h_grpsph) { - /* Gradient information */ - io_write_attribute_s(h_grpsph, "Gradient reconstruction model", - HYDRO_GRADIENT_IMPLEMENTATION); - - /* Slope limiter information */ - io_write_attribute_s(h_grpsph, "Cell wide slope limiter model", - HYDRO_SLOPE_LIMITER_CELL_IMPLEMENTATION); - io_write_attribute_s(h_grpsph, "Piecewise slope limiter model", - HYDRO_SLOPE_LIMITER_FACE_IMPLEMENTATION); - - /* Riemann solver information */ - io_write_attribute_s(h_grpsph, "Riemann solver type", - RIEMANN_SOLVER_IMPLEMENTATION); -} +INLINE static void hydro_write_flavour(hid_t h_grpsph) {} /** * @brief Are we writing entropy in the internal energy field ? @@ -260,3 +90,5 @@ INLINE static void hydro_write_flavour(hid_t h_grpsph) { * @return 1 if entropy is in 'internal energy', 0 otherwise. */ INLINE static int writeEntropyFlag(void) { return 0; } + +#endif /* SWIFT_NONE_HYDRO_IO_H */ diff --git a/src/hydro/Shadowswift/hydro_parameters.h b/src/hydro/Shadowswift/hydro_parameters.h index 4db2a31bbdc08cbc490ffa308105a0a4fc9783f8..6c5ea3313b7000f0e11c2defcea74cae0caff884 100644 --- a/src/hydro/Shadowswift/hydro_parameters.h +++ b/src/hydro/Shadowswift/hydro_parameters.h @@ -1,7 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) - * + * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leideuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -17,9 +16,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ - -#ifndef SWIFT_SHADOWSWIFT_HYDRO_PARAMETERS_H -#define SWIFT_SHADOWSWIFT_HYDRO_PARAMETERS_H +#ifndef SWIFT_NONE_HYDRO_PARAMETERS_H +#define SWIFT_NONE_HYDRO_PARAMETERS_H /* Configuration file */ #include <config.h> @@ -36,7 +34,7 @@ /** * @file Shadowswift/hydro_parameters.h - * @brief Shadowswift implementation. (default parameters) + * @brief Empty implementation * * This file defines a number of things that are used in * hydro_properties.c as defaults for run-time parameters @@ -48,7 +46,6 @@ /* Cosmology default beta=3.0. * Alpha can be set in the parameter file. * Beta is defined as in e.g. Price (2010) Eqn (103) */ -#define const_viscosity_beta 3.0f /* Structs that store the relevant variables */ @@ -72,7 +69,7 @@ struct unit_system; * the parameter file, or sets them to defaults. * * @param params: the pointer to the swift_params file - * @param unit_system: pointer to the unit system + * @param us: pointer to the internal unit system * @param phys_const: pointer to the physical constants system * @param viscosity: pointer to the viscosity_global_data struct to be filled. **/ @@ -107,9 +104,7 @@ static INLINE void viscosity_print( * @param viscosity: pointer to the viscosity_global_data struct. **/ static INLINE void viscosity_print_snapshot( - hid_t h_grpsph, const struct viscosity_global_data* viscosity) { - io_write_attribute_f(h_grpsph, "Beta viscosity", const_viscosity_beta); -} + hid_t h_grpsph, const struct viscosity_global_data* viscosity) {} #endif /* Diffusion */ @@ -119,9 +114,9 @@ static INLINE void viscosity_print_snapshot( * the parameter file, or sets them to defaults. * * @param params: the pointer to the swift_params file - * @param unit_system: pointer to the unit system + * @param us: pointer to the internal unit system * @param phys_const: pointer to the physical constants system - * @param diffusion_global_data: pointer to the diffusion struct to be filled. + * @param diffusion: pointer to the diffusion struct to be filled. **/ static INLINE void diffusion_init(struct swift_params* params, const struct unit_system* us, @@ -157,4 +152,4 @@ static INLINE void diffusion_print_snapshot( hid_t h_grpsph, const struct diffusion_global_data* diffusion) {} #endif -#endif /* SWIFT_SHADOWSWIFT_HYDRO_PARAMETERS_H */ +#endif /* SWIFT_NONE_HYDRO_PARAMETERS_H */ diff --git a/src/hydro/Shadowswift/hydro_part.h b/src/hydro/Shadowswift/hydro_part.h index 397dba20d6b0d05fd473f6c1dc9c519cb8777493..a9b000a2b9c48475618e18381c1b9ab98d297d61 100644 --- a/src/hydro/Shadowswift/hydro_part.h +++ b/src/hydro/Shadowswift/hydro_part.h @@ -1,8 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (schaller@strw.leidenuniv.nl) - * 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -18,178 +16,141 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#ifndef SWIFT_SHADOWSWIFT_HYDRO_PART_H -#define SWIFT_SHADOWSWIFT_HYDRO_PART_H +#ifndef SWIFT_NONE_HYDRO_PART_H +#define SWIFT_NONE_HYDRO_PART_H + +/** + * @file Shadowswift/hydro_part.h + * @brief Empty implementation + */ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" +#include "pressure_floor_struct.h" #include "rt_struct.h" #include "sink_struct.h" +#include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" -#include "voronoi_cell.h" -/* Extra particle data not needed during the computation. */ +/** + * @brief Particle fields not needed during the SPH loops over neighbours. + * + * This structure contains the particle fields that are not used in the + * density or force loops. Quantities should be used in the kick, drift and + * potentially ghost tasks only. + */ struct xpart { - /* Offset between current position and position at last tree rebuild. */ + /*! Offset between current position and position at last tree rebuild. */ float x_diff[3]; /*! Offset between the current position and position at the last sort. */ float x_diff_sort[3]; - /* Velocity at the last full step. */ + /*! Velocity at the last full step. */ float v_full[3]; - /*! Gravitational acceleration at the end of the last step */ + /*! Gravitational acceleration at the last full step. */ float a_grav[3]; /*! Additional data used to record particle splits */ struct particle_splitting_data split_data; - /* Additional data used to record cooling information */ + /*! Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; /* Additional data used by the tracers */ struct tracers_xpart_data tracers_data; + /* Additional data used by the tracers */ + struct star_formation_xpart_data sf_data; + /* Additional data used by the feedback */ - struct feedback_part_data feedback_data; + struct feedback_xpart_data feedback_data; + + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; } SWIFT_STRUCT_ALIGN; -/* Data of a single particle. */ +/** + * @brief Particle fields for the SPH particles + * + * The density and force substructures are used to contain variables only used + * within the density and force loops over neighbours. All more permanent + * variables should be declared in the main part of the part structure, + */ struct part { - /* Particle ID. */ + /*! Particle unique ID. */ long long id; - /* Associated gravitas. */ - struct gpart *gpart; + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; - /* Particle position. */ + /*! Particle position. */ double x[3]; - /* Particle predicted velocity. */ + /*! Particle predicted velocity. */ float v[3]; - /* Particle acceleration. */ + /*! Particle acceleration. */ float a_hydro[3]; - /* Particle cutoff radius. */ - float h; - - /* The primitive hydrodynamical variables. */ - struct { + /*! Particle mass. */ + float mass; - /* Fluid velocity. */ - float v[3]; + /*! Particle smoothing length. */ + float h; - /* Density. */ - float rho; + /*! Particle density */ + float rho; - /* Pressure. */ - float P; + /* Store density/force specific stuff. */ + union { - /* Gradients of the primitive variables. */ + /** + * @brief Structure for the variables only used in the density loop over + * neighbours. + * + * Quantities in this sub-structure should only be accessed in the density + * loop over neighbours and the ghost task. + */ struct { - /* Density gradients. */ - float rho[3]; + /*! Neighbour number count. */ + float wcount; - /* Fluid velocity gradients. */ - float v[3][3]; + /*! Derivative of the neighbour number with respect to h. */ + float wcount_dh; - /* Pressure gradients. */ - float P[3]; + /*! Derivative of the density with respect to h. */ + float rho_dh; - } gradients; + } density; - /* Quantities needed by the slope limiter. */ + /** + * @brief Structure for the variables only used in the force loop over + * neighbours. + * + * Quantities in this sub-structure should only be accessed in the force + * loop over neighbours and the ghost, drift and kick tasks. + */ struct { - /* Extreme values of the density among the neighbours. */ - float rho[2]; - - /* Extreme values of the fluid velocity among the neighbours. */ - float v[3][2]; - - /* Extreme values of the pressure among the neighbours. */ - float P[2]; - - /* Maximal distance to all neighbouring faces. */ - float maxr; - - } limiter; - - } primitives; - - /* The conserved hydrodynamical variables. */ - struct { - - /* Fluid momentum. */ - float momentum[3]; - - /* Fluid mass (this field already exists outside of this struct as well). */ - float mass; - - /* Fluid thermal energy (not per unit mass!). */ - float energy; - - /* Fluxes. */ - struct { + /*! Time derivative of smoothing length */ + float h_dt; - /* Mass flux. */ - float mass; + } force; + }; - /* Momentum flux. */ - float momentum[3]; - - /* Energy flux. */ - float energy; - - } flux; - - } conserved; - - /* Variables used for timestep calculation (currently not used). */ - struct { - - /* Maximum fluid velocity among all neighbours. */ - float vmax; - - } timestepvars; - - /* Quantities used during the volume (=density) loop. */ - struct { - - /* Derivative of particle number density. */ - float wcount_dh; - - /* Particle number density. */ - float wcount; - - } density; - - /* Quantities used during the force loop. */ - struct { - - /* Needed to drift the primitive variables. */ - float h_dt; - - /* Physical time step of the particle. */ - float dt; - - /* Active flag. */ - char active; - - /* Actual velocity of the particle. */ - float v_full[3]; - - } force; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -197,12 +158,18 @@ struct part { /*! Cooling information */ struct cooling_part_data cooling_data; + /*! Additional data used by the feedback */ + struct feedback_part_data feedback_data; + /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; /*! Sink information (e.g. swallowing ID) */ struct sink_part_data sink_data; + /*! Additional data used by the pressure floor */ + struct pressure_floor_part_data pressure_floor_data; + /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; @@ -225,9 +192,6 @@ struct part { #endif - /* Voronoi cell. */ - struct voronoi_cell cell; - } SWIFT_STRUCT_ALIGN; -#endif /* SWIFT_SHADOWSWIFT_HYDRO_PART_H */ +#endif /* SWIFT_NONE_HYDRO_PART_H */ diff --git a/src/hydro/Shadowswift/hydro_slope_limiters.h b/src/hydro/Shadowswift/hydro_slope_limiters.h deleted file mode 100644 index a443007b4df3833964b7ec1780e2bc55bf02a03e..0000000000000000000000000000000000000000 --- a/src/hydro/Shadowswift/hydro_slope_limiters.h +++ /dev/null @@ -1,94 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -#ifndef SWIFT_HYDRO_SLOPE_LIMITERS_H -#define SWIFT_HYDRO_SLOPE_LIMITERS_H - -#include "dimension.h" -#include "kernel_hydro.h" - -#ifdef SHADOWFAX_SLOPE_LIMITER_PER_FACE - -#define HYDRO_SLOPE_LIMITER_FACE_IMPLEMENTATION \ - "GIZMO piecewise slope limiter (Hopkins 2015)" -#include "hydro_slope_limiters_face.h" - -#else - -#define HYDRO_SLOPE_LIMITER_FACE_IMPLEMENTATION "No piecewise slope limiter" - -/** - * @brief Slope limit the slopes at the interface between two particles - * - * @param Wi Hydrodynamic variables of particle i. - * @param Wj Hydrodynamic variables of particle j. - * @param dWi Difference between the hydrodynamic variables of particle i at the - * position of particle i and at the interface position. - * @param dWj Difference between the hydrodynamic variables of particle j at the - * position of particle j and at the interface position. - * @param xij_i Relative position vector of the interface w.r.t. particle i. - * @param xij_j Relative position vector of the interface w.r.t. partilce j. - * @param r Distance between particle i and particle j. - */ -__attribute__((always_inline)) INLINE static void hydro_slope_limit_face( - float *Wi, float *Wj, float *dWi, float *dWj, float *xij_i, float *xij_j, - float r) {} - -#endif - -#ifdef SHADOWFAX_SLOPE_LIMITER_CELL_WIDE - -#define HYDRO_SLOPE_LIMITER_CELL_IMPLEMENTATION \ - "Cell wide slope limiter (Springel 2010)" -#include "hydro_slope_limiters_cell.h" - -#else - -#define HYDRO_SLOPE_LIMITER_CELL_IMPLEMENTATION "No cell wide slope limiter" - -/** - * @brief Initialize variables for the cell wide slope limiter - * - * @param p Particle. - */ -__attribute__((always_inline)) INLINE static void hydro_slope_limit_cell_init( - struct part *p) {} - -/** - * @brief Collect information for the cell wide slope limiter during the - * neighbour loop - * - * @param pi Particle i. - * @param pj Particle j. - * @param r Distance between particle i and particle j. - */ -__attribute__((always_inline)) INLINE static void -hydro_slope_limit_cell_collect(struct part *pi, struct part *pj, float r) {} - -/** - * @brief Slope limit cell gradients - * - * @param p Particle. - */ -__attribute__((always_inline)) INLINE static void hydro_slope_limit_cell( - struct part *p) {} - -#endif - -#endif // SWIFT_HYDRO_SLOPE_LIMITERS_H diff --git a/src/hydro/Shadowswift/hydro_slope_limiters_cell.h b/src/hydro/Shadowswift/hydro_slope_limiters_cell.h deleted file mode 100644 index d746ffc59538fcc625a2e095245adfd7a946a95e..0000000000000000000000000000000000000000 --- a/src/hydro/Shadowswift/hydro_slope_limiters_cell.h +++ /dev/null @@ -1,142 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -#include <float.h> - -/** - * @brief Initialize variables for the cell wide slope limiter - * - * @param p Particle. - */ -__attribute__((always_inline)) INLINE static void hydro_slope_limit_cell_init( - struct part* p) { - - p->primitives.limiter.rho[0] = FLT_MAX; - p->primitives.limiter.rho[1] = -FLT_MAX; - p->primitives.limiter.v[0][0] = FLT_MAX; - p->primitives.limiter.v[0][1] = -FLT_MAX; - p->primitives.limiter.v[1][0] = FLT_MAX; - p->primitives.limiter.v[1][1] = -FLT_MAX; - p->primitives.limiter.v[2][0] = FLT_MAX; - p->primitives.limiter.v[2][1] = -FLT_MAX; - p->primitives.limiter.P[0] = FLT_MAX; - p->primitives.limiter.P[1] = -FLT_MAX; - - p->primitives.limiter.maxr = -FLT_MAX; -} - -/** - * @brief Collect information for the cell wide slope limiter during the - * neighbour loop - * - * @param pi Particle i. - * @param pj Particle j. - * @param r Distance between particle i and particle j. - */ -__attribute__((always_inline)) INLINE static void -hydro_slope_limit_cell_collect(struct part* pi, const struct part* pj, - float r) { - - /* basic slope limiter: collect the maximal and the minimal value for the - * primitive variables among the ngbs */ - pi->primitives.limiter.rho[0] = - fmin(pj->primitives.rho, pi->primitives.limiter.rho[0]); - pi->primitives.limiter.rho[1] = - fmax(pj->primitives.rho, pi->primitives.limiter.rho[1]); - - pi->primitives.limiter.v[0][0] = - fmin(pj->primitives.v[0], pi->primitives.limiter.v[0][0]); - pi->primitives.limiter.v[0][1] = - fmax(pj->primitives.v[0], pi->primitives.limiter.v[0][1]); - pi->primitives.limiter.v[1][0] = - fmin(pj->primitives.v[1], pi->primitives.limiter.v[1][0]); - pi->primitives.limiter.v[1][1] = - fmax(pj->primitives.v[1], pi->primitives.limiter.v[1][1]); - pi->primitives.limiter.v[2][0] = - fmin(pj->primitives.v[2], pi->primitives.limiter.v[2][0]); - pi->primitives.limiter.v[2][1] = - fmax(pj->primitives.v[2], pi->primitives.limiter.v[2][1]); - - pi->primitives.limiter.P[0] = - fmin(pj->primitives.P, pi->primitives.limiter.P[0]); - pi->primitives.limiter.P[1] = - fmax(pj->primitives.P, pi->primitives.limiter.P[1]); - - pi->primitives.limiter.maxr = fmax(r, pi->primitives.limiter.maxr); -} - -/** - * @brief Apply the cell wide slope limiter to the gradient of a single quantity - * - * This corresponds to equation (B2) in Hopkins (2015). - * - * @param grad Gradient to slope limit - * @param qval Value of the quantity at the cell generator - * @param qmin Minimal value of the quantity among all cell neighbours - * @param qmax Maximal value of the quantity among all cell neighbours - * @param maxr Maximal distance between the generator and all of its neighbours - */ -__attribute__((always_inline)) INLINE static void -hydro_slope_limit_cell_quantity(float* grad, float qval, float qmin, float qmax, - float maxr) { - - float gradtrue, gradmax, gradmin, alpha; - - gradtrue = sqrtf(grad[0] * grad[0] + grad[1] * grad[1] + grad[2] * grad[2]); - if (gradtrue) { - gradtrue *= maxr; - gradmax = qmax - qval; - gradmin = qval - qmin; - alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue)); - grad[0] *= alpha; - grad[1] *= alpha; - grad[2] *= alpha; - } -} - -/** - * @brief Slope limit cell gradients - * - * @param p Particle. - */ -__attribute__((always_inline)) INLINE static void hydro_slope_limit_cell( - struct part* p) { - - hydro_slope_limit_cell_quantity( - p->primitives.gradients.rho, p->primitives.rho, - p->primitives.limiter.rho[0], p->primitives.limiter.rho[1], - p->primitives.limiter.maxr); - - hydro_slope_limit_cell_quantity( - p->primitives.gradients.v[0], p->primitives.v[0], - p->primitives.limiter.v[0][0], p->primitives.limiter.v[0][1], - p->primitives.limiter.maxr); - hydro_slope_limit_cell_quantity( - p->primitives.gradients.v[1], p->primitives.v[1], - p->primitives.limiter.v[1][0], p->primitives.limiter.v[1][1], - p->primitives.limiter.maxr); - hydro_slope_limit_cell_quantity( - p->primitives.gradients.v[2], p->primitives.v[2], - p->primitives.limiter.v[2][0], p->primitives.limiter.v[2][1], - p->primitives.limiter.maxr); - - hydro_slope_limit_cell_quantity( - p->primitives.gradients.P, p->primitives.P, p->primitives.limiter.P[0], - p->primitives.limiter.P[1], p->primitives.limiter.maxr); -} diff --git a/src/hydro/Shadowswift/hydro_slope_limiters_face.h b/src/hydro/Shadowswift/hydro_slope_limiters_face.h deleted file mode 100644 index 7ae5dd2eb073d9aae8ab6f2efffdf8df15b4bb4a..0000000000000000000000000000000000000000 --- a/src/hydro/Shadowswift/hydro_slope_limiters_face.h +++ /dev/null @@ -1,121 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -/** - * @brief Slope limit a single quantity at the interface - * - * @param phi_i Value of the quantity at the particle position. - * @param phi_j Value of the quantity at the neighbouring particle position. - * @param phi_mid0 Extrapolated value of the quantity at the interface position. - * @param xij_norm Distance between the particle position and the interface - * position. - * @param r Distance between the particle and its neighbour. - * @return The slope limited difference between the quantity at the particle - * position and the quantity at the interface position. - */ -__attribute__((always_inline)) INLINE static float -hydro_slope_limit_face_quantity(float phi_i, float phi_j, float phi_mid0, - float xij_norm, float r) { - - float delta1, delta2, phimin, phimax, phibar, phiplus, phiminus, phi_mid; - const float psi1 = 0.5f; - const float psi2 = 0.25f; - - if (phi_i == phi_j) { - return 0.0f; - } - - delta1 = psi1 * fabs(phi_i - phi_j); - delta2 = psi2 * fabs(phi_i - phi_j); - - phimin = fmin(phi_i, phi_j); - phimax = fmax(phi_i, phi_j); - - phibar = phi_i + xij_norm / r * (phi_j - phi_i); - - /* if sign(phimax+delta1) == sign(phimax) */ - if ((phimax + delta1) * phimax > 0.0f) { - phiplus = phimax + delta1; - } else { - phiplus = phimax / (1.0f + delta1 / fabs(phimax)); - } - - /* if sign(phimin-delta1) == sign(phimin) */ - if ((phimin - delta1) * phimin > 0.0f) { - phiminus = phimin - delta1; - } else { - phiminus = phimin / (1.0f + delta1 / fabs(phimin)); - } - - if (phi_i < phi_j) { - phi_mid = fmax(phiminus, fmin(phibar + delta2, phi_mid0)); - } else { - phi_mid = fmin(phiplus, fmax(phibar - delta2, phi_mid0)); - } - - return phi_mid - phi_i; -} - -/** - * @brief Slope limit the slopes at the interface between two particles - * - * @param Wi Hydrodynamic variables of particle i. - * @param Wj Hydrodynamic variables of particle j. - * @param dWi Difference between the hydrodynamic variables of particle i at the - * position of particle i and at the interface position. - * @param dWj Difference between the hydrodynamic variables of particle j at the - * position of particle j and at the interface position. - * @param xij_i Relative position vector of the interface w.r.t. particle i. - * @param xij_j Relative position vector of the interface w.r.t. partilce j. - * @param r Distance between particle i and particle j. - */ -__attribute__((always_inline)) INLINE static void hydro_slope_limit_face( - float *Wi, float *Wj, float *dWi, float *dWj, float *xij_i, float *xij_j, - float r) { - - float xij_i_norm, xij_j_norm; - - xij_i_norm = - sqrtf(xij_i[0] * xij_i[0] + xij_i[1] * xij_i[1] + xij_i[2] * xij_i[2]); - - xij_j_norm = - sqrtf(xij_j[0] * xij_j[0] + xij_j[1] * xij_j[1] + xij_j[2] * xij_j[2]); - - dWi[0] = hydro_slope_limit_face_quantity(Wi[0], Wj[0], Wi[0] + dWi[0], - xij_i_norm, r); - dWi[1] = hydro_slope_limit_face_quantity(Wi[1], Wj[1], Wi[1] + dWi[1], - xij_i_norm, r); - dWi[2] = hydro_slope_limit_face_quantity(Wi[2], Wj[2], Wi[2] + dWi[2], - xij_i_norm, r); - dWi[3] = hydro_slope_limit_face_quantity(Wi[3], Wj[3], Wi[3] + dWi[3], - xij_i_norm, r); - dWi[4] = hydro_slope_limit_face_quantity(Wi[4], Wj[4], Wi[4] + dWi[4], - xij_i_norm, r); - - dWj[0] = hydro_slope_limit_face_quantity(Wj[0], Wi[0], Wj[0] + dWj[0], - xij_j_norm, r); - dWj[1] = hydro_slope_limit_face_quantity(Wj[1], Wi[1], Wj[1] + dWj[1], - xij_j_norm, r); - dWj[2] = hydro_slope_limit_face_quantity(Wj[2], Wi[2], Wj[2] + dWj[2], - xij_j_norm, r); - dWj[3] = hydro_slope_limit_face_quantity(Wj[3], Wi[3], Wj[3] + dWj[3], - xij_j_norm, r); - dWj[4] = hydro_slope_limit_face_quantity(Wj[4], Wi[4], Wj[4] + dWj[4], - xij_j_norm, r); -} diff --git a/src/hydro/Shadowswift/voronoi1d_algorithm.h b/src/hydro/Shadowswift/voronoi1d_algorithm.h deleted file mode 100644 index 6dcdc1355f02bf304e95a4c46d7e4f4c1943100c..0000000000000000000000000000000000000000 --- a/src/hydro/Shadowswift/voronoi1d_algorithm.h +++ /dev/null @@ -1,193 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -#ifndef SWIFT_VORONOIXD_ALGORITHM_H -#define SWIFT_VORONOIXD_ALGORITHM_H - -#include "error.h" -#include "inline.h" -#include "voronoi1d_cell.h" - -#include <math.h> -#include <stdlib.h> - -/** - * @brief Store the extents of the simulation box in the global variables. - * - * @param anchor Corner of the simulation box with the lowest coordinate values. - * @param side Side lengths of the simulation box. - */ -__attribute__((always_inline)) INLINE static void voronoi_set_box( - const float *anchor, const float *side) {} - -/** - * @brief Initialize a 1D Voronoi cell. - * - * Sets the positions of left and right neighbours to very large values, the - * generator position to the given particle position, and all other quantities - * to zero. - * - * @param cell 1D Voronoi cell to initialize. - * @param x Position of the generator of the cell. - * @param anchor Anchor of the simulation box. - * @param side Side lengths of the simulation box. - */ -__attribute__((always_inline)) INLINE void voronoi_cell_init( - struct voronoi_cell *cell, const double *x, const double *anchor, - const double *side) { - cell->x = x[0]; - cell->xL = anchor[0] - cell->x; - cell->xR = anchor[0] + side[0] - cell->x; - cell->idL = 0; - cell->idR = 0; - cell->volume = 0.0f; - cell->centroid = 0.0f; -} - -/** - * @brief Interact a 1D Voronoi cell with a particle with given relative - * position and ID. - * - * This method checks if the given relative position is closer to the cell - * generator than the current left or right neighbour and updates neighbours - * accordingly. - * - * @param cell 1D Voronoi cell. - * @param dx Relative position of the interacting generator w.r.t. the cell - * generator (in fact: dx = generator - neighbour). - * @param id ID of the interacting neighbour. - */ -__attribute__((always_inline)) INLINE void voronoi_cell_interact( - struct voronoi_cell *cell, const float *dx, unsigned long long id) { - - /* Check for stupidity */ - if (dx[0] == 0.0f) { - error("Cannot interact a Voronoi cell generator with itself!"); - } - - if (-dx[0] < 0.0f) { - /* New left neighbour? */ - if (-dx[0] > cell->xL) { - cell->xL = -dx[0]; - cell->idL = id; - } - } else { - /* New right neighbour? */ - if (-dx[0] < cell->xR) { - cell->xR = -dx[0]; - cell->idR = id; - } - } -} - -/** - * @brief Finalize a 1D Voronoi cell. - * - * Calculates the relative positions of the midpoints of the faces (which in - * this case are just the midpoints of the segments connecting the generator - * with the two neighbours) w.r.t. the generator, and the cell volume (length) - * and centroid (midpoint of the segment connecting the midpoints of the faces). - * This function returns the maximal radius at which a particle could still - * change the structure of the cell, i.e. twice the largest distance between - * the cell generator and one of its faces. If the cell has been interacted with - * all neighbours within this radius, we know for sure that the cell is - * complete. - * - * @param cell 1D Voronoi cell. - * @return Maximal radius that could still change the structure of the cell. - */ -__attribute__((always_inline)) INLINE float voronoi_cell_finalize( - struct voronoi_cell *cell) { - - float xL, xR; - float max_radius; - - max_radius = fmax(-cell->xL, cell->xR); - cell->xL = xL = 0.5f * cell->xL; - cell->xR = xR = 0.5f * cell->xR; - - cell->volume = xR - xL; - cell->centroid = cell->x + 0.5f * (xL + xR); - - return max_radius; -} - -/** - * @brief Get the oriented surface area and midpoint of the face between a - * 1D Voronoi cell and the given neighbour. - * - * This function also checks if the given neighbour is in fact a neighbour of - * this cell. Since we perform gradient and flux calculations for all neighbour - * pairs within the smoothing length, which assumes the cell to be spherical, - * it can happen that this is not the case. It is the responsibility of the - * routine that calls this function to check for a zero return value and - * deal with it appropriately. - * - * For this specific case, we simply check if the neighbour is the left or - * right neighbour and set the surface area to 1. The midpoint is set to the - * relative position vector of the appropriate face. - * - * @param cell 1D Voronoi cell. - * @param ngb ID of a particle that is possibly a neighbour of this cell. - * @param midpoint Array to store the relative position of the face in. - * @return 0 if the given neighbour is not a neighbour, surface area 1.0f - * otherwise. - */ -__attribute__((always_inline)) INLINE float voronoi_get_face( - const struct voronoi_cell *cell, unsigned long long ngb, float *midpoint) { - - if (ngb != cell->idL && ngb != cell->idR) { - /* this is perfectly possible: we interact with all particles within the - smoothing length, and they do not need to be all neighbours. - If this happens, we return 0, so that the flux method can return */ - return 0.0f; - } - - if (ngb == cell->idL) { - /* Left face */ - midpoint[0] = cell->xL; - } else { - /* Right face */ - midpoint[0] = cell->xR; - } - /* The other components of midpoint are just zero */ - midpoint[1] = 0.0f; - midpoint[2] = 0.0f; - - return 1.0f; -} - -/** - * @brief Get the centroid of a 1D Voronoi cell. - * - * We store only the relevant coordinate of the centroid, but need to return - * a 3D vector. - * - * @param cell 1D Voronoi cell. - * @param centroid Array to store the centroid in. - */ -__attribute__((always_inline)) INLINE void voronoi_get_centroid( - const struct voronoi_cell *cell, float *centroid) { - - centroid[0] = cell->centroid; - centroid[1] = 0.0f; - centroid[2] = 0.0f; -} - -#endif // SWIFT_VORONOIXD_ALGORITHM_H diff --git a/src/hydro/Shadowswift/voronoi2d_algorithm.h b/src/hydro/Shadowswift/voronoi2d_algorithm.h deleted file mode 100644 index 16efe290a9c8144eb77953676c0c424288a76f63..0000000000000000000000000000000000000000 --- a/src/hydro/Shadowswift/voronoi2d_algorithm.h +++ /dev/null @@ -1,548 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -#ifndef SWIFT_VORONOIXD_ALGORITHM_H -#define SWIFT_VORONOIXD_ALGORITHM_H - -#include "error.h" -#include "inline.h" -#include "minmax.h" -#include "voronoi2d_cell.h" - -#include <float.h> -#include <math.h> -#include <stdlib.h> - -/* Check if the number of vertices exceeds the maximal allowed number */ -#define VORONOI_CHECK_SIZE() \ - if (nvert > VORONOI2D_MAXNUMVERT) { \ - error("Too many vertices!"); \ - } - -/* IDs used to keep track of cells neighbouring walls of the simulation box - This will only work if these IDs are never used for actual particles (which - in practice means you want to have less than 2^63-4 (~9e18) particles in your - simulation) */ -#define VORONOI2D_BOX_LEFT 18446744073709551602llu -#define VORONOI2D_BOX_RIGHT 18446744073709551603llu -#define VORONOI2D_BOX_TOP 18446744073709551604llu -#define VORONOI2D_BOX_BOTTOM 18446744073709551605llu - -#define VORONOI2D_TOLERANCE 1.e-6f - -/** - * @brief Initialize a 2D Voronoi cell. - * - * @param cell 2D Voronoi cell to initialize. - * @param x Position of the generator of the cell. - * @param anchor Anchor of the simulation box containing all particles. - * @param side Side lengths of the simulation box containing all particles. - */ -__attribute__((always_inline)) INLINE void voronoi_cell_init( - struct voronoi_cell *cell, const double *x, const double *anchor, - const double *side) { - - /* Set the position of the generator of the cell (for reference) */ - cell->x[0] = x[0]; - cell->x[1] = x[1]; - - /* Initialize the cell as a box with the same extents as the simulation box - (note: all vertex coordinates are relative w.r.t. the cell generator) */ - cell->nvert = 4; - - cell->vertices[0][0] = anchor[0] - cell->x[0]; - cell->vertices[0][1] = anchor[1] - cell->x[1]; - - cell->vertices[1][0] = anchor[0] - cell->x[0]; - cell->vertices[1][1] = anchor[1] + side[1] - cell->x[1]; - - cell->vertices[2][0] = anchor[0] + side[0] - cell->x[0]; - cell->vertices[2][1] = anchor[1] + side[1] - cell->x[1]; - - cell->vertices[3][0] = anchor[0] + side[0] - cell->x[0]; - cell->vertices[3][1] = anchor[1] - cell->x[1]; - - /* The neighbours are ordered such that neighbour i shares the face in between - vertices i and i+1 (with last vertex + 1 = first vertex) - We walk around the cell in clockwise direction */ - cell->ngbs[0] = VORONOI2D_BOX_LEFT; - cell->ngbs[1] = VORONOI2D_BOX_TOP; - cell->ngbs[2] = VORONOI2D_BOX_RIGHT; - cell->ngbs[3] = VORONOI2D_BOX_BOTTOM; - - /* These variables are initialized to zero, we will compute them after the - neighbour iteration has finished */ - cell->volume = 0.0f; - cell->centroid[0] = 0.0f; - cell->centroid[1] = 0.0f; -} - -/** - * @brief Interact a 2D Voronoi cell with a particle with given relative - * position and ID. - * - * @param cell 2D Voronoi cell. - * @param dx Relative position of the interacting generator w.r.t. the cell - * generator (in fact: dx = generator - neighbour). - * @param id ID of the interacting neighbour. - */ -__attribute__((always_inline)) INLINE void voronoi_cell_interact( - struct voronoi_cell *cell, const float *dx, unsigned long long id) { - - /* variables used for geometrical tests */ - float half_dx[2]; - float r2; - /* variables used to store test results */ - float test, b1, b2, a1, a2; - /* general loop index */ - int i; - /* variables used to store indices of intersected edges */ - int index_above1, index_above2; - int index_below1, index_below2; - /* variable used to store directionality in edge traversal */ - int increment; - /* new number of vertices and new vertex coordinates */ - int nvert; - float vertices[VORONOI2D_MAXNUMVERT][2]; - unsigned long long ngbs[VORONOI2D_MAXNUMVERT]; - - /* The process of cutting the current cell with the midline of the generator - and the given relative neighbour position proceeds in two steps: - - first we need to locate an edge of the current cell that is intersected - by this midline. Such an edge does not necessarily exist; in this case - the given neighbour is not an actual neighbour of this cell - - Once we have an intersected edge, we create a new edge starting at the - intersection point. We follow the edges connected to the intersected - edge until we find another intersected edge, and use its intersection - point as end point of the new edge. */ - - /* First, we set up some variables that are used to check if a vertex is above - or below the midplane. */ - - /* we need a vector with half the size of the vector joining generator and - neighbour, pointing to the neighbour */ - half_dx[0] = -0.5f * dx[0]; - half_dx[1] = -0.5f * dx[1]; - - /* we need the squared length of this vector */ - r2 = half_dx[0] * half_dx[0] + half_dx[1] * half_dx[1]; - - /* a vertex v = (vx, vy) is above the midline if - vx*half_dx[0] + vy*half_dx[1] > r2 - i.e., if the length of the projected vertex position is longer than the - length of the vector pointing to the closest point on the midline (both - vectors originate at the position of the generator) - the vertex is below the midline if the projected position vector is shorter - if the projected position vector has the same length, the vertex is on the - midline */ - - /* start testing a random vertex: the first one */ - test = cell->vertices[0][0] * half_dx[0] + cell->vertices[0][1] * half_dx[1] - - r2; - if (test < -VORONOI2D_TOLERANCE) { -/* vertex is below midline */ -#ifdef VORONOI_VERBOSE - message("First vertex is below midline (%g %g --> %g)!", - cell->vertices[0][0] + cell->x[0], - cell->vertices[0][1] + cell->x[1], test); -#endif - - /* store the test result; we might need it to compute the intersection - coordinates */ - b1 = test; - - /* move on until we find a vertex that is above or on the midline */ - i = 1; - test = cell->vertices[i][0] * half_dx[0] + - cell->vertices[i][1] * half_dx[1] - r2; - while (i < cell->nvert && test < 0.) { - /* make sure we always store the latest test result */ - b1 = test; - ++i; - test = cell->vertices[i][0] * half_dx[0] + - cell->vertices[i][1] * half_dx[1] - r2; - } - - /* loop finished, there are two possibilities: - - i == cell->nvert, all vertices lie below the midline and the given - neighbour is not an actual neighbour of this cell - - test >= 0., we found a vertex above (or on) the midline */ - if (i == cell->nvert) { -/* the given neighbour is not an actual neighbour: exit the routine */ -#ifdef VORONOI_VERBOSE - message("Not a neighbour!"); -#endif - return; - } - - /* we have found an intersected edge: i-1 -> i - we store the index of the vertices above and below the midline, make sure - we store the test result for later intersection computation, and set the - increment to positive, so that we look for the other intersected edge in - clockwise direction */ - index_below1 = i - 1; - index_above1 = i; - a1 = test; - increment = 1; - } else { -/* vertex is above or on midline - in the case where it is on the midline, we count that as above as well: - the vertex will be removed, and a new vertex will be created at the same - position */ -#ifdef VORONOI_VERBOSE - message("First vertex is above midline (%g %g --> %g)!", - cell->vertices[0][0] + cell->x[0], - cell->vertices[0][1] + cell->x[1], test); -#endif - - /* store the test result */ - a1 = test; - - /* move on until we find a vertex that is below the midline */ - i = 1; - test = cell->vertices[i][0] * half_dx[0] + - cell->vertices[i][1] * half_dx[1] - r2; - while (i < cell->nvert && test > -VORONOI2D_TOLERANCE) { - /* make sure we always store the most recent test result */ - a1 = test; - ++i; - test = cell->vertices[i][0] * half_dx[0] + - cell->vertices[i][1] * half_dx[1] - r2; - } - - /* loop finished, there are two possibilities: - - i == cell->nvert, all vertices lie above the midline. This should - never happen. - - test <= 0., we found a vertex below (or on) the midline */ - if (i == cell->nvert) { - /* fatal error! */ - error("Could not find a vertex below the midline!"); - } - - /* we have found an intersected edge: i-1 -> i - we store the index of the vertices above and below the midline, make sure - we store the test result for later intersection computation, and set the - increment to negative, so that we look for the other intersected edge in - counterclockwise direction */ - index_below1 = i; - index_above1 = i - 1; - increment = -1; - b1 = test; - } - -#ifdef VORONOI_VERBOSE - message("First intersected edge: %g %g --> %g %g (%i --> %i)", - cell->vertices[index_below1][0] + cell->x[0], - cell->vertices[index_below1][1] + cell->x[1], - cell->vertices[index_above1][0] + cell->x[0], - cell->vertices[index_above1][1] + cell->x[1], index_below1, - index_above1); -#endif - - /* now we need to find the second intersected edge - we start from the vertex above (or on) the midline and search in the - direction opposite to the intersected edge direction until we find a vertex - below the midline */ - - /* we make sure we store the test result for the second vertex above the - midline as well, since we need this for intersection point computations - the second vertex can be equal to the first */ - a2 = a1; - i = index_above1 + increment; - if (i < 0) { - i = cell->nvert - 1; - } - if (i == cell->nvert) { - i = 0; - } - test = cell->vertices[i][0] * half_dx[0] + cell->vertices[i][1] * half_dx[1] - - r2; - /* this loop can never deadlock, as we know there is at least 1 vertex below - the midline */ - while (test > -VORONOI2D_TOLERANCE) { - /* make sure we always store the most recent test result */ - a2 = test; - i += increment; - if (i < 0) { - i = cell->nvert - 1; - } - if (i == cell->nvert) { - i = 0; - } - test = cell->vertices[i][0] * half_dx[0] + - cell->vertices[i][1] * half_dx[1] - r2; - } - - index_below2 = i; - index_above2 = i - increment; - if (index_above2 < 0) { - index_above2 = cell->nvert - 1; - } - if (index_above2 == cell->nvert) { - index_above2 = 0; - } - /* we also store the test result for the second vertex below the midline */ - b2 = test; - - if (index_above1 == index_above2 && index_below1 == index_below2) { - /* There can be only 1 vertex above or below the midline, but we need 2 - intersected edges, so if the vertices above the midline are the same, the - ones below need to be different and vice versa */ - error("Only 1 intersected edge found!"); - } - - /* there is exactly one degenerate case we have not addressed yet: the case - where index_above1 and index_above2 are the same and are on the midline. - In this case we don't want to create 2 new vertices. Instead, we just keep - index_above1, which basically means nothing happens at all and we can just - return */ - if (index_above1 == index_above2 && a1 == 0.) { - return; - } - - /* to make the code below more clear, we make sure index_above1 always holds - the first vertex to remove, and index_above2 the last one, in clockwise - order - This means we need to interchange 1 and 2 if we were searching in counter- - clockwise direction above */ - if (increment < 0) { - i = index_below1; - index_below1 = index_below2; - index_below2 = i; - i = index_above1; - index_above1 = index_above2; - index_above2 = i; - test = b1; - b1 = b2; - b2 = test; - test = a1; - a1 = a2; - a2 = test; - } - -#ifdef VORONOI_VERBOSE - message("First vertex below: %g %g (%i, %g)", - cell->vertices[index_below1][0] + cell->x[0], - cell->vertices[index_below1][1] + cell->x[1], index_below1, b1); - message("First vertex above: %g %g (%i, %g)", - cell->vertices[index_above1][0] + cell->x[0], - cell->vertices[index_above1][1] + cell->x[1], index_above1, a1); - message("Second vertex below: %g %g (%i, %g)", - cell->vertices[index_below2][0] + cell->x[0], - cell->vertices[index_below2][1] + cell->x[1], index_below2, b2); - message("Second vertex above: %g %g (%i, %g)", - cell->vertices[index_above2][0] + cell->x[0], - cell->vertices[index_above2][1] + cell->x[1], index_above2, a2); -#endif - - if (b1 == 0. || b2 == 0.) { - error("Vertex below midline is on midline!"); - } - - /* convert the test results (which correspond to the projected distance - between the vertex and the midline) to the fractions of the intersected - edges above and below the midline */ - test = a1 / (a1 - b1); - a1 = test; - b1 = 1.0f - test; - - test = a2 / (a2 - b2); - a2 = test; - b2 = 1.0f - test; - - /* remove the vertices above the midline, and insert two new vertices, - corresponding to the intersection points of the intersected edges and the - midline - In practice, we just copy all remaining vertices, starting from the first - vertex below the midline (in clockwise order) */ - nvert = 0; - i = index_below2; - while (i != index_above1) { - vertices[nvert][0] = cell->vertices[i][0]; - vertices[nvert][1] = cell->vertices[i][1]; - ngbs[nvert] = cell->ngbs[i]; - ++nvert; - VORONOI_CHECK_SIZE(); - ++i; - if (i == cell->nvert) { - i = 0; - } - } - /* now add the new vertices, they are always last */ - vertices[nvert][0] = a1 * cell->vertices[index_below1][0] + - b1 * cell->vertices[index_above1][0]; - vertices[nvert][1] = a1 * cell->vertices[index_below1][1] + - b1 * cell->vertices[index_above1][1]; - ngbs[nvert] = id; - ++nvert; - VORONOI_CHECK_SIZE(); - vertices[nvert][0] = a2 * cell->vertices[index_below2][0] + - b2 * cell->vertices[index_above2][0]; - vertices[nvert][1] = a2 * cell->vertices[index_below2][1] + - b2 * cell->vertices[index_above2][1]; - ngbs[nvert] = cell->ngbs[index_above2]; - ++nvert; - VORONOI_CHECK_SIZE(); - - /* overwrite the original vertices */ - cell->nvert = nvert; - for (i = 0; i < cell->nvert; ++i) { - cell->vertices[i][0] = vertices[i][0]; - cell->vertices[i][1] = vertices[i][1]; - cell->ngbs[i] = ngbs[i]; - } -} - -/** - * @brief Finalize a 2D Voronoi cell. - * - * @param cell 2D Voronoi cell. - * @return Maximal radius that could still change the structure of the cell. - */ -__attribute__((always_inline)) INLINE float voronoi_cell_finalize( - struct voronoi_cell *cell) { - - int i; - float vertices[VORONOI2D_MAXNUMVERT][2]; - float A, x[2], y[2], r2, r2max; - - /* make a copy of the vertices (they are overwritten when the face midpoints - are computed */ - for (i = 0; i < cell->nvert; ++i) { - vertices[i][0] = cell->vertices[i][0]; - vertices[i][1] = cell->vertices[i][1]; - } - - r2max = 0.0f; - for (i = 0; i < cell->nvert; ++i) { - if (i < cell->nvert - 1) { - x[0] = vertices[i][0]; - y[0] = vertices[i][1]; - x[1] = vertices[i + 1][0]; - y[1] = vertices[i + 1][1]; - } else { - x[0] = vertices[i][0]; - y[0] = vertices[i][1]; - x[1] = vertices[0][0]; - y[1] = vertices[0][1]; - } - A = x[1] * y[0] - x[0] * y[1]; - cell->volume += A; - cell->centroid[0] += (x[0] + x[1]) * A; - cell->centroid[1] += (y[0] + y[1]) * A; - - /* Note that we only need the RELATIVE positions of the midpoints */ - cell->face_midpoints[i][0] = 0.5f * (x[0] + x[1]); - cell->face_midpoints[i][1] = 0.5f * (y[0] + y[1]); - - r2 = x[0] * x[0] + y[0] * y[0]; - r2max = max(r2max, r2); - - x[0] -= x[1]; - y[0] -= y[1]; - cell->face_lengths[i] = sqrtf(x[0] * x[0] + y[0] * y[0]); - } - - cell->volume *= 0.5f; - A = 6 * cell->volume; - cell->centroid[0] /= A; - cell->centroid[1] /= A; - - cell->centroid[0] += cell->x[0]; - cell->centroid[1] += cell->x[1]; - - return 2.0f * sqrtf(r2max); -} - -/** - * @brief Get the oriented surface area and midpoint of the face between a - * 2D Voronoi cell and the given neighbour. - * - * @param cell 2D Voronoi cell. - * @param ngb ID of a particle that is possibly a neighbour of this cell. - * @param midpoint Array to store the relative position of the face in. - * @return 0 if the given neighbour is not a neighbour, surface area otherwise. - */ -__attribute__((always_inline)) INLINE float voronoi_get_face( - const struct voronoi_cell *cell, unsigned long long ngb, float *midpoint) { - - /* look up the neighbour */ - int i = 0; - while (i < cell->nvert && cell->ngbs[i] != ngb) { - ++i; - } - - if (i == cell->nvert) { - /* The given cell is not a neighbour. */ - return 0.0f; - } - - midpoint[0] = cell->face_midpoints[i][0]; - midpoint[1] = cell->face_midpoints[i][1]; - midpoint[2] = 0.0f; - - return cell->face_lengths[i]; -} - -/** - * @brief Get the centroid of a 2D Voronoi cell. - * - * @param cell 2D Voronoi cell. - * @param centroid Array to store the centroid in. - */ -__attribute__((always_inline)) INLINE void voronoi_get_centroid( - const struct voronoi_cell *cell, float *centroid) { - - centroid[0] = cell->centroid[0]; - centroid[1] = cell->centroid[1]; - centroid[2] = 0.0f; -} - -/******************************************************************************* - ** EXTRA FUNCTIONS USED FOR DEBUGGING ***************************************** - ******************************************************************************/ - -/** - * @brief Print the given cell to the stdout in a format that can be plotted - * using gnuplot. - * - * @param cell voronoi_cell to print. - */ -__attribute__((always_inline)) INLINE void voronoi_print_cell( - const struct voronoi_cell *cell) { - - int i, ip1; - - /* print cell generator */ - printf("%g %g\n\n", cell->x[0], cell->x[1]); - - /* print cell vertices */ - for (i = 0; i < cell->nvert; ++i) { - ip1 = i + 1; - if (ip1 == cell->nvert) { - ip1 = 0; - } - printf("%g %g\n%g %g\n\n", cell->vertices[i][0] + cell->x[0], - cell->vertices[i][1] + cell->x[1], - cell->vertices[ip1][0] + cell->x[0], - cell->vertices[ip1][1] + cell->x[1]); - } -} - -#endif // SWIFT_VORONOIXD_ALGORITHM_H diff --git a/src/hydro/Shadowswift/voronoi2d_cell.h b/src/hydro/Shadowswift/voronoi2d_cell.h deleted file mode 100644 index 3c54ea8d0aa9ca1c915da1f245f889ebab2073d3..0000000000000000000000000000000000000000 --- a/src/hydro/Shadowswift/voronoi2d_cell.h +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -#ifndef SWIFT_VORONOIXD_CELL_H -#define SWIFT_VORONOIXD_CELL_H - -/* Maximal number of vertices (and neighbours) that can be stored in a - voronoi_cell struct. */ -#define VORONOI2D_MAXNUMVERT 100 - -/* 2D Voronoi cell */ -struct voronoi_cell { - - /* The position of the generator of the cell. */ - double x[2]; - - /* The "volume" of the 2D cell. */ - float volume; - - /* The centroid of the cell. */ - float centroid[2]; - - /* Number of cell vertices (and neighbours). */ - int nvert; - - /* We only need to store one of these at the same time. */ - union { - /* The relative positions of the vertices of the cell. */ - float vertices[VORONOI2D_MAXNUMVERT][2]; - - /* The midpoints of the faces. */ - float face_midpoints[VORONOI2D_MAXNUMVERT][2]; - }; - - /* The ids of the neighbouring cells. */ - unsigned long long ngbs[VORONOI2D_MAXNUMVERT]; - - /* The lengths of the faces. */ - float face_lengths[VORONOI2D_MAXNUMVERT]; -}; - -#endif // SWIFT_VORONOIXD_CELL_H diff --git a/src/hydro/Shadowswift/voronoi3d_algorithm.h b/src/hydro/Shadowswift/voronoi3d_algorithm.h deleted file mode 100644 index 449e1f22a38939fec49a3861dbe32683cf85d315..0000000000000000000000000000000000000000 --- a/src/hydro/Shadowswift/voronoi3d_algorithm.h +++ /dev/null @@ -1,2216 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -#ifndef SWIFT_VORONOIXD_ALGORITHM_H -#define SWIFT_VORONOIXD_ALGORITHM_H - -#include "error.h" -#include "inline.h" -#include "voronoi3d_cell.h" - -#include <float.h> -#include <math.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -/* For debugging purposes */ -// #define LOOP_CHECK 1000 - -#ifdef LOOP_CHECK -/* We need to do the trickery below to get a unique counter for each call to the - macro. This only works if the macro is never called twice on the same line. - */ -#define MERGE(a, b) a##b -#define LOOPCOUNTER_NAME(line) MERGE(loopcount, line) - -/** - * @brief Increase the given counter variable and check if it is still valid. - * - * @param counter Counter to increase. - * @param line_number Line number where the while is called. - * @return 1 if the counter is still valid, 0 otherwise. - */ -__attribute__((always_inline)) INLINE int check_counter(int *counter, - int line_number) { - ++(*counter); - if ((*counter) == LOOP_CHECK) { - error("Number of iterations reached maximum (=%i) in while on line %i!", - LOOP_CHECK, line_number); - } - return 1; -} - -/* safewhile is a wrapper around a while that adds a unique counter variable to - the loop that is increased by 1 for each time the loop is executed, and - causes the code to crash if this number exceeds a given value. - We use this to quickly enable or disable number of iterations checks for a - large number of while loops */ -#define safewhile(condition) \ - int LOOPCOUNTER_NAME(__LINE__) = 0; \ - while (check_counter(&LOOPCOUNTER_NAME(__LINE__), __LINE__) && (condition)) - -#else /* LOOP_CHECK */ - -/* If LOOP_CHECK is not defined, safewhile and while are EXACTLY the same */ -#define safewhile(condition) while (condition) - -#endif /* LOOP_CHECK */ - -/* This flag activates a number of expensive geometrical checks that help - finding bugs. */ -// #define VORONOI3D_EXPENSIVE_CHECKS - -/* Tolerance parameter used to decide when to use more precise geometric - criteria */ -#define VORONOI3D_TOLERANCE 1.e-6f - -/* Box boundary flags used to signal cells neighbouring the box boundary - These values correspond to the top range of possible 64-bit integers, and - we make the strong assumption that there will never be a particle that has - one of these as particle ID. */ -#define VORONOI3D_BOX_FRONT 18446744073709551600llu -#define VORONOI3D_BOX_BACK 18446744073709551601llu -#define VORONOI3D_BOX_TOP 18446744073709551602llu -#define VORONOI3D_BOX_BOTTOM 18446744073709551603llu -#define VORONOI3D_BOX_LEFT 18446744073709551604llu -#define VORONOI3D_BOX_RIGHT 18446744073709551605llu - -/******************************************************************************* - * 3D specific methods - * - * Most of these methods are based on the source code of voro++: - * http://math.lbl.gov/voro++/ - ******************************************************************************/ - -/** - * @brief Print the given cell to the stderr in a format that can be easily - * plotted using gnuplot. - * - * This method prints to the stderr instead of stdout to make it possible to use - * it right before crashing the code. - * - * @param c Voronoi cell to print. - */ -__attribute__((always_inline)) INLINE void voronoi_print_gnuplot_c( - const struct voronoi_cell *c) { - - int i, j, v; - const double *x = c->x; - - fprintf(stderr, "%g\t%g\t%g\n\n", x[0], x[1], x[2]); - - for (i = 0; i < c->nvert; ++i) { - for (j = 0; j < c->orders[i]; ++j) { - v = c->edges[c->offsets[i] + j]; - if (v < 0) { - v = -v - 1; - } - fprintf(stderr, "%g\t%g\t%g\n", c->vertices[3 * i + 0] + x[0], - c->vertices[3 * i + 1] + x[1], c->vertices[3 * i + 2] + x[2]); - fprintf(stderr, "%g\t%g\t%g\n\n", c->vertices[3 * v + 0] + x[0], - c->vertices[3 * v + 1] + x[1], c->vertices[3 * v + 2] + x[2]); - } - } - fprintf(stderr, "\n"); -} - -/** - * @brief Print the contents of a 3D Voronoi cell - * - * @param cell 3D Voronoi cell - */ -__attribute__((always_inline)) INLINE void voronoi_print_cell( - const struct voronoi_cell *cell) { - - int i, j; - - fprintf(stderr, "x: %g %g %g\n", cell->x[0], cell->x[1], cell->x[2]); - fprintf(stderr, "nvert: %i\n", cell->nvert); - - for (i = 0; i < cell->nvert; ++i) { - fprintf(stderr, "%i: %g %g %g (%i)\n", i, cell->vertices[3 * i], - cell->vertices[3 * i + 1], cell->vertices[3 * i + 2], - cell->orders[i]); - for (j = 0; j < cell->orders[i]; ++j) { - fprintf(stderr, "%i (%i)\n", cell->edges[cell->offsets[i] + j], - cell->edgeindices[cell->offsets[i] + j]); - } - } - fprintf(stderr, "\n"); -} - -/** - * @brief Get the index of the vertex pointed to by the given edge of the given - * vertex. - * - * @param c 3D Voronoi cell. - * @param vertex Index of a vertex of the cell. - * @param edge Edge of that vertex. - * @return Index of the vertex on the other side of the edge. - */ -__attribute__((always_inline)) INLINE int voronoi_get_edge( - const struct voronoi_cell *c, int vertex, int edge) { - return c->edges[c->offsets[vertex] + edge]; -} - -/** - * @brief Get the index of the given edge in the edge list of the vertex on the - * other side of the edge of the given vertex. - * - * Suppose that the given vertex has edges [edge1, edge2, given_edge], and that - * the vertex on the other side of given_edge has edges [edge1, given_edge, - * edge2], then this method returns 1. - * - * @param c 3D Voronoi cell. - * @param vertex Index of a vertex of the cell. - * @param edge Edge of that vertex. - * @return Index of that edge in the edge list of the vertex on the other side - * of the edge. - */ -__attribute__((always_inline)) INLINE int voronoi_get_edgeindex( - const struct voronoi_cell *c, int vertex, int edge) { - return c->edgeindices[c->offsets[vertex] + edge]; -} - -/** - * @brief Set the index of the vertex on the other side of the given edge of the - * given vertex. - * - * @param c 3D Voronoi cell. - * @param vertex Index of a vertex of the cell. - * @param edge Edge of that vertex. - * @param value Index of the vertex on the other side of that edge. - */ -__attribute__((always_inline)) INLINE void voronoi_set_edge( - struct voronoi_cell *c, int vertex, int edge, int value) { - c->edges[c->offsets[vertex] + edge] = value; -} - -/** - * @brief Set the index of the given edge in the edge list of the vertex on the - * other side of the edge of the given vertex. - * - * Suppose that the given vertex has edges [edge1, edge2, given_edge], and we - * want to tell this method that the vertex on the other side of given_edge has - * edges [edge1, given_edge, edge2], then we need to pass on a value of 1 to - * this method. - * - * @param c 3D Voronoi cell. - * @param vertex Index of a vertex of that cell. - * @param edge Edge of that vertex. - * @param value Index of that edge in the edge list of the vertex on the other - * side of the edge. - */ -__attribute__((always_inline)) INLINE void voronoi_set_edgeindex( - struct voronoi_cell *c, int vertex, int edge, int value) { - c->edgeindices[c->offsets[vertex] + edge] = value; -} - -/** - * @brief Get the neighbour for the given edge of the given vertex. - * - * An edge is shared by two faces, and each face has a neighbour. Luckily, each - * edge also has two endpoints, so we can get away with storing only one - * neighbour per endpoint of an edge. We have complete freedom in choosing which - * neighbour to store in which endpoint, but we need to be consistent about it. - * Here we use the following convention: if we take a vector pointing away from - * the given vertex along the given edge direction, then we store the neighbour - * that corresponds to the face to the right if looking to the cell from the - * outside. This is the face that you encounter first when rotating counter- - * clockwise around that vector, starting from outside the cell. - * - * @param c 3D Voronoi cell. - * @param vertex Index of a vertex of that cell. - * @param edge Edge of that vertex. - * @return Index of the neighbour corresponding to that edge and vertex. - */ -__attribute__((always_inline)) INLINE int voronoi_get_ngb( - const struct voronoi_cell *c, int vertex, int edge) { - return c->ngbs[c->offsets[vertex] + edge]; -} - -/** - * @brief Set the neighbour for the given edge of the given vertex. - * - * An edge is shared by two faces, and each face has a neighbour. Luckily, each - * edge also has two endpoints, so we can get away with storing only one - * neighbour per endpoint of an edge. We have complete freedom in choosing which - * neighbour to store in which endpoint, but we need to be consistent about it. - * Here we use the following convention: if we take a vector pointing away from - * the given vertex along the given edge direction, then we store the neighbour - * that corresponds to the face to the right if looking to the cell from the - * outside. This is the face that you encounter first when rotating counter- - * clockwise around that vector, starting from outside the cell. - * - * @param c 3D Voronoi cell. - * @param vertex Index of a vertex of that cell. - * @param edge Edge of that vertex. - * @param value Index of the neighbour corresponding to that edge and vertex. - */ -__attribute__((always_inline)) INLINE void voronoi_set_ngb( - struct voronoi_cell *c, int vertex, int edge, int value) { - c->ngbs[c->offsets[vertex] + edge] = value; -} - -/** - * @brief Check if the 3D Voronoi cell is still consistent. - * - * A cell is consistent if its edges are consistent, i.e. if edge e of vertex v1 - * points to vertex v2, then v2 should have an edge that points to v1 as well, - * and then the edge index of vertex v1 should contain the index of that edge - * in the edge list of v2. We also check if all vertices have orders of at least - * 3, and if all vertices are actually part of the vertex list. - * Oh, and we check if the cell actually has vertices. - * - * @param cell 3D Voronoi cell to check - */ -__attribute__((always_inline)) INLINE void voronoi_check_cell_consistency( - const struct voronoi_cell *c) { - - int i, j, e, l, m; - - if (c->nvert < 4) { - error("Found cell with only %i vertices!", c->nvert); - } - - for (i = 0; i < c->nvert; ++i) { - if (c->orders[i] < 3) { - voronoi_print_cell(c); - error("Found cell with vertex of order %i!", c->orders[i]); - } - for (j = 0; j < c->orders[i]; ++j) { - e = voronoi_get_edge(c, i, j); - if (e >= c->nvert) { - voronoi_print_cell(c); - error("Found cell with edges that lead to non-existing vertices!"); - } - if (e < 0) { - continue; - } - l = voronoi_get_edgeindex(c, i, j); - m = voronoi_get_edge(c, e, l); - if (m != i) { - /* voronoi_print_gnuplot_c(c); */ - voronoi_print_cell(c); - fprintf(stderr, "i: %i, j: %i, e: %i, l: %i, m: %i\n", i, j, e, l, m); - error("Cell inconsistency!"); - } - } - } -} - -/** - * @brief Check if the given vertex is above, below or on the cutting plane - * defined by the given parameters. - * - * @param v Coordinates of a cell vertex, relative w.r.t. the position of the - * generator of the cell. - * @param dx Half of the relative distance vector between the position of the - * generator of the cell and the position of the neighbouring cell that - * generates the cutting plane, pointing from the generator position to the - * cutting plane. - * @param r2 Squared length of dx. - * @param test Variable to store the result of the geometric test in, which - * corresponds to the projected distance between the generator and the vertex - * along dx. - * @param teststack Stack to store the results of the N last tests in (for - * debugging purposes only). - * @param teststack_size Next available field in the teststack, is reset to 0 if - * the teststack is full (so the N+1th results is overwritten; for debugging - * purposes only). - * @return Result of the test: -1 if the vertex is below the cutting plane, +1 - * if it is above, and 0 if it is on the cutting plane. - */ -__attribute__((always_inline)) INLINE int voronoi_test_vertex( - const float *v, const float *dx, float r2, float *test, float *teststack, - int *teststack_size) { - - *test = v[0] * dx[0] + v[1] * dx[1] + v[2] * dx[2] - r2; - - teststack[*teststack_size] = *test; - *teststack_size = *teststack_size + 1; - if (*teststack_size == 2 * VORONOI3D_MAXNUMVERT) { - *teststack_size = 0; - } - - if (*test < -VORONOI3D_TOLERANCE) { - return -1; - } - if (*test > VORONOI3D_TOLERANCE) { - return 1; - } - return 0; -} - -/** - * @brief Initialize the cell as a cube that spans the entire simulation box. - * - * @param c 3D Voronoi cell to initialize. - * @param anchor Anchor of the simulation box. - * @param side Side lengths of the simulation box. - */ -__attribute__((always_inline)) INLINE void voronoi_initialize( - struct voronoi_cell *cell, const double *anchor, const double *side) { - - cell->nvert = 8; - - /* (0, 0, 0) -- 0 */ - cell->vertices[0] = anchor[0] - cell->x[0]; - cell->vertices[1] = anchor[1] - cell->x[1]; - cell->vertices[2] = anchor[2] - cell->x[2]; - - /* (0, 0, 1)-- 1 */ - cell->vertices[3] = anchor[0] - cell->x[0]; - cell->vertices[4] = anchor[1] - cell->x[1]; - cell->vertices[5] = anchor[2] + side[2] - cell->x[2]; - - /* (0, 1, 0) -- 2 */ - cell->vertices[6] = anchor[0] - cell->x[0]; - cell->vertices[7] = anchor[1] + side[1] - cell->x[1]; - cell->vertices[8] = anchor[2] - cell->x[2]; - - /* (0, 1, 1) -- 3 */ - cell->vertices[9] = anchor[0] - cell->x[0]; - cell->vertices[10] = anchor[1] + side[1] - cell->x[1]; - cell->vertices[11] = anchor[2] + side[2] - cell->x[2]; - - /* (1, 0, 0) -- 4 */ - cell->vertices[12] = anchor[0] + side[0] - cell->x[0]; - cell->vertices[13] = anchor[1] - cell->x[1]; - cell->vertices[14] = anchor[2] - cell->x[2]; - - /* (1, 0, 1) -- 5 */ - cell->vertices[15] = anchor[0] + side[0] - cell->x[0]; - cell->vertices[16] = anchor[1] - cell->x[1]; - cell->vertices[17] = anchor[2] + side[2] - cell->x[2]; - - /* (1, 1, 0) -- 6 */ - cell->vertices[18] = anchor[0] + side[0] - cell->x[0]; - cell->vertices[19] = anchor[1] + side[1] - cell->x[1]; - cell->vertices[20] = anchor[2] - cell->x[2]; - - /* (1, 1, 1) -- 7 */ - cell->vertices[21] = anchor[0] + side[0] - cell->x[0]; - cell->vertices[22] = anchor[1] + side[1] - cell->x[1]; - cell->vertices[23] = anchor[2] + side[2] - cell->x[2]; - - cell->orders[0] = 3; - cell->orders[1] = 3; - cell->orders[2] = 3; - cell->orders[3] = 3; - cell->orders[4] = 3; - cell->orders[5] = 3; - cell->orders[6] = 3; - cell->orders[7] = 3; - - /* edges are ordered counterclockwise w.r.t. a vector pointing from the - cell generator to the vertex - (0, 0, 0) corner */ - cell->offsets[0] = 0; - cell->edges[0] = 1; - cell->edges[1] = 2; - cell->edges[2] = 4; - cell->edgeindices[0] = 0; - cell->edgeindices[1] = 2; - cell->edgeindices[2] = 0; - - /* (0, 0, 1) corner */ - cell->offsets[1] = 3; - cell->edges[3] = 0; - cell->edges[4] = 5; - cell->edges[5] = 3; - cell->edgeindices[3] = 0; - cell->edgeindices[4] = 2; - cell->edgeindices[5] = 1; - - /* (0, 1, 0) corner */ - cell->offsets[2] = 6; - cell->edges[6] = 3; - cell->edges[7] = 6; - cell->edges[8] = 0; - cell->edgeindices[6] = 0; - cell->edgeindices[7] = 0; - cell->edgeindices[8] = 1; - - /* (0, 1, 1) corner */ - cell->offsets[3] = 9; - cell->edges[9] = 2; - cell->edges[10] = 1; - cell->edges[11] = 7; - cell->edgeindices[9] = 0; - cell->edgeindices[10] = 2; - cell->edgeindices[11] = 0; - - /* (1, 0, 0) corner */ - cell->offsets[4] = 12; - cell->edges[12] = 0; - cell->edges[13] = 6; - cell->edges[14] = 5; - cell->edgeindices[12] = 2; - cell->edgeindices[13] = 2; - cell->edgeindices[14] = 0; - - /* (1, 0, 1) corner */ - cell->offsets[5] = 15; - cell->edges[15] = 4; - cell->edges[16] = 7; - cell->edges[17] = 1; - cell->edgeindices[15] = 2; - cell->edgeindices[16] = 1; - cell->edgeindices[17] = 1; - - /* (1, 1, 0) corner */ - cell->offsets[6] = 18; - cell->edges[18] = 2; - cell->edges[19] = 7; - cell->edges[20] = 4; - cell->edgeindices[18] = 1; - cell->edgeindices[19] = 2; - cell->edgeindices[20] = 1; - - /* (1, 1, 1) corner */ - cell->offsets[7] = 21; - cell->edges[21] = 3; - cell->edges[22] = 5; - cell->edges[23] = 6; - cell->edgeindices[21] = 2; - cell->edgeindices[22] = 1; - cell->edgeindices[23] = 1; - - /* ngbs[3*i+j] is the neighbour corresponding to the plane clockwise of - edge j of vertex i (when going from edge j to vertex i) - we set them to a ridiculously large value to be able to track faces without - neighbour */ - cell->ngbs[0] = VORONOI3D_BOX_FRONT; /* (000) - (001) */ - cell->ngbs[1] = VORONOI3D_BOX_LEFT; /* (000) - (010) */ - cell->ngbs[2] = VORONOI3D_BOX_BOTTOM; /* (000) - (100) */ - - cell->ngbs[3] = VORONOI3D_BOX_LEFT; /* (001) - (000) */ - cell->ngbs[4] = VORONOI3D_BOX_FRONT; /* (001) - (101) */ - cell->ngbs[5] = VORONOI3D_BOX_TOP; /* (001) - (011) */ - - cell->ngbs[6] = VORONOI3D_BOX_LEFT; /* (010) - (011) */ - cell->ngbs[7] = VORONOI3D_BOX_BACK; /* (010) - (110) */ - cell->ngbs[8] = VORONOI3D_BOX_BOTTOM; /* (010) - (000) */ - - cell->ngbs[9] = VORONOI3D_BOX_BACK; /* (011) - (010) */ - cell->ngbs[10] = VORONOI3D_BOX_LEFT; /* (011) - (001) */ - cell->ngbs[11] = VORONOI3D_BOX_TOP; /* (011) - (111) */ - - cell->ngbs[12] = VORONOI3D_BOX_FRONT; /* (100) - (000) */ - cell->ngbs[13] = VORONOI3D_BOX_BOTTOM; /* (100) - (110) */ - cell->ngbs[14] = VORONOI3D_BOX_RIGHT; /* (100) - (101) */ - - cell->ngbs[15] = VORONOI3D_BOX_FRONT; /* (101) - (100) */ - cell->ngbs[16] = VORONOI3D_BOX_RIGHT; /* (101) - (111) */ - cell->ngbs[17] = VORONOI3D_BOX_TOP; /* (101) - (001) */ - - cell->ngbs[18] = VORONOI3D_BOX_BOTTOM; /* (110) - (010) */ - cell->ngbs[19] = VORONOI3D_BOX_BACK; /* (110) - (111) */ - cell->ngbs[20] = VORONOI3D_BOX_RIGHT; /* (110) - (100) */ - - cell->ngbs[21] = VORONOI3D_BOX_BACK; /* (111) - (011) */ - cell->ngbs[22] = VORONOI3D_BOX_TOP; /* (111) - (101) */ - cell->ngbs[23] = VORONOI3D_BOX_RIGHT; /* (111) - (110) */ -} - -/** - * @brief Find an edge of the voronoi_cell that intersects the cutting plane. - * - * There is a large number of possible paths through this method, each of which - * is covered by a separate unit test in testVoronoi3D. Paths have been numbered - * in the inline comments to help identify them. - * - * @param c 3D Voronoi cell. - * @param dx Vector pointing from pj to the midpoint of the line segment between - * pi and pj. - * @param r2 Squared length of dx. - * @param u Projected distance between the plane and the closest vertex above - * the plane, along dx. - * @param up Index of the closest vertex above the plane. - * @param us Index of the edge of vertex up that intersects the plane. - * @param uw Result of the last test_vertex call for vertex up. - * @param l Projected distance between the plane and the closest vertex below - * the plane, along dx. - * @param lp Index of the closest vertex below the plane. - * @param ls Index of the edge of vertex lp that intersects the plane. - * @param lw Result of the last test_vertex call for vertex lp. - * @param q Projected distance between the plane and a test vertex, along dx. - * @param qp Index of the test vertex. - * @param qs Index of the edge of the test vertex that is connected to up. - * @param qw Result of the last test_vertex call involving qp. - * @return A negative value if an error occurred, 0 if the plane does not - * intersect the cell, 1 if nothing special happened and 2 if we have a - * complicated setup. - */ -__attribute__((always_inline)) INLINE int voronoi_intersect_find_closest_vertex( - struct voronoi_cell *c, const float *dx, float r2, float *u, int *up, - int *us, int *uw, float *l, int *lp, int *ls, int *lw, float *q, int *qp, - int *qs, int *qw) { - - /* stack to store all vertices that have already been tested (debugging - only) */ - float teststack[2 * VORONOI3D_MAXNUMVERT]; - /* size of the used part of the stack */ - int teststack_size = 0; - /* flag signalling a complicated setup */ - int complicated; - - /* test the first vertex: uw = -1 if it is below the plane, 1 if it is above - 0 if it is very close to the plane, and things become complicated... */ - *uw = voronoi_test_vertex(&c->vertices[0], dx, r2, u, teststack, - &teststack_size); - *up = 0; - complicated = 0; - if ((*uw) == 0) { - - /* PATH 0 */ - complicated = 1; - - } else { - - /* two options: either the vertex is above or below the plane */ - - if ((*uw) == 1) { - - /* PATH 1 */ - - /* above: try to find a vertex below - we test all edges of the current vertex stored in up (vertex 0) until - we either find one below the plane or closer to the plane */ - *lp = voronoi_get_edge(c, (*up), 0); - *lw = voronoi_test_vertex(&c->vertices[3 * (*lp)], dx, r2, l, teststack, - &teststack_size); - *us = 1; - /* Not in while: PATH 1.0 */ - /* somewhere in while: PATH 1.1 */ - /* last valid option of while: PATH 1.2 */ - safewhile((*us) < c->orders[(*up)] && (*l) >= (*u)) { - *lp = voronoi_get_edge(c, (*up), (*us)); - *lw = voronoi_test_vertex(&c->vertices[3 * (*lp)], dx, r2, l, teststack, - &teststack_size); - ++(*us); - } - /* we increased us too much, correct this */ - --(*us); - if ((*l) >= (*u)) { - /* PATH 1.3 */ - /* up is the closest vertex to the plane, but is above the plane - since the entire cell is convex, up is the closest vertex of all - vertices of the cell - this means the entire cell is supposedly above the plane, which is - impossible */ - message( - "Cell completely gone! This should not happen. (l >= u, l = %g, u " - "= %g)", - (*l), (*u)); - return -1; - } - /* we know that lp is closer to the plane or below the plane - now find the index of the edge up-lp in the edge list of lp */ - *ls = voronoi_get_edgeindex(c, (*up), (*us)); - - /* if lp is also above the plane, replace up by lp and repeat the process - until lp is below the plane */ - safewhile((*lw) == 1) { - /* PATH 1.4 */ - *u = (*l); - *up = (*lp); - *us = 0; - /* no while: PATH 1.4.0 */ - /* somewhere in while: PATH 1.4.1 */ - /* last valid option of while: PATH 1.4.2 */ - safewhile((*us) < (*ls) && (*l) >= (*u)) { - *lp = voronoi_get_edge(c, (*up), (*us)); - *lw = voronoi_test_vertex(&c->vertices[3 * (*lp)], dx, r2, l, - teststack, &teststack_size); - ++(*us); - } - if ((*l) >= (*u)) { - ++(*us); - /* no while: PATH 1.4.3 */ - /* somewhere in while: PATH 1.4.4 */ - /* last valid option of while: PATH 1.4.5 */ - safewhile((*us) < c->orders[(*up)] && (*l) >= (*u)) { - *lp = voronoi_get_edge(c, (*up), (*us)); - *lw = voronoi_test_vertex(&c->vertices[3 * (*lp)], dx, r2, l, - teststack, &teststack_size); - ++(*us); - } - if ((*l) >= (*u)) { - /* PATH 1.4.6 */ - message( - "Cell completely gone! This should not happen. (l >= u, l = " - "%g, u = %g)", - (*l), (*u)); - return -1; - } - } - --(*us); - *ls = voronoi_get_edgeindex(c, (*up), (*us)); - } - /* if lp is too close to the plane, replace up by lp and proceed to - complicated setup */ - if ((*lw) == 0) { - /* PATH 1.5 */ - *up = (*lp); - complicated = 1; - } - } else { /* if(uw == 1) */ - - /* PATH 2 */ - - /* below: try to find a vertex above - we test all edges of the current vertex stored in up (vertex 0) until - we either find one above the plane or closer to the plane */ - - *qp = voronoi_get_edge(c, (*up), 0); - *qw = voronoi_test_vertex(&c->vertices[3 * (*qp)], dx, r2, q, teststack, - &teststack_size); - *us = 1; - /* not in while: PATH 2.0 */ - /* somewhere in while: PATH 2.1 */ - /* last valid option of while: PATH 2.2 */ - safewhile((*us) < c->orders[(*up)] && (*u) >= (*q)) { - *qp = voronoi_get_edge(c, (*up), (*us)); - *qw = voronoi_test_vertex(&c->vertices[3 * (*qp)], dx, r2, q, teststack, - &teststack_size); - ++(*us); - } - if ((*u) >= (*q)) { - /* PATH 2.3 */ - /* up is the closest vertex to the plane and is below the plane - since the cell is convex, up is the closest vertex of all vertices of - the cell - this means that the entire cell is below the plane - The cell is unaltered. */ - return 0; - } else { - /* the last increase in the loop pushed us too far, correct this */ - --(*us); - } - - /* repeat the above process until qp is closer or above the plane */ - safewhile((*qw) == -1) { - /* PATH 2.4 */ - *qs = voronoi_get_edgeindex(c, (*up), (*us)); - *u = (*q); - *up = (*qp); - *us = 0; - /* no while: PATH 2.4.0 */ - /* somewhere in while: PATH 2.4.1 */ - /* last valid option of while: 2.4.2 */ - safewhile((*us) < (*qs) && (*u) >= (*q)) { - *qp = voronoi_get_edge(c, (*up), (*us)); - *qw = voronoi_test_vertex(&c->vertices[3 * (*qp)], dx, r2, q, - teststack, &teststack_size); - ++(*us); - } - if ((*u) >= (*q)) { - ++(*us); - /* no while: PATH 2.4.3 */ - /* somewhere in while: PATH 2.4.4 */ - /* last valid option of while: PATH 2.4.5 */ - safewhile((*us) < c->orders[(*up)] && (*u) >= (*q)) { - *qp = voronoi_get_edge(c, (*up), (*us)); - *qw = voronoi_test_vertex(&c->vertices[3 * (*qp)], dx, r2, q, - teststack, &teststack_size); - ++(*us); - } - if ((*u) >= (*q)) { - /* PATH 2.4.6 */ - /* cell unaltered */ - return 0; - } - } - --(*us); - } - if ((*qw) == 1) { - /* qp is above the plane: initialize lp to up and replace up by qp */ - *lp = (*up); - *ls = (*us); - *l = (*u); - *up = (*qp); - *us = voronoi_get_edgeindex(c, (*lp), (*ls)); - *u = (*q); - } else { - /* PATH 2.5 */ - /* too close to call: go to complicated setup */ - *up = (*qp); - complicated = 1; - } - - } /* if(uw == 1) */ - - } /* if(uw == 0) */ - - if (complicated) { - return 2; - } else { - return 1; - } -} - -/** - * @brief Intersect the given cell with the midplane between the cell generator - * and a neighbouring cell at the given relative position and with the given ID. - * - * This method is the core of the Voronoi algorithm. If anything goes wrong - * geometrically, it most likely goes wrong somewhere within this method. - * - * @param c 3D Voronoi cell. - * @param odx The original relative distance vector between the cell generator - * and the intersecting neighbour, as it is passed on to runner_iact_density - * (remember: odx = pi->x - pj->x). - * @param ngb ID of the intersecting neighbour (pj->id in runner_iact_density). - */ -__attribute__((always_inline)) INLINE void voronoi_intersect( - struct voronoi_cell *c, const float *odx, unsigned long long ngb) { - - /* vector pointing from pi to the midpoint of the line segment between pi and - pj. This corresponds to -0.5*odx */ - float dx[3]; - /* squared norm of dx */ - float r2; - /* u: distance between the plane and the closest vertex above the plane (up) - l: distance between the plane and the closest vertex below the plane (lp) - q: distance between the plane and the vertex that is currently being - tested (qp) */ - float u = 0.0f, l = 0.0f, q = 0.0f; - /* up: index of the closest vertex above the plane - us: index of the edge of vertex up that intersects the plane - uw: result of the last orientation test involving vertex u - same naming used for vertex l and vertex q */ - int up = -1, us = -1, uw = -1, lp = -1, ls = -1, lw = -1, qp = -1, qs = -1, - qw = -1; - /* auxiliary flag used to capture degeneracies */ - int complicated = -1; - - /* stack to store all vertices that have already been tested (debugging - only) */ - float teststack[2 * VORONOI3D_MAXNUMVERT]; - /* size of the used part of the stack */ - int teststack_size = 0; - -#ifdef VORONOI3D_EXPENSIVE_CHECKS - voronoi_check_cell_consistency(c); -#endif - - /* initialize dx and r2 */ - dx[0] = -0.5f * odx[0]; - dx[1] = -0.5f * odx[1]; - dx[2] = -0.5f * odx[2]; - r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; - - /* find an intersected edge of the cell */ - int result = voronoi_intersect_find_closest_vertex( - c, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - if (result < 0) { - /* the closest_vertex test only found vertices above the intersecting plane - this would mean that the entire cell lies above the midplane of the line - segment connecting a point inside the cell (the generator) and a point - that could be inside or outside the cell (the neighbour). This is - geometrically absurd and should NEVER happen. */ - voronoi_print_gnuplot_c(c); - error("Error while searching intersected edge!"); - } - if (result == 0) { - /* no intersection */ - return; - } - if (result == 2) { - complicated = 1; - } else { - complicated = 0; - } - - /* At this point: - up contains a vertex above the plane - lp contains a vertex below the plane - us and ls contain the index of the edge that connects up and lp, this edge - is intersected by the midplane - u and l contain the projected distances of up and lp to the midplane, - along dx - IF complicated is 1, up contains a vertex that is considered to be on the - plane. All other variables can be considered to be uninitialized in this - case. */ - - int vindex = -1; - int visitflags[VORONOI3D_MAXNUMVERT]; - int dstack[2 * VORONOI3D_MAXNUMVERT]; - int dstack_size = 0; - float r = 0.0f; - int cs = -1, rp = -1; - int double_edge = 0; - int i = -1, j = -1, k = -1; - - /* initialize visitflags */ - for (i = 0; i < VORONOI3D_MAXNUMVERT; ++i) { - visitflags[i] = 0; - } - - if (complicated) { - - /* We've entered the complicated setup, which means that somewhere along the - way we found a vertex that is on or very close to the midplane. The index - of that vertex is stored in up, all other variables are meaningless at - this point. */ - - /* first of all, we need to find a vertex which has edges that extend below - the plane (since the remainder of our algorithm depends on that). This is - not necessarily the case: in principle a vertex can only have edges that - extend inside or above the plane. - we create a stack of vertices to test (we use dstack for this), and add - vertex up. For each vertex on the stack, we then traverse its edges. If - the edge extends above the plane, we ignore it. If it extends below, we - stop. If the edge lies in the plane, we add the vertex on the other end - to the stack. - We make sure that up contains the index of a vertex extending beyond the - plane on exit. */ - dstack[dstack_size] = up; - ++dstack_size; - lw = 0; - j = 0; - safewhile(j < dstack_size && lw != -1) { - up = dstack[j]; - for (i = 0; i < c->orders[up]; ++i) { - lp = voronoi_get_edge(c, up, i); - lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, - &teststack_size); - if (lw == -1) { - /* jump out of the for loop */ - break; - } - if (lw == 0) { - /* only add each vertex to the stack once */ - k = 0; - safewhile(k < dstack_size && dstack[k] != lp) { ++k; } - if (k == dstack_size) { - dstack[dstack_size] = lp; - ++dstack_size; - } - } - } - ++j; - } - - /* we increased j after lw was calculated, so only the value of lw should be - used to determine whether or not the loop was successful */ - if (lw != -1) { - /* we did not find an edge that extends below the plane. There are two - possible reasons for this: either all vertices of the cell lie above - or inside the midplane of the segment connecting a point inside the - cell (the generator) with a point inside or outside the cell (the - neighbour). This is geometrically absurd. - Another reason might be that somehow all vertices in the midplane only - have edges that extend outwards. This is contradictory to the fact that - a Voronoi cell is convex, and therefore also unacceptable. - We conclude that we should NEVER end up here. */ - voronoi_print_cell(c); - error("Unable to find a vertex below the midplane!"); - } - /* reset the delete stack, we need it later on */ - dstack_size = 0; - - /* the search routine detected a vertex very close to or in the midplane - the index of this vertex is stored in up - we proceed by checking the edges of this vertex */ - - lp = voronoi_get_edge(c, up, 0); - lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, - &teststack_size); - - /* the first edge can be below, above or on the plane */ - if (lw != -1) { - - /* above or on the plane: we try to find one below the plane */ - - rp = lw; - i = 1; - lp = voronoi_get_edge(c, up, i); - lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, - &teststack_size); - safewhile(lw != -1) { - ++i; - if (i == c->orders[up]) { - /* none of the edges of up is below the plane. Since the cell is - supposed to be convex, this means the entire cell is above or on - the plane. This should not happen... - Furthermore, we should really NEVER end up here, as in this case - an error should already have be thrown above. */ - voronoi_print_gnuplot_c(c); - error( - "Cell completely gone! This should not happen. (i == " - "c->order[up], i = %d, c->orders[up] = %d, up = %d)\n" - "dx: [%g %g %g]\nv[up]: [%g %g %g]\nx: [%g %g %g]", - i, c->orders[up], up, dx[0], dx[1], dx[2], c->vertices[3 * up], - c->vertices[3 * up + 1], c->vertices[3 * up + 2], c->x[0], - c->x[1], c->x[2]); - } - lp = voronoi_get_edge(c, up, i); - lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, - &teststack_size); - } - - /* lp, l and lw now contain values corresponding to an edge below the - plane - rp contains the result of test_vertex for the first edge of up, for - reference */ - - /* we go on to the next edge of up, and see if we can find an edge that - does not extend below the plane */ - - j = i + 1; - safewhile(j < c->orders[up] && lw == -1) { - lp = voronoi_get_edge(c, up, j); - lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, - &teststack_size); - ++j; - } - - if (lw != -1) { - /* the last iteration increased j by 1 too many, correct this */ - --j; - } - - /* j-i now contains the number of edges below the plane. We will replace - up by a new vertex of order this number + 2 (since 2 new edges will be - created inside the plane) - however, we do not do this if there is exactly one edge that lies in - the plane, and all other edges lie below, because in this case we can - just keep vertex up as is */ - - if (j == c->orders[up] && i == 1 && rp == 0) { - /* keep the order of up, and flag this event for later reference */ - k = c->orders[up]; - double_edge = 1; - } else { - /* general case: keep all edges below the plane, and create 2 new ones - in the plane */ - k = j - i + 2; - } - - /* create new order k vertex */ - vindex = c->nvert; - ++c->nvert; - if (c->nvert == VORONOI3D_MAXNUMVERT) { - error("Too many vertices!"); - } - c->orders[vindex] = k; - c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; - if (c->offsets[vindex] + k >= VORONOI3D_MAXNUMEDGE) { - error("Too many edges!"); - } - - visitflags[vindex] = -vindex; - /* the new vertex adopts the coordinates of the old vertex */ - c->vertices[3 * vindex + 0] = c->vertices[3 * up + 0]; - c->vertices[3 * vindex + 1] = c->vertices[3 * up + 1]; - c->vertices[3 * vindex + 2] = c->vertices[3 * up + 2]; - - /* us contains the index of the last edge NOT below the plane - note that i is at least 1, so there is no need to wrap in this case */ - us = i - 1; - - /* copy all edges of up below the plane into the new vertex, starting from - edge 1 (edge 0 is reserved to connect to a newly created vertex - below) */ - k = 1; - safewhile(i < j) { - qp = voronoi_get_edge(c, up, i); - qs = voronoi_get_edgeindex(c, up, i); - voronoi_set_ngb(c, vindex, k, voronoi_get_ngb(c, up, i)); - voronoi_set_edge(c, vindex, k, qp); - voronoi_set_edgeindex(c, vindex, k, qs); - voronoi_set_edge(c, qp, qs, vindex); - voronoi_set_edgeindex(c, qp, qs, k); - /* disconnect up, since this vertex will be removed */ - voronoi_set_edge(c, up, i, -1); - ++i; - ++k; - } - - /* store the index of the first edge not below the plane */ - if (i == c->orders[up]) { - qs = 0; - } else { - qs = i; - } - } else { /* if(lw != -1) */ - - /* the first edge lies below the plane, try to find one that does not */ - - /* we first do a reverse search */ - i = c->orders[up] - 1; - lp = voronoi_get_edge(c, up, i); - lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, - &teststack_size); - safewhile(lw == -1) { - --i; - if (i == 0) { - /* No edge above or in the plane found: the cell is unaltered */ - return; - } - lp = voronoi_get_edge(c, up, i); - lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, - &teststack_size); - } - - /* now we do a forward search */ - j = 1; - qp = voronoi_get_edge(c, up, j); - qw = voronoi_test_vertex(&c->vertices[3 * qp], dx, r2, &q, teststack, - &teststack_size); - safewhile(qw == -1) { - ++j; - qp = voronoi_get_edge(c, up, j); - qw = voronoi_test_vertex(&c->vertices[3 * qp], dx, r2, &l, teststack, - &teststack_size); - } - - /* at this point j contains the index of the first edge not below the - plane, i the index of the last edge not below the plane - we use this to compute the number of edges below the plane. up is - replaced by a new vertex that has that number + 2 edges (since 2 new - edges are created inside the plane). We again capture the special event - where there is only one edge not below the plane, which lies inside the - plane. In this case up is copied as is. */ - - if (i == j && qw == 0) { - /* we keep up as is, and flag this event */ - double_edge = 1; - k = c->orders[up]; - } else { - /* (c->orders[up]-1 - i) + j is the number of edges below the plane */ - k = c->orders[up] - i + j + 1; - } - - /* create new order k vertex */ - vindex = c->nvert; - ++c->nvert; - if (c->nvert == VORONOI3D_MAXNUMVERT) { - error("Too many vertices!"); - } - c->orders[vindex] = k; - c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; - if (c->offsets[vindex] + k >= VORONOI3D_MAXNUMEDGE) { - error("Too many edges!"); - } - - visitflags[vindex] = -vindex; - /* the new vertex is just a copy of vertex up */ - c->vertices[3 * vindex + 0] = c->vertices[3 * up + 0]; - c->vertices[3 * vindex + 1] = c->vertices[3 * up + 1]; - c->vertices[3 * vindex + 2] = c->vertices[3 * up + 2]; - - /* as above, us stores the index of the last edge NOT below the plane */ - us = i; - - /* copy all edges below the plane into the new vertex, starting from edge - 1 (edge 0 will be connected to a newly created vertex below) - We have to do this in two steps: first we copy the high index edges of - up, then the low index ones (since the edges below the plane are not a - continuous block of indices in this case) */ - k = 1; - ++i; - safewhile(i < c->orders[up]) { - qp = voronoi_get_edge(c, up, i); - qs = voronoi_get_edgeindex(c, up, i); - voronoi_set_ngb(c, vindex, k, voronoi_get_ngb(c, up, i)); - voronoi_set_edge(c, vindex, k, qp); - voronoi_set_edgeindex(c, vindex, k, qs); - voronoi_set_edge(c, qp, qs, vindex); - voronoi_set_edgeindex(c, qp, qs, k); - /* disconnect up, it will be removed */ - voronoi_set_edge(c, up, i, -1); - ++i; - ++k; - } - i = 0; - safewhile(i < j) { - qp = voronoi_get_edge(c, up, i); - qs = voronoi_get_edgeindex(c, up, i); - voronoi_set_ngb(c, vindex, k, voronoi_get_ngb(c, up, i)); - voronoi_set_edge(c, vindex, k, qp); - voronoi_set_edgeindex(c, vindex, k, qs); - voronoi_set_edge(c, qp, qs, vindex); - voronoi_set_edgeindex(c, qp, qs, k); - voronoi_set_edge(c, up, i, -1); - ++i; - ++k; - } - /* qs stores the index of the first edge not below the plane */ - qs = j; - } - - /* at this point, we have created a new vertex that contains all edges of up - below the plane, and two dangling edges: 0 and k - Furthermore, us stores the index of the last edge not below the plane, - qs the index of the first edge not below the plane */ - - /* now set the neighbours for the dangling edge(s) */ - if (!double_edge) { - /* the last edge has the same neighbour as the first edge not below the - plane */ - voronoi_set_ngb(c, vindex, k, voronoi_get_ngb(c, up, qs)); - /* the first edge has the new neighbour as neighbour */ - voronoi_set_ngb(c, vindex, 0, ngb); - } else { - /* up is copied as is, so we also copy its last remaining neighbour */ - voronoi_set_ngb(c, vindex, 0, voronoi_get_ngb(c, up, qs)); - } - - /* add up to the delete stack */ - dstack[dstack_size] = up; - ++dstack_size; - - /* make sure the variables below have the same meaning as they would have - if we had the non complicated setup: - cs contains the index of the last dangling edge of the new vertex - qp and q correspond to the last vertex that has been deleted - qs corresponds to the first edge not below the plane - up and us correspond to the last edge not below the plane, i.e. the edge - that will be the last one to connect to the new vertex - note that the value of i is ignored below, it is just used to temporary - store the new value of up */ - cs = k; - qp = up; - q = u; - i = voronoi_get_edge(c, up, us); - us = voronoi_get_edgeindex(c, up, us); - up = i; - /* we store the index of the newly created vertex in the visitflags of the - last deleted vertex */ - visitflags[qp] = vindex; - } else { /* if(complicated) */ - - if (u == l) { - error("Upper and lower vertex are the same!"); - } - - /* the line joining up and lp has general (vector) equation - x = lp + (up-lp)*t, - with t a parameter ranging from 0 to 1 - we can rewrite this as - x = lp*(1-t) + up*t - the value for t corresponding to the intersection of the line and the - midplane can be found as the ratio of the projected distance between one - of the vertices and the midplane, and the total projected distance - between the two vertices: u-l (remember that u > 0 and l < 0) */ - r = u / (u - l); - l = 1.0f - r; - - if (r > FLT_MAX || r < -FLT_MAX || l > FLT_MAX || l < -FLT_MAX) { - error("Value overflow (r: %g, l: %g)", r, l); - } - - /* create a new order 3 vertex */ - vindex = c->nvert; - ++c->nvert; - if (c->nvert == VORONOI3D_MAXNUMVERT) { - error("Too many vertices!"); - } - c->orders[vindex] = 3; - c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; - if (c->offsets[vindex] + 3 >= VORONOI3D_MAXNUMEDGE) { - error("Too many edges!"); - } - - visitflags[vindex] = -vindex; - c->vertices[3 * vindex + 0] = - c->vertices[3 * lp + 0] * r + c->vertices[3 * up + 0] * l; - c->vertices[3 * vindex + 1] = - c->vertices[3 * lp + 1] * r + c->vertices[3 * up + 1] * l; - c->vertices[3 * vindex + 2] = - c->vertices[3 * lp + 2] * r + c->vertices[3 * up + 2] * l; - - /* add vertex up to the delete stack */ - dstack[dstack_size] = up; - ++dstack_size; - - /* connect the new vertex to lp (and update lp as well) */ - voronoi_set_edge(c, vindex, 1, lp); - voronoi_set_edgeindex(c, vindex, 1, ls); - voronoi_set_edge(c, lp, ls, vindex); - voronoi_set_edgeindex(c, lp, ls, 1); - /* disconnect vertex up, it will be deleted */ - voronoi_set_edge(c, up, us, -1); - /* note that we do not connect edges 0 and 2: edge 2 will be connected to - the next new vertex that we created, while edge 0 will be connected to - the last new vertex */ - - /* set neighbour relations for the new vertex: - - edge 0 will be connected to the next intersection point (below), and - hence has pj as ngb - - edge 1 is connected to lp and has the original neighbour of the - intersected edge corresponding to up as neighbour - - edge 2 has the neighbour on the other side of the original intersected - edge as neighbour, which is the same as the neighbour of the edge - corresponding to lp */ - voronoi_set_ngb(c, vindex, 0, ngb); - voronoi_set_ngb(c, vindex, 1, voronoi_get_ngb(c, up, us)); - voronoi_set_ngb(c, vindex, 2, voronoi_get_ngb(c, lp, ls)); - - qs = us + 1; - if (qs == c->orders[up]) { - qs = 0; - } - qp = up; - q = u; - - cs = 2; - - } /* if(complicated) */ - - /* at this point: - qp corresponds to the last vertex that has been deleted - up corresponds to the last vertex that should be used to connect a new - vertex to the newly created vertex above. In the normal case, qp and up - are the same vertex, but qp and up can be different if the newly created - vertex lies in the midplane - qs contains the index of the edge of qp that is next in line to be tested: - the edge that comes after the intersected edge that was deleted above - us corresponds to the edge of up that was connected to the vertex that is - now connected to the newly created vertex above - q contains the projected distance between qp and the midplane, along dx - cs contains the index of the last dangling edge of the last vertex that - was created above; we still need to connect this edge to a vertex below */ - - /* we have found one intersected edge (or at least an edge that lies inside - the midplane) and created one new vertex that lies in the midplane, with - dangling edges. We now try to find other intersected edges and create other - new vertices that will be connected to the first new vertex. */ - - int cp = -1; - int iqs = -1; - int new_double_edge = -1; - - /* cp and rp both contain the index of the last vertex that was created - cp will be updated if we add more vertices, rp will be kept, as we need it - to link the last new vertex to the first new vertex in the end */ - cp = vindex; - rp = vindex; - /* we traverse connections of the first removed vertex, until we arrive at an - edge that links to this vertex (or its equivalent in the degenerate - case) */ - safewhile(qp != up || qs != us) { - /* test the next edge of qp */ - lp = voronoi_get_edge(c, qp, qs); - lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, - &teststack_size); - if (lw == 0) { - - /* degenerate case: next vertex lies inside the plane */ - - k = 2; - if (double_edge) { - k = 1; - } - /* store the vertex and edge on the other side of the edge in qp and qs */ - qs = voronoi_get_edgeindex(c, qp, qs); - qp = lp; - - /* move on to the next edge of qp and keep the original edge for - reference */ - iqs = qs; - ++qs; - if (qs == c->orders[qp]) { - qs = 0; - } - - /* test the next edges, and try to find one that does NOT lie below the - plane */ - lp = voronoi_get_edge(c, qp, qs); - lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, - &teststack_size); - safewhile(lw == -1) { - ++k; - ++qs; - if (qs == c->orders[qp]) { - qs = 0; - } - lp = voronoi_get_edge(c, qp, qs); - lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, - &teststack_size); - } - - /* qs now contains the next edge NOT below the plane - k contains the order of the new vertex to create: the number of edges - below the plane + 2 (+1 if we have a double edge) */ - - /* if qp (the vertex in the plane) was already visited before, visitflags - will contain the index of the newly created vertex that replaces it */ - j = visitflags[qp]; - - /* we need to find out what the order of the new vertex will be, and if we - are dealing with a new double edge or not */ - if (qp == up && qs == us) { - new_double_edge = 0; - if (j > 0) { - k += c->orders[j]; - } - } else { - if (j > 0) { - k += c->orders[j]; - if (lw == 0) { - i = -visitflags[lp]; - if (i > 0) { - if (voronoi_get_edge(c, i, c->orders[i] - 1) == j) { - new_double_edge = 1; - --k; - } else { - new_double_edge = 0; - } - } else { - if (j == rp && lp == up && voronoi_get_edge(c, qp, qs) == us) { - new_double_edge = 1; - --k; - } else { - new_double_edge = 0; - } - } - } else { - new_double_edge = 0; - } - } else { - if (lw == 0) { - i = -visitflags[lp]; - if (i == cp) { - new_double_edge = 1; - --k; - } else { - new_double_edge = 0; - } - } else { - new_double_edge = 0; - } - } - } - - // if (j > 0) { - // error("Case not handled!"); - // } - - /* create new order k vertex */ - vindex = c->nvert; - ++c->nvert; - if (c->nvert == VORONOI3D_MAXNUMVERT) { - error("Too many vertices!"); - } - c->orders[vindex] = k; - c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; - if (c->offsets[vindex] + k >= VORONOI3D_MAXNUMEDGE) { - error("Too many edges!"); - } - - visitflags[vindex] = -vindex; - c->vertices[3 * vindex + 0] = c->vertices[3 * qp + 0]; - c->vertices[3 * vindex + 1] = c->vertices[3 * qp + 1]; - c->vertices[3 * vindex + 2] = c->vertices[3 * qp + 2]; - - visitflags[qp] = vindex; - dstack[dstack_size] = qp; - ++dstack_size; - j = vindex; - i = 0; - - if (!double_edge) { - voronoi_set_ngb(c, j, i, ngb); - voronoi_set_edge(c, j, i, cp); - voronoi_set_edgeindex(c, j, i, cs); - voronoi_set_edge(c, cp, cs, j); - voronoi_set_edgeindex(c, cp, cs, i); - ++i; - } - - qs = iqs; - iqs = k - 1; - if (new_double_edge) { - iqs = k; - } - safewhile(i < iqs) { - ++qs; - if (qs == c->orders[qp]) { - qs = 0; - } - lp = voronoi_get_edge(c, qp, qs); - ls = voronoi_get_edgeindex(c, qp, qs); - voronoi_set_ngb(c, j, i, voronoi_get_ngb(c, qp, qs)); - voronoi_set_edge(c, j, i, lp); - voronoi_set_edgeindex(c, j, i, ls); - voronoi_set_edge(c, lp, ls, j); - voronoi_set_edgeindex(c, lp, ls, i); - voronoi_set_edge(c, qp, qs, -1); - ++i; - } - ++qs; - if (qs == c->orders[qp]) { - qs = 0; - } - cs = i; - cp = j; - - if (new_double_edge) { - voronoi_set_ngb(c, j, 0, voronoi_get_ngb(c, qp, qs)); - } else { - voronoi_set_ngb(c, j, cs, voronoi_get_ngb(c, qp, qs)); - } - - double_edge = new_double_edge; - } else { /* if(lw == 0) */ - - /* normal case: next vertex lies below or above the plane */ - - if (lw == 1) { - - /* vertex lies above the plane */ - - /* we just delete the vertex and continue with the next edge of this - vertex */ - - qs = voronoi_get_edgeindex(c, qp, qs) + 1; - if (qs == c->orders[lp]) { - qs = 0; - } - qp = lp; - q = l; - dstack[dstack_size] = qp; - ++dstack_size; - } else { - - /* vertex lies below the plane */ - - /* we have found our next intersected edge: create a new vertex and link - it to the other vertices */ - - if (q == l) { - error("Upper and lower vertex are the same!"); - } - - r = q / (q - l); - l = 1.0f - r; - - if (r > FLT_MAX || r < -FLT_MAX || l > FLT_MAX || l < -FLT_MAX) { - error("Value out of bounds (r: %g, l: %g)!", r, l); - } - - /* create new order 3 vertex */ - vindex = c->nvert; - ++c->nvert; - if (c->nvert == VORONOI3D_MAXNUMVERT) { - error("Too many vertices!"); - } - visitflags[vindex] = -vindex; - c->orders[vindex] = 3; - c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; - if (c->offsets[vindex] + 3 >= VORONOI3D_MAXNUMEDGE) { - error("Too many edges!"); - } - - c->vertices[3 * vindex + 0] = - c->vertices[3 * lp + 0] * r + c->vertices[3 * qp + 0] * l; - c->vertices[3 * vindex + 1] = - c->vertices[3 * lp + 1] * r + c->vertices[3 * qp + 1] * l; - c->vertices[3 * vindex + 2] = - c->vertices[3 * lp + 2] * r + c->vertices[3 * qp + 2] * l; - - /* link the edges: - the first edge is connected to the last edge of the previous new - vertex. The last edge will be connected to the next new vertex, and - is left open for the moment */ - ls = voronoi_get_edgeindex(c, qp, qs); - voronoi_set_edge(c, vindex, 0, cp); - voronoi_set_edge(c, vindex, 1, lp); - voronoi_set_edgeindex(c, vindex, 0, cs); - voronoi_set_edgeindex(c, vindex, 1, ls); - voronoi_set_edge(c, lp, ls, vindex); - voronoi_set_edgeindex(c, lp, ls, 1); - voronoi_set_edge(c, cp, cs, vindex); - voronoi_set_edgeindex(c, cp, cs, 0); - voronoi_set_edge(c, qp, qs, -1); - - voronoi_set_ngb(c, vindex, 0, ngb); - voronoi_set_ngb(c, vindex, 1, voronoi_get_ngb(c, qp, qs)); - voronoi_set_ngb(c, vindex, 2, voronoi_get_ngb(c, lp, ls)); - - /* continue with the next edge of qp (the last vertex above the - midplane */ - ++qs; - if (qs == c->orders[qp]) { - qs = 0; - } - /* store the last newly created vertex and its dangling edge for the - next iteration */ - cp = vindex; - cs = 2; - } /* if(lw == 1) */ - - } /* if(lw == 0) */ - - } /* while() */ - - /* we finished adding new vertices. Now connect the last dangling edge of the - last newly created vertex to the first dangling edge of the first newly - created vertex */ - voronoi_set_edge(c, cp, cs, rp); - voronoi_set_edge(c, rp, 0, cp); - voronoi_set_edgeindex(c, cp, cs, 0); - voronoi_set_edgeindex(c, rp, 0, cs); - - /* now remove the vertices in the delete stack */ - - /* the algorithm above did not necessarily visit all vertices above the plane. - here we scan for vertices that are linked to vertices that are to be - removed and add them to the delete stack if necessary - this only works because we made sure that all deleted vertices no longer - have edges that connect them to vertices that need to stay */ - for (i = 0; i < dstack_size; ++i) { - for (j = 0; j < c->orders[dstack[i]]; ++j) { - if (voronoi_get_edge(c, dstack[i], j) >= 0) { - dstack[dstack_size] = voronoi_get_edge(c, dstack[i], j); - ++dstack_size; - voronoi_set_edge(c, dstack[i], j, -1); - voronoi_set_edgeindex(c, dstack[i], j, -1); - } - } - } - - /* collapse order 1 and 2 vertices: vertices with only 1 edge or 2 edges that - can be created during the plane intersection routine */ - /* first flag them */ - int low_order_stack[VORONOI3D_MAXNUMVERT]; - int low_order_index = 0; - for (i = 0; i < c->nvert; ++i) { - if (voronoi_get_edge(c, i, 0) >= 0 && c->orders[i] < 3) { - low_order_stack[low_order_index] = i; - ++low_order_index; - } - } - - /* now remove them */ - safewhile(low_order_index) { - int v = low_order_stack[low_order_index - 1]; - /* the vertex might already have been deleted by a previous operation */ - if (voronoi_get_edge(c, v, 0) < 0) { - --low_order_index; - continue; - } - if (c->orders[v] == 2) { - int jj = voronoi_get_edge(c, v, 0); - int kk = voronoi_get_edge(c, v, 1); - int bb = voronoi_get_edgeindex(c, v, 1); - int ll = 0; - safewhile(ll < c->orders[jj] && voronoi_get_edge(c, jj, ll) != kk) { - ++ll; - } - if (ll == c->orders[jj]) { - int a = voronoi_get_edgeindex(c, v, 0); - /* jj and kk are not joined together. Replace their edges pointing to v - with a new edge pointing from jj to kk */ - voronoi_set_edge(c, jj, a, k); - voronoi_set_edgeindex(c, jj, a, bb); - voronoi_set_edge(c, kk, bb, jj); - voronoi_set_edgeindex(c, kk, bb, a); - /* no new elements added to the stack: decrease the counter */ - --low_order_index; - } else { - /* just remove the edges from jj to v and from kk to v: create two new - vertices */ - /* vertex jj */ - vindex = c->nvert; - ++c->nvert; - c->vertices[3 * vindex] = c->vertices[3 * jj]; - c->vertices[3 * vindex + 1] = c->vertices[3 * jj + 1]; - c->vertices[3 * vindex + 2] = c->vertices[3 * jj + 2]; - c->orders[vindex] = c->orders[jj] - 1; - c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; - int m = 0; - for (int n = 0; n < c->orders[jj]; ++n) { - int lll = voronoi_get_edge(c, jj, n); - if (lll != v) { - /* make a new edge */ - voronoi_set_edge(c, vindex, m, lll); - voronoi_set_edgeindex(c, vindex, m, - voronoi_get_edgeindex(c, jj, n)); - /* update the other vertex */ - voronoi_set_edge(c, lll, voronoi_get_edgeindex(c, jj, n), vindex); - voronoi_set_edgeindex(c, lll, voronoi_get_edgeindex(c, jj, n), m); - /* copy ngb information */ - voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, jj, n)); - ++m; - } - /* remove the old vertex */ - voronoi_set_edge(c, jj, n, -1); - voronoi_set_edgeindex(c, jj, n, -1); - } - /* vertex kk */ - vindex = c->nvert; - ++c->nvert; - c->vertices[3 * vindex] = c->vertices[3 * kk]; - c->vertices[3 * vindex + 1] = c->vertices[3 * kk + 1]; - c->vertices[3 * vindex + 2] = c->vertices[3 * kk + 2]; - c->orders[vindex] = c->orders[kk] - 1; - c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; - m = 0; - for (int n = 0; n < c->orders[kk]; ++n) { - int lll = voronoi_get_edge(c, kk, n); - if (lll != v) { - /* make a new edge */ - voronoi_set_edge(c, vindex, m, lll); - voronoi_set_edgeindex(c, vindex, m, - voronoi_get_edgeindex(c, kk, n)); - /* update the other vertex */ - voronoi_set_edge(c, lll, voronoi_get_edgeindex(c, kk, n), vindex); - voronoi_set_edgeindex(c, lll, voronoi_get_edgeindex(c, kk, n), m); - /* copy ngb information */ - /* this one is special: we copy the ngb corresponding to the - deleted edge and skip the one after that */ - if (n == bb + 1) { - voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, kk, bb)); - } else { - voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, kk, n)); - } - ++m; - } - /* remove the old vertex */ - voronoi_set_edge(c, kk, n, -1); - voronoi_set_edgeindex(c, kk, n, -1); - } - /* check if jj or kk has become an order 2 vertex */ - /* if they have become an order 1 vertex, they were already an order 2 - vertex, and they should already be in the list... */ - if (c->orders[vindex] == 2) { - if (c->orders[vindex - 1] == 2) { - low_order_stack[low_order_index] = vindex - 1; - ++low_order_index; - low_order_stack[low_order_index] = vindex; - /* we do not increase the index here: we want this element to be the - next element that is processed */ - } else { - low_order_stack[low_order_index] = vindex; - } - } else { - if (c->orders[vindex - 1] == 2) { - low_order_stack[low_order_index] = vindex - 1; - } else { - /* no new vertices added to the stack: decrease the counter */ - --low_order_index; - } - } - } - /* Remove the vertex */ - voronoi_set_edge(c, v, 0, -1); - voronoi_set_edgeindex(c, v, 0, -1); - voronoi_set_edge(c, v, 1, -1); - voronoi_set_edgeindex(c, v, 1, -1); - } else if (c->orders[v] == 1) { - int jj = voronoi_get_edge(c, v, 0); - /* we have to remove the edge between j and v. We create a new vertex */ - vindex = c->nvert; - ++c->nvert; - c->vertices[3 * vindex] = c->vertices[3 * jj]; - c->vertices[3 * vindex + 1] = c->vertices[3 * jj + 1]; - c->vertices[3 * vindex + 2] = c->vertices[3 * jj + 2]; - c->orders[vindex] = c->orders[j] - 1; - c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; - int m = 0; - for (int kk = 0; kk < c->orders[j]; ++kk) { - int ll = voronoi_get_edge(c, jj, kk); - if (ll != v) { - /* make a new edge */ - voronoi_set_edge(c, vindex, m, ll); - voronoi_set_edgeindex(c, vindex, m, voronoi_get_edgeindex(c, jj, kk)); - /* update the other vertex */ - voronoi_set_edge(c, ll, voronoi_get_edgeindex(c, jj, kk), vindex); - voronoi_set_edgeindex(c, ll, voronoi_get_edgeindex(c, jj, kk), m); - /* copy ngb information */ - voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, jj, kk)); - ++m; - } - /* remove the old vertex */ - voronoi_set_edge(c, jj, kk, -1); - voronoi_set_edgeindex(c, jj, kk, -1); - } - /* if the new vertex is a new order 2 vertex, add it to the stack */ - if (c->orders[vindex] == 2) { - low_order_stack[low_order_index - 1] = vindex; - } else { - --low_order_index; - } - /* remove the order 1 vertex */ - voronoi_set_edge(c, v, 0, -1); - voronoi_set_edgeindex(c, v, 0, -1); - } else { - error("Vertex with order %i. This should not happen!", c->orders[v]); - } - } - - /* remove deleted vertices from all arrays */ - struct voronoi_cell new_cell; - /* make sure the contents of the new cell are the same as for the old cell */ - memcpy(&new_cell, c, sizeof(struct voronoi_cell)); - int m, n; - for (vindex = 0; vindex < c->nvert; ++vindex) { - j = vindex; - /* find next edge that is not deleted */ - safewhile(j < c->nvert && voronoi_get_edge(c, j, 0) < 0) { ++j; } - - if (j == c->nvert) { - /* ready */ - break; - } - - /* copy vertices */ - new_cell.vertices[3 * vindex + 0] = c->vertices[3 * j + 0]; - new_cell.vertices[3 * vindex + 1] = c->vertices[3 * j + 1]; - new_cell.vertices[3 * vindex + 2] = c->vertices[3 * j + 2]; - - /* copy order */ - new_cell.orders[vindex] = c->orders[j]; - - /* set offset */ - if (vindex) { - new_cell.offsets[vindex] = - new_cell.offsets[vindex - 1] + new_cell.orders[vindex - 1]; - } else { - new_cell.offsets[vindex] = 0; - } - - /* copy edges, edgeindices and ngbs */ - for (k = 0; k < c->orders[j]; ++k) { - voronoi_set_edge(&new_cell, vindex, k, voronoi_get_edge(c, j, k)); - voronoi_set_edgeindex(&new_cell, vindex, k, - voronoi_get_edgeindex(c, j, k)); - voronoi_set_ngb(&new_cell, vindex, k, voronoi_get_ngb(c, j, k)); - } - - /* update other edges */ - for (k = 0; k < c->orders[j]; ++k) { - m = voronoi_get_edge(c, j, k); - n = voronoi_get_edgeindex(c, j, k); - if (m < vindex) { - voronoi_set_edge(&new_cell, m, n, vindex); - } else { - voronoi_set_edge(c, m, n, vindex); - } - } - - /* deactivate edge */ - voronoi_set_edge(c, j, 0, -1); - } - new_cell.nvert = vindex; - - new_cell.x[0] = c->x[0]; - new_cell.x[1] = c->x[1]; - new_cell.x[2] = c->x[2]; - new_cell.centroid[0] = c->centroid[0]; - new_cell.centroid[1] = c->centroid[1]; - new_cell.centroid[2] = c->centroid[2]; - new_cell.volume = c->volume; - new_cell.nface = c->nface; - - /* Update the cell values. */ - voronoi3d_cell_copy(&new_cell, c); - -#ifdef VORONOI3D_EXPENSIVE_CHECKS - voronoi_check_cell_consistency(c); -#endif -} - -/** - * @brief Get the volume of the tetrahedron made up by the four given vertices. - * - * The vertices are not expected to be oriented in a specific way. If the input - * happens to be coplanar or colinear, the returned volume will just be zero. - * - * @param v1 First vertex. - * @param v2 Second vertex. - * @param v3 Third vertex. - * @param v4 Fourth vertex. - * @return Volume of the tetrahedron. - */ -__attribute__((always_inline)) INLINE float voronoi_volume_tetrahedron( - const float *v1, const float *v2, const float *v3, const float *v4) { - - float V; - float r1[3], r2[3], r3[3]; - - r1[0] = v2[0] - v1[0]; - r1[1] = v2[1] - v1[1]; - r1[2] = v2[2] - v1[2]; - r2[0] = v3[0] - v1[0]; - r2[1] = v3[1] - v1[1]; - r2[2] = v3[2] - v1[2]; - r3[0] = v4[0] - v1[0]; - r3[1] = v4[1] - v1[1]; - r3[2] = v4[2] - v1[2]; - V = fabs(r1[0] * r2[1] * r3[2] + r1[1] * r2[2] * r3[0] + - r1[2] * r2[0] * r3[1] - r1[2] * r2[1] * r3[0] - - r2[2] * r3[1] * r1[0] - r3[2] * r1[1] * r2[0]); - V /= 6.; - return V; -} - -/** - * @brief Get the centroid of the tetrahedron made up by the four given - * vertices. - * - * The centroid is just the average of four vertex coordinates. - * - * @param centroid Array to store the centroid in. - * @param v1 First vertex. - * @param v2 Second vertex. - * @param v3 Third vertex. - * @param v4 Fourth vertex. - */ -__attribute__((always_inline)) INLINE void voronoi_centroid_tetrahedron( - float *centroid, const float *v1, const float *v2, const float *v3, - const float *v4) { - - centroid[0] = 0.25f * (v1[0] + v2[0] + v3[0] + v4[0]); - centroid[1] = 0.25f * (v1[1] + v2[1] + v3[1] + v4[1]); - centroid[2] = 0.25f * (v1[2] + v2[2] + v3[2] + v4[2]); -} - -/** - * @brief Calculate the volume and centroid of a 3D Voronoi cell. - * - * @param cell 3D Voronoi cell. - */ -__attribute__((always_inline)) INLINE void voronoi_calculate_cell( - struct voronoi_cell *cell) { - - float v1[3], v2[3], v3[3], v4[3]; - int i, j, k, l, m, n; - float tcentroid[3]; - float tvol; - - /* we need to calculate the volume of the tetrahedra formed by the first - vertex and the triangles that make up the other faces - since we do not store faces explicitly, this means keeping track of the - edges that have been processed somehow - we follow the method used in voro++ and "flip" processed edges to - negative values - this also means that we need to process all triangles corresponding to - an edge at once */ - cell->volume = 0.0f; - v1[0] = cell->vertices[0]; - v1[1] = cell->vertices[1]; - v1[2] = cell->vertices[2]; - cell->centroid[0] = 0.0f; - cell->centroid[1] = 0.0f; - cell->centroid[2] = 0.0f; - - /* loop over all vertices (except the first one) */ - for (i = 1; i < cell->nvert; ++i) { - - v2[0] = cell->vertices[3 * i + 0]; - v2[1] = cell->vertices[3 * i + 1]; - v2[2] = cell->vertices[3 * i + 2]; - - /* loop over the edges of the vertex*/ - for (j = 0; j < cell->orders[i]; ++j) { - - k = voronoi_get_edge(cell, i, j); - - if (k >= 0) { - - /* mark the edge as processed */ - voronoi_set_edge(cell, i, j, -k - 1); - - l = voronoi_get_edgeindex(cell, i, j) + 1; - if (l == cell->orders[k]) { - l = 0; - } - v3[0] = cell->vertices[3 * k + 0]; - v3[1] = cell->vertices[3 * k + 1]; - v3[2] = cell->vertices[3 * k + 2]; - m = voronoi_get_edge(cell, k, l); - voronoi_set_edge(cell, k, l, -1 - m); - - int loopcount = 0; - safewhile(m != i) { - if (loopcount == 999) { - voronoi_print_cell(cell); - voronoi_print_gnuplot_c(cell); - } - ++loopcount; - n = voronoi_get_edgeindex(cell, k, l) + 1; - if (n == cell->orders[m]) { - n = 0; - } - v4[0] = cell->vertices[3 * m + 0]; - v4[1] = cell->vertices[3 * m + 1]; - v4[2] = cell->vertices[3 * m + 2]; - tvol = voronoi_volume_tetrahedron(v1, v2, v3, v4); - cell->volume += tvol; - voronoi_centroid_tetrahedron(tcentroid, v1, v2, v3, v4); - cell->centroid[0] += tcentroid[0] * tvol; - cell->centroid[1] += tcentroid[1] * tvol; - cell->centroid[2] += tcentroid[2] * tvol; - k = m; - l = n; - v3[0] = v4[0]; - v3[1] = v4[1]; - v3[2] = v4[2]; - m = voronoi_get_edge(cell, k, l); - voronoi_set_edge(cell, k, l, -1 - m); - } /* while() */ - - } /* if(k >= 0) */ - - } /* for(j) */ - - } /* for(i) */ - - cell->centroid[0] /= cell->volume; - cell->centroid[1] /= cell->volume; - cell->centroid[2] /= cell->volume; - - /* centroid was calculated relative w.r.t. particle position */ - cell->centroid[0] += cell->x[0]; - cell->centroid[1] += cell->x[1]; - cell->centroid[2] += cell->x[2]; - - /* Reset the edges: we still need them for the face calculation */ - for (i = 0; i < VORONOI3D_MAXNUMEDGE; ++i) { - if (cell->edges[i] < 0) { - cell->edges[i] = -1 - cell->edges[i]; - } - } -} - -/** - * @brief Calculate the faces for a 3D Voronoi cell. This reorganizes the - * internal variables of the cell, so no new neighbours can be added after - * this method has been called! - * - * Note that the face midpoints are calculated relative w.r.t. the cell - * generator! - * - * @param cell 3D Voronoi cell. - */ -__attribute__((always_inline)) INLINE void voronoi_calculate_faces( - struct voronoi_cell *cell) { - - int i, j, k, l, m, n; - float area; - float midpoint[3]; - float u[3], v[3], w[3]; - float loc_area; - unsigned long long newngbs[VORONOI3D_MAXNUMEDGE]; - - cell->nface = 0; - for (i = 0; i < cell->nvert; ++i) { - - for (j = 0; j < cell->orders[i]; ++j) { - - k = voronoi_get_edge(cell, i, j); - - if (k >= 0) { - - newngbs[cell->nface] = voronoi_get_ngb(cell, i, j); - area = 0.; - midpoint[0] = 0.; - midpoint[1] = 0.; - midpoint[2] = 0.; - voronoi_set_edge(cell, i, j, -1 - k); - l = voronoi_get_edgeindex(cell, i, j) + 1; - if (l == cell->orders[k]) { - l = 0; - } - m = voronoi_get_edge(cell, k, l); - voronoi_set_edge(cell, k, l, -1 - m); - - safewhile(m != i) { - n = voronoi_get_edgeindex(cell, k, l) + 1; - if (n == cell->orders[m]) { - n = 0; - } - u[0] = cell->vertices[3 * k + 0] - cell->vertices[3 * i + 0]; - u[1] = cell->vertices[3 * k + 1] - cell->vertices[3 * i + 1]; - u[2] = cell->vertices[3 * k + 2] - cell->vertices[3 * i + 2]; - v[0] = cell->vertices[3 * m + 0] - cell->vertices[3 * i + 0]; - v[1] = cell->vertices[3 * m + 1] - cell->vertices[3 * i + 1]; - v[2] = cell->vertices[3 * m + 2] - cell->vertices[3 * i + 2]; - w[0] = u[1] * v[2] - u[2] * v[1]; - w[1] = u[2] * v[0] - u[0] * v[2]; - w[2] = u[0] * v[1] - u[1] * v[0]; - loc_area = sqrtf(w[0] * w[0] + w[1] * w[1] + w[2] * w[2]); - area += loc_area; - midpoint[0] += loc_area * (cell->vertices[3 * k + 0] + - cell->vertices[3 * i + 0] + - cell->vertices[3 * m + 0]); - midpoint[1] += loc_area * (cell->vertices[3 * k + 1] + - cell->vertices[3 * i + 1] + - cell->vertices[3 * m + 1]); - midpoint[2] += loc_area * (cell->vertices[3 * k + 2] + - cell->vertices[3 * i + 2] + - cell->vertices[3 * m + 2]); - k = m; - l = n; - m = voronoi_get_edge(cell, k, l); - voronoi_set_edge(cell, k, l, -1 - m); - } - - cell->face_areas[cell->nface] = 0.5f * area; - cell->face_midpoints[cell->nface][0] = midpoint[0] / area / 3.0f; - cell->face_midpoints[cell->nface][1] = midpoint[1] / area / 3.0f; - cell->face_midpoints[cell->nface][2] = midpoint[2] / area / 3.0f; - ++cell->nface; - - if (cell->nface == VORONOI3D_MAXFACE) { - error("Too many faces!"); - } - - } /* if(k >= 0) */ - - } /* for(j) */ - - } /* for(i) */ - - /* Overwrite the old neighbour array. */ - for (i = 0; i < cell->nface; ++i) { - cell->ngbs[i] = newngbs[i]; - } -} - -/******************************************************************************* - * voronoi_algorithm interface implementations - * - * If you change any function parameters below, you also have to change them in - * the 1D and 2D algorithm! - ******************************************************************************/ - -/** - * @brief Initialize a 3D Voronoi cell. - * - * @param cell 3D Voronoi cell to initialize. - * @param x Position of the generator of the cell. - * @param anchor Anchor of the simulation box. - * @param side Side lengths of the simulation box. - */ -__attribute__((always_inline)) INLINE void voronoi_cell_init( - struct voronoi_cell *cell, const double *x, const double *anchor, - const double *side) { - - cell->x[0] = x[0]; - cell->x[1] = x[1]; - cell->x[2] = x[2]; - - voronoi_initialize(cell, anchor, side); - - cell->volume = 0.0f; - cell->centroid[0] = 0.0f; - cell->centroid[1] = 0.0f; - cell->centroid[2] = 0.0f; - cell->nface = 0; -} - -/** - * @brief Interact a 3D Voronoi cell with a particle with given relative - * position and ID. - * - * @param cell 3D Voronoi cell. - * @param dx Relative position of the interacting generator w.r.t. the cell - * generator (in fact: dx = generator - neighbour). - * @param id ID of the interacting neighbour. - */ -__attribute__((always_inline)) INLINE void voronoi_cell_interact( - struct voronoi_cell *cell, const float *dx, unsigned long long id) { - - voronoi_intersect(cell, dx, id); -} - -/** - * @brief Finalize a 3D Voronoi cell. - * - * @param cell 3D Voronoi cell. - * @return Maximal radius that could still change the structure of the cell. - */ -__attribute__((always_inline)) INLINE float voronoi_cell_finalize( - struct voronoi_cell *cell) { - - int i; - float max_radius, v[3], v2; - - /* Calculate the volume and centroid of the cell. */ - voronoi_calculate_cell(cell); - /* Calculate the faces. */ - voronoi_calculate_faces(cell); - - /* Loop over the vertices and calculate the maximum radius. */ - max_radius = 0.0f; - for (i = 0; i < cell->nvert; ++i) { - v[0] = cell->vertices[3 * i]; - v[1] = cell->vertices[3 * i + 1]; - v[2] = cell->vertices[3 * i + 2]; - v2 = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; - max_radius = fmaxf(max_radius, v2); - } - max_radius = sqrtf(max_radius); - - return 2.0f * max_radius; -} - -/** - * @brief Get the surface area and midpoint of the face between a 3D Voronoi - * cell and the given neighbour. - * - * @param cell 3D Voronoi cell. - * @param ngb ID of a particle that is possibly a neighbour of this cell. - * @param midpoint Array to store the relative position of the face in. - * @return 0 if the given neighbour is not a neighbour, the surface area of - * the face otherwise. - */ -__attribute__((always_inline)) INLINE float voronoi_get_face( - const struct voronoi_cell *cell, unsigned long long ngb, float *midpoint) { - - int i = 0; - while (i < cell->nface && cell->ngbs[i] != ngb) { - ++i; - } - if (i == cell->nface) { - /* Ngb not found */ - return 0.0f; - } - - midpoint[0] = cell->face_midpoints[i][0]; - midpoint[1] = cell->face_midpoints[i][1]; - midpoint[2] = cell->face_midpoints[i][2]; - - return cell->face_areas[i]; -} - -/** - * @brief Get the centroid of a 3D Voronoi cell. - * - * @param cell 3D Voronoi cell. - * @param centroid Array to store the centroid in. - */ -__attribute__((always_inline)) INLINE void voronoi_get_centroid( - const struct voronoi_cell *cell, float *centroid) { - - centroid[0] = cell->centroid[0]; - centroid[1] = cell->centroid[1]; - centroid[2] = cell->centroid[2]; -} - -#endif // SWIFT_VORONOIXD_ALGORITHM_H diff --git a/src/hydro/Shadowswift/voronoi3d_cell.h b/src/hydro/Shadowswift/voronoi3d_cell.h deleted file mode 100644 index ef43eff1745f48219af14aec2455aaa5e5b0d47a..0000000000000000000000000000000000000000 --- a/src/hydro/Shadowswift/voronoi3d_cell.h +++ /dev/null @@ -1,143 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -#ifndef SWIFT_VORONOIXD_CELL_H -#define SWIFT_VORONOIXD_CELL_H - -/* Maximal number of neighbours that can be stored in a voronoi_cell struct */ -#define VORONOI3D_MAXNUMNGB 100 -/* Maximal number of vertices that can be stored in a voronoi_cell struct */ -#define VORONOI3D_MAXNUMVERT 500 -/* Maximal number of edges that can be stored in a voronoi_cell struct */ -#define VORONOI3D_MAXNUMEDGE 1500 -/* Maximal number of faces that can be stored in a voronoi_cell struct */ -#define VORONOI3D_MAXFACE 100 - -/* 3D Voronoi cell */ -struct voronoi_cell { - - /* The position of the generator of the cell. */ - double x[3]; - - /* The volume of the 3D cell. */ - float volume; - - /* The centroid of the cell. */ - float centroid[3]; - - /* Number of cell vertices. */ - int nvert; - - /* Vertex coordinates. */ - float vertices[3 * VORONOI3D_MAXNUMVERT]; - - /* Number of edges for every vertex. */ - char orders[VORONOI3D_MAXNUMVERT]; - - /* Offsets of the edges, edgeindices and neighbours corresponding to a - particular vertex in the internal arrays */ - int offsets[VORONOI3D_MAXNUMVERT]; - - /* Edge information. Edges are ordered counterclockwise w.r.t. a vector - pointing from the cell generator to the vertex. */ - int edges[VORONOI3D_MAXNUMEDGE]; - - /* Additional edge information. */ - char edgeindices[VORONOI3D_MAXNUMEDGE]; - - /* Neighbour information. This field is used differently depending on where we - are in the algorithm. During cell construction, it contains, for every edge - of every vertex, the index of the neighbour that generates the face - counterclockwise of the edge w.r.t. a vector pointing from the vertex along - the edge. After cell finalization, it contains a neighbour for every face, - in the same order as the face_areas and face_midpoints arrays. */ - unsigned long long ngbs[VORONOI3D_MAXNUMEDGE]; - - /* Number of faces of the cell. */ - unsigned char nface; - - /* Surface areas of the cell faces. */ - float face_areas[VORONOI3D_MAXFACE]; - - /* Midpoints of the cell faces. */ - float face_midpoints[VORONOI3D_MAXFACE][3]; -}; - -/** - * @brief Copy the contents of the 3D Voronoi cell pointed to by source into the - * 3D Voronoi cell pointed to by destination - * - * @param source Pointer to a 3D Voronoi cell to read from. - * @param destination Pointer to a 3D Voronoi cell to write to. - */ -__attribute__((always_inline)) INLINE void voronoi3d_cell_copy( - struct voronoi_cell *source, struct voronoi_cell *destination) { - - /* Copy the position of the generator of the cell. */ - destination->x[0] = source->x[0]; - destination->x[1] = source->x[1]; - destination->x[2] = source->x[2]; - - /* Copy the volume of the 3D cell. */ - destination->volume = source->volume; - - /* Copy the centroid of the cell. */ - destination->centroid[0] = source->centroid[0]; - destination->centroid[1] = source->centroid[1]; - destination->centroid[2] = source->centroid[2]; - - /* Copy the number of cell vertices. */ - destination->nvert = source->nvert; - - /* Copy the vertex coordinates. We only copy the 3*nvert first coordinates. */ - for (int i = 0; i < 3 * source->nvert; ++i) { - destination->vertices[i] = source->vertices[i]; - } - - /* Copy the number of edges for every vertex. Again, we only copy the nvert - first values. */ - for (int i = 0; i < source->nvert; ++i) { - destination->orders[i] = source->orders[i]; - } - - /* Copy the nvert first values of the offsets. */ - for (int i = 0; i < source->nvert; ++i) { - destination->offsets[i] = source->offsets[i]; - } - - /* Copy the edge information. No idea how many edges we have, so we copy - everything. */ - for (int i = 0; i < VORONOI3D_MAXNUMEDGE; ++i) { - destination->edges[i] = source->edges[i]; - } - - /* Copy all additional edge information. */ - for (int i = 0; i < VORONOI3D_MAXNUMEDGE; ++i) { - destination->edgeindices[i] = source->edgeindices[i]; - } - - /* Copy neighbour information. Since neighbours are stored per edge, the total - number of neighbours in this list is larger than numngb and we copy - everything. */ - for (int i = 0; i < VORONOI3D_MAXNUMEDGE; ++i) { - destination->ngbs[i] = source->ngbs[i]; - } -} - -#endif // SWIFT_VORONOIXD_CELL_H diff --git a/src/hydro_io.h b/src/hydro_io.h index c0f82c2e7b4f52adc870b59f41d12c0e14e84a46..ed78acf691d2ac172cd2b8fa81456a2514fdcb98 100644 --- a/src/hydro_io.h +++ b/src/hydro_io.h @@ -39,7 +39,7 @@ #include "./hydro/Phantom/hydro_io.h" #elif defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) #include "./hydro/Gizmo/hydro_io.h" -#elif defined(SHADOWFAX_SPH) +#elif defined(SHADOWSWIFT) #include "./hydro/Shadowswift/hydro_io.h" #elif defined(PLANETARY_SPH) #include "./hydro/Planetary/hydro_io.h" diff --git a/src/hydro_parameters.h b/src/hydro_parameters.h index 5d1f3909632ac055b00587c01c276242b7bf76f9..645a5fe5af9be5b67e4e1c9f17acc13c95938d96 100644 --- a/src/hydro_parameters.h +++ b/src/hydro_parameters.h @@ -48,7 +48,7 @@ #include "./hydro/Phantom/hydro_parameters.h" #elif defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) #include "./hydro/Gizmo/hydro_parameters.h" -#elif defined(SHADOWFAX_SPH) +#elif defined(SHADOWSWIFT) #include "./hydro/Shadowswift/hydro_parameters.h" #elif defined(PLANETARY_SPH) #include "./hydro/Planetary/hydro_parameters.h" diff --git a/src/hydro_properties.c b/src/hydro_properties.c index c160e6c9891f4729907465c5f54c6a9477a74d96..f8b547bc567a73be010365dcf46e11f299e5f02a 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -207,6 +207,9 @@ void hydro_props_init(struct hydro_props *p, p->generate_random_ids = parser_get_opt_param_int( params, "SPH:particle_splitting_generate_random_ids", 0); + + p->log_extra_splits_in_file = parser_get_opt_param_int( + params, "SPH:particle_splitting_log_extra_splits", 0); } } diff --git a/src/hydro_properties.h b/src/hydro_properties.h index b86329737edaf7964367bf1260eefd0ea63a57b0..5815f58f3bf170da3d5a2655f2a9f5be975fcd8a 100644 --- a/src/hydro_properties.h +++ b/src/hydro_properties.h @@ -123,6 +123,10 @@ struct hydro_props { /*! Are we generating random IDs when splitting particles? */ int generate_random_ids; + /*! Are we logging the particle splits beyond the limit in a file before + * reseting? */ + int log_extra_splits_in_file; + /* ------ Viscosity and diffusion ---------------- */ /*! Artificial viscosity parameters */ diff --git a/src/part.h b/src/part.h index 241418a808cb091dcfa676fe1a11e62fd0b74720..c0e87bfde7c6525b7cdc05974d56fa4bb5be5bd9 100644 --- a/src/part.h +++ b/src/part.h @@ -73,7 +73,7 @@ struct threadpool; #define hydro_need_extra_init_loop 0 #define EXTRA_HYDRO_LOOP #define MPI_SYMMETRIC_FORCE_INTERACTION -#elif defined(SHADOWFAX_SPH) +#elif defined(SHADOWSWIFT) #include "./hydro/Shadowswift/hydro_part.h" #define hydro_need_extra_init_loop 0 #define EXTRA_HYDRO_LOOP diff --git a/src/particle_splitting.h b/src/particle_splitting.h index 202f2e3b7195351145c12200ab1eb2401bb17245..a3f99a1d61ffb6fb16eb9ab9a61479b64480ef3e 100644 --- a/src/particle_splitting.h +++ b/src/particle_splitting.h @@ -36,7 +36,7 @@ */ __attribute__((always_inline)) INLINE static void particle_splitting_mark_part_as_not_split( - struct particle_splitting_data* restrict splitting_data, int id) { + struct particle_splitting_data* restrict splitting_data, long long id) { splitting_data->progenitor_id = id; splitting_data->split_tree = 0; @@ -53,30 +53,61 @@ particle_splitting_mark_part_as_not_split( * the splitting event. * @param sdj second particle_splitting_data* resulting from * the splitting event. + * @param id_i ID of the first particle. + * @param id_j ID of the first particle. + * @param extra_split_logger File pointer (opened) where to log + * the resetting of the split counters. */ __attribute__((always_inline)) INLINE static void particle_splitting_update_binary_tree( struct particle_splitting_data* restrict sdi, - struct particle_splitting_data* restrict sdj) { + struct particle_splitting_data* restrict sdj, const long long id_i, + const long long id_j, FILE* extra_split_logger, + swift_lock_type* file_lock) { + + /* Print warnings if we have split these particles more + * than the number of times the tree can accommodate. + * Warning is only printed once for each particle */ + if (sdi->split_count > 0 && + sdi->split_count % (8 * sizeof(sdi->split_tree)) == 0) { + message( + "Warning: Particle (%lld) with progenitor ID %lld with binary tree " + "%lld has been split over the maximum %zu times, making its binary " + "tree invalid.", + id_i, sdi->progenitor_id, sdi->split_tree, sizeof(sdi->split_tree)); + + /* Are we logging this? */ + if (extra_split_logger != NULL) { + + /* Log the old state before reseting. Use the lock to prevent multiple + * threads from writing at the same time. */ + lock_lock(file_lock); + fprintf(extra_split_logger, " %12d %20lld %20lld %20d %20lld\n", + engine_current_step, id_i, sdi->progenitor_id, sdi->split_count, + sdi->split_tree); + fflush(extra_split_logger); + + /* Release the lock and continue in parallel */ + if (lock_unlock(file_lock) != 0) + error("Impossible to unlock particle splitting"); + } + + /* Reset both counters and trees */ + sdi->split_tree = 0LL; + sdj->split_tree = 0LL; + + /* Set both particles as having particle i as their progenitor */ + sdi->progenitor_id = id_i; + sdj->progenitor_id = id_i; + } /* Update the binary tree */ - sdj->split_tree |= 1LL << sdj->split_count; + sdj->split_tree |= 1LL << sdj->split_count % (8 * sizeof(sdi->split_tree)); /* Increase counters on both; sdi implicitly has a zero * in the relevant spot in its binary tree */ sdj->split_count++; sdi->split_count++; - - /* Print warnings if we have split these particles more - * than the number of times the tree can accommodate. - * Warning is only printed once for each particle */ - if (sdi->split_count == 8 * sizeof(sdi->split_tree)) { - message( - "Warning: Particle with progenitor ID %lld with binary tree %lld has " - "been split over the maximum %zu times, making its binary tree " - "invalid.", - sdi->progenitor_id, sdi->split_tree, sizeof(sdi->split_tree)); - } } /** @@ -107,9 +138,7 @@ INLINE static int particle_splitting_write_particles(const struct part* parts, "SplitCounts", UINT8, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, split_data.split_count, "Number of times this particle has been split. Note that both particles " - "that take part in the splitting have counter incremented, so the " - "number of splitting events in an entire simulation is half of the sum " - "of all of these numbers."); + "that take part in the splitting have their counter incremented."); list[2] = io_make_output_field( "SplitTrees", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, @@ -146,8 +175,7 @@ INLINE static int particle_splitting_write_sparticles( split_data.split_count, "Number of times the gas particle that turned into this star particle " "was split. Note that both particles that take part in the splitting " - "have this counter incremented, so the number of splitting events in an " - "entire simulation is half of the sum of all of these numbers."); + "have their counter incremented."); list[2] = io_make_output_field( "SplitTrees", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, @@ -183,8 +211,7 @@ INLINE static int particle_splitting_write_bparticles( split_data.split_count, "Number of times the gas particle that became this BH seed " "was split. Note that both particles that take part in the splitting " - "have this counter incremented, so the number of splitting events in an " - "entire simulation is half of the sum of all of these numbers."); + "have their counter incremented."); list[2] = io_make_output_field( "SplitTrees", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, diff --git a/src/proxy.c b/src/proxy.c index dec4acf708b931f304df78a74e87c1c91a9f224a..830926e0df694bdef6abddc0c86cb2058053bd3a 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -187,6 +187,147 @@ void proxy_tags_exchange(struct proxy *proxies, int num_proxies, #endif } +/** + * @brief Exchange extra information about the grid construction between nodes. + * + * Note that this function assumes that the cell structures have already + * been exchanged, e.g. via #proxy_cells_exchange. + * + * @param proxies The list of #proxy that will send/recv tags + * @param num_proxies The number of proxies. + * @param s The space into which the tags will be unpacked. + */ +void proxy_grid_extra_exchange(struct proxy *proxies, int num_proxies, + struct space *s) { +#ifdef WITH_MPI + + ticks tic2 = getticks(); + + /* Run through the cells and get the size of the info that will be sent off. + */ + int count_out = 0; + int *offset_out = + (int *)swift_malloc("info_offsets_out", s->nr_cells * sizeof(int)); + if (offset_out == NULL) error("Error allocating memory for info offsets"); + + for (int k = 0; k < s->nr_cells; k++) { + offset_out[k] = count_out; + if (s->cells_top[k].mpi.sendto) { + count_out += s->cells_top[k].mpi.pcell_size; + } + } + + /* Run through the proxies and get the count of incoming info. */ + int count_in = 0; + int *offset_in = + (int *)swift_malloc("info_offsets_in", s->nr_cells * sizeof(int)); + if (offset_in == NULL) error("Error allocating memory for info offsets"); + + for (int k = 0; k < num_proxies; k++) { + for (int j = 0; j < proxies[k].nr_cells_in; j++) { + offset_in[proxies[k].cells_in[j] - s->cells_top] = count_in; + count_in += proxies[k].cells_in[j]->mpi.pcell_size; + } + } + + /* Allocate the tags. */ + enum grid_construction_level *extra_info_in = NULL; + enum grid_construction_level *extra_info_out = NULL; + if (swift_memalign("extra_info_in", (void **)&extra_info_in, + SWIFT_CACHE_ALIGNMENT, + sizeof(enum grid_construction_level) * count_in) != 0 || + swift_memalign("extra_info_out", (void **)&extra_info_out, + SWIFT_CACHE_ALIGNMENT, + sizeof(enum grid_construction_level) * count_out) != 0) + error("Failed to allocate extra info buffers."); + + /* Pack the local grid info. */ + for (int k = 0; k < s->nr_cells; k++) { + if (s->cells_top[k].mpi.sendto) { + cell_pack_grid_extra(&s->cells_top[k], &extra_info_out[offset_out[k]]); + } + } + + if (s->e->verbose) + message("Cell pack grid extra took %.3f %s.", + clocks_from_ticks(getticks() - tic2), clocks_getunit()); + + /* Allocate the incoming and outgoing request handles. */ + int num_reqs_out = 0; + int num_reqs_in = 0; + for (int k = 0; k < num_proxies; k++) { + num_reqs_in += proxies[k].nr_cells_in; + num_reqs_out += proxies[k].nr_cells_out; + } + MPI_Request *reqs_in = NULL; + int *cids_in = NULL; + if ((reqs_in = (MPI_Request *)malloc(sizeof(MPI_Request) * + (num_reqs_in + num_reqs_out))) == NULL || + (cids_in = (int *)malloc(sizeof(int) * (num_reqs_in + num_reqs_out))) == + NULL) + error("Failed to allocate MPI_Request arrays."); + MPI_Request *reqs_out = &reqs_in[num_reqs_in]; + int *cids_out = &cids_in[num_reqs_in]; + + /* Emit the sends and recvs. */ + for (int send_rid = 0, recv_rid = 0, k = 0; k < num_proxies; k++) { + for (int j = 0; j < proxies[k].nr_cells_in; j++) { + const int cid = proxies[k].cells_in[j] - s->cells_top; + cids_in[recv_rid] = cid; + int err = + MPI_Irecv(&extra_info_in[offset_in[cid]], + proxies[k].cells_in[j]->mpi.pcell_size, MPI_INT, + proxies[k].nodeID, cid, MPI_COMM_WORLD, &reqs_in[recv_rid]); + if (err != MPI_SUCCESS) mpi_error(err, "Failed to irecv grid info."); + recv_rid += 1; + } + for (int j = 0; j < proxies[k].nr_cells_out; j++) { + const int cid = proxies[k].cells_out[j] - s->cells_top; + cids_out[send_rid] = cid; + int err = MPI_Isend(&extra_info_out[offset_out[cid]], + proxies[k].cells_out[j]->mpi.pcell_size, MPI_INT, + proxies[k].nodeID, cid, MPI_COMM_WORLD, + &reqs_out[send_rid]); + if (err != MPI_SUCCESS) mpi_error(err, "Failed to isend grid info."); + send_rid += 1; + } + } + + tic2 = getticks(); + + /* Wait for each recv and unpack the grid info into the local cells. */ + for (int k = 0; k < num_reqs_in; k++) { + int pid = MPI_UNDEFINED; + MPI_Status status; + if (MPI_Waitany(num_reqs_in, reqs_in, &pid, &status) != MPI_SUCCESS || + pid == MPI_UNDEFINED) + error("MPI_Waitany failed."); + const int cid = cids_in[pid]; + cell_unpack_grid_extra(&extra_info_in[offset_in[cid]], &s->cells_top[cid], + NULL); + } + + if (s->e->verbose) + message("Cell unpack grid extra took %.3f %s.", + clocks_from_ticks(getticks() - tic2), clocks_getunit()); + + /* Wait for all the sends to have completed. */ + if (MPI_Waitall(num_reqs_out, reqs_out, MPI_STATUSES_IGNORE) != MPI_SUCCESS) + error("MPI_Waitall on sends failed."); + + /* Clean up. */ + swift_free("extra_info_in", extra_info_in); + swift_free("extra_info_out", extra_info_out); + swift_free("info_offsets_in", offset_in); + swift_free("info_offsets_out", offset_out); + free(reqs_in); + free(cids_in); + +#else + error("SWIFT was not compiled with MPI support."); +#endif +} + /** * @brief Exchange cells with a remote node, first part. * diff --git a/src/proxy.h b/src/proxy.h index 846cc909ea10215b2aeef4501197a47c913a59b7..f7db68dc12292844bde894f24bb5bcf25b0a6a32 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -119,6 +119,8 @@ void proxy_cells_exchange(struct proxy *proxies, int num_proxies, struct space *s, int with_gravity); void proxy_tags_exchange(struct proxy *proxies, int num_proxies, struct space *s); +void proxy_grid_extra_exchange(struct proxy *proxies, int num_proxies, + struct space *s); void proxy_create_mpi_type(void); void proxy_free_mpi_type(void); diff --git a/src/rt/GEAR/rt.h b/src/rt/GEAR/rt.h index e2d6b8272d5a2585be780248fea6d93a63dd0859..b27e92e738aa13a2b3d30ebf0e4616f8efe0b7fd 100644 --- a/src/rt/GEAR/rt.h +++ b/src/rt/GEAR/rt.h @@ -580,6 +580,8 @@ __attribute__((always_inline)) INLINE static void rt_kick_extra( } #endif +#ifdef GIZMO_MFV_SPH + /* Note: We need to mimick here what Gizmo does for the mass fluxes. * The relevant time scale is the hydro time step for the mass fluxes, * not the RT times. We also need to prevent the kick to apply the mass @@ -653,6 +655,8 @@ __attribute__((always_inline)) INLINE static void rt_kick_extra( * hydro_kick_extra calls */ } +#endif + rt_check_unphysical_mass_fractions(p); } diff --git a/src/rt/GEAR/rt_flux.h b/src/rt/GEAR/rt_flux.h index f16ac13b0f28af05515eca2aeb68479e9edc58d9..642ba060629e47908691999e5bae04b4705350e0 100644 --- a/src/rt/GEAR/rt_flux.h +++ b/src/rt/GEAR/rt_flux.h @@ -102,11 +102,13 @@ __attribute__((always_inline)) INLINE static void rt_compute_flux( **/ __attribute__((always_inline)) INLINE static void rt_part_reset_mass_fluxes( struct part* restrict p) { +#ifdef GIZMO_MFV_SPH p->rt_data.mass_flux.HI = 0.f; p->rt_data.mass_flux.HII = 0.f; p->rt_data.mass_flux.HeI = 0.f; p->rt_data.mass_flux.HeII = 0.f; p->rt_data.mass_flux.HeIII = 0.f; +#endif } #endif /* SWIFT_GEAR_RT_FLUX_H */ diff --git a/src/rt/GEAR/rt_gradients.h b/src/rt/GEAR/rt_gradients.h index dc116c940240f68255dbb3523ddd20d1340498eb..4b2032aec05fad98ca1a1a80a054aecda1d2b7a7 100644 --- a/src/rt/GEAR/rt_gradients.h +++ b/src/rt/GEAR/rt_gradients.h @@ -20,12 +20,7 @@ #ifndef SWIFT_RT_GRADIENTS_GEAR_H #define SWIFT_RT_GRADIENTS_GEAR_H -/* better safe than sorry */ -#ifndef GIZMO_MFV_SPH -#error "Cannot compile GEAR-RT without gizmo-mfv hydro!" -#endif - -#include "hydro.h" /* needed for hydro_part_geometry_well_behaved() */ +#include "fvpm_geometry.h" #include "rt_getters.h" /* #include "rt_slope_limiters_cell.h" [> skipped for now <] */ #include "rt_slope_limiters_face.h" @@ -106,7 +101,7 @@ __attribute__((always_inline)) INLINE static void rt_finalise_gradient_part( const float h_inv = 1.0f / h; float norm; - if (hydro_part_geometry_well_behaved(p)) { + if (fvpm_part_geometry_well_behaved(p)) { const float hinvdim = pow_dimension(h_inv); norm = hinvdim; } else { @@ -183,7 +178,7 @@ __attribute__((always_inline)) INLINE static void rt_gradients_collect( /* Compute psi tilde */ float psii_tilde[3]; - if (hydro_part_geometry_well_behaved(pi)) { + if (fvpm_part_geometry_well_behaved(pi)) { psii_tilde[0] = wi * (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]); psii_tilde[1] = @@ -198,7 +193,7 @@ __attribute__((always_inline)) INLINE static void rt_gradients_collect( } float psij_tilde[3]; - if (hydro_part_geometry_well_behaved(pj)) { + if (fvpm_part_geometry_well_behaved(pj)) { psij_tilde[0] = wi * (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); psij_tilde[1] = @@ -311,7 +306,7 @@ __attribute__((always_inline)) INLINE static void rt_gradients_nonsym_collect( /* Compute psi tilde */ float psii_tilde[3]; - if (hydro_part_geometry_well_behaved(pi)) { + if (fvpm_part_geometry_well_behaved(pi)) { psii_tilde[0] = wi * (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]); psii_tilde[1] = diff --git a/src/rt/GEAR/rt_iact.h b/src/rt/GEAR/rt_iact.h index 3ad71d3b66616369ee45851cca73b4cbb9fae0ed..21d2be8f5b46339a6aeb4f34d0aeefca7287391f 100644 --- a/src/rt/GEAR/rt_iact.h +++ b/src/rt/GEAR/rt_iact.h @@ -19,6 +19,7 @@ #ifndef SWIFT_RT_IACT_GEAR_H #define SWIFT_RT_IACT_GEAR_H +#include "fvpm_geometry.h" #include "rt_debugging.h" #include "rt_flux.h" #include "rt_gradients.h" @@ -254,8 +255,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_flux_common( /* eqn. (7) */ float Anorm2 = 0.0f; float A[3]; - if (hydro_part_geometry_well_behaved(pi) && - hydro_part_geometry_well_behaved(pj)) { + if (fvpm_part_geometry_well_behaved(pi) && + fvpm_part_geometry_well_behaved(pj)) { /* in principle, we use Vi and Vj as weights for the left and right * contributions to the generalized surface vector. * However, if Vi and Vj are very different (because they have very diff --git a/src/rt/GEAR/rt_io.h b/src/rt/GEAR/rt_io.h index cc457bd3787fd2c7405d1dcd3c0938bb9bdb0ac0..5aefc3ec997740eccb7ed433832966b6ce2c76b2 100644 --- a/src/rt/GEAR/rt_io.h +++ b/src/rt/GEAR/rt_io.h @@ -163,12 +163,14 @@ INLINE static int rt_write_particles(const struct part* parts, list[0] = io_make_physical_output_field_convert_part( "PhotonEnergies", FLOAT, RT_NGROUPS, UNIT_CONV_ENERGY, 0, parts, - /*xparts=*/NULL, rt_convert_radiation_energies, - /*convertible to comoving=*/1, "Photon Energies (all groups)"); + /*xparts=*/NULL, + /*convertible to comoving=*/1, rt_convert_radiation_energies, + "Photon Energies (all groups)"); + list[1] = io_make_physical_output_field_convert_part( "PhotonFluxes", FLOAT, 3 * RT_NGROUPS, UNIT_CONV_RADIATION_FLUX, 0, parts, - /*xparts=*/NULL, rt_convert_radiation_fluxes, - /*convertible to comoving=*/1, + /*xparts=*/NULL, + /*convertible to comoving=*/1, rt_convert_radiation_fluxes, "Photon Fluxes (all groups; x, y, and z coordinates)"); list[2] = io_make_output_field_convert_part( "IonMassFractions", FLOAT, 5, UNIT_CONV_NO_UNITS, 0, parts, diff --git a/src/rt/GEAR/rt_struct.h b/src/rt/GEAR/rt_struct.h index 63cfebbb6a2e3337e317edea206d7dd183f7ac6e..caadfc0b304f755326158023bc6e0f3723a220be 100644 --- a/src/rt/GEAR/rt_struct.h +++ b/src/rt/GEAR/rt_struct.h @@ -73,6 +73,7 @@ struct rt_part_data { float number_density_electrons; /* number density of electrons */ } tchem; +#ifdef GIZMO_MFV_SPH /* Keep track of the actual mass fluxes of the gas species */ struct { float HI; /* mass fraction taken by HI */ @@ -81,6 +82,7 @@ struct rt_part_data { float HeII; /* mass fraction taken by HeII */ float HeIII; /* mass fraction taken by HeIII */ } mass_flux; +#endif #ifdef SWIFT_RT_DEBUG_CHECKS /* debugging data to store during entire run */ diff --git a/src/rt/GEAR/rt_thermochemistry.h b/src/rt/GEAR/rt_thermochemistry.h index a473f180393e601534c4225d8d18b72db62faf72..dc2155caca16b31f99215583d49977a7c548c1d0 100644 --- a/src/rt/GEAR/rt_thermochemistry.h +++ b/src/rt/GEAR/rt_thermochemistry.h @@ -182,7 +182,11 @@ INLINE static void rt_do_thermochemistry( } /* If we're good, update the particle data from grackle results */ +#ifdef GIZMO_MFV_SPH hydro_set_physical_internal_energy(p, xp, cosmo, u_new); +#else + hydro_set_physical_internal_energy_TESTING_SPH_RT(p, cosmo, u_new); +#endif /* Update mass fractions */ const gr_float one_over_rho = 1. / density; diff --git a/src/rt/GEAR/rt_thermochemistry_utils.h b/src/rt/GEAR/rt_thermochemistry_utils.h index 580813471307f99884e2c9a03fbdf9d6bf177935..5417594052048119691d7b5e9fa8ccc8f8ed5084 100644 --- a/src/rt/GEAR/rt_thermochemistry_utils.h +++ b/src/rt/GEAR/rt_thermochemistry_utils.h @@ -195,6 +195,8 @@ rt_tchem_get_gas_temperature(const struct part* restrict p, __attribute__((always_inline)) INLINE static void rt_tchem_set_particle_quantities_for_test(struct part* restrict p) { +#ifdef GIZMO_MFV_SPH + /* Set the values that you actually want. Needs to be in internal units.*/ /* 1 hydrogen_atom_mass / cm^3 / (1.98848e18 g/IMU * 3.0857e15cm/ILU^3) */ /* float density = 2.471e+04; */ @@ -218,6 +220,12 @@ rt_tchem_set_particle_quantities_for_test(struct part* restrict p) { /* This assumes zero velocity */ p->conserved.energy = p->conserved.mass * internal_energy; hydro_set_internal_energy(p, internal_energy); + +#else + + error("This isn't implemented for SPH yet."); + +#endif } /** diff --git a/src/rt/GEAR/rt_unphysical.h b/src/rt/GEAR/rt_unphysical.h index c256178b6cb2990cf97a989700a4b070b15c1ed2..ba44a9cc0764b9c1c72a71e8399ad549798ac2ea 100644 --- a/src/rt/GEAR/rt_unphysical.h +++ b/src/rt/GEAR/rt_unphysical.h @@ -185,7 +185,7 @@ rt_check_unphysical_mass_fractions(struct part* restrict p) { * inactive particles however remains zero until the particle is active * again. See issue #833. */ - if (p->conserved.mass <= 0.f || p->rho <= 0.f) { + if (hydro_get_mass(p) <= 0.f || p->rho <= 0.f) { /* Deal with unphysical situations and vacuum. */ p->rt_data.tchem.mass_fraction_HI = RT_GEAR_TINY_MASS_FRACTION; p->rt_data.tchem.mass_fraction_HII = RT_GEAR_TINY_MASS_FRACTION; diff --git a/src/runner_sinks.c b/src/runner_sinks.c index 1c87403ae1f9df3afcdbee9418e77dd8a5260a34..71d6d418fb37b2f4596dbe510d8b73450ccd13af 100644 --- a/src/runner_sinks.c +++ b/src/runner_sinks.c @@ -160,9 +160,9 @@ void runner_doself_sinks_swallow(struct runner *r, struct cell *c, int timer) { #endif if (r2 < ri2 || r2 < rj2) { - runner_iact_nonsym_sinks_sink_swallow(r2, dx, ri, rj, si, sj, - with_cosmology, cosmo, - e->gravity_properties); + runner_iact_nonsym_sinks_sink_swallow( + r2, dx, ri, rj, si, sj, with_cosmology, cosmo, + e->gravity_properties, e->sink_properties); } } /* loop over the sinks in ci. */ } /* loop over the sinks in ci. */ @@ -310,9 +310,9 @@ void runner_do_nonsym_pair_sinks_naive_swallow(struct runner *r, /* MPI note: This function is allowed to access the gpart's data. Hence, it must be performed only on the local node. (GEAR uses gpart's data, e.g. to compute potential) */ - runner_iact_nonsym_sinks_sink_swallow(r2, dx, ri, rj, si, sj, - with_cosmology, cosmo, - e->gravity_properties); + runner_iact_nonsym_sinks_sink_swallow( + r2, dx, ri, rj, si, sj, with_cosmology, cosmo, + e->gravity_properties, e->sink_properties); } } /* loop over the sinks in cj. */ } /* loop over the sinks in ci. */ @@ -1034,10 +1034,6 @@ void runner_do_prepare_part_sink_formation(struct runner *r, struct cell *c, sink_props); } /* End of gas neighbour loop */ - /* Shall we reset the values of the energies for the next timestep? No, it is - done in cell_drift.c and space_init.c, for active particles. The - potential is set in runner_others.c->runner_do_end_grav_force() */ - /* Check that we are not forming a sink in the accretion radius of another one. The new sink may be swallowed by the older one.) */ const int scount = c->sinks.count; diff --git a/src/hydro/Shadowswift/voronoi1d_cell.h b/src/shadowswift/voronoi.h similarity index 56% rename from src/hydro/Shadowswift/voronoi1d_cell.h rename to src/shadowswift/voronoi.h index 29fff097c4129b1b3ac81f6e1d4291c4efcbce90..bf8be746009ede022270b5e319387874e83eea7e 100644 --- a/src/hydro/Shadowswift/voronoi1d_cell.h +++ b/src/shadowswift/voronoi.h @@ -1,6 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). + * Copyright (c) 2024 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Yolan Uyttenhove (Yolan.Uyttenhove@UGent.be) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -17,32 +18,19 @@ * ******************************************************************************/ -#ifndef SWIFT_VORONOIXD_CELL_H -#define SWIFT_VORONOIXD_CELL_H +#ifndef SWIFTSIM_SHADOWSWIFT_VORONOI_H +#define SWIFTSIM_SHADOWSWIFT_VORONOI_H -/* 1D Voronoi cell */ -struct voronoi_cell { +/* Local includes */ +#include "inline.h" - /* The position of the generator of the cell. */ - double x; - - /* The position of the left neighbour of the cell. */ - double xL; - - /* The position of the right neighbour of the cell. */ - double xR; - - /* The particle ID of the left neighbour. */ - unsigned long long idL; - - /* The particle ID of the right neighbour. */ - unsigned long long idR; +struct voronoi { + int pair_count[27]; +}; - /* The "volume" of the 1D cell. */ - float volume; +struct voronoi_pair {}; - /* The centroid of the cell. */ - float centroid; -}; +__attribute__((always_inline)) INLINE static void voronoi_destroy( + struct voronoi* v) {} -#endif // SWIFT_VORONOIXD_CELL_H +#endif // SWIFTSIM_SHADOWSWIFT_VORONOI_H diff --git a/src/sink/Default/sink_iact.h b/src/sink/Default/sink_iact.h index f7547d6cd177fb61be74419f3ad4f69ce7592bae..72373da7292fbfd33cb26017a42786d35e79346c 100644 --- a/src/sink/Default/sink_iact.h +++ b/src/sink/Default/sink_iact.h @@ -31,6 +31,7 @@ * @param pj Second particle. * @param a Current scale factor. * @param H Current Hubble parameter. + * @param cut_off_radius Sink cut off radius. */ __attribute__((always_inline)) INLINE static void runner_iact_sink( const float r2, const float dx[3], const float hi, const float hj, @@ -49,6 +50,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_sink( * @param pj Second particle (not updated). * @param a Current scale factor. * @param H Current Hubble parameter. + * @param cut_off_radius Sink cut off radius. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_sink( const float r2, const float dx[3], const float hi, const float hj, @@ -64,15 +66,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_sink( * @param rj Comoving cut off radius of particle j. * @param si First sink particle. * @param sj Second sink particle. + * @param with_cosmology if we run with cosmology. + * @param cosmo The cosmological parameters and properties. + * @param grav_props The gravity scheme parameters and properties. + * @param sink_props the sink properties to use. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_sinks_sink_swallow(const float r2, const float dx[3], - const float ri, const float rj, - struct sink *restrict si, - struct sink *restrict sj, - const int with_cosmology, - const struct cosmology *cosmo, - const struct gravity_props *grav_props) {} +runner_iact_nonsym_sinks_sink_swallow( + const float r2, const float dx[3], const float ri, const float rj, + struct sink *restrict si, struct sink *restrict sj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct sink_props *sink_properties) {} /** * @brief Compute sink-gas swallow interaction (non-symmetric). @@ -83,6 +88,10 @@ runner_iact_nonsym_sinks_sink_swallow(const float r2, const float dx[3], * @param hj Comoving smoothing-length of particle j. * @param si First sink particle. * @param pj Second particle. + * @param with_cosmology if we run with cosmology. + * @param cosmo The cosmological parameters and properties. + * @param grav_props The gravity scheme parameters and properties. + * @param sink_props the sink properties to use. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_sinks_gas_swallow(const float r2, const float dx[3], diff --git a/src/sink/Default/sink_properties.h b/src/sink/Default/sink_properties.h index 98166add5a144335f5ac05fe16f7051390651029..08b458c77597daba7d94903d71af895ad925c55e 100644 --- a/src/sink/Default/sink_properties.h +++ b/src/sink/Default/sink_properties.h @@ -36,13 +36,15 @@ struct sink_props { * @param us The internal unit system. * @param params The parsed parameters. * @param cosmo The cosmological model. + * @param with_feedback Are we running with feedback? */ INLINE static void sink_props_init(struct sink_props *sp, struct feedback_props *fp, const struct phys_const *phys_const, const struct unit_system *us, struct swift_params *params, - const struct cosmology *cosmo) { + const struct cosmology *cosmo, + const int with_feedback) { sp->cut_off_radius = parser_get_param_float(params, "DefaultSink:cut_off_radius"); diff --git a/src/sink/GEAR/sink.h b/src/sink/GEAR/sink.h index 5146349efb03d2dbfd4db12ad31168b6ab42a80b..e5d54a9659569560167a40b06a087f23d26b5f66 100644 --- a/src/sink/GEAR/sink.h +++ b/src/sink/GEAR/sink.h @@ -21,6 +21,11 @@ #include <float.h> +/* Put pragma if gsl around here */ +#ifdef HAVE_LIBGSL +#include <gsl/gsl_cdf.h> +#endif + /* Local includes */ #include "active.h" #include "chemistry.h" @@ -30,6 +35,7 @@ #include "random.h" #include "sink_part.h" #include "sink_properties.h" +#include "star_formation.h" /** * @brief Computes the time-step of a given sink particle. @@ -69,22 +75,23 @@ INLINE static void sink_update_target_mass(struct sink* sink, /* If metal < threshold, then the sink generates first star particles. */ const int is_first_star = metal < threshold; const struct stellar_model* model; - double minimal_discrete_mass; + double minimal_discrete_mass_Msun; /* Take the correct values if your are a first star or not. */ if (!is_first_star) /* (metal >= threshold)*/ { model = &feedback_props->stellar_model; - minimal_discrete_mass = sink_props->minimal_discrete_mass; + minimal_discrete_mass_Msun = sink_props->minimal_discrete_mass_Msun; } else { model = &feedback_props->stellar_model_first_stars; - minimal_discrete_mass = sink_props->minimal_discrete_mass_first_stars; + minimal_discrete_mass_Msun = + sink_props->minimal_discrete_mass_first_stars_Msun; } const struct initial_mass_function* imf = &model->imf; if (random_number < imf->sink_Pc) { /* We are dealing with the continous part of the IMF. */ - sink->target_mass = imf->stellar_particle_mass; + sink->target_mass_Msun = imf->stellar_particle_mass_Msun; sink->target_type = star_population_continuous_IMF; } else { /* We are dealing with the discrete part of the IMF. */ @@ -92,9 +99,9 @@ INLINE static void sink_update_target_mass(struct sink* sink, sink->id, star_counter + 1, e->ti_current, random_number_sink_formation); double m = initial_mass_function_sample_power_law( - minimal_discrete_mass, imf->mass_max, imf->exp[imf->n_parts - 1], + minimal_discrete_mass_Msun, imf->mass_max, imf->exp[imf->n_parts - 1], random_number); - sink->target_mass = m; + sink->target_mass_Msun = m; sink->target_type = single_star; } } @@ -130,9 +137,12 @@ __attribute__((always_inline)) INLINE static void sink_first_init_sink( sink_mark_sink_as_not_swallowed(&sp->merger_data); /* Bug fix: Setup the target mass for sink formation after reading the - ICs. Otherwise sink->target_mass = 0.0 and a sink present in the IC spawn - a star of mass 0.0... */ + ICs. Otherwise sink->target_mass_Msun = 0.0 and a sink present in the IC + spawn a star of mass 0.0... */ sink_update_target_mass(sp, sink_props, e, 0); + + /* Initialize to the mass of the sink */ + sp->mass_tot_before_star_spawning = sp->mass; } /** @@ -178,6 +188,10 @@ __attribute__((always_inline)) INLINE static void sink_init_part( */ __attribute__((always_inline)) INLINE static void sink_init_sink( struct sink* sp) { + + /* Reset to the mass of the sink */ + sp->mass_tot_before_star_spawning = sp->mass; + #ifdef DEBUG_INTERACTIONS_SINKS for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_SINKS; ++i) sp->ids_ngbs_accretion[i] = -1; @@ -536,6 +550,9 @@ __attribute__((always_inline)) INLINE static void sink_swallow_part( sp->number_of_gas_swallows++; sp->number_of_direct_gas_swallows++; + /* Update the total mass before star spawning */ + sp->mass_tot_before_star_spawning = sp->mass; + #ifdef SWIFT_DEBUG_CHECKS const float dr = sqrt(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]); message( @@ -603,10 +620,11 @@ __attribute__((always_inline)) INLINE static void sink_swallow_sink( /* Add the stars spawned by the swallowed sink */ spi->n_stars += spj->n_stars; -#ifdef SWIFT_DEBUG_CHECKS + /* Update the total mass before star spawning */ + spi->mass_tot_before_star_spawning = spi->mass; + message("sink %lld swallow sink particle %lld. New mass: %e.", spi->id, spj->id, spi->mass); -#endif } /** @@ -631,21 +649,25 @@ INLINE static int sink_spawn_star(struct sink* sink, const struct engine* e, return 0; } - if (sink->mass > sink->target_mass * phys_const->const_solar_mass) + if (sink->mass > sink->target_mass_Msun * phys_const->const_solar_mass) return 1; else return 0; } /** - * @brief Separate the #spart and #part by randomly moving both of them. + * @brief Give the #spart a new position. + * + * In GEAR: Positions are set by randomly sampling coordinates in an homogeneous + * sphere centered on the #sink with radius the sink's r_cut. * * @param e The #engine. * @param si The #sink generating a star. - * @param sp The new #spart. + * @param sp The #spart generated. */ -INLINE static void sink_star_formation_separate_particles( - const struct engine* e, struct sink* si, struct spart* sp) { +INLINE static void sink_star_formation_give_new_position(const struct engine* e, + struct sink* si, + struct spart* sp) { #ifdef SWIFT_DEBUG_CHECKS if (si->x[0] != sp->x[0] || si->x[1] != sp->x[1] || si->x[2] != sp->x[2]) { error( @@ -655,46 +677,78 @@ INLINE static void sink_star_formation_separate_particles( } #endif - /* Move a bit the particle in order to avoid - division by 0. - */ - const float max_displacement = 0.1; - const double delta_x = - 2.f * random_unit_interval(si->id, e->ti_current, - (enum random_number_type)0) - - 1.f; - const double delta_y = - 2.f * random_unit_interval(si->id, e->ti_current, - (enum random_number_type)1) - - 1.f; - const double delta_z = - 2.f * random_unit_interval(si->id, e->ti_current, - (enum random_number_type)2) - - 1.f; - - sp->x[0] += delta_x * max_displacement * si->r_cut; - sp->x[1] += delta_y * max_displacement * si->r_cut; - sp->x[2] += delta_z * max_displacement * si->r_cut; - - /* Copy the position to the gpart */ + /* Put the star randomly within the accretion radius of the sink */ + const double phi = + 2 * M_PI * + random_unit_interval(sp->id, e->ti_current, (enum random_number_type)3); + const double r = si->r_cut * random_unit_interval(sp->id, e->ti_current, + (enum random_number_type)4); + const double cos_theta = + 1.0 - 2.0 * random_unit_interval(sp->id, e->ti_current, + (enum random_number_type)5); + const double sin_theta = sqrt(1.0 - cos_theta * cos_theta); + + double new_pos[3] = {r * sin_theta * cos(phi), r * sin_theta * sin(phi), + r * cos_theta}; + + /* Assign this new position to the star and its gpart */ + sp->x[0] += new_pos[0]; + sp->x[1] += new_pos[1]; + sp->x[2] += new_pos[2]; sp->gpart->x[0] = sp->x[0]; sp->gpart->x[1] = sp->x[1]; sp->gpart->x[2] = sp->x[2]; +} + +/** + * @brief Give a velocity to the #spart. + * + * In GEAR: Currently, a gaussian centered on 0 is used. The standard deviation + * is computed based on the local gravitational dynamics of the system. + * + * @param e The #engine. + * @param si The #sink generating a star. + * @param sp The new #spart. + * @param sink_props The sink properties to use. + */ +INLINE static void sink_star_formation_give_new_velocity( + const struct engine* e, struct sink* si, struct spart* sp, + const struct sink_props* sink_props) { - /* Do the sink particle. */ - const double mass_ratio = sp->mass / si->mass; - const double dx[3] = {mass_ratio * delta_x * max_displacement * si->r_cut, - mass_ratio * delta_y * max_displacement * si->r_cut, - mass_ratio * delta_z * max_displacement * si->r_cut}; +#ifdef HAVE_LIBGSL + /* Those intermediate variables are the values that will be given to the star + and subtracted from the sink. */ + double v_given[3] = {0.0, 0.0, 0.0}; + const double G_newton = e->physical_constants->const_newton_G; + const double sigma_2 = + G_newton * si->mass_tot_before_star_spawning / si->r_cut; + const double sigma = sink_props->star_spawning_sigma_factor * sqrt(sigma_2); - si->x[0] -= dx[0]; - si->x[1] -= dx[1]; - si->x[2] -= dx[2]; + for (int i = 0; i < 3; ++i) { - /* Copy the position to the gpart */ - si->gpart->x[0] = si->x[0]; - si->gpart->x[1] = si->x[1]; - si->gpart->x[2] = si->x[2]; + /* Draw a random value in unform interval (0, 1] */ + const double random_number = random_unit_interval_part_ID_and_index( + sp->id, i, e->ti_current, (enum random_number_type)1); + + /* Sample a gaussian with mu=0 and sigma=sigma */ + double v_i_random = gsl_cdf_gaussian_Pinv(random_number, sigma); + v_given[i] = v_i_random; + } + + /* Update the star velocity. Do not forget to update the gpart velocity */ + sp->v[0] = si->v[0] + v_given[0]; + sp->v[1] = si->v[1] + v_given[1]; + sp->v[2] = si->v[2] + v_given[2]; + sp->gpart->v_full[0] = sp->v[0]; + sp->gpart->v_full[1] = sp->v[1]; + sp->gpart->v_full[2] = sp->v[2]; + message( + "New star velocity: v = (%lf %lf %lf). Sink velocity: v = (%lf %lf %lf). " + "Sigma = %lf", + sp->v[0], sp->v[1], sp->v[2], si->v[0], si->v[1], si->v[2], sigma); +#else + error("Code not compiled with GSL. Can't compute Star new velocity."); +#endif } /** @@ -718,36 +772,45 @@ INLINE static void sink_copy_properties_to_star( const int with_cosmology, const struct phys_const* phys_const, const struct unit_system* restrict us) { - sink_star_formation_separate_particles(e, sink, sp); + /* Give the stars a new position */ + sink_star_formation_give_new_position(e, sink, sp); - /* set the mass */ - sp->mass = sink->target_mass * phys_const->const_solar_mass; + /* Set the mass (do not forget the sink's gpart friend!) */ + sp->mass = sink->target_mass_Msun * phys_const->const_solar_mass; sp->gpart->mass = sp->mass; - /* set feedback type */ - sp->feedback_data.star_type = (enum star_feedback_type)sink->target_type; + /* Give a new velocity to the stars */ + sink_star_formation_give_new_velocity(e, sink, sp, sink_props); + + /* Sph smoothing length */ + sp->h = sink->r_cut; + + /* Feedback related initialisation */ + /* ------------------------------- */ /* Initialize the feedback */ feedback_init_after_star_formation(sp, e->feedback_props, sink->target_type); - /* sph smoothing */ - sp->h = sink->r_cut; + /* Star formation related initalisation */ + /* ------------------------------------ */ /* Note: The sink module need to be compiled with GEAR SF as we store data in the SF struct. However, we do not need to run with --star-formation */ - /* Store either the birth_scale_factor or birth_time depending */ - if (with_cosmology) { - sp->birth_scale_factor = cosmo->a; - } else { - sp->birth_time = e->time; - } + /* Mass at birth */ + star_formation_set_spart_birth_mass(sp, sp->mass); - /* Copy the chemistry properties */ - chemistry_copy_sink_properties_to_star(sink, sp); + /* Store either the birth_scale_factor or birth_time */ + star_formation_set_spart_birth_time_or_scale_factor(sp, e->time, cosmo->a, + with_cosmology); /* Copy the progenitor id */ - sp->sf_data.progenitor_id = sink->id; + star_formation_set_spart_progenitor_id(sp, sink->id); + + /* Copy the chemistry properties */ + /* ----------------------------- */ + + chemistry_copy_sink_properties_to_star(sink, sp); } /** @@ -768,8 +831,8 @@ INLINE static void sink_update_sink_properties_before_star_formation( /* Has the sink accumulated enough metallicity so that the target mass should be updated before spawning stars? - Between the last update of the target_mass, the sink may have accreted gas - with metallicities that that are higher than those of population III + Between the last update of the target_mass_Msun, the sink may have accreted + gas with metallicities that that are higher than those of population III stars. However, the target mass was set with the pop III IMF. */ @@ -785,14 +848,14 @@ INLINE static void sink_update_sink_properties_before_star_formation( /* If the sink has not changed its IMF yet (has_IMF_changed_from_popIII_to_popII = 0) - but is eligible to (sink metal > threshold), get a target_mass of the pop - II stars. */ + but is eligible to (sink metal > threshold), get a target_mass_Msun of the + pop II stars. */ if (!(sink->has_IMF_changed_from_popIII_to_popII) && !is_first_star) { sink_update_target_mass(sink, sink_props, e, 0); /* Flag the sink to have made the transition of IMF. This ensures that next - time we do not update the target_mass because metal > threshold (otherwise - we would update it without needing to) */ + time we do not update the target_mass_Msun because metal > threshold + (otherwise we would update it without needing to) */ sink->has_IMF_changed_from_popIII_to_popII = 1; message("IMF transition : Sink %lld will now spawn Pop II stars.", sink->id); @@ -803,7 +866,7 @@ INLINE static void sink_update_sink_properties_before_star_formation( * @brief Update the #sink particle properties right after spawning a star. * * In GEAR: Important properties that are updated are the sink mass and the - * sink->target_mass to draw the next star mass. + * sink->target_mass_Msun to draw the next star mass. * * @param sink The #sink particle that spawed stars. * @param sp The #spart particle spawned. @@ -821,7 +884,8 @@ INLINE static void sink_update_sink_properties_during_star_formation( sink->n_stars++; /* Update the mass */ - sink->mass = sink->mass - sink->target_mass * phys_const->const_solar_mass; + sink->mass = + sink->mass - sink->target_mass_Msun * phys_const->const_solar_mass; /* Bug fix: Do not forget to update the sink gpart's mass. */ sink->gpart->mass = sink->mass; diff --git a/src/sink/GEAR/sink_iact.h b/src/sink/GEAR/sink_iact.h index f51183fa9c5f1638df75911246a8e959b380c623..60e09a4a120b40001b65b72f1507572489301ac7 100644 --- a/src/sink/GEAR/sink_iact.h +++ b/src/sink/GEAR/sink_iact.h @@ -22,12 +22,16 @@ /* Local includes */ #include "gravity.h" #include "gravity_iact.h" +#include "sink.h" #include "sink_properties.h" /** * @brief do sink computation after the runner_iact_density (symmetric * version) * + * In GEAR: This function deactivates the sink formation ability of #part not + * at a potential minimum. + * * Note: This functions breaks MPI. * * @param r2 Comoving square distance between the two particles. @@ -38,6 +42,7 @@ * @param pj Second particle. * @param a Current scale factor. * @param H Current Hubble parameter. + * @param cut_off_radius Sink cut off radius. */ __attribute__((always_inline)) INLINE static void runner_iact_sink( const float r2, const float dx[3], const float hi, const float hj, @@ -89,6 +94,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_sink( * @brief do sink computation after the runner_iact_density (non symmetric * version) * + * In GEAR: This function deactivates the sink formation ability of #part not + * at a potential minimum. + * * @param r2 Comoving square distance between the two particles. * @param dx Comoving vector separating both particles (pi - pj). * @param hi Comoving smoothing-length of particle i. @@ -97,6 +105,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_sink( * @param pj Second particle (not updated). * @param a Current scale factor. * @param H Current Hubble parameter. + * @param cut_off_radius Sink cut off radius. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_sink( const float r2, const float dx[3], const float hi, const float hj, @@ -139,88 +148,113 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_sink( * @param rj Comoving cut off radius of particle j. * @param si First sink particle. * @param sj Second sink particle. + * @param with_cosmology if we run with cosmology. + * @param cosmo The cosmological parameters and properties. + * @param grav_props The gravity scheme parameters and properties. + * @param sink_props the sink properties to use. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_sinks_sink_swallow(const float r2, const float dx[3], - const float ri, const float rj, - struct sink *restrict si, - struct sink *restrict sj, - const int with_cosmology, - const struct cosmology *cosmo, - const struct gravity_props *grav_props) { - - /* Relative velocity between th sinks */ - const float dv[3] = {sj->v[0] - si->v[0], sj->v[1] - si->v[1], - sj->v[2] - si->v[2]}; - - const float a = cosmo->a; - const float H = cosmo->H; - const float a2H = a * a * H; - - /* Calculate the velocity with the Hubble flow */ - const float v_plus_H_flow[3] = {a2H * dx[0] + dv[0], a2H * dx[1] + dv[1], - a2H * dx[2] + dv[2]}; - - /* Binding energy check */ - /* Compute the physical relative velocity between the particles */ - const float dv_physical[3] = {v_plus_H_flow[0] * cosmo->a_inv, - v_plus_H_flow[1] * cosmo->a_inv, - v_plus_H_flow[2] * cosmo->a_inv}; - - const float dv_physical_squared = dv_physical[0] * dv_physical[0] + - dv_physical[1] * dv_physical[1] + - dv_physical[2] * dv_physical[2]; - - /* Kinetic energy of the gas */ - const float E_kin_rel = 0.5f * dv_physical_squared; - - /* Compute the Newtonian or truncated potential the sink exherts onto the - gas particle */ - const float eps = 1e-4; //gravity_get_softening(si->gpart, grav_props); - const float eps2 = eps * eps; - const float eps_inv = 1.f / eps; - const float eps_inv3 = eps_inv * eps_inv * eps_inv; - const float si_mass = si->mass; - const float sj_mass = sj->mass; - - float dummy, pot_ij, pot_ji; - runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, si_mass, &dummy, - &pot_ij); - runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, sj_mass, &dummy, - &pot_ji); - - /* Compute the physical potential energies : - E_pot_phys = G*pot_grav*a^(-1) + c(z). */ - /* The normalization is c(z) = 0 for all redshift z. */ - const float E_pot_ij = grav_props->G_Newton * pot_ij * cosmo->a_inv; - const float E_pot_ji = grav_props->G_Newton * pot_ji * cosmo->a_inv; - - /* Mechanical energy of the pair i-j and j-i */ - const float E_mec_si = E_kin_rel + E_pot_ij; - const float E_mec_sj = E_kin_rel + E_pot_ji; - - /* Now, check if one is bound to the other */ - if ((E_mec_si > 0) && (E_mec_sj > 0)) { - return; - } +runner_iact_nonsym_sinks_sink_swallow( + const float r2, const float dx[3], const float ri, const float rj, + struct sink *restrict si, struct sink *restrict sj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct sink_props *sink_properties) { - /* The sink with the smaller mass will be merged onto the one with the - * larger mass. - * To avoid rounding issues, we additionally check for IDs if the sink - * have the exact same mass. */ - if ((sj->mass < si->mass) || (sj->mass == si->mass && sj->id < si->id)) { - /* This particle is swallowed by the sink with the largest mass of all the - * candidates wanting to swallow it (we use IDs to break ties)*/ - if ((sj->merger_data.swallow_mass < si->mass) || - (sj->merger_data.swallow_mass == si->mass && - sj->merger_data.swallow_id < si->id)) { - sj->merger_data.swallow_id = si->id; - sj->merger_data.swallow_mass = si->mass; + const float r = sqrtf(r2); + const float f_acc_r_acc_i = sink_properties->f_acc * ri; + + /* If the sink j falls within f_acc*r_acc of sink i, then the + lightest is accreted on the most massive without further check. + Note that this is a non-symmetric interaction. So, we do not need to check + for the f_acc*r_acc_j case here. */ + if (r < f_acc_r_acc_i) { + /* The sink with the smaller mass will be merged onto the one with the + * larger mass. + * To avoid rounding issues, we additionally check for IDs if the sink + * have the exact same mass. */ + if ((sj->mass < si->mass) || (sj->mass == si->mass && sj->id < si->id)) { + /* This particle is swallowed by the sink with the largest mass of all the + * candidates wanting to swallow it (we use IDs to break ties)*/ + if ((sj->merger_data.swallow_mass < si->mass) || + (sj->merger_data.swallow_mass == si->mass && + sj->merger_data.swallow_id < si->id)) { + sj->merger_data.swallow_id = si->id; + sj->merger_data.swallow_mass = si->mass; + } } - } + } else { + + /* Relative velocity between the sinks */ + const float dv[3] = {sj->v[0] - si->v[0], sj->v[1] - si->v[1], + sj->v[2] - si->v[2]}; + + const float a = cosmo->a; + const float H = cosmo->H; + const float a2H = a * a * H; + + /* Calculate the velocity with the Hubble flow */ + const float v_plus_H_flow[3] = {a2H * dx[0] + dv[0], a2H * dx[1] + dv[1], + a2H * dx[2] + dv[2]}; + + /* Binding energy check */ + /* Compute the physical relative velocity between the particles */ + const float dv_physical[3] = {v_plus_H_flow[0] * cosmo->a_inv, + v_plus_H_flow[1] * cosmo->a_inv, + v_plus_H_flow[2] * cosmo->a_inv}; - message("Sink %lld swallows sink %lld", si->id, sj->id); + const float dv_physical_squared = dv_physical[0] * dv_physical[0] + + dv_physical[1] * dv_physical[1] + + dv_physical[2] * dv_physical[2]; + + /* Kinetic energy per unit mass of the gas */ + const float E_kin_rel = 0.5f * dv_physical_squared; + + /* Compute the Newtonian or softened potential the sink exherts onto the + gas particle */ + const float eps = gravity_get_softening(si->gpart, grav_props); + const float eps2 = eps * eps; + const float eps_inv = 1.f / eps; + const float eps_inv3 = eps_inv * eps_inv * eps_inv; + const float si_mass = si->mass; + const float sj_mass = sj->mass; + float dummy, pot_ij, pot_ji; + runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, si_mass, &dummy, + &pot_ij); + runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, sj_mass, &dummy, + &pot_ji); + + /* Compute the physical potential energies per unit mass : + E_pot_phys = G*pot_grav*a^(-1) + c(a). + The normalization is c(a) = 0. */ + const float E_pot_ij = grav_props->G_Newton * pot_ij * cosmo->a_inv; + const float E_pot_ji = grav_props->G_Newton * pot_ji * cosmo->a_inv; + + /* Mechanical energy per unit mass of the pair i-j and j-i */ + const float E_mec_si = E_kin_rel + E_pot_ij; + const float E_mec_sj = E_kin_rel + E_pot_ji; + + /* Now, check if one is bound to the other */ + if ((E_mec_si > 0) || (E_mec_sj > 0)) { + return; + } + + /* The sink with the smaller mass will be merged onto the one with the + * larger mass. + * To avoid rounding issues, we additionally check for IDs if the sink + * have the exact same mass. */ + if ((sj->mass < si->mass) || (sj->mass == si->mass && sj->id < si->id)) { + /* This particle is swallowed by the sink with the largest mass of all the + * candidates wanting to swallow it (we use IDs to break ties)*/ + if ((sj->merger_data.swallow_mass < si->mass) || + (sj->merger_data.swallow_mass == si->mass && + sj->merger_data.swallow_id < si->id)) { + sj->merger_data.swallow_id = si->id; + sj->merger_data.swallow_mass = si->mass; + } + } + } #ifdef DEBUG_INTERACTIONS_SINKS /* Update ngb counters */ if (si->num_ngb_formation < MAX_NUM_OF_NEIGHBOURS_SINKS) @@ -245,6 +279,10 @@ runner_iact_nonsym_sinks_sink_swallow(const float r2, const float dx[3], * @param hj Comoving smoothing-length of particle j. * @param si First sink particle. * @param pj Second particle. + * @param with_cosmology if we run with cosmology. + * @param cosmo The cosmological parameters and properties. + * @param grav_props The gravity scheme parameters and properties. + * @param sink_props the sink properties to use. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_sinks_gas_swallow(const float r2, const float dx[3], @@ -271,7 +309,7 @@ runner_iact_nonsym_sinks_gas_swallow(const float r2, const float dx[3], /* f_acc*r_acc <= r <= r_acc, we perform other checks */ } else if ((r >= f_acc_r_acc) && (r < ri)) { - /* Relative velocity between th sinks */ + /* Relative velocity between the sinks */ const float dv[3] = {pj->v[0] - si->v[0], pj->v[1] - si->v[1], pj->v[2] - si->v[2]}; @@ -297,7 +335,7 @@ runner_iact_nonsym_sinks_gas_swallow(const float r2, const float dx[3], dx[2] * cosmo->a}; const float r_physical = r * cosmo->a; - /* Momentum check */ + /* Momentum check------------------------------------------------------- */ /* Relative momentum of the gas */ const float specific_angular_momentum_gas[3] = { dx_physical[1] * dv_physical[2] - dx_physical[2] * dv_physical[1], @@ -322,11 +360,11 @@ runner_iact_nonsym_sinks_gas_swallow(const float r2, const float dx[3], return; } - /* Energy check */ - /* Kinetic energy of the gas */ + /* Energy check--------------------------------------------------------- */ + /* Kinetic energy per unit mass of the gas */ float E_kin_relative_gas = 0.5f * dv_physical_squared; - /* Compute the Newtonian or truncated potential the sink exherts onto the + /* Compute the Newtonian or softened potential the sink exherts onto the gas particle */ const float eps = 1e-4 ; //gravity_get_softening(si->gpart, grav_props); const float eps2 = eps * eps; @@ -337,20 +375,23 @@ runner_iact_nonsym_sinks_gas_swallow(const float r2, const float dx[3], runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, sink_mass, &dummy, &pot_ij); - /* Compute the potential energy that the sink exerts in the gas (do not - forget to convert to physical quantity)*/ - /* Compute the potential energy : - E_pot_phys = G*pot_grav*a^(-1) + c(z). */ - /* The normalization is c(z) = 0 for all redshift z. */ - float E_pot_gas = grav_props->G_Newton * pot_ij * cosmo->a_inv; + /* Compute the physical potential energy per unit mass that the sink + exerts in the gas : + E_pot_phys = G*pot_grav*a^(-1) + c(a). + The normalization is c(a) = 0. */ + const float E_pot_gas = grav_props->G_Newton * pot_ij * cosmo->a_inv; + + /* Update: Add thermal energy per unit mass to avoid the sink to swallow + hot gas regions */ + const float E_therm = hydro_get_drifted_physical_internal_energy(pj, cosmo); - /* Mechanical energy of the pair sink-gas */ - float E_mec_sink_part = E_kin_relative_gas + E_pot_gas; + /* Energy per unit mass of the pair sink-gas */ + const float E_mec_sink_part = E_kin_relative_gas + E_pot_gas + E_therm; /* To be accreted, the gas must be gravitationally bound to the sink. */ if (E_mec_sink_part >= 0) return; - /* Most bound pair check */ + /* Most bound pair check------------------------------------------------ */ /* The pair gas-sink must be the most bound among all sinks */ if (E_mec_sink_part >= pj->sink_data.E_mec_bound) { return; diff --git a/src/sink/GEAR/sink_io.h b/src/sink/GEAR/sink_io.h index 789cff4c680dab5419ed15b1863719d3eb74a5d1..c8fb01a30ea27ea2581e4b6778e10ecf3bebe1e3 100644 --- a/src/sink/GEAR/sink_io.h +++ b/src/sink/GEAR/sink_io.h @@ -97,9 +97,9 @@ INLINE static void convert_sink_vel(const struct engine* e, INLINE static void convert_sink_target_mass(const struct engine* e, const struct sink* sink, float* ret) { - /* Recall that the target_mass is in M_sun in the code. We nee to convert it - to internal units for consistency in the output. */ - ret[0] = sink->target_mass * e->physical_constants->const_solar_mass; + /* Recall that the target_mass_Msun is in M_sun in the code. We nee to convert + it to internal units for consistency in the output. */ + ret[0] = sink->target_mass_Msun * e->physical_constants->const_solar_mass; } INLINE static void convert_sink_swallowed_angular_momentum( diff --git a/src/sink/GEAR/sink_part.h b/src/sink/GEAR/sink_part.h index a9765672aceb7df8bd880da5ad0901cc45b8dd91..b6c37c8a63b5089755266e6124ca918671a8b0cc 100644 --- a/src/sink/GEAR/sink_part.h +++ b/src/sink/GEAR/sink_part.h @@ -51,16 +51,19 @@ struct sink { /*! Sink particle mass */ float mass; - /*! Sink target mass */ - float target_mass; + /*! Sink target mass. In Msun. */ + float target_mass_Msun; + + /*! Mass of the sink before starting the star spawning loop */ + float mass_tot_before_star_spawning; /*! Sink target stellar type */ - enum star_feedback_type target_type; + enum stellar_type target_type; /*! Particle time bin */ timebin_t time_bin; - /*! number of stars contained in the sink */ + /*! Number of stars spawned by this sink */ int n_stars; /*! Total (physical) angular momentum accumulated by swallowing particles */ diff --git a/src/sink/GEAR/sink_properties.h b/src/sink/GEAR/sink_properties.h index 2667fa2b6ceafec03a1de8dd55e9f3f0f263ded0..e7e179386f8db58bab2c6e92041f09688489847e 100644 --- a/src/sink/GEAR/sink_properties.h +++ b/src/sink/GEAR/sink_properties.h @@ -41,24 +41,20 @@ struct sink_props { /*! Minimal gas density for forming a star. */ float density_threshold; - /*! Size of the calibration sample used to determine the probabilities - * to form stellar particles with mass stellar_particle_mass */ - int size_of_calibration_sample; - /*! Mass of the stellar particle representing the low mass stars - * (continuous IMF sampling). */ - float stellar_particle_mass; + * (continuous IMF sampling). In M_sun. */ + float stellar_particle_mass_Msun; - /*! Minimal mass of stars represented by discrete particles */ - float minimal_discrete_mass; + /*! Minimal mass of stars represented by discrete particles. In M_sun. */ + float minimal_discrete_mass_Msun; /*! Mass of the stellar particle representing the low mass stars - * (continuous IMF sampling). First stars */ - float stellar_particle_mass_first_stars; + * (continuous IMF sampling). In M_sun. First stars */ + float stellar_particle_mass_first_stars_Msun; - /*! Minimal mass of stars represented by discrete particles. + /*! Minimal mass of stars represented by discrete particles. In M_sun. * First stars. */ - float minimal_discrete_mass_first_stars; + float minimal_discrete_mass_first_stars_Msun; /*! Sink formation criteria selecter : some criteria can be left out. */ char sink_formation_contracting_gas_criterion; @@ -73,6 +69,10 @@ struct sink_props { /* Disable star formation? Default: 0 (i.e. enable star formation) */ uint8_t disable_star_formation; + + /* Factor to rescale the velocity dispersion of the stars when they are + spawned */ + double star_spawning_sigma_factor; }; /** @@ -93,62 +93,49 @@ INLINE static void sink_props_init_probabilities( float mass_min = imf->mass_min; float mass_max = imf->mass_max; - float minimal_discrete_mass; - float stellar_particle_mass; - + /* Treat separately the cases of first star or not. */ if (!first_stars) { - minimal_discrete_mass = sp->minimal_discrete_mass; - stellar_particle_mass = - sp->stellar_particle_mass / phys_const->const_solar_mass; + imf->minimal_discrete_mass_Msun = sp->minimal_discrete_mass_Msun; + imf->stellar_particle_mass_Msun = sp->stellar_particle_mass_Msun; } else { - minimal_discrete_mass = sp->minimal_discrete_mass_first_stars; - stellar_particle_mass = - sp->stellar_particle_mass_first_stars / phys_const->const_solar_mass; + imf->minimal_discrete_mass_Msun = + sp->minimal_discrete_mass_first_stars_Msun; + imf->stellar_particle_mass_Msun = + sp->stellar_particle_mass_first_stars_Msun; } - /* sanity check */ - if (minimal_discrete_mass < imf->mass_limits[imf->n_parts - 1]) + /* Sanity check */ + if (imf->minimal_discrete_mass_Msun < imf->mass_limits[imf->n_parts - 1]) error( "minimal_discrete_mass (=%8.3f) cannot be smaller than the mass limit " "(=%8.3f) of the last IMF segment,", - minimal_discrete_mass, imf->mass_limits[imf->n_parts - 1]); + imf->minimal_discrete_mass_Msun, imf->mass_limits[imf->n_parts - 1]); - /* Compute the IMF mass below the minimal IMF discrete mass (continuous part) - */ + /* Compute the IMF mass (in solar mass) below the minimal IMF discrete mass + (continuous part). */ double Mtot, Md, Mc; - Mc = initial_mass_function_get_imf_mass_fraction(imf, mass_min, - minimal_discrete_mass); - - if (Mc > 0) { - Mtot = stellar_particle_mass / Mc; - Md = Mtot - stellar_particle_mass; - Mc = stellar_particle_mass; - } else { - Mtot = stellar_particle_mass; - Md = Mtot; - Mc = 0; - } + initial_mass_function_compute_Mc_Md_Mtot(imf, &Mc, &Md, &Mtot); /* Compute the number of stars in the continuous part of the IMF */ double Nc = initial_mass_function_get_imf_number_fraction( - imf, mass_min, minimal_discrete_mass) * + imf, mass_min, imf->minimal_discrete_mass_Msun) * Mtot; /* Compute the number of stars in the discrete part of the IMF */ double Nd = initial_mass_function_get_imf_number_fraction( - imf, minimal_discrete_mass, mass_max) * + imf, imf->minimal_discrete_mass_Msun, mass_max) * Mtot; - message("Mass of the continuous part : %g", Mc); - message("Mass of the discrete part : %g", Md); - message("Total IMF mass : %g", Mtot); + message("Mass of the continuous part (in M_sun) : %g", Mc); + message("Mass of the discrete part (in M_sun) : %g", Md); + message("Total IMF mass (in M_sun) : %g", Mtot); message("Number of stars in the continuous part : %g", Nc); message("Number of stars in the discrete part : %g", Nd); /* if no continous part, return */ if (Mc == 0) { imf->sink_Pc = 0; - imf->stellar_particle_mass = 0; + imf->stellar_particle_mass_Msun = 0; message("probability of the continuous part : %g", 0.); message("probability of the discrete part : %g", 1.); return; @@ -158,7 +145,6 @@ INLINE static void sink_props_init_probabilities( double Pc = 1 / (1 + Nd); double Pd = 1 - Pc; imf->sink_Pc = Pc; - imf->stellar_particle_mass = Mc; message("probability of the continuous part : %g", Pc); message("probability of the discrete part : %g", Pd); @@ -172,17 +158,25 @@ INLINE static void sink_props_init_probabilities( * @param us The internal unit system. * @param params The parsed parameters. * @param cosmo The cosmological model. + * @param with_feedback Are we running with feedback? */ INLINE static void sink_props_init(struct sink_props *sp, struct feedback_props *fp, const struct phys_const *phys_const, const struct unit_system *us, struct swift_params *params, - const struct cosmology *cosmo) { + const struct cosmology *cosmo, + const int with_feedback) { + + /* If we do not run with feedback, abort and print an error */ + if (!with_feedback) + error( + "ERROR: Running with sink but without feedback. GEAR sink model needs " + "to be run with --sink and --feedback"); /* Default values */ const float default_f_acc = 0.8; - + const float default_star_spawning_sigma_factor = 0.2; const char default_disable_sink_formation = 0; /* Sink formation is activated */ const char default_disable_star_formation = 0; /* SF is activated */ @@ -190,6 +184,7 @@ INLINE static void sink_props_init(struct sink_props *sp, /* By default all current implemented criteria are active */ const uint8_t default_sink_formation_criterion_all = 1; + /* Read the parameters from the parameter file */ sp->cut_off_radius = parser_get_param_float(params, "GEARSink:cut_off_radius"); @@ -208,22 +203,23 @@ INLINE static void sink_props_init(struct sink_props *sp, parser_get_param_float(params, "GEARSink:maximal_temperature"); sp->density_threshold = - parser_get_param_float(params, "GEARSink:density_threshold"); + parser_get_param_float(params, "GEARSink:density_threshold_g_per_cm3"); - sp->size_of_calibration_sample = - parser_get_param_int(params, "GEARSink:size_of_calibration_sample"); + sp->stellar_particle_mass_Msun = + parser_get_param_float(params, "GEARSink:stellar_particle_mass_Msun"); - sp->stellar_particle_mass = - parser_get_param_float(params, "GEARSink:stellar_particle_mass"); + sp->minimal_discrete_mass_Msun = + parser_get_param_float(params, "GEARSink:minimal_discrete_mass_Msun"); - sp->minimal_discrete_mass = - parser_get_param_float(params, "GEARSink:minimal_discrete_mass"); + sp->stellar_particle_mass_first_stars_Msun = parser_get_param_float( + params, "GEARSink:stellar_particle_mass_first_stars_Msun"); - sp->stellar_particle_mass_first_stars = parser_get_param_float( - params, "GEARSink:stellar_particle_mass_first_stars"); + sp->minimal_discrete_mass_first_stars_Msun = parser_get_param_float( + params, "GEARSink:minimal_discrete_mass_first_stars_Msun"); - sp->minimal_discrete_mass_first_stars = parser_get_param_float( - params, "GEARSink:minimal_discrete_mass_first_stars"); + sp->star_spawning_sigma_factor = + parser_get_opt_param_float(params, "GEARSink:star_spawning_sigma_factor", + default_star_spawning_sigma_factor); /* Sink formation criterion parameters (all active by default) */ sp->sink_formation_contracting_gas_criterion = parser_get_opt_param_int( @@ -262,9 +258,6 @@ INLINE static void sink_props_init(struct sink_props *sp, sp->density_threshold /= units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); - sp->stellar_particle_mass *= phys_const->const_solar_mass; - sp->stellar_particle_mass_first_stars *= phys_const->const_solar_mass; - /* here, we need to differenciate between the stellar models */ struct initial_mass_function *imf; struct stellar_model *sm; @@ -282,30 +275,33 @@ INLINE static void sink_props_init(struct sink_props *sp, sink_props_init_probabilities(sp, imf, phys_const, 1); } - message("maximal_temperature = %g", sp->maximal_temperature); - message("density_threshold = %g", sp->density_threshold); - message("size_of_calibration_sample = %d", - sp->size_of_calibration_sample); + message("maximal_temperature = %g", + sp->maximal_temperature); + message("density_threshold = %g", + sp->density_threshold); - message("stellar_particle_mass = %g", sp->stellar_particle_mass); - message("minimal_discrete_mass = %g", sp->minimal_discrete_mass); + message("stellar_particle_mass (in M_sun) = %g", + sp->stellar_particle_mass_Msun); + message("minimal_discrete_mass (in M_sun) = %g", + sp->minimal_discrete_mass_Msun); - message("stellar_particle_mass_first_stars = %g", - sp->stellar_particle_mass_first_stars); - message("minimal_discrete_mass_first_stars = %g", - sp->minimal_discrete_mass_first_stars); + message("stellar_particle_mass_first_stars (in M_sun) = %g", + sp->stellar_particle_mass_first_stars_Msun); + message("minimal_discrete_mass_first_stars (in M_sun) = %g", + sp->minimal_discrete_mass_first_stars_Msun); /* Print information about the functionalities */ - message("disable_sink_formation = %d", sp->disable_sink_formation); - message("sink_formation_contracting_gas_criterion = %d", + message("disable_sink_formation = %d", + sp->disable_sink_formation); + message("sink_formation_contracting_gas_criterion = %d", sp->sink_formation_contracting_gas_criterion); - message("sink_formation_smoothing_length_criterion = %d", + message("sink_formation_smoothing_length_criterion = %d", sp->sink_formation_smoothing_length_criterion); - message("sink_formation_jeans_instability_criterion = %d", + message("sink_formation_jeans_instability_criterion = %d", sp->sink_formation_jeans_instability_criterion); - message("sink_formation_bound_state_criterion = %d", + message("sink_formation_bound_state_criterion = %d", sp->sink_formation_bound_state_criterion); - message("sink_formation_overlapping_sink_criterion = %d", + message("sink_formation_overlapping_sink_criterion = %d", sp->sink_formation_overlapping_sink_criterion); } diff --git a/src/space.c b/src/space.c index c2aaacd43b91000ffaefeb586205dc126b1252df..77a1a28f58523aef800df2afa8eab416c830d1e6 100644 --- a/src/space.c +++ b/src/space.c @@ -73,6 +73,7 @@ int space_subsize_pair_grav = space_subsize_pair_grav_default; int space_subsize_self_grav = space_subsize_self_grav_default; int space_subdepth_diff_grav = space_subdepth_diff_grav_default; int space_maxsize = space_maxsize_default; +int space_grid_split_threshold = space_grid_split_threshold_default; /*! Number of extra #part we allocate memory for per top-level cell */ int space_extra_parts = space_extra_parts_default; @@ -1252,6 +1253,8 @@ void space_init(struct space *s, struct swift_params *params, space_subsize_self_grav_default); space_splitsize = parser_get_opt_param_int( params, "Scheduler:cell_split_size", space_splitsize_default); + space_grid_split_threshold = parser_get_opt_param_int( + params, "Scheduler:grid_split_threshold", space_grid_split_threshold); space_subdepth_diff_grav = parser_get_opt_param_int(params, "Scheduler:cell_subdepth_diff_grav", space_subdepth_diff_grav_default); @@ -2525,6 +2528,9 @@ void space_struct_dump(struct space *s, FILE *stream) { "space_splitsize", "space_splitsize"); restart_write_blocks(&space_maxsize, sizeof(int), 1, stream, "space_maxsize", "space_maxsize"); + restart_write_blocks(&space_grid_split_threshold, sizeof(int), 1, stream, + "space_grid_split_threshold", + "space_grid_split_threshold"); restart_write_blocks(&space_subsize_pair_hydro, sizeof(int), 1, stream, "space_subsize_pair_hydro", "space_subsize_pair_hydro"); restart_write_blocks(&space_subsize_self_hydro, sizeof(int), 1, stream, @@ -2610,6 +2616,8 @@ void space_struct_restore(struct space *s, FILE *stream) { "space_splitsize"); restart_read_blocks(&space_maxsize, sizeof(int), 1, stream, NULL, "space_maxsize"); + restart_read_blocks(&space_grid_split_threshold, sizeof(int), 1, stream, NULL, + "space_grid_split_threshold"); restart_read_blocks(&space_subsize_pair_hydro, sizeof(int), 1, stream, NULL, "space_subsize_pair_hydro"); restart_read_blocks(&space_subsize_self_hydro, sizeof(int), 1, stream, NULL, diff --git a/src/space.h b/src/space.h index 08feb20fd9cd1cdeba103734b620bd5f55bbdace..58af1b0508b668562a86eecc3a3fa2b40592e635 100644 --- a/src/space.h +++ b/src/space.h @@ -48,6 +48,7 @@ struct hydro_props; #define space_cellallocchunk 1000 #define space_splitsize_default 400 #define space_maxsize_default 8000000 +#define space_grid_split_threshold_default 400 #define space_extra_parts_default 0 #define space_extra_gparts_default 0 #define space_extra_sparts_default 100 @@ -72,6 +73,7 @@ struct hydro_props; * restore these. */ extern int space_splitsize; extern int space_maxsize; +extern int space_grid_split_threshold; extern int space_subsize_pair_hydro; extern int space_subsize_self_hydro; extern int space_subsize_pair_stars; diff --git a/src/star_formation/GEAR/star_formation.h b/src/star_formation/GEAR/star_formation.h index 8568c26cd422d060a3d148ee3bb7a810c725fe46..ac3e04e1eae6b938853a045cd930022d5dbf9c4b 100644 --- a/src/star_formation/GEAR/star_formation.h +++ b/src/star_formation/GEAR/star_formation.h @@ -33,7 +33,9 @@ #include "part.h" #include "physical_constants.h" #include "random.h" +#include "star_formation_setters.h" #include "star_formation_struct.h" +#include "stars.h" #include "units.h" #define star_formation_need_update_dx_max 1 diff --git a/src/star_formation/GEAR/star_formation_io.h b/src/star_formation/GEAR/star_formation_io.h index 48469e9aefda1af8d814824daadc3248ec2d11eb..51e3c99bc31444297b2339edb184b831f313653b 100644 --- a/src/star_formation/GEAR/star_formation_io.h +++ b/src/star_formation/GEAR/star_formation_io.h @@ -94,13 +94,7 @@ star_formation_write_sparticles(const struct spart* sparts, "ProgenitorIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, sf_data.progenitor_id, "Unique IDs of the progenitor particle"); - list[4] = - io_make_output_field("StellarParticleType", CHAR, 1, UNIT_CONV_NO_UNITS, - 0.f, sparts, feedback_data.star_type, - "Type of stellar particle: 0=single star ; 1=stellar" - " part. without SNII ; 2=normal"); - - return 5; + return 4; } /** diff --git a/src/star_formation/GEAR/star_formation_setters.h b/src/star_formation/GEAR/star_formation_setters.h new file mode 100644 index 0000000000000000000000000000000000000000..31e6d06781c044888aaa6f87d07d2266adb78ce2 --- /dev/null +++ b/src/star_formation/GEAR/star_formation_setters.h @@ -0,0 +1,101 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2024 Darwin Roduit (darwin.roduit@epfl.ch) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + *******************************************************************************/ +#ifndef SWIFT_GEAR_STAR_FORMATION_SETTERS_H +#define SWIFT_GEAR_STAR_FORMATION_SETTERS_H + +#include "star_formation_struct.h" + +/** + * @file src/star_formation/GEAR/star_formation_setters.h + * @brief Setters functions for GEAR star formation scheme to avoid exposing + * implementation details to the outer world. Keep the code clean and lean. + */ + +/** + * @brief Set the birth density of a star particle. + * + * @param sp The #spart. + * @param birth_density Birth density of the star. + */ + +__attribute__((always_inline)) INLINE void +star_formation_set_spart_birth_density(struct spart *restrict sp, + const float birth_density) { + sp->sf_data.birth_density = birth_density; +} + +/** + * @brief Set the birth temperature of a star particle. + * + * @param sp The #spart. + * @param birth_temperature Birth temperature of the star. + */ + +__attribute__((always_inline)) INLINE void +star_formation_set_spart_birth_temperature(struct spart *restrict sp, + const float birth_temperature) { + sp->sf_data.birth_temperature = birth_temperature; +} + +/** + * @brief Set the birth mass of a star particle. + * + * @param sp The #spart. + * @param birth_mass Birth mass of the star. + */ + +__attribute__((always_inline)) INLINE void star_formation_set_spart_birth_mass( + struct spart *restrict sp, const float birth_mass) { + sp->sf_data.birth_mass = birth_mass; +} + +/** + * @brief Set the id of the particle creating the star particle. + * + * @param sp The #spart. + * @param progenitor_id The id of the particle creating sp. + */ + +__attribute__((always_inline)) INLINE void +star_formation_set_spart_progenitor_id(struct spart *restrict sp, + const long long progenitor_id) { + sp->sf_data.progenitor_id = progenitor_id; +} + +/** + * @brief Set the birth time/scale-factor of a star particle. + * + * @param sp The #spart. + * @param birth_time Birth time of the star. + * @param birth_scale_factor Birth scale-factor of the star. + * @param with_cosmology If we run with cosmology. + */ + +__attribute__((always_inline)) INLINE void +star_formation_set_spart_birth_time_or_scale_factor( + struct spart *restrict sp, const float birth_time, + const float birth_scale_factor, const int with_cosmology) { + if (with_cosmology) { + sp->birth_scale_factor = birth_scale_factor; + } else { + sp->birth_time = birth_time; + } +} + +#endif /* SWIFT_GEAR_STAR_FORMATION_SETTERS_H */ diff --git a/src/stars.h b/src/stars.h index 68b8d7fa52e27238db642ca8b7d1e1ea1923401a..539f9654c102c2509b445c00fc190b6dfc38a68e 100644 --- a/src/stars.h +++ b/src/stars.h @@ -35,6 +35,7 @@ #elif defined(STARS_GEAR) #include "./stars/GEAR/stars.h" #include "./stars/GEAR/stars_iact.h" +#include "./stars/GEAR/stars_stellar_type.h" #else #error "Invalid choice of star model" #endif diff --git a/src/stars/GEAR/stars_io.h b/src/stars/GEAR/stars_io.h index d1537cbdd60a203fe0777c599ddf9c6c686d3c59..346fa8f56349f3292f4269297e1045c38e57992a 100644 --- a/src/stars/GEAR/stars_io.h +++ b/src/stars/GEAR/stars_io.h @@ -21,6 +21,7 @@ #include "io_properties.h" #include "kick.h" +#include "stars/GEAR/stars_stellar_type.h" #include "stars_part.h" /** @@ -35,7 +36,7 @@ INLINE static void stars_read_particles(struct spart *sparts, int *num_fields) { /* Say how much we want to read */ - *num_fields = 6; + *num_fields = 7; /* List what we want to read */ list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, @@ -50,6 +51,10 @@ INLINE static void stars_read_particles(struct spart *sparts, UNIT_CONV_LENGTH, sparts, h); list[5] = io_make_input_field("BirthTime", FLOAT, 1, OPTIONAL, UNIT_CONV_MASS, sparts, birth_time); + + /* By default, stars are set to star_population */ + list[6] = io_make_input_field("StellarParticleType", INT, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, sparts, star_type); } INLINE static void convert_spart_pos(const struct engine *e, @@ -129,7 +134,7 @@ INLINE static void stars_write_particles(const struct spart *sparts, const int with_cosmology) { /* Say how much we want to write */ - *num_fields = 7; + *num_fields = 8; /* List what we want to write */ list[0] = io_make_output_field_convert_spart( @@ -167,6 +172,12 @@ INLINE static void stars_write_particles(const struct spart *sparts, "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, sparts, convert_spart_potential, "Gravitational potentials of the particles"); + list[7] = + io_make_output_field("StellarParticleTypes", CHAR, 1, UNIT_CONV_NO_UNITS, + 0.f, sparts, star_type, + "Type of stellar particle: 0=single star ; 1=stellar" + " cont. IMF part. ; 2=normal"); + #ifdef DEBUG_INTERACTIONS_STARS list += *num_fields; diff --git a/src/stars/GEAR/stars_part.h b/src/stars/GEAR/stars_part.h index fb3dcf2e3477a28560d057a95595fc857dc05982..5d8ee3cd5f6db5b48d76b5b664ec73b22ee34be2 100644 --- a/src/stars/GEAR/stars_part.h +++ b/src/stars/GEAR/stars_part.h @@ -28,6 +28,7 @@ #include "particle_splitting_struct.h" #include "rt_struct.h" #include "star_formation_struct.h" +#include "stars_stellar_type.h" #include "tracers_struct.h" /** @@ -81,6 +82,8 @@ struct spart { float birth_scale_factor; }; + enum stellar_type star_type; + /*! Star formation struct */ struct star_formation_spart_data sf_data; diff --git a/src/stars/GEAR/stars_stellar_type.h b/src/stars/GEAR/stars_stellar_type.h new file mode 100644 index 0000000000000000000000000000000000000000..bd105b013de34a8aa0c9301827d15543675380b8 --- /dev/null +++ b/src/stars/GEAR/stars_stellar_type.h @@ -0,0 +1,41 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2024 Darwin Roduit (darwin.roduit@epfl.ch) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_STARS_GEAR_STELLAR_TYPE_H +#define SWIFT_STARS_GEAR_STELLAR_TYPE_H + +/** + * @file src/stars/GEAR/stars_stellar_type.h + * @brief header file concerning the stellar type of the star particle. + **/ + +/** + * @brief The stellar type. + * + * Star particles can represent a single star ("single_star"), a stellar + * population from a continuous IMF or a stellar population from a whole IMF. + */ +enum stellar_type { + single_star = 0, /* particle representing a single star */ + star_population_continuous_IMF, /* particle representing a population of the + continuous part of the IMF */ + star_population, /* particle representing a population with the whole IMF */ + stellar_type_count +}; + +#endif /* SWIFT_STARS_GEAR_STELLAR_TYPE_H */ diff --git a/swift.c b/swift.c index c51d36b1c809fe618a15f7fb861181b745ec1af1..9c2cf942ad8f83de47e434d45704c98df4c7389c 100644 --- a/swift.c +++ b/swift.c @@ -177,6 +177,10 @@ int main(int argc, char *argv[]) { int with_cooling = 0; int with_self_gravity = 0; int with_hydro = 0; +#ifdef MOVING_MESH + int with_grid_hydro = 0; + int with_grid = 0; +#endif int with_stars = 0; int with_fof = 0; int with_lightcone = 0; @@ -409,6 +413,11 @@ int main(int argc, char *argv[]) { with_cooling = 1; with_feedback = 1; } +#ifdef MOVING_MESH + if (with_hydro) { + with_grid = 1; + } +#endif /* Deal with thread numbers */ if (nr_threads <= 0) @@ -1067,6 +1076,11 @@ int main(int argc, char *argv[]) { if (with_hydro) { #ifdef NONE_SPH error("Can't run with hydro when compiled without a hydro model!"); +#endif +#ifdef MOVING_MESH + warning( + "Moving mesh hydrodynamics is in the process of being merged and " + "will not perform as expected right now!"); #endif } if (with_stars) { @@ -1147,7 +1161,7 @@ int main(int argc, char *argv[]) { /* Initialise the sink properties */ if (with_sinks) { sink_props_init(&sink_properties, &feedback_properties, &prog_const, &us, - params, &cosmo); + params, &cosmo, with_feedback); } else bzero(&sink_properties, sizeof(struct sink_props)); @@ -1500,7 +1514,12 @@ int main(int argc, char *argv[]) { if (with_drift_all) engine_policies |= engine_policy_drift_all; if (with_mpole_reconstruction) engine_policies |= engine_policy_reconstruct_mpoles; +#ifndef MOVING_MESH if (with_hydro) engine_policies |= engine_policy_hydro; +#else + if (with_hydro) engine_policies |= engine_policy_grid_hydro; + if (with_grid) engine_policies |= engine_policy_grid; +#endif if (with_self_gravity) engine_policies |= engine_policy_self_gravity; if (with_external_gravity) engine_policies |= engine_policy_external_gravity; diff --git a/tests/Makefile.am b/tests/Makefile.am index 1a9993e6628740e3bb3d678112132cc4362a835b..213537c0957511b2f0b2fba519515b91f343a68b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -29,26 +29,24 @@ TESTS = testGreetings testMaths testReading.sh testKernel testKernelLongGrav \ testParser.sh test125cells.sh test125cellsPerturbed.sh testFFT \ testAdiabaticIndex testRandom testRandomSpacing testRandomPoisson testErfc \ testMatrixInversion testThreadpool testDump testCSDS testInteractions.sh \ - testVoronoi1D testVoronoi3D testGravityDerivatives \ - testPeriodicBC.sh testPeriodicBCPerturbed.sh testPotentialSelf \ - testPotentialPair testEOS testUtilities testSelectOutput.sh \ - testCbrt testCosmology testRandomCone testOutputList testFormat.sh \ - test27cellsStars.sh test27cellsStarsPerturbed.sh testHydroMPIrules \ + testGravityDerivatives testPeriodicBC.sh testPeriodicBCPerturbed.sh \ + testPotentialSelf testPotentialPair testEOS testUtilities testSelectOutput.sh \ + testCbrt testCosmology testRandomCone testOutputList testFormat.sh \ + test27cellsStars.sh test27cellsStarsPerturbed.sh testHydroMPIrules \ testAtomic testGravitySpeed testNeutrinoCosmology.sh testNeutrinoFermiDirac \ - testLog testDistance testTimeline + testLog testDistance testTimeline # List of test programs to compile check_PROGRAMS = testGreetings testReading testTimeIntegration testKernelLongGrav \ testActivePair test27cells test27cells_subset test125cells testParser \ - testKernel testFFT testInteractions testMaths testRandom testExp \ - testSymmetry testDistance testThreadpool testRandomSpacing testErfc \ - testAdiabaticIndex testRiemannExact testRiemannTRRS testRandomPoisson testRandomCone \ - testRiemannHLLC testMatrixInversion testDump testCSDS \ - testVoronoi1D testVoronoi3D testPeriodicBC \ - testGravityDerivatives testPotentialSelf testPotentialPair testEOS testUtilities \ - testSelectOutput testCbrt testCosmology testOutputList test27cellsStars \ - test27cellsStars_subset testCooling testComovingCooling testFeedback testHashmap \ - testAtomic testHydroMPIrules testGravitySpeed testNeutrinoCosmology \ + testKernel testFFT testInteractions testMaths testRandom testExp \ + testSymmetry testDistance testThreadpool testRandomSpacing testErfc \ + testAdiabaticIndex testRiemannExact testRiemannTRRS testRandomPoisson testRandomCone \ + testRiemannHLLC testMatrixInversion testDump testCSDS \ + testPeriodicBC testGravityDerivatives testPotentialSelf testPotentialPair testEOS \ + testUtilities testSelectOutput testCbrt testCosmology testOutputList \ + test27cellsStars test27cellsStars_subset testCooling testComovingCooling testFeedback \ + testHashmap testAtomic testHydroMPIrules testGravitySpeed testNeutrinoCosmology \ testNeutrinoFermiDirac testLog testTimeline # Rebuild tests when SWIFT is updated. @@ -128,12 +126,6 @@ testRiemannHLLC_SOURCES = testRiemannHLLC.c testMatrixInversion_SOURCES = testMatrixInversion.c -testVoronoi1D_SOURCES = testVoronoi1D.c - -#testVoronoi2D_SOURCES = testVoronoi2D.c - -testVoronoi3D_SOURCES = testVoronoi3D.c - testThreadpool_SOURCES = testThreadpool.c testDump_SOURCES = testDump.c @@ -172,15 +164,15 @@ testHydroMPIrules = testHydroMPIrules.c # Files necessary for distribution EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \ - test27cells.sh test27cellsPerturbed.sh testParser.sh testPeriodicBC.sh \ - testPeriodicBCPerturbed.sh test125cells.sh test125cellsPerturbed.sh testParserInput.yaml \ - difffloat.py tolerance_125_normal.dat tolerance_125_perturbed.dat \ + test27cells.sh test27cellsPerturbed.sh testParser.sh testPeriodicBC.sh \ + testPeriodicBCPerturbed.sh test125cells.sh test125cellsPerturbed.sh testParserInput.yaml \ + difffloat.py tolerance_125_normal.dat tolerance_125_perturbed.dat \ tolerance_27_normal.dat tolerance_27_perturbed.dat tolerance_27_perturbed_h.dat tolerance_27_perturbed_h2.dat \ - tolerance_testInteractions.dat tolerance_pair_active.dat tolerance_pair_force_active.dat \ - fft_params.yml tolerance_periodic_BC_normal.dat tolerance_periodic_BC_perturbed.dat \ - testEOS.sh testEOS_plot.sh testSelectOutput.sh selectOutput.yml \ + tolerance_testInteractions.dat tolerance_pair_active.dat tolerance_pair_force_active.dat \ + fft_params.yml tolerance_periodic_BC_normal.dat tolerance_periodic_BC_perturbed.dat \ + testEOS.sh testEOS_plot.sh testSelectOutput.sh selectOutput.yml \ output_list_params.yml output_list_time.txt output_list_redshift.txt \ output_list_scale_factor.txt testEOS.sh testEOS_plot.sh \ - test27cellsStars.sh test27cellsStarsPerturbed.sh star_tolerance_27_normal.dat \ - star_tolerance_27_perturbed.dat star_tolerance_27_perturbed_h.dat star_tolerance_27_perturbed_h2.dat \ - testNeutrinoCosmology.dat testNeutrinoCosmology.sh + test27cellsStars.sh test27cellsStarsPerturbed.sh star_tolerance_27_normal.dat \ + star_tolerance_27_perturbed.dat star_tolerance_27_perturbed_h.dat star_tolerance_27_perturbed_h2.dat \ + testNeutrinoCosmology.dat testNeutrinoCosmology.sh diff --git a/tests/test125cells.c b/tests/test125cells.c index 7c83691552395fcbb5a890c28c67dd663c12fe2c..1425104485305a299317d79c4cbcc28b1e15170a 100644 --- a/tests/test125cells.c +++ b/tests/test125cells.c @@ -119,8 +119,6 @@ void set_energy_state(struct part *part, enum pressure_field press, float size, part->u = pressure / (hydro_gamma_minus_one * density); #elif defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) part->conserved.energy = pressure / (hydro_gamma_minus_one * density); -#elif defined(SHADOWFAX_SPH) - part->primitives.P = pressure; #else error("Need to define pressure here !"); #endif @@ -225,25 +223,6 @@ void reset_particles(struct cell *c, struct hydro_space *hs, hydro_init_part(p, hs); adaptive_softening_init_part(p); mhd_init_part(p); - -#if defined(SHADOWFAX_SPH) - float volume = p->conserved.mass / density; - p->cell.volume = volume; - p->primitives.rho = density; - p->primitives.v[0] = p->v[0]; - p->primitives.v[1] = p->v[1]; - p->primitives.v[2] = p->v[2]; - p->conserved.momentum[0] = p->conserved.mass * p->v[0]; - p->conserved.momentum[1] = p->conserved.mass * p->v[1]; - p->conserved.momentum[2] = p->conserved.mass * p->v[2]; - p->conserved.energy = - p->primitives.P / hydro_gamma_minus_one * volume + - 0.5f * - (p->conserved.momentum[0] * p->conserved.momentum[0] + - p->conserved.momentum[1] * p->conserved.momentum[1] + - p->conserved.momentum[2] * p->conserved.momentum[2]) / - p->conserved.mass; -#endif } } @@ -304,7 +283,7 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h, part->h = size * h / (float)n; h_max = fmax(h_max, part->h); -#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) part->conserved.mass = density * volume / count; #else part->mass = density * volume / count; @@ -392,10 +371,10 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, main_cell->hydro.parts[pid].v[0], main_cell->hydro.parts[pid].v[1], main_cell->hydro.parts[pid].v[2], main_cell->hydro.parts[pid].h, hydro_get_comoving_density(&main_cell->hydro.parts[pid]), -#if defined(MINIMAL_SPH) || defined(PLANETARY_SPH) || \ - defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || \ - defined(SHADOWFAX_SPH) || defined(HOPKINS_PU_SPH) || \ - defined(HOPKINS_PU_SPH_MONAGHAN) || defined(GASOLINE_SPH) +#if defined(MINIMAL_SPH) || defined(PLANETARY_SPH) || \ + defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || \ + defined(HOPKINS_PU_SPH) || defined(HOPKINS_PU_SPH_MONAGHAN) || \ + defined(GASOLINE_SPH) 0.f, #elif defined(ANARCHY_PU_SPH) || defined(SPHENIX_SPH) || defined(PHANTOM_SPH) main_cell->hydro.parts[pid].viscosity.div_v, diff --git a/tests/test27cells.c b/tests/test27cells.c index b5755d14b61b847909618ebff69f9d9607393367..46dfe5fc1dde3321dd919a7cbf6ee7f2dc0aa861 100644 --- a/tests/test27cells.c +++ b/tests/test27cells.c @@ -153,15 +153,9 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, h_max = fmaxf(h_max, part->h); part->id = ++(*partId); -#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) part->conserved.mass = density * volume / count; -#ifdef SHADOWFAX_SPH - double anchor[3] = {0., 0., 0.}; - double side[3] = {1., 1., 1.}; - voronoi_cell_init(&part->cell, part->x, anchor, side); -#endif - #else part->mass = density * volume / count; #endif @@ -220,18 +214,8 @@ void clean_up(struct cell *ci) { * @brief Initializes all particles field to be ready for a density calculation */ void zero_particle_fields(struct cell *c) { -#ifdef SHADOWFAX_SPH - struct hydro_space hs; - hs.anchor[0] = 0.; - hs.anchor[1] = 0.; - hs.anchor[2] = 0.; - hs.side[0] = 1.; - hs.side[1] = 1.; - hs.side[2] = 1.; - struct hydro_space *hspointer = &hs; -#else struct hydro_space *hspointer = NULL; -#endif + for (int pid = 0; pid < c->hydro.count; pid++) { hydro_init_part(&c->hydro.parts[pid], hspointer); adaptive_softening_init_part(&c->hydro.parts[pid]); @@ -282,7 +266,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, main_cell->hydro.parts[pid].v[0], main_cell->hydro.parts[pid].v[1], main_cell->hydro.parts[pid].v[2], hydro_get_comoving_density(&main_cell->hydro.parts[pid]), -#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) 0.f, #elif defined(HOPKINS_PU_SPH) || defined(HOPKINS_PU_SPH_MONAGHAN) || \ defined(ANARCHY_PU_SPH) @@ -331,7 +315,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, cj->hydro.parts[pjd].v[0], cj->hydro.parts[pjd].v[1], cj->hydro.parts[pjd].v[2], hydro_get_comoving_density(&cj->hydro.parts[pjd]), -#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) 0.f, #else main_cell->hydro.parts[pjd].density.rho_dh, diff --git a/tests/testActivePair.c b/tests/testActivePair.c index efe13b069bd34b5f0599ca9de1d7190d684a6016..25c229d591e9c73953c2b1c85d108ceb662d0d73 100644 --- a/tests/testActivePair.c +++ b/tests/testActivePair.c @@ -105,15 +105,8 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, part->id = ++(*partId); /* Set the mass */ -#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) part->conserved.mass = density * volume / count; - -#ifdef SHADOWFAX_SPH - double anchor[3] = {0., 0., 0.}; - double side[3] = {1., 1., 1.}; - voronoi_cell_init(&part->cell, part->x, anchor, side); -#endif /* SHADOWFAX_SPH */ - #else part->mass = density * volume / count; #endif diff --git a/tests/testInteractions.c b/tests/testInteractions.c index a4d7ead66ad0ef94accb722d61a48795617182ea..005c4666b71bc41f8e31f0afa0bd017358eaa9bb 100644 --- a/tests/testInteractions.c +++ b/tests/testInteractions.c @@ -78,7 +78,7 @@ struct part *make_particles(size_t count, double *offset, double spacing, p->h = h; p->id = ++(*partId); -#if !defined(GIZMO_MFV_SPH) && !defined(SHADOWFAX_SPH) +#if !defined(GIZMO_MFV_SPH) p->mass = 1.0f; #endif @@ -99,7 +99,7 @@ struct part *make_particles(size_t count, double *offset, double spacing, p->h = h; p->id = ++(*partId); -#if !defined(GIZMO_SPH) && !defined(SHADOWFAX_SPH) +#if !defined(GIZMO_SPH) p->mass = 1.0f; #endif } @@ -111,11 +111,10 @@ struct part *make_particles(size_t count, double *offset, double spacing, */ void prepare_force(struct part *parts, size_t count) { -#if !defined(GIZMO_MFV_SPH) && !defined(SHADOWFAX_SPH) && \ - !defined(MINIMAL_SPH) && !defined(PLANETARY_SPH) && \ - !defined(HOPKINS_PU_SPH) && !defined(HOPKINS_PU_SPH_MONAGHAN) && \ - !defined(ANARCHY_PU_SPH) && !defined(SPHENIX_SPH) && \ - !defined(PHANTOM_SPH) && !defined(GASOLINE_SPH) +#if !defined(GIZMO_MFV_SPH) && !defined(MINIMAL_SPH) && \ + !defined(PLANETARY_SPH) && !defined(HOPKINS_PU_SPH) && \ + !defined(HOPKINS_PU_SPH_MONAGHAN) && !defined(ANARCHY_PU_SPH) && \ + !defined(SPHENIX_SPH) && !defined(PHANTOM_SPH) && !defined(GASOLINE_SPH) struct part *p; for (size_t i = 0; i < count; ++i) { p = &parts[i]; @@ -142,8 +141,8 @@ void dump_indv_particle_fields(char *fileName, struct part *p) { "%8.5f %8.5f %13e %13e %13e %13e %13e %8.5f %8.5f\n", p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], p->h, hydro_get_comoving_density(p), -#if defined(MINIMAL_SPH) || defined(PLANETARY_SPH) || \ - defined(SHADOWFAX_SPH) || defined(PHANTOM_SPH) || defined(GASOLINE_SPH) +#if defined(MINIMAL_SPH) || defined(PLANETARY_SPH) || defined(PHANTOM_SPH) || \ + defined(GASOLINE_SPH) 0.f, #else p->density.div_v, diff --git a/tests/testPeriodicBC.c b/tests/testPeriodicBC.c index 6f4cfb99d2632ec2567f86aa78ef7017129ec34f..4d540c0594569de62abe86d4275160ed495f35ce 100644 --- a/tests/testPeriodicBC.c +++ b/tests/testPeriodicBC.c @@ -132,15 +132,9 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, h_max = fmax(h_max, part->h); part->id = ++(*partId); -#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) part->conserved.mass = density * volume / count; -#ifdef SHADOWFAX_SPH - double anchor[3] = {0., 0., 0.}; - double side[3] = {1., 1., 1.}; - voronoi_cell_init(&part->cell, part->x, anchor, side); -#endif - #else part->mass = density * volume / count; #endif @@ -245,7 +239,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, int i, int j, main_cell->hydro.parts[pid].v[0], main_cell->hydro.parts[pid].v[1], main_cell->hydro.parts[pid].v[2], hydro_get_comoving_density(&main_cell->hydro.parts[pid]), -#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) 0.f, #else main_cell->hydro.parts[pid].density.rho_dh, diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c index 9f04466652c37b1c805d96df7c9aecf33024501f..e423b7dd01c1ba84fd404e8204b0a9b02bc10acd 100644 --- a/tests/testSymmetry.c +++ b/tests/testSymmetry.c @@ -39,13 +39,6 @@ void print_bytes(void *p, size_t len) { void test(void) { -#if defined(SHADOWFAX_SPH) - /* Initialize the Voronoi simulation box */ - double box_anchor[3] = {-2.0f, -2.0f, -2.0f}; - double box_side[3] = {6.0f, 6.0f, 6.0f}; -/* voronoi_set_box(box_anchor, box_side);*/ -#endif - /* Start with some values for the cosmological parameters */ const float a = (float)random_uniform(0.8, 1.); const float H = 1.f; @@ -72,59 +65,6 @@ void test(void) { pi.time_bin = 1; pj.time_bin = 1; -#if defined(SHADOWFAX_SPH) - /* Give the primitive variables sensible values, since the Riemann solver does - not like negative densities and pressures */ - pi.primitives.rho = random_uniform(0.1f, 1.0f); - pi.primitives.v[0] = random_uniform(-10.0f, 10.0f); - pi.primitives.v[1] = random_uniform(-10.0f, 10.0f); - pi.primitives.v[2] = random_uniform(-10.0f, 10.0f); - pi.primitives.P = random_uniform(0.1f, 1.0f); - pj.primitives.rho = random_uniform(0.1f, 1.0f); - pj.primitives.v[0] = random_uniform(-10.0f, 10.0f); - pj.primitives.v[1] = random_uniform(-10.0f, 10.0f); - pj.primitives.v[2] = random_uniform(-10.0f, 10.0f); - pj.primitives.P = random_uniform(0.1f, 1.0f); - /* make gradients zero */ - pi.primitives.gradients.rho[0] = 0.0f; - pi.primitives.gradients.rho[1] = 0.0f; - pi.primitives.gradients.rho[2] = 0.0f; - pi.primitives.gradients.v[0][0] = 0.0f; - pi.primitives.gradients.v[0][1] = 0.0f; - pi.primitives.gradients.v[0][2] = 0.0f; - pi.primitives.gradients.v[1][0] = 0.0f; - pi.primitives.gradients.v[1][1] = 0.0f; - pi.primitives.gradients.v[1][2] = 0.0f; - pi.primitives.gradients.v[2][0] = 0.0f; - pi.primitives.gradients.v[2][1] = 0.0f; - pi.primitives.gradients.v[2][2] = 0.0f; - pi.primitives.gradients.P[0] = 0.0f; - pi.primitives.gradients.P[1] = 0.0f; - pi.primitives.gradients.P[2] = 0.0f; - pj.primitives.gradients.rho[0] = 0.0f; - pj.primitives.gradients.rho[1] = 0.0f; - pj.primitives.gradients.rho[2] = 0.0f; - pj.primitives.gradients.v[0][0] = 0.0f; - pj.primitives.gradients.v[0][1] = 0.0f; - pj.primitives.gradients.v[0][2] = 0.0f; - pj.primitives.gradients.v[1][0] = 0.0f; - pj.primitives.gradients.v[1][1] = 0.0f; - pj.primitives.gradients.v[1][2] = 0.0f; - pj.primitives.gradients.v[2][0] = 0.0f; - pj.primitives.gradients.v[2][1] = 0.0f; - pj.primitives.gradients.v[2][2] = 0.0f; - pj.primitives.gradients.P[0] = 0.0f; - pj.primitives.gradients.P[1] = 0.0f; - pj.primitives.gradients.P[2] = 0.0f; - - /* set time step to reasonable value */ - pi.force.dt = 0.001; - pj.force.dt = 0.001; - - voronoi_cell_init(&pi.cell, pi.x, box_anchor, box_side); - voronoi_cell_init(&pj.cell, pj.x, box_anchor, box_side); -#endif - #if defined(GIZMO_MFV_SPH) /* Give the primitive variables sensible values, since the Riemann solver does not like negative densities and pressures */ diff --git a/tests/testVoronoi1D.c b/tests/testVoronoi1D.c deleted file mode 100644 index 083d9aaa279f241ae1ac4d0bfaeb2780a39574a4..0000000000000000000000000000000000000000 --- a/tests/testVoronoi1D.c +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (C) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ -#include "hydro/Shadowswift/voronoi1d_algorithm.h" - -int main(int argc, char *argv[]) { - - double box_anchor[1] = {-0.5}; - double box_side[1] = {2.}; - - /* Create a Voronoi cell */ - double x[1] = {0.5f}; - struct voronoi_cell cell; - voronoi_cell_init(&cell, x, box_anchor, box_side); - - /* Interact with a left and right neighbour */ - float xL[1] = {0.5f}; - float xR[1] = {-0.5f}; - voronoi_cell_interact(&cell, xL, 1); - voronoi_cell_interact(&cell, xR, 2); - - /* Interact with some more neighbours to check if they are properly ignored */ - float x0[1] = {0.6f}; - float x1[1] = {-0.7f}; - voronoi_cell_interact(&cell, x0, 3); - voronoi_cell_interact(&cell, x1, 4); - - /* Finalize cell and check results */ - voronoi_cell_finalize(&cell); - - if (cell.volume != 0.5f) { - error("Wrong volume: %g!", cell.volume); - } - if (cell.centroid != 0.5f) { - error("Wrong centroid: %g!", cell.centroid); - } - if (cell.idL != 1) { - error("Wrong left neighbour: %llu!", cell.idL); - } - if (cell.idR != 2) { - error("Wrong right neighbour: %llu!", cell.idR); - } - - /* Check face method */ - float A; - float midpoint[3]; - A = voronoi_get_face(&cell, 1, midpoint); - if (A != 1.0f) { - error("Wrong surface area returned for left neighbour!"); - } - if (midpoint[0] != -0.25f) { - error("Wrong midpoint returned for left neighbour!"); - } - A = voronoi_get_face(&cell, 2, midpoint); - if (A != 1.0f) { - error("Wrong surface area returned for right neighbour!"); - } - if (midpoint[0] != 0.25f) { - error("Wrong midpoint returned for right neighbour!"); - } - - return 0; -} diff --git a/tests/testVoronoi2D.c b/tests/testVoronoi2D.c deleted file mode 100644 index 4708f7bdd44aea6b4ce9d022a5f5a0fc2e65f8c2..0000000000000000000000000000000000000000 --- a/tests/testVoronoi2D.c +++ /dev/null @@ -1,225 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (C) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ -#include <config.h> - -/* Local headers. */ -#include "hydro/Shadowswift/voronoi2d_algorithm.h" -#include "tools.h" - -/* Number of cells used to test the 2D interaction algorithm */ -#define TESTVORONOI2D_NUMCELL 100 - -int main(int argc, char *argv[]) { - - /* initialize simulation box */ - double anchor[3] = {-0.5f, -0.5f, -0.5f}; - double side[3] = {2.0f, 2.0f, 2.0f}; - - /* test initialization and finalization algorithms */ - { - message("Testing initialization and finalization algorithm..."); - - struct voronoi_cell cell; - double x[3] = {0.5, 0.5, 0.5}; - - voronoi_cell_init(&cell, x, anchor, side); - - float maxradius = voronoi_cell_finalize(&cell); - - assert(maxradius == 2.0f * sqrtf(2.0f)); - - assert(cell.volume == 4.0f); - - assert(cell.centroid[0] == 0.5f); - assert(cell.centroid[1] == 0.5f); - - message("Done."); - } - - /* test interaction algorithm: normal case */ - { - message("Testing %i cell grid with random positions...", - TESTVORONOI2D_NUMCELL); - - /* create 100 cells with random positions in [0,1]x[0,1] */ - struct voronoi_cell cells[TESTVORONOI2D_NUMCELL]; - double x[2]; - float dx[2]; - int i, j; - float Atot; - struct voronoi_cell *cell_i, *cell_j; - - for (i = 0; i < TESTVORONOI2D_NUMCELL; ++i) { - x[0] = random_uniform(0., 1.); - x[1] = random_uniform(0., 1.); - voronoi_cell_init(&cells[i], x, anchor, side); -#ifdef VORONOI_VERBOSE - message("cell[%i]: %g %g", i, x[0], x[1]); -#endif - } - - /* interact the cells (with periodic boundaries) */ - for (i = 0; i < TESTVORONOI2D_NUMCELL; ++i) { - cell_i = &cells[i]; - for (j = 0; j < TESTVORONOI2D_NUMCELL; ++j) { - if (i != j) { - cell_j = &cells[j]; - dx[0] = cell_i->x[0] - cell_j->x[0]; - dx[1] = cell_i->x[1] - cell_j->x[1]; - /* periodic boundaries */ - if (dx[0] >= 0.5f) { - dx[0] -= 1.0f; - } - if (dx[0] < -0.5f) { - dx[0] += 1.0f; - } - if (dx[1] >= 0.5f) { - dx[1] -= 1.0f; - } - if (dx[1] < -0.5f) { - dx[1] += 1.0f; - } -#ifdef VORONOI_VERBOSE - message("Cell %i before:", i); - voronoi_print_cell(&cells[i]); - message("Interacting cell %i with cell %i (%g %g, %g %g", i, j, - cells[i].x[0], cells[i].x[1], cells[j].x[0], cells[j].x[1]); -#endif - voronoi_cell_interact(cell_i, dx, j); - } - } - } - - Atot = 0.0f; - /* print the cells to the stdout */ - for (i = 0; i < TESTVORONOI2D_NUMCELL; ++i) { -#ifdef VORONOI_VERBOSE - printf("Cell %i:\n", i); - voronoi_print_cell(&cells[i]); -#endif - voronoi_cell_finalize(&cells[i]); - Atot += cells[i].volume; - } - - /* Check the total surface area */ - assert(fabs(Atot - 1.0f) < 1.e-6); - - /* Check the neighbour relations for an arbitrary cell: cell 44 - We plotted the grid and manually found the correct neighbours and their - order. */ - assert(cells[44].nvert == 7); - assert(cells[44].ngbs[0] == 26); - assert(cells[44].ngbs[1] == 38); - assert(cells[44].ngbs[2] == 3); - assert(cells[44].ngbs[3] == 33); - assert(cells[44].ngbs[4] == 5); - assert(cells[44].ngbs[5] == 90); - assert(cells[44].ngbs[6] == 4); - - message("Done."); - } - - /* test interaction algorithm */ - { - message("Testing 100 cell grid with Cartesian mesh positions..."); - - struct voronoi_cell cells[100]; - double x[2]; - float dx[2]; - int i, j; - float Atot; - struct voronoi_cell *cell_i, *cell_j; - - for (i = 0; i < 10; ++i) { - for (j = 0; j < 10; ++j) { - x[0] = (i + 0.5f) * 0.1; - x[1] = (j + 0.5f) * 0.1; - voronoi_cell_init(&cells[10 * i + j], x, anchor, side); - } - } - - /* interact the cells (with periodic boundaries) */ - for (i = 0; i < 100; ++i) { - cell_i = &cells[i]; - for (j = 0; j < 100; ++j) { - if (i != j) { - cell_j = &cells[j]; - dx[0] = cell_i->x[0] - cell_j->x[0]; - dx[1] = cell_i->x[1] - cell_j->x[1]; - /* periodic boundaries */ - if (dx[0] >= 0.5f) { - dx[0] -= 1.0f; - } - if (dx[0] < -0.5f) { - dx[0] += 1.0f; - } - if (dx[1] >= 0.5f) { - dx[1] -= 1.0f; - } - if (dx[1] < -0.5f) { - dx[1] += 1.0f; - } -#ifdef VORONOI_VERBOSE - message("Cell %i before:", i); - voronoi_print_cell(&cells[i]); - message("Interacting cell %i with cell %i (%g %g, %g %g", i, j, - cells[i].x[0], cells[i].x[1], cells[j].x[0], cells[j].x[1]); -#endif - voronoi_cell_interact(cell_i, dx, j); - } - } - } - - Atot = 0.0f; - /* print the cells to the stdout */ - for (i = 0; i < 100; ++i) { -#ifdef VORONOI_VERBOSE - printf("Cell %i:\n", i); - voronoi_print_cell(&cells[i]); -#endif - voronoi_cell_finalize(&cells[i]); - Atot += cells[i].volume; - } - - /* Check the total surface area */ - assert(fabs(Atot - 1.0f) < 1.e-6); - - /* Check the neighbour relations for an arbitrary cell: cell 44 We plotted - the grid and manually found the correct neighbours and their - order. Variation is found when optimizing, so we have two possible - outcomes... */ - if (cells[44].nvert == 5) { - assert(cells[44].nvert == 5); - assert(cells[44].ngbs[0] == 43); - assert(cells[44].ngbs[1] == 34); - assert(cells[44].ngbs[2] == 45); - assert(cells[44].ngbs[3] == 55); - - } else { - assert(cells[44].nvert == 4); - assert(cells[44].ngbs[0] == 34); - assert(cells[44].ngbs[1] == 45); - assert(cells[44].ngbs[2] == 54); - assert(cells[44].ngbs[3] == 43); - } - message("Done."); - } - - return 0; -} diff --git a/tests/testVoronoi3D.c b/tests/testVoronoi3D.c deleted file mode 100644 index de3eaf2d50782a20f526229484e2b255ec24dd52..0000000000000000000000000000000000000000 --- a/tests/testVoronoi3D.c +++ /dev/null @@ -1,1522 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (C) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ -#include <config.h> - -/* Some standard headers. */ -#include <stdlib.h> - -/* Local headers. */ -#include "error.h" -#include "hydro/Shadowswift/voronoi3d_algorithm.h" -#include "part.h" -#include "tools.h" - -/* Number of random generators to use in the first grid build test */ -#define TESTVORONOI3D_NUMCELL_RANDOM 100 - -/* Number of cartesian generators to use (in one coordinate direction) for the - second grid build test. The total number of generators is the third power of - this number (so be careful with large numbers) */ -#define TESTVORONOI3D_NUMCELL_CARTESIAN_1D 5 - -/* Total number of generators in the second grid build test. Do not change this - value, but change the 1D value above instead. */ -#define TESTVORONOI3D_NUMCELL_CARTESIAN_3D \ - (TESTVORONOI3D_NUMCELL_CARTESIAN_1D * TESTVORONOI3D_NUMCELL_CARTESIAN_1D * \ - TESTVORONOI3D_NUMCELL_CARTESIAN_1D) - -/* Bottom front left corner and side lengths of the large box that contains all - particles and is used as initial cell at the start of the construction */ -#define VORONOI3D_BOX_ANCHOR_X 0.0f -#define VORONOI3D_BOX_ANCHOR_Y 0.0f -#define VORONOI3D_BOX_ANCHOR_Z 0.0f -#define VORONOI3D_BOX_SIDE_X 1.0f -#define VORONOI3D_BOX_SIDE_Y 1.0f -#define VORONOI3D_BOX_SIDE_Z 1.0f - -/** - * @brief Get the volume of the simulation box stored in the global variables. - * - * This method is only used for debugging purposes. - * - * @return Volume of the simulation box as it is stored in the global variables. - */ -float voronoi_get_box_volume(void) { - return VORONOI3D_BOX_SIDE_X * VORONOI3D_BOX_SIDE_Y * VORONOI3D_BOX_SIDE_Z; -} - -/** - * @brief Get the centroid of the simulation box stored in the global variables. - * - * This method is only used for debugging purposes. - * - * @param box_centroid Array to store the resulting coordinates in. - */ -void voronoi_get_box_centroid(float *box_centroid) { - box_centroid[0] = 0.5f * VORONOI3D_BOX_SIDE_X + VORONOI3D_BOX_ANCHOR_X; - box_centroid[1] = 0.5f * VORONOI3D_BOX_SIDE_Y + VORONOI3D_BOX_ANCHOR_Y; - box_centroid[2] = 0.5f * VORONOI3D_BOX_SIDE_Z + VORONOI3D_BOX_ANCHOR_Z; -} - -/** - * @brief Get the surface area and coordinates of the face midpoint for the - * face of the simulation box with the given unique ID. - * - * This method is only used for debugging purposes. - * - * @param id Unique ID of one of the 6 faces of the simulation box. - * @param face_midpoint Array to store the coordinates of the requested - * midpoint in. - * @return Surface area of the face, or 0 if the given ID does not correspond to - * a known face of the simulation box. - */ -float voronoi_get_box_face(unsigned long long id, float *face_midpoint) { - - if (id == VORONOI3D_BOX_FRONT) { - face_midpoint[0] = 0.5f * VORONOI3D_BOX_SIDE_X + VORONOI3D_BOX_ANCHOR_X; - face_midpoint[1] = VORONOI3D_BOX_ANCHOR_Y; - face_midpoint[2] = 0.5f * VORONOI3D_BOX_SIDE_Z + VORONOI3D_BOX_ANCHOR_Z; - return VORONOI3D_BOX_SIDE_X * VORONOI3D_BOX_SIDE_Z; - } - if (id == VORONOI3D_BOX_BACK) { - face_midpoint[0] = 0.5f * VORONOI3D_BOX_SIDE_X + VORONOI3D_BOX_ANCHOR_X; - face_midpoint[1] = VORONOI3D_BOX_ANCHOR_Y + VORONOI3D_BOX_SIDE_Y; - face_midpoint[2] = 0.5f * VORONOI3D_BOX_SIDE_Z + VORONOI3D_BOX_ANCHOR_Z; - return VORONOI3D_BOX_SIDE_X * VORONOI3D_BOX_SIDE_Z; - } - - if (id == VORONOI3D_BOX_BOTTOM) { - face_midpoint[0] = 0.5f * VORONOI3D_BOX_SIDE_X + VORONOI3D_BOX_ANCHOR_X; - face_midpoint[1] = 0.5f * VORONOI3D_BOX_SIDE_Y + VORONOI3D_BOX_ANCHOR_Y; - face_midpoint[2] = VORONOI3D_BOX_ANCHOR_Z; - return VORONOI3D_BOX_SIDE_X * VORONOI3D_BOX_SIDE_Y; - } - if (id == VORONOI3D_BOX_TOP) { - face_midpoint[0] = 0.5f * VORONOI3D_BOX_SIDE_X + VORONOI3D_BOX_ANCHOR_X; - face_midpoint[1] = 0.5f * VORONOI3D_BOX_SIDE_Y + VORONOI3D_BOX_ANCHOR_Y; - face_midpoint[2] = VORONOI3D_BOX_ANCHOR_Z + VORONOI3D_BOX_SIDE_Z; - return VORONOI3D_BOX_SIDE_X * VORONOI3D_BOX_SIDE_Y; - } - - if (id == VORONOI3D_BOX_LEFT) { - face_midpoint[0] = VORONOI3D_BOX_ANCHOR_X; - face_midpoint[1] = 0.5f * VORONOI3D_BOX_SIDE_Y + VORONOI3D_BOX_ANCHOR_Y; - face_midpoint[2] = 0.5f * VORONOI3D_BOX_SIDE_Z + VORONOI3D_BOX_ANCHOR_Z; - return VORONOI3D_BOX_SIDE_X * VORONOI3D_BOX_SIDE_Y; - } - if (id == VORONOI3D_BOX_RIGHT) { - face_midpoint[0] = VORONOI3D_BOX_ANCHOR_X + VORONOI3D_BOX_SIDE_X; - face_midpoint[1] = 0.5f * VORONOI3D_BOX_SIDE_Y + VORONOI3D_BOX_ANCHOR_Y; - face_midpoint[2] = 0.5f * VORONOI3D_BOX_SIDE_Z + VORONOI3D_BOX_ANCHOR_Z; - return VORONOI3D_BOX_SIDE_X * VORONOI3D_BOX_SIDE_Y; - } - - return 0.0f; -} - -/** - * @brief Check if voronoi_volume_tetrahedron() works - */ -void test_voronoi_volume_tetrahedron(void) { - float v1[3] = {0., 0., 0.}; - float v2[3] = {0., 0., 1.}; - float v3[3] = {0., 1., 0.}; - float v4[3] = {1., 0., 0.}; - - float V = voronoi_volume_tetrahedron(v1, v2, v3, v4); - assert(V == 1.0f / 6.0f); -} - -/** - * @brief Check if voronoi_centroid_tetrahedron() works - */ -void test_voronoi_centroid_tetrahedron(void) { - float v1[3] = {0., 0., 0.}; - float v2[3] = {0., 0., 1.}; - float v3[3] = {0., 1., 0.}; - float v4[3] = {1., 0., 0.}; - - float centroid[3]; - voronoi_centroid_tetrahedron(centroid, v1, v2, v3, v4); - assert(centroid[0] == 0.25f); - assert(centroid[1] == 0.25f); - assert(centroid[2] == 0.25f); -} - -/** - * @brief Check if voronoi_calculate_cell() works - */ -void test_calculate_cell(void) { - - double box_anchor[3] = {VORONOI3D_BOX_ANCHOR_X, VORONOI3D_BOX_ANCHOR_Y, - VORONOI3D_BOX_ANCHOR_Z}; - double box_side[3] = {VORONOI3D_BOX_SIDE_X, VORONOI3D_BOX_SIDE_Y, - VORONOI3D_BOX_SIDE_Z}; - - struct voronoi_cell cell; - - cell.x[0] = 0.5f; - cell.x[1] = 0.5f; - cell.x[2] = 0.5f; - - /* Initialize the cell to a large cube. */ - voronoi_initialize(&cell, box_anchor, box_side); - /* Calculate the volume and centroid of the large cube. */ - voronoi_calculate_cell(&cell); - /* Calculate the faces. */ - voronoi_calculate_faces(&cell); - - /* Update these values if you ever change to another large cube! */ - assert(cell.volume == voronoi_get_box_volume()); - float box_centroid[3]; - voronoi_get_box_centroid(box_centroid); - assert(cell.centroid[0] = box_centroid[0]); - assert(cell.centroid[1] = box_centroid[1]); - assert(cell.centroid[2] = box_centroid[2]); - - /* Check cell neighbours. */ - assert(cell.nface == 6); - assert(cell.ngbs[0] == VORONOI3D_BOX_FRONT); - assert(cell.ngbs[1] == VORONOI3D_BOX_LEFT); - assert(cell.ngbs[2] == VORONOI3D_BOX_BOTTOM); - assert(cell.ngbs[3] == VORONOI3D_BOX_TOP); - assert(cell.ngbs[4] == VORONOI3D_BOX_BACK); - assert(cell.ngbs[5] == VORONOI3D_BOX_RIGHT); - - /* Check cell faces */ - float face_midpoint[3], face_area; - face_area = voronoi_get_box_face(VORONOI3D_BOX_FRONT, face_midpoint); - assert(cell.face_areas[0] == face_area); - assert(cell.face_midpoints[0][0] == face_midpoint[0] - cell.x[0]); - assert(cell.face_midpoints[0][1] == face_midpoint[1] - cell.x[1]); - assert(cell.face_midpoints[0][2] == face_midpoint[2] - cell.x[2]); - - face_area = voronoi_get_box_face(VORONOI3D_BOX_LEFT, face_midpoint); - assert(cell.face_areas[1] == face_area); - assert(cell.face_midpoints[1][0] == face_midpoint[0] - cell.x[0]); - assert(cell.face_midpoints[1][1] == face_midpoint[1] - cell.x[1]); - assert(cell.face_midpoints[1][2] == face_midpoint[2] - cell.x[2]); - - face_area = voronoi_get_box_face(VORONOI3D_BOX_BOTTOM, face_midpoint); - assert(cell.face_areas[2] == face_area); - assert(cell.face_midpoints[2][0] == face_midpoint[0] - cell.x[0]); - assert(cell.face_midpoints[2][1] == face_midpoint[1] - cell.x[1]); - assert(cell.face_midpoints[2][2] == face_midpoint[2] - cell.x[2]); - - face_area = voronoi_get_box_face(VORONOI3D_BOX_TOP, face_midpoint); - assert(cell.face_areas[3] == face_area); - assert(cell.face_midpoints[3][0] == face_midpoint[0] - cell.x[0]); - assert(cell.face_midpoints[3][1] == face_midpoint[1] - cell.x[1]); - assert(cell.face_midpoints[3][2] == face_midpoint[2] - cell.x[2]); - - face_area = voronoi_get_box_face(VORONOI3D_BOX_BACK, face_midpoint); - assert(cell.face_areas[4] == face_area); - assert(cell.face_midpoints[4][0] == face_midpoint[0] - cell.x[0]); - assert(cell.face_midpoints[4][1] == face_midpoint[1] - cell.x[1]); - assert(cell.face_midpoints[4][2] == face_midpoint[2] - cell.x[2]); - - face_area = voronoi_get_box_face(VORONOI3D_BOX_RIGHT, face_midpoint); - assert(cell.face_areas[5] == face_area); - assert(cell.face_midpoints[5][0] == face_midpoint[0] - cell.x[0]); - assert(cell.face_midpoints[5][1] == face_midpoint[1] - cell.x[1]); - assert(cell.face_midpoints[5][2] == face_midpoint[2] - cell.x[2]); -} - -void test_paths(void) { - float u, l, q; - int up, us, uw, lp, ls, lw, qp, qs, qw; - float r2, dx[3]; - struct voronoi_cell cell; - - /* PATH 1.0 */ - // the first vertex is above the cutting plane and its first edge is below the - // plane - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = -1.0f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.nvert = 2; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.edges[0] = 1; - cell.edgeindices[0] = 0; - cell.edges[3] = 0; - cell.edgeindices[3] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 0); - assert(us == 0); - assert(uw == 1); - assert(u == 0.25f); - assert(lp == 1); - assert(ls == 0); - assert(lw == -1); - assert(l == -0.75f); - } - - /* PATH 1.1 */ - // the first vertex is above the cutting plane and its second edge is below - // the plane - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 2.0f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = -1.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.nvert = 3; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 6; - cell.edges[0] = 1; - cell.edges[1] = 2; - cell.edges[6] = 0; - cell.edgeindices[1] = 0; - cell.edgeindices[6] = 1; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 0); - assert(us == 1); - assert(uw == 1); - assert(u == 0.25f); - assert(lp == 2); - assert(ls == 0); - assert(lw == -1); - assert(l == -0.75f); - } - - /* PATH 1.2 */ - // the first vertex is above the cutting plane and its second and last edge - // is below the plane - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 2.0f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = -1.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.nvert = 3; - cell.orders[0] = 2; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 2; - cell.offsets[2] = 5; - cell.edges[0] = 1; - cell.edges[1] = 2; - cell.edges[6] = 0; - cell.edgeindices[1] = 0; - cell.edgeindices[5] = 1; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 0); - assert(us == 1); - assert(uw == 1); - assert(u == 0.25f); - assert(lp == 2); - assert(ls == 0); - assert(lw == -1); - assert(l == -0.75f); - } - - /* PATH 1.3 */ - // the first vertex is above the cutting plane and is the closest vertex to - // the plane. The code should crash. - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 2.0f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = 2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.vertices[9] = 2.0f; - cell.vertices[10] = 0.0f; - cell.vertices[11] = 0.0f; - cell.nvert = 4; - cell.orders[0] = 3; - cell.offsets[0] = 0; - cell.edges[0] = 1; - cell.edges[1] = 2; - cell.edges[2] = 3; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == -1); - } - - /* PATH 1.4.0 */ - // first vertex is above the plane, second vertex is closer and third vertex - // lies below - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 0.75f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = -1.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.nvert = 3; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 6; - cell.edges[0] = 1; - cell.edges[3] = 2; - cell.edges[6] = 1; - cell.edgeindices[0] = 2; - cell.edgeindices[3] = 0; - cell.edgeindices[6] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 1); - assert(us == 0); - assert(u == 0.125f); - assert(lp == 2); - assert(ls == 0); - assert(l == -0.75f); - } - - /* PATH 1.4.1 */ - // first vertex is above the plane, second vertex is closer and fourth vertex - // is below - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 0.75f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = 2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.vertices[9] = -1.0f; - cell.vertices[10] = 0.0f; - cell.vertices[11] = 0.0f; - cell.nvert = 4; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.orders[3] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 6; - cell.offsets[3] = 9; - cell.edges[0] = 1; - cell.edges[3] = 2; - cell.edges[4] = 3; - cell.edges[5] = 0; - cell.edges[6] = 1; - cell.edges[9] = 1; - // this is the only difference between PATH 1.4.1 and PATH 1.4.2 - cell.edgeindices[0] = 3; - cell.edgeindices[3] = 0; - cell.edgeindices[4] = 0; - cell.edgeindices[9] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 1); - assert(us == 1); - assert(u == 0.125f); - assert(lp == 3); - assert(ls == 0); - assert(l == -0.75f); - } - - /* PATH 1.4.2 */ - // first vertex is above the plane, second is closer, fourth is below - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 0.75f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = 2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.vertices[9] = -1.0f; - cell.vertices[10] = 0.0f; - cell.vertices[11] = 0.0f; - cell.nvert = 4; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.orders[3] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 6; - cell.offsets[3] = 9; - cell.edges[0] = 1; - cell.edges[3] = 2; - cell.edges[4] = 3; - cell.edges[5] = 0; - cell.edges[6] = 1; - cell.edges[9] = 1; - // this is the only difference between PATH 1.4.1 and PATH 1.4.2 - cell.edgeindices[0] = 2; - cell.edgeindices[3] = 1; - cell.edgeindices[4] = 0; - cell.edgeindices[9] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 1); - assert(us == 1); - assert(u == 0.125f); - assert(lp == 3); - assert(ls == 0); - assert(l == -0.75f); - } - - /* PATH 1.4.3 */ - // first vertex is above the plane, second is closer, third is below - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 0.75f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = -1.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.nvert = 3; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 6; - cell.edges[0] = 1; - // this is the difference between PATH 1.4.0 and this path - cell.edges[3] = 0; - cell.edges[4] = 2; - cell.edges[6] = 1; - cell.edgeindices[0] = 0; - cell.edgeindices[3] = 0; - cell.edgeindices[6] = 1; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 1); - assert(us == 1); - assert(u == 0.125f); - assert(lp == 2); - assert(ls == 0); - assert(l == -0.75f); - } - - /* PATH 1.4.4 */ - // first vertex is above the plane, second is closer, fourth is below - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 0.75f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = 2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.vertices[9] = -1.0f; - cell.vertices[10] = 0.0f; - cell.vertices[11] = 0.0f; - cell.nvert = 4; - cell.orders[0] = 3; - cell.orders[1] = 4; - cell.orders[2] = 3; - cell.orders[3] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 7; - cell.offsets[3] = 10; - cell.edges[0] = 1; - cell.edges[3] = 0; - cell.edges[4] = 2; - cell.edges[5] = 3; - cell.edges[7] = 1; - cell.edges[10] = 1; - cell.edgeindices[0] = 0; - cell.edgeindices[3] = 0; - cell.edgeindices[5] = 0; - cell.edgeindices[10] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 1); - assert(us == 2); - assert(u == 0.125f); - assert(lp == 3); - assert(ls == 0); - assert(l == -0.75f); - } - - /* PATH 1.4.5 */ - // same as 1.4.4, but with an order 3 second vertex - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 0.75f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = 2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.vertices[9] = -1.0f; - cell.vertices[10] = 0.0f; - cell.vertices[11] = 0.0f; - cell.nvert = 4; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.orders[3] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 6; - cell.offsets[3] = 9; - cell.edges[0] = 1; - cell.edges[3] = 0; - cell.edges[4] = 2; - cell.edges[5] = 3; - cell.edges[6] = 1; - cell.edges[9] = 1; - cell.edgeindices[0] = 0; - cell.edgeindices[3] = 0; - cell.edgeindices[5] = 0; - cell.edgeindices[9] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 1); - assert(us == 2); - assert(u == 0.125f); - assert(lp == 3); - assert(ls == 0); - assert(l == -0.75f); - } - - /* PATH 1.4.6 */ - // first vertex is above the plane, second is closer and is the closest - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 0.75f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = 2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.nvert = 3; - cell.orders[0] = 3; - cell.orders[1] = 2; - cell.orders[2] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 5; - cell.edges[0] = 1; - cell.edges[3] = 0; - cell.edges[4] = 2; - cell.edgeindices[0] = 0; - cell.edgeindices[3] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == -1); - } - - /* PATH 1.5 */ - // first vertex is above the plane, second vertex is too close to call - { - cell.vertices[0] = 1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 0.5f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.nvert = 2; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.edges[0] = 1; - cell.edges[3] = 0; - cell.edgeindices[0] = 0; - cell.edgeindices[3] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 2); - assert(up == 1); - } - - /* PATH 2.0 */ - // the first vertex is below the plane and its first edge is above the plane - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 1.0f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.nvert = 2; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.edges[0] = 1; - cell.edgeindices[0] = 0; - cell.edges[3] = 0; - cell.edgeindices[3] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 1); - assert(us == 0); - assert(uw == -1); - assert(u == 0.25f); - assert(lp == 0); - assert(ls == 0); - assert(qw == 1); - assert(l == -0.75f); - } - - /* PATH 2.1 */ - // the first vertex is below the plane and its second edge is above the plane - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = -2.0f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = 1.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.nvert = 3; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 6; - cell.edges[0] = 1; - cell.edges[1] = 2; - cell.edges[6] = 0; - cell.edgeindices[1] = 0; - cell.edgeindices[6] = 1; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 2); - assert(us == 0); - assert(uw == -1); - assert(u == 0.25f); - assert(lp == 0); - assert(ls == 1); - assert(qw == 1); - assert(l == -0.75f); - } - - /* PATH 2.2 */ - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = -2.0f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = 1.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.nvert = 3; - cell.orders[0] = 2; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 2; - cell.offsets[2] = 5; - cell.edges[0] = 1; - cell.edges[1] = 2; - cell.edges[5] = 0; - cell.edgeindices[1] = 0; - cell.edgeindices[5] = 1; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 2); - assert(us == 0); - assert(uw == -1); - assert(u == 0.25f); - assert(lp == 0); - assert(ls == 1); - assert(qw == 1); - assert(l == -0.75f); - } - - /* PATH 2.3 */ - // the first vertex is below the plane and is the closest vertex to the plane - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = -2.0f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = -2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.vertices[9] = -2.0f; - cell.vertices[10] = 0.0f; - cell.vertices[11] = 0.0f; - cell.nvert = 4; - cell.orders[0] = 3; - cell.offsets[0] = 0; - cell.edges[0] = 1; - cell.edges[1] = 2; - cell.edges[2] = 3; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 0); - } - - /* PATH 2.4.0 */ - // first vertex is below the plane, second is closer and third is above - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = -0.5f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = 1.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.nvert = 3; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 6; - cell.edges[0] = 1; - cell.edges[3] = 2; - cell.edges[5] = 0; - cell.edges[6] = 1; - cell.edgeindices[0] = 2; - cell.edgeindices[3] = 0; - cell.edgeindices[5] = 0; - cell.edgeindices[6] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 2); - assert(u == 0.25f); - assert(lp == 1); - assert(l == -0.5f); - } - - /* PATH 2.4.1 */ - // first vertex is below, second is closer and fourth is above - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = -0.5f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = -2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.vertices[9] = 1.0f; - cell.vertices[10] = 0.0f; - cell.vertices[11] = 0.0f; - cell.nvert = 4; - cell.orders[0] = 3; - cell.orders[1] = 4; - cell.orders[2] = 3; - cell.orders[3] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 7; - cell.offsets[3] = 10; - cell.edges[0] = 1; - cell.edges[3] = 2; - cell.edges[4] = 3; - cell.edges[6] = 0; - cell.edges[10] = 1; - cell.edgeindices[0] = 3; - cell.edgeindices[3] = 0; - cell.edgeindices[4] = 0; - cell.edgeindices[6] = 0; - cell.edgeindices[10] = 1; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 3); - assert(us == 0); - assert(u == 0.25f); - assert(lp == 1); - assert(ls == 1); - assert(l == -0.5f); - } - - /* PATH 2.4.2 */ - // first vertex is below, second is closer and fourth is above - // same as 2.4.1, but with order 3 second vertex - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = -0.5f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = -2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.vertices[9] = 1.0f; - cell.vertices[10] = 0.0f; - cell.vertices[11] = 0.0f; - cell.nvert = 4; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.orders[3] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 6; - cell.offsets[3] = 9; - cell.edges[0] = 1; - cell.edges[3] = 2; - cell.edges[4] = 3; - cell.edges[5] = 0; - cell.edges[9] = 1; - cell.edgeindices[0] = 3; - cell.edgeindices[3] = 0; - cell.edgeindices[4] = 0; - cell.edgeindices[5] = 0; - cell.edgeindices[9] = 1; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 3); - assert(us == 0); - assert(u == 0.25f); - assert(lp == 1); - assert(ls == 1); - assert(l == -0.5f); - } - - /* PATH 2.4.3 */ - // first vertex is below, second is closer, third is above - // first vertex is first edge of second - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = -0.5f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = 1.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.nvert = 3; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 6; - cell.edges[0] = 1; - cell.edges[3] = 0; - cell.edges[4] = 2; - cell.edges[6] = 1; - cell.edgeindices[0] = 0; - cell.edgeindices[3] = 0; - cell.edgeindices[4] = 0; - cell.edgeindices[6] = 1; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 2); - assert(us == 0); - assert(u == 0.25f); - assert(lp == 1); - assert(ls == 1); - assert(l == -0.5f); - } - - /* PATH 2.4.4 */ - // first vertex is below, second is closer, fourth is above - // first vertex is first edge of second - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = -0.5f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = -2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.vertices[9] = 1.0f; - cell.vertices[10] = 0.0f; - cell.vertices[11] = 0.0f; - cell.nvert = 4; - cell.orders[0] = 3; - cell.orders[1] = 4; - cell.orders[2] = 3; - cell.orders[3] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 7; - cell.offsets[3] = 10; - cell.edges[0] = 1; - cell.edges[3] = 0; - cell.edges[4] = 2; - cell.edges[5] = 3; - cell.edges[10] = 1; - cell.edgeindices[0] = 0; - cell.edgeindices[3] = 0; - cell.edgeindices[5] = 0; - cell.edgeindices[10] = 2; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 3); - assert(us == 0); - assert(u == 0.25f); - assert(lp == 1); - assert(ls == 2); - assert(l == -0.5f); - } - - /* PATH 2.4.5 */ - // first vertex is below, second is closer, fourth is above - // first vertex is first edge of second - // second vertex is order 3 vertex (and not order 4 like 2.4.4) - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = -0.5f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = -2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.vertices[9] = 1.0f; - cell.vertices[10] = 0.0f; - cell.vertices[11] = 0.0f; - cell.nvert = 4; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.orders[2] = 3; - cell.orders[3] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 6; - cell.offsets[3] = 9; - cell.edges[0] = 1; - cell.edges[3] = 0; - cell.edges[4] = 2; - cell.edges[5] = 3; - cell.edges[9] = 1; - cell.edgeindices[0] = 0; - cell.edgeindices[3] = 0; - cell.edgeindices[5] = 0; - cell.edgeindices[9] = 2; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 1); - assert(up == 3); - assert(us == 0); - assert(u == 0.25f); - assert(lp == 1); - assert(ls == 2); - assert(l == -0.5f); - } - - /* PATH 2.4.6 */ - // first vertex is below, second is closer and is closest - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = -0.5f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.vertices[6] = -2.0f; - cell.vertices[7] = 0.0f; - cell.vertices[8] = 0.0f; - cell.nvert = 3; - cell.orders[0] = 3; - cell.orders[1] = 2; - cell.orders[2] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.offsets[2] = 5; - cell.edges[0] = 1; - cell.edges[3] = 0; - cell.edges[4] = 2; - cell.edgeindices[0] = 0; - cell.edgeindices[3] = 0; - cell.edgeindices[4] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 0); - } - - /* PATH 2.5 */ - // first vertex is below, second is too close to call - { - cell.vertices[0] = -1.0f; - cell.vertices[1] = 0.0f; - cell.vertices[2] = 0.0f; - cell.vertices[3] = 0.5f; - cell.vertices[4] = 0.0f; - cell.vertices[5] = 0.0f; - cell.nvert = 2; - cell.orders[0] = 3; - cell.orders[1] = 3; - cell.offsets[0] = 0; - cell.offsets[1] = 3; - cell.edges[0] = 1; - cell.edges[3] = 0; - cell.edgeindices[0] = 0; - cell.edgeindices[3] = 0; - dx[0] = 0.5f; - dx[1] = 0.0f; - dx[2] = 0.0f; - r2 = 0.25f; - int result = voronoi_intersect_find_closest_vertex( - &cell, dx, r2, &u, &up, &us, &uw, &l, &lp, &ls, &lw, &q, &qp, &qs, &qw); - assert(result == 2); - } -} - -#ifdef SHADOWFAX_SPH -void set_coordinates(struct part *p, double x, double y, double z, - unsigned int id) { - - double box_anchor[3] = {VORONOI3D_BOX_ANCHOR_X, VORONOI3D_BOX_ANCHOR_Y, - VORONOI3D_BOX_ANCHOR_Z}; - double box_side[3] = {VORONOI3D_BOX_SIDE_X, VORONOI3D_BOX_SIDE_Y, - VORONOI3D_BOX_SIDE_Z}; - - p->x[0] = x; - p->x[1] = y; - p->x[2] = z; - p->id = id; - voronoi_cell_init(&p->cell, p->x, box_anchor, box_side); -} -#endif - -void test_degeneracies(void) { -#ifdef SHADOWFAX_SPH - int idx = 0; - /* make a small cube */ - struct part particles[100]; - set_coordinates(&particles[idx], 0.1, 0.1, 0.1, idx); - idx++; - set_coordinates(&particles[idx], 0.2, 0.1, 0.1, idx); - idx++; - set_coordinates(&particles[idx], 0.1, 0.2, 0.1, idx); - idx++; - set_coordinates(&particles[idx], 0.1, 0.1, 0.2, idx); - idx++; - /* corner on cutting plane */ - set_coordinates(&particles[idx], 0.2, 0.2, 0.2, idx); - idx++; - /* edge on cutting plane */ - set_coordinates(&particles[idx], 0.2, 0.1, 0.2, idx); - idx++; - set_coordinates(&particles[idx], 0.2, 0.2, 0.1, idx); - idx++; - /* cutting plane is diagonal */ - set_coordinates(&particles[idx], 0.05, 0.1, 0.05, idx); - idx++; - /* order 4 vertex (found after an impressive display of analytical geometry - of which I'm rather proud) */ - float t = 0.5 / 0.0475; - set_coordinates(&particles[idx], 0.0075 * t + 0.1, 0.0075 * t + 0.1, - 0.1 - 0.0025 * t, idx); - idx++; - /* order 4 vertex with float edge */ - t = 0.35 / 0.06125; - set_coordinates(&particles[idx], 0.0075 * t + 0.1, 0.015 * t + 0.1, - 0.1 - 0.005 * t, idx); - idx++; - /* plane that was already encountered */ - t = 0.5 / 0.0475; - set_coordinates(&particles[idx], 0.0075 * t + 0.1, 0.0075 * t + 0.1, - 0.1 - 0.0025 * t, idx); - idx++; - /* no intersection (just to cover all code) */ - set_coordinates(&particles[idx], 0.3, 0.3, 0.3, idx); - idx++; - set_coordinates(&particles[idx], 0.3, 0.1, 0.3, idx); - idx++; - /* order 5 vertex */ - t = 0.04 / 0.0175; - set_coordinates(&particles[idx], 0.1 - 0.0075 * t, 0.1 + 0.00375 * t, - 0.1 + 0.00625 * t, idx); - idx++; - /* plane with order 5 vertex */ - set_coordinates(&particles[idx], 0.1, 0.2, 0.1, idx); - idx++; - /* edge with order 5 vertex that looses an edge */ - t = -0.1 / 0.095; - set_coordinates(&particles[idx], 0.1 - 0.015 * t, 0.1 + 0.015 * t, - 0.1 - 0.005 * t, idx); - idx++; - for (int i = 1; i < idx; i++) { - float dx[3]; - dx[0] = particles[0].x[0] - particles[i].x[0]; - dx[1] = particles[0].x[1] - particles[i].x[1]; - dx[2] = particles[0].x[2] - particles[i].x[2]; - voronoi_cell_interact(&particles[0].cell, dx, particles[i].id); - } -#endif -} - -int main(int argc, char *argv[]) { - - /* Set the all enclosing simulation box dimensions */ - double box_anchor[3] = {VORONOI3D_BOX_ANCHOR_X, VORONOI3D_BOX_ANCHOR_Y, - VORONOI3D_BOX_ANCHOR_Z}; - double box_side[3] = {VORONOI3D_BOX_SIDE_X, VORONOI3D_BOX_SIDE_Y, - VORONOI3D_BOX_SIDE_Z}; - - /* Check basic Voronoi cell functions */ - test_voronoi_volume_tetrahedron(); - test_voronoi_centroid_tetrahedron(); - test_calculate_cell(); - - /* Test the different paths */ - test_paths(); - - /* Test the interaction and geometry algorithms */ - { - /* Create a Voronoi cell */ - double x[3] = {0.5f, 0.5f, 0.5f}; - struct voronoi_cell cell; - voronoi_cell_init(&cell, x, box_anchor, box_side); - - /* Interact with neighbours */ - float x0[3] = {0.5f, 0.0f, 0.0f}; - float x1[3] = {-0.5f, 0.0f, 0.0f}; - float x2[3] = {0.0f, 0.5f, 0.0f}; - float x3[3] = {0.0f, -0.5f, 0.0f}; - float x4[3] = {0.0f, 0.0f, 0.5f}; - float x5[3] = {0.0f, 0.0f, -0.5f}; - voronoi_cell_interact(&cell, x0, 1); - voronoi_cell_interact(&cell, x1, 2); - voronoi_cell_interact(&cell, x2, 3); - voronoi_cell_interact(&cell, x3, 4); - voronoi_cell_interact(&cell, x4, 5); - voronoi_cell_interact(&cell, x5, 6); - float expected_midpoints[6][3], expected_areas[6]; - expected_areas[0] = 0.25f; - expected_midpoints[0][0] = 0.25f; - expected_midpoints[0][1] = 0.5f; - expected_midpoints[0][2] = 0.5f; - expected_areas[1] = 0.25f; - expected_midpoints[1][0] = 0.75f; - expected_midpoints[1][1] = 0.5f; - expected_midpoints[1][2] = 0.5f; - expected_areas[2] = 0.25f; - expected_midpoints[2][0] = 0.5f; - expected_midpoints[2][1] = 0.25f; - expected_midpoints[2][2] = 0.5f; - expected_areas[3] = 0.25f; - expected_midpoints[3][0] = 0.5f; - expected_midpoints[3][1] = 0.75f; - expected_midpoints[3][2] = 0.5f; - expected_areas[4] = 0.25f; - expected_midpoints[4][0] = 0.5f; - expected_midpoints[4][1] = 0.5f; - expected_midpoints[4][2] = 0.25f; - expected_areas[5] = 0.25f; - expected_midpoints[5][0] = 0.5f; - expected_midpoints[5][1] = 0.5f; - expected_midpoints[5][2] = 0.75f; - - /* Interact with some more neighbours to check if they are properly - ignored */ - float xE0[3] = {0.6f, 0.0f, 0.1f}; - float xE1[3] = {-0.7f, 0.2f, 0.04f}; - voronoi_cell_interact(&cell, xE0, 7); - voronoi_cell_interact(&cell, xE1, 8); - - /* Finalize cell and check results */ - voronoi_cell_finalize(&cell); - - if (fabs(cell.volume - 0.125f) > 1.e-5) { - error("Wrong volume: %g!", cell.volume); - } - if (fabs(cell.centroid[0] - 0.5f) > 1.e-5f || - fabs(cell.centroid[1] - 0.5f) > 1.e-5f || - fabs(cell.centroid[2] - 0.5f) > 1.e-5f) { - error("Wrong centroid: %g %g %g!", cell.centroid[0], cell.centroid[1], - cell.centroid[2]); - } - - /* Check faces. */ - float A, midpoint[3]; - for (int i = 0; i < 6; ++i) { - A = voronoi_get_face(&cell, i + 1, midpoint); - if (A) { - if (fabs(A - expected_areas[i]) > 1.e-5) { - error("Wrong surface area: %g!", A); - } - if (fabs(midpoint[0] - expected_midpoints[i][0] + cell.x[0]) > 1.e-5 || - fabs(midpoint[1] - expected_midpoints[i][1] + cell.x[1]) > 1.e-5 || - fabs(midpoint[2] - expected_midpoints[i][2] + cell.x[2]) > 1.e-5) { - error("Wrong face midpoint: %g %g %g (should be %g %g %g)!", - midpoint[0], midpoint[1], midpoint[2], expected_midpoints[i][0], - expected_midpoints[i][1], expected_midpoints[i][2]); - } - } else { - error("Neighbour %i not found!", i); - } - } - } - - /* Test degenerate cases */ - test_degeneracies(); - - /* Construct a small random grid */ - { - message("Constructing a small random grid..."); - - int i, j; - double x[3]; - float dx[3]; - float Vtot; - struct voronoi_cell cells[TESTVORONOI3D_NUMCELL_RANDOM]; - struct voronoi_cell *cell_i, *cell_j; - - /* initialize cells with random generator locations */ - for (i = 0; i < TESTVORONOI3D_NUMCELL_RANDOM; ++i) { - x[0] = random_uniform(0., 1.); - x[1] = random_uniform(0., 1.); - x[2] = random_uniform(0., 1.); - voronoi_cell_init(&cells[i], x, box_anchor, box_side); - } - - /* interact the cells */ - for (i = 0; i < TESTVORONOI3D_NUMCELL_RANDOM; ++i) { - cell_i = &cells[i]; - for (j = 0; j < TESTVORONOI3D_NUMCELL_RANDOM; ++j) { - if (i != j) { - cell_j = &cells[j]; - dx[0] = cell_i->x[0] - cell_j->x[0]; - dx[1] = cell_i->x[1] - cell_j->x[1]; - dx[2] = cell_i->x[2] - cell_j->x[2]; - voronoi_cell_interact(cell_i, dx, j); - } - } - } - - Vtot = 0.0f; - /* print the cells to the stdout */ - for (i = 0; i < TESTVORONOI3D_NUMCELL_RANDOM; ++i) { - /* voronoi_print_gnuplot_c(&cells[i]);*/ - voronoi_cell_finalize(&cells[i]); - Vtot += cells[i].volume; - } - - assert(fabs(Vtot - 1.0f) < 1.e-6); - - message("Done."); - } - - /* Construct a small Cartesian grid full of degeneracies */ - { - message("Constructing a Cartesian grid..."); - - int i, j, k; - double x[3]; - float dx[3]; - float Vtot; - struct voronoi_cell cells[TESTVORONOI3D_NUMCELL_CARTESIAN_3D]; - struct voronoi_cell *cell_i, *cell_j; - - /* initialize cells with Cartesian generator locations */ - for (i = 0; i < TESTVORONOI3D_NUMCELL_CARTESIAN_1D; ++i) { - for (j = 0; j < TESTVORONOI3D_NUMCELL_CARTESIAN_1D; ++j) { - for (k = 0; k < TESTVORONOI3D_NUMCELL_CARTESIAN_1D; ++k) { - x[0] = (i + 0.5f) * 1.0 / TESTVORONOI3D_NUMCELL_CARTESIAN_1D; - x[1] = (j + 0.5f) * 1.0 / TESTVORONOI3D_NUMCELL_CARTESIAN_1D; - x[2] = (k + 0.5f) * 1.0 / TESTVORONOI3D_NUMCELL_CARTESIAN_1D; - voronoi_cell_init(&cells[TESTVORONOI3D_NUMCELL_CARTESIAN_1D * - TESTVORONOI3D_NUMCELL_CARTESIAN_1D * i + - TESTVORONOI3D_NUMCELL_CARTESIAN_1D * j + k], - x, box_anchor, box_side); - } - } - } - - /* interact the cells */ - for (i = 0; i < TESTVORONOI3D_NUMCELL_CARTESIAN_3D; ++i) { - cell_i = &cells[i]; - for (j = 0; j < TESTVORONOI3D_NUMCELL_CARTESIAN_3D; ++j) { - if (i != j) { - cell_j = &cells[j]; - dx[0] = cell_i->x[0] - cell_j->x[0]; - dx[1] = cell_i->x[1] - cell_j->x[1]; - dx[2] = cell_i->x[2] - cell_j->x[2]; - voronoi_cell_interact(cell_i, dx, j); - } - } - } - - Vtot = 0.0f; - /* print the cells to the stdout */ - for (i = 0; i < TESTVORONOI3D_NUMCELL_CARTESIAN_3D; ++i) { - /* voronoi_print_gnuplot_c(&cells[i]);*/ - voronoi_cell_finalize(&cells[i]); - Vtot += cells[i].volume; - } - - message("Vtot: %g (Vtot-1.0f: %g)", Vtot, (Vtot - 1.0f)); - assert(fabs(Vtot - 1.0f) < 2.e-6); - - message("Done."); - } - - return 0; -} diff --git a/theory/MovingMesh/ShadowSWIFT/implementation-details.tex b/theory/MovingMesh/ShadowSWIFT/implementation-details.tex new file mode 100644 index 0000000000000000000000000000000000000000..9d970d6d5a7bdf0618e6a8c5581edd8f8d97430c --- /dev/null +++ b/theory/MovingMesh/ShadowSWIFT/implementation-details.tex @@ -0,0 +1,39 @@ +\include{./header.tex} % should be present in directory where build command is invoked + + + +%------------------------------------------ +%: Metadata +%------------------------------------------ + +\title{ShadowSWIFT: Moving mesh hydrodynamics in SWIFT} +\subtitle{(Implementation details)} +\author{Yolan Uyttenhove (Yolan.Uyttenhove@UGent.be)} +\date{} + +%------------------------------------------ + + + + + + +\begin{document} + +%-------------------------------------------- +% Stuff that needs to be done before all else +%-------------------------------------------- +%\pagestyle{plain} +%\nocite{*} % show all entries of bibliography, even if they are not cited. + + + +%Titlepage +\maketitle + + + + +\textbf{TODO} + +\end{document} \ No newline at end of file diff --git a/theory/MovingMesh/VoronoiConstruction/construction-level.tex b/theory/MovingMesh/VoronoiConstruction/construction-level.tex new file mode 100644 index 0000000000000000000000000000000000000000..d13dbc1c32497da47ed6a982c83b55b100b8628e --- /dev/null +++ b/theory/MovingMesh/VoronoiConstruction/construction-level.tex @@ -0,0 +1,61 @@ + + +\section{Setting the construction level for the Voronoi grid} + +Like (almost) everything, the construction of the Voronoi grid in SWIFT happens at the granularity of SWIFT cells. +No global Voronoi grid is constructed, but parts of the Voronoi grid are constructed independently for SWIFT cells ensuring that neighbouring cells' Voronoi grids mesh together properly. + +The chosen construction algorithm, stores the grid information in a cell-global data structure (earlier attempts storing the grid information in the particles resulted in an unusably large memory overhead and very slow performance). +This means that the Voronoi grid must be stored at some unique level in the AMR tree of SWIFT cells at every location. +Special care must be taken when selecting this level. The following constraints \emph{must} be fulfilled: +\begin{enumerate} + \item The cell has neighbouring cells on the same level in every direction + \item The part of the Voronoi grid of the SWIFT cell only depends on the locations of the particles of the SWIFT cell and those of its neighbours. +\end{enumerate} + +Specifically, these constraints are needed to be able to identify the neighbouring particles of faces between a local particle of a cell and a particle of a neighbouring cell, which we will call \emph{ghost particles}, e.g. when exchanging fluxes. While this can be achieved using pointers on a single node application, when running SWIFT on multiple nodes over MPI, these pointers would no longer be valid when they are sent to the other node, so we instead store the \texttt{sid} of the neighbouring cell and the index of the ghost particle in that neighbouring cell in the faces. + +Besides those constraints, we also try to balance the following opposing effects: +\begin{itemize} + \item We want to construct the parts of the Voronoi grid at a low enough level to achieve fine grained tasking and good load balancing + \item Constructing the Voronoi grid in very small parts (i.e. at a very low level in the AMR tree) increases overhead, as faces between neighbouring SWIFT cells (boundary faces) are constructed twice (once for the part of the Voronoi grid of each cell). The fewer particles there are in the SWIFT cells, the more boundary faces relative to internal faces. +\end{itemize} + +\subsection{The definition of completeness} +We define a SWIFT cell as \emph{complete} if it satisfies both of the above constraints. +It is intuitively clear that if there are not enough particles in the cells, it could happen that information from particles two cells over might be needed to correctly construct the Voronoi grid of a SWIFT cell. Figure \ref{fig:problem-completeness} shows an example in 2D where the second constrained is not fulfilled. +It can be proven that a sufficient condition for completeness in three dimensions, and for cubic cells, is that a SWIFT cell and its neighbours all have at least one particle in every $1/27^\text{th}$ sub-cube obtained by dividing the cell in three along every direction. + +\begin{figure} + \centering + \input{VoronoiConstruction/figures/problem.tex} + \caption{2D example of a problematic configuration arising from SWIFT cells containing too few particles. The dual Delaunay tessellation of the Voronoi grid is drawn. Any particle inside the red circle could influence the Voronoi cell of particle A in the blue SWIFT cell, even particle D, which is not part of a directly neighbouring SWIFT cell of the blue cell, violating the second constraint.} + \label{fig:problem-completeness} +\end{figure} + +To determine the completeness of every cell in the AMR tree, we first set the \emph{self-completeness} for every cell. This is just whether the cell itself has at least one particle in every $1/27^\text{th}$ sub-cube. This is done recursively: once all 8 subcells of a SWIFT cell are self-complete, the cell itself is also self complete. +The self-completeness flag should be invalidated when drifting the particles. + +Once the self-completeness flags have been set, and communicated over MPI if necessary, each cell's completeness (\texttt{true} or \texttt{false}) is initialized according to its self-completeness flag. We then recursively check all pairs of neighbouring cells and invalidate the completeness flag of a cell as soon as one of its neighbours is not self-complete. Note that technically, the cell might in theory still be complete at this point (i.e. only depend on particles from its neighbouring cells), but there is no easy way to check this in practice, so we impose this slightly more stringent constraint. + +If the \texttt{SHADOWSWIFT\_RELAXED\_COMPLETENESS} directive is defined, we use a slightly more relaxed condition. We only invalidate the completeness of a SWIFT cell \texttt{ci}, if it has a neighbouring cell \texttt{cj} that is not self-complete \emph{and} the maximal search radius of any particle in \texttt{ci} is smaller than half the width of \texttt{cj}, indicating there are still enough particles close-by to completely determine \texttt{ci}'s Voronoi grid and consider the pair (\texttt{ci}, \texttt{cj}) complete. + + +\subsection{Setting the construction level based on completeness} \label{sec:construction-level} + +Once the completeness is set for every swift cell in the AMR tree, the \emph{construction level}, i.e. the level at which the Voronoi grid will be stored and constructed, can be decided. This is again done recursively. Starting from the top level cells, we recurse down and set the construction level at the the first level where one of the following conditions is true: +\begin{enumerate} + \item The current cell does not have subcells + \item The current cell does not have 8 complete subcells + \item The current cell has fewer than \texttt{grid\_split\_threshold} particles +\end{enumerate} +Cells below the construction level store a pointer to their parent cell on the construction level, cells above the construction level store \texttt{NULL} (similar to how the \texttt{super} levels work, but note that the construction level is decided \emph{before} any tasks are constructed). +Ideally, to have the most flexibility when setting the construction level, \texttt{grid\_split\_threshold} should be larger than \texttt{cell\_split\_size} and the latter should be set to a pretty small value (e.g. 40 instead of the default 400). An example of the resulting configuration of construction levels is shown in Figure \ref{fig:construction-level} + +\begin{figure} + \centering + \input{VoronoiConstruction/figures/construction_level} + \caption{Example configuration. Thick colored borders indicate a level on which the Voronoi grid will be constructed and the appropriately colored shaded areas depict the neighbouring used for construction. Cells always and only use information of neighbouring cells on the same level in the AMR tree (diagonal dependencies have been omitted for clarity). Grid construction may happen at any level in the AMR tree depending on the conditions discussed in \S\ref{sec:construction-level}.} + \label{fig:construction-level} +\end{figure} + diff --git a/theory/MovingMesh/VoronoiConstruction/current-status.tex b/theory/MovingMesh/VoronoiConstruction/current-status.tex new file mode 100644 index 0000000000000000000000000000000000000000..ab81bbc7e2e785a095c7a71644c0b036deec93b2 --- /dev/null +++ b/theory/MovingMesh/VoronoiConstruction/current-status.tex @@ -0,0 +1,13 @@ +\section{Current status (merging in master)} +The following is implemented/being merged: +\begin{itemize} + \item Setting completeness of cells + \item Communicating completeness over MPI + \item Setting Construction level +\end{itemize} +The following is not yet being merged: +\begin{itemize} + \item Invalidating completeness on drifting + \item Creating and scheduling tasks for grid construction + \item Implementation of construction tasks +\end{itemize} \ No newline at end of file diff --git a/theory/MovingMesh/VoronoiConstruction/figures/construction_level.tex b/theory/MovingMesh/VoronoiConstruction/figures/construction_level.tex new file mode 100644 index 0000000000000000000000000000000000000000..6a6c39d8135cdad5527c9c2cf90bd36be4b4cdba --- /dev/null +++ b/theory/MovingMesh/VoronoiConstruction/figures/construction_level.tex @@ -0,0 +1,45 @@ +\begin{tikzpicture} + % Shaded areas + \fill[violet, opacity=0.05] (4, 4) rectangle (8, 8) (0, 0) rectangle (4, 4) (8, 0) rectangle (12, 4); + \shade[top color=violet,bottom color=white, opacity=0.05] (4, 0) rectangle (8, -1); + \fill[orange, opacity=0.1] (8, 2) rectangle (10, 4) (10, 4) rectangle (12, 6) (6, 4) rectangle (8, 6) (8, 6) rectangle (10, 8); + \fill[teal, opacity=0.1] (9, 1) rectangle (10, 2) (10, 2) rectangle (11, 3) (10, 0) rectangle (11, 1) (11, 1) rectangle (12, 2); + + % Grid + \draw[line width=1pt] (0,0) grid[step=4] (12,8); + \draw[dashed, line width=0.5pt] (4.001,0.0001) grid[step=2] (8.999,7.999); + \draw[line width=1pt] (8.001,0.001) grid[step=2] (9.999,7.999); + \draw[dashed, line width=0.5pt] (8.001,0.001) grid[step=1] (9.999,7.999); + \draw[line width=1pt] (10.001,0.001) grid[step=1] (11.999,7.999); + + % Arrows + \draw[line width=1.5pt, violet] (4, 0) grid[step=4] (8, 4); + \draw[violet, ->, line width=1.5pt] (2, 2) to[bend left=15] (5.9, 2); + \draw[violet, ->, line width=1.5pt] (10, 2) to[bend right=15] (6.1, 2); + \draw[violet, ->, line width=1.5pt] (6, 6) to[bend left=15] (6, 2.1); + \draw[violet, ->, line width=1.5pt] (6, -1) to[bend right=15] (6, 1.9); + + \draw[line width=1.5pt, orange] (8, 4) grid[step=2] (10, 6); + \draw[orange, ->, line width=1.5pt] (7, 5) to[bend left=15] (8.95, 5); + \draw[orange, ->, line width=1.5pt] (9, 7) to[bend left=15] (9, 5.05); + \draw[orange, ->, line width=1.5pt] (11, 5) to[bend right=15] (9.05, 5); + \draw[orange, ->, line width=1.5pt] (9, 3) to[bend right=15] (9, 4.95); + + \draw[line width=1.5pt, teal] (10, 1) grid[step=1] (11, 2); + \draw[teal, ->, line width=1.25pt] (9.5, 1.5) to[bend left=15] (10.45, 1.5); + \draw[teal, <-, line width=1.25pt] (10.5, 1.55) to[bend right=15] (10.5, 2.5); + \draw[teal, <-, line width=1.25pt] (10.55, 1.5) to[bend left=15] (11.5, 1.5); + \draw[teal, <-, line width=1.25pt] (10.5, 1.45) to[bend left=15] (10.5, 0.5); + + % Fadings + \draw[line width=1pt, path fading=south] (0, -1) grid[step=4] (12, 0); + \draw[dashed, line width=0.5pt, path fading=south] (4.1, -1) grid[step=2] (7.9, 0); + \draw[dashed, line width=0.5pt, path fading=south] (8.1, -0.95) grid[step=1] (9.9, 0); + \draw[line width=1pt, path fading=south] (10, -0.95) grid[step=1] (11.9, 0); + \draw[black, line width=1pt, path fading=east] (12, 0) grid[step=1] (12.8, 8); + \draw[black, line width=1pt, path fading=north] (0, 8) grid[step=4] (12, 9); + \draw[dashed, line width=0.5pt, path fading=north] (4.1, 8) grid[step=2] (7.9, 9); + \draw[dashed, line width=0.5pt, path fading=north] (8.1, 8) grid[step=1] (9.9, 8.95); + \draw[line width=1pt, path fading=north] (10, 8) grid[step=1] (11.9, 8.95); + \draw[black, line width=1pt, path fading=west] (-1, 0) grid[step=4] (0, 8); +\end{tikzpicture} \ No newline at end of file diff --git a/theory/MovingMesh/VoronoiConstruction/figures/problem.tex b/theory/MovingMesh/VoronoiConstruction/figures/problem.tex new file mode 100644 index 0000000000000000000000000000000000000000..397c71548ee6892cb07aeb78d50d5a143bba6410 --- /dev/null +++ b/theory/MovingMesh/VoronoiConstruction/figures/problem.tex @@ -0,0 +1,22 @@ +\begin{tikzpicture}[scale=3] + \draw[step=1.0,black,thin] (0,0) grid (4,3); + \draw[line width=1pt, blue] (1,1) -- (2,1) -- (2,2) -- (1,2) -- (1,1); + \path coordinate (a) at (1.9, 1.9) + coordinate (b) at (2.5, 1.1) + coordinate (c) at (2.5, 2.7) + coordinate (d) at (2.6,0.35) + coordinate (e) at (1.5,0.4) + coordinate (f) at (0.3,0.3) + coordinate (g) at (0.5,1.5) + coordinate (h) at (0.7,2.3) + coordinate (i) at (1.5,2.8); + \fill [radius=0.75pt] (a) circle[] node[left=1pt] {A} (b) circle[] node[left=1pt] {B} (c) circle[] node[left=1pt] {C} (d) circle[] (e) circle[] (f) circle[] (g) circle[] (h) circle[] (i) circle; + \node[circle through 3 points={a}{b}{c},draw=red]{}; + \draw [black, line width=1pt] (a) -- (b) -- (c) -- (a); + \draw [gray] (b) -- (d) -- (e) -- (b); + \draw [gray] (b) -- (d) -- (e) -- (b); + \draw [gray] (a) -- (e) -- (g) -- (f) -- (e); + \draw [gray] (a) -- (g) -- (h) -- (a); + \draw [gray] (h) -- (i) -- (a); + \draw [gray] (i) -- (c); +\end{tikzpicture} \ No newline at end of file diff --git a/theory/MovingMesh/VoronoiConstruction/implementation-details.tex b/theory/MovingMesh/VoronoiConstruction/implementation-details.tex new file mode 100644 index 0000000000000000000000000000000000000000..121851ca1ea4c5a94a33f855ae13485f712a6771 --- /dev/null +++ b/theory/MovingMesh/VoronoiConstruction/implementation-details.tex @@ -0,0 +1,48 @@ +\include{./header.tex} + + + +%------------------------------------------ +%: Metadata +%------------------------------------------ + +\title{Voronoi grid construction in SWIFT} +\subtitle{(Implementation details)} +\author{Yolan Uyttenhove (Yolan.Uyttenhove@UGent.be)} +\date{} + +%------------------------------------------ + + + + + + +\newcommand{\Aij}{$\mathbf{A}_{ij}$} +\newcommand{\Aijm}{\mathbf{A}_{ij}} % A_ij math +\newcommand{\U}{\mathbf{U}} +\newcommand{\F}{\mathbf{F}} +\newcommand{\psitilde}{\tilde{\boldsymbol{\psi}}} + + + + + +\begin{document} + +%-------------------------------------------- +% Stuff that needs to be done before all else +%-------------------------------------------- +%\pagestyle{plain} +%\nocite{*} % show all entries of bibliography, even if they are not cited. + + + +%Titlepage +\maketitle + + +\input{VoronoiConstruction/construction-level} +\input{VoronoiConstruction/current-status} + +\end{document} \ No newline at end of file diff --git a/theory/MovingMesh/header.tex b/theory/MovingMesh/header.tex new file mode 100644 index 0000000000000000000000000000000000000000..00ffbf839800a26532ac5afe324b3b0fc960ea0c --- /dev/null +++ b/theory/MovingMesh/header.tex @@ -0,0 +1,127 @@ +\documentclass[12pt, a4paper, english, singlespacing, parskip]{scrartcl} + +%\documentclass[ +%11pt, % The default document font size, options: 10pt, 11pt, 12pt +%oneside, % Two side (alternating margins) for binding by default, uncomment to switch to one side +%chapterinoneline, % Have the chapter title next to the number in one single line +%english, % ngerman for German +%singlespacing, % Single line spacing, alternatives: onehalfspacing or doublespacing +%draft, % Uncomment to enable draft mode (no pictures, no links, overfull hboxes indicated) +%nolistspacing, % If the document is onehalfspacing or doublespacing, uncomment this to set spacing in lists to single +%liststotoc, % Uncomment to add the list of figures/tables/etc to the table of contents +%toctotoc, % Uncomment to add the main table of contents to the table of contents +%parskip, % Uncomment to add space between paragraphs +%nohyperref, % Uncomment to not load the hyperref package +%headsepline, % Uncomment to get a line under the header +%]{scrartcl or scrreprt or scrbook} % The class file specifying the document structure + +\usepackage{lmodern} % Diese beiden packages sorgen für echte +\usepackage[T1]{fontenc} % Umlaute. + +\usepackage{amssymb, amsmath, color, graphicx, float, setspace, tipa} +\usepackage[utf8]{inputenc} +\usepackage[english]{babel} +\usepackage[pdfpagelabels, + pdfstartview = FitH, + bookmarksopen = true, + bookmarksnumbered = true, + linkcolor = black, + plainpages = false, + hypertexnames = false, + citecolor = black, + breaklinks]{hyperref} +\usepackage{url} +\usepackage{tikz} +\usetikzlibrary{3d,arrows,calc,fadings,through,fit,shapes.geometric} +\tikzset{circle through 3 points/.style n args={3}{% +insert path={let \p1=($(#1)!0.5!(#2)$), + \p2=($(#1)!0.5!(#3)$), + \p3=($(#1)!0.5!(#2)!1!-90:(#2)$), + \p4=($(#1)!0.5!(#3)!1!90:(#3)$), + \p5=(intersection of \p1--\p3 and \p2--\p4) + in }, +at={(\p5)}, +circle through= {(#1)} +}} + +\usepackage{authblk} % titlepage stuff +\usepackage[titletoc, title]{appendix} + +%=================== +% BIBLIOGRAPHY +%=================== +\usepackage[]{natbib} +\bibliographystyle{unsrtnat} +%DONT FORGET TO COMPILE THE BIBLIOGRAPHY WITH BIBTEX WHEN CHANGES ARE MADE. + + + + +%-------------------------------------------- +% NEW COMMANDS +%-------------------------------------------- + +\newcommand{\corresponds}{\mathrel{\widehat{=}}} % equals with hat + +\newcommand {\arctanh}{\mathrm{arctanh}} % Atanh +\newcommand{\arccot}{\mathrm{arccot }} % Acotanh + +\newcommand{\limz}[1]{\lim\limits_{#1 \rightarrow 0}} % Limes of something towars zero + +\newcommand{\bm}{\boldmath} % Bold font in math +\newcommand{\dps}{\displaystyle} + +\newcommand{\e}{\mbox{e}} % e noncursive in math mode + +\newcommand{\del}{\partial} % partial diff operator +\newcommand{\de}{\mathrm{d}} % differential d +\newcommand{\D}{\mathrm{d}} % differential d +\newcommand{\GRAD}{\mathrm{grad}\ } % gradient +\newcommand{\DIV}{\mathrm{div}\ } % divergence +\newcommand{\ROT}{\mathrm{rot}\ } % rotation + +\newcommand{\CONST}{\mathrm{const.\ }} % constant +\newcommand{\var}{\mathrm{var}} % variance + +\newcommand{\g}{^\circ} % degrees +\newcommand{\degr}{^\circ} % degrees + +\newcommand{\msol}{M_\odot} % solar mass + + +\newcommand{\x}{\mathbf{x}} % x vector +\newcommand{\xdot}{\dot{\mathbf{x}}} % x dot vector +\newcommand{\xddot}{\ddot{\mathbf{x}}} % x doubledot vector +\newcommand{\R}{\mathbf{r}} % r vector +\newcommand{\rdot}{\dot{\mathbf{r}}} % r dot vector +\newcommand{\rddot}{\ddot{\mathbf{r}}} % r doubledot vector +\newcommand{\vel}{\mathbf{v}} % v vector +\newcommand{\V}{\mathbf{v}} % v vector +\newcommand{\vdot}{\dot{\mathbf{v}}} % v dot vector +\newcommand{\vddot}{\ddot{\mathbf{v}}} % v doubledot vector + +\newcommand{\dete}{\mathrm{d}t} % dt +\newcommand{\delte}{\del t} % partial t +\newcommand{\dex}{\mathrm{d}x} % dx +\newcommand{\delx}{\del x} % partial x +\newcommand{\der}{\mathrm{d}r} % dr +\newcommand{\delr}{\del r} % partial r + +\newcommand{\deldt}{\frac{\del}{\del t}} % shortcut partial derivative, in line +\newcommand{\ddt}{\frac{\de}{\de t}} % shortcut total derivative, in line +\newcommand{\DELDT}[1]{\frac{\del #1}{\del t}} % shortcut partial derivative, on fraction +\newcommand{\DDT}[1]{\frac{\de #1}{\de t}} % shortcut total derivative, on fraction + +\newcommand{\deldx}{\frac{\del}{\del x}} % shortcut partial derivative, in line +\newcommand{\ddx}{\frac{\de}{\de x}} % shortcut total derivative, in line +\newcommand{\DELDX}[1]{\frac{\del #1}{\del x}} % shortcut partial derivative, on fraction +\newcommand{\DDX}[1]{\frac{\de #1}{\de x}} % shortcut total derivative, on fraction + +\newcommand{\deldr}{\frac{\del}{\del r}} % shortcut partial derivative, in line +\newcommand{\ddr}{\frac{\de}{\de r}} % shortcut total derivative, in line +\newcommand{\DELDR}[1]{\frac{\del #1}{\del r}} % shortcut partial derivative, on fraction +\newcommand{\DDR}[1]{\frac{\de #1}{\de r}} % shortcut total derivative, on fraction + +% replace \sum with \sum\limits +\let\oldsum\sum +\renewcommand{\sum}{\oldsum\limits} diff --git a/theory/MovingMesh/references.bib b/theory/MovingMesh/references.bib new file mode 100644 index 0000000000000000000000000000000000000000..fd40910d9e70d6412e5e9919bb62a2d649c27a7c --- /dev/null +++ b/theory/MovingMesh/references.bib @@ -0,0 +1,4 @@ + + + + diff --git a/theory/SinkParticles/ar-1col-S2O.cls b/theory/SinkParticles/ar-1col-S2O.cls new file mode 100644 index 0000000000000000000000000000000000000000..c3279597d149e03a4d54972a33bab0638c7beca3 --- /dev/null +++ b/theory/SinkParticles/ar-1col-S2O.cls @@ -0,0 +1,1162 @@ +%% ar.cls - version 1.1 +%% Aptara Inc., dated 07 Mar. 2014 +%% +%% Version 1.1 (History) +%% --------------------- +%% 1) Implemented the color.sty to avoide the +%% color related problems. +%% 2) Used the ifpdf.sty for providing the PDF Paper +%% Size in case of pdflatex. +%% 3) Seperation between author and year removed for Harvard style reference (unnumbered) +%% 4) Removed the clashing of marginnote environment with amsmath.sty +%% 5) Introduced an option to change format of Equation Number and a command ``\firstpagenote''to appear text on first page +%% +%% For AR journals +%% +%% Steps to compile: latex latex latex +%% +%% \CharacterTable +%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +%% Digits \0\1\2\3\4\5\6\7\8\9 +%% Exclamation \! Double quote \" Hash (number) \# +%% Dollar \$ Percent \% Ampersand \& +%% Acute accent \' Left paren \( Right paren \) +%% Asterisk \* Plus \+ Comma \, +%% Minus \- Point \. Solidus \/ +%% Colon \: Semicolon \; Less than \< +%% Equals \= Greater than \> Question mark \? +%% Commercial at \@ Left bracket \[ Backslash \\ +%% Right bracket \] Circumflex \^ Underscore \_ +%% Grave accent \` Left brace \{ Vertical bar \| +%% Right brace \} Tilde \~} +\NeedsTeXFormat{LaTeX2e}[1995/12/01] +\ProvidesClass{styles/ar-1col-S2O}[2013/03/07 v1.1 Standard LaTeX document class] +% +\newif\if@restonecol +\newif\if@DotinEqNum\global\@DotinEqNumtrue +% +% Global Variable Declaration +% +\def\doi#1{\gdef\@doi{#1}}\doi{}% +\def\fstpage#1{\gdef\@fstpage{#1}}\fstpage{}% +\def\endpage#1{\gdef\@endpage{#1}}\endpage{}% +\def\jvol#1{\gdef\@jvol{#1}}\jvol{00}% +\def\jyear#1{\gdef\@jyear{#1}}\jyear{0000}% +\def\jname#1{\gdef\@jname{#1}}\jname{xxxxxx}% +% +% Font size declaration +% +% \def\rhfont{\fontsize{7.5}{7.5}\itshape\selectfont} +% \def\titlefont{\fontsize{20}{24}\selectfont\bfseries\leftskip14.25pc\rightskip0\p@ plus1fill} +% \def\authorfont{\fontsize{12}{15}\selectfont\bfseries\leftskip14.25pc\rightskip0\p@ plus1fill\mathversion{bold}} +% \def\abstractfont{\fontsize{9}{12}\selectfont} +% \def\keywordsfont{\fontsize{9}{12}\selectfont\rightskip0\p@ plus1fill\mathversion{normal}} +% \def\affilfont{\fontsize{7}{9}\selectfont\leftskip14.25pc\rightskip0\p@ plus1fil} +% \def\jinfofont{\fontsize{7}{9}\selectfont\rightskip12\p@ plus1fil} +% \def\foliofont{\fontsize{7.5}{7.5}\itshape\selectfont} +% \def\figcaptionfont{\fontsize{8}{10}\selectfont\mathversion{normal}} +% \def\figcaptionnumfont{\sffamily\fontsize{8}{10}\selectfont\bfseries\mathversion{bold}} +% \def\tablecaptionfont{\fontsize{8}{12}\bfseries\selectfont\mathversion{normal}}% +% \def\tablecaptionnumfont{\fontsize{8}{12}\bfseries\selectfont} +% \def\tablefont{\fontsize{8}{11}\selectfont\mathversion{normal}}% +% \def\tabnotefont{\fontsize{7}{10}\selectfont}% +% \def\textboxfont{\normalfont\normalsize\leftskip12\p@\rightskip12\p@} +% \def\textboxheadfont{\fontsize{10}{12}\selectfont\bfseries\leftskip12\p@\rightskip12\p@ plus1fill\mathversion{normal}} +% % +% \def\sectionfont{\sffamily\fontsize{10}{12}\selectfont\bfseries\raggedright\mathversion{bold}} +% \def\subsectionfont{\sffamily\fontsize{10}{12}\selectfont\bfseries\raggedright\mathversion{bold}} +% \def\subsubsectionfont{\sffamily\fontsize{9}{12}\bfseries\selectfont\mathversion{bold}} +% \def\paragraphfont{\bfseries\itshape} +% \def\subparagraphfont{\itshape} +% \def\extractfont{\fontsize{8}{12}\selectfont\mathversion{normal}} +% \def\marginnotefont{\fontsize{8}{10}\selectfont\rightskip0\p@ plus1fill\mathversion{normal}} +% \def\bibmarginnotefont{\fontsize{7}{10}\selectfont\bfseries\rightskip0\p@ plus1fill\mathversion{normal}} +% \def\bibliofont{\fontsize{8}{11}\selectfont\mathversion{normal}} +\def\rhfont{\fontsize{7.5}{7.5}\itshape\selectfont} +\def\titlefont{\fontsize{21}{25}\selectfont\bfseries\leftskip14.25pc\rightskip0\p@ plus1fill} +\def\authorfont{\fontsize{12}{15}\selectfont\bfseries\leftskip14.25pc\rightskip0\p@ plus1fill\mathversion{bold}} +\def\abstractfont{\fontsize{10}{12}\selectfont} +\def\keywordsfont{\fontsize{10}{12}\selectfont\rightskip0\p@ plus1fill\mathversion{normal}} +\def\affilfont{\fontsize{8}{10}\selectfont\leftskip14.25pc\rightskip0\p@ plus1fil} +\def\jinfofont{\fontsize{8}{10}\selectfont\rightskip12\p@ plus1fil} +\def\foliofont{\fontsize{8.5}{8.5}\itshape\selectfont} +\def\figcaptionfont{\fontsize{9}{10.5}\selectfont\mathversion{normal}} +\def\figcaptionnumfont{\sffamily\fontsize{9}{10.5}\selectfont\bfseries\mathversion{bold}} +\def\tablecaptionfont{\fontsize{9}{12}\bfseries\selectfont\mathversion{normal}}% +\def\tablecaptionnumfont{\fontsize{9}{12}\bfseries\selectfont} +\def\tablefont{\fontsize{9}{11.5}\selectfont\mathversion{normal}}% +\def\tabnotefont{\fontsize{8}{10.5}\selectfont}% +\def\textboxfont{\normalfont\normalsize\leftskip12\p@\rightskip12\p@} +\def\textboxheadfont{\fontsize{11}{13}\selectfont\bfseries\leftskip12\p@\rightskip12\p@ plus1fill\mathversion{normal}} +% +\def\sectionfont{\sffamily\fontsize{11}{13}\selectfont\bfseries\raggedright\mathversion{bold}} +\def\subsectionfont{\sffamily\fontsize{11}{13}\selectfont\bfseries\raggedright\mathversion{bold}} +\def\subsubsectionfont{\sffamily\fontsize{10}{13}\bfseries\selectfont\mathversion{bold}} +\def\paragraphfont{\bfseries\itshape} +\def\subparagraphfont{\itshape} +\def\extractfont{\fontsize{8.5}{12.5}\selectfont\mathversion{normal}} +\def\marginnotefont{\fontsize{8.5}{10.5}\selectfont\rightskip0\p@ plus1fill\mathversion{normal}} +\def\bibmarginnotefont{\fontsize{7.5}{10.5}\selectfont\bfseries\rightskip0\p@ plus1fill\mathversion{normal}} +\def\bibliofont{\fontsize{8.5}{11.5}\selectfont\mathversion{normal}} +% +% +%% \if@compatibility\else +\DeclareOption{a4paper} + {\setlength\paperheight {297mm}% + \setlength\paperwidth {210mm}} +\DeclareOption{a5paper} + {\setlength\paperheight {210mm}% + \setlength\paperwidth {148mm}} +\DeclareOption{b5paper} + {\setlength\paperheight {250mm}% + \setlength\paperwidth {176mm}} +\DeclareOption{letterpaper} + {\setlength\paperheight {11in}% + \setlength\paperwidth {8.5in}} +\DeclareOption{legalpaper} + {\setlength\paperheight {14in}% + \setlength\paperwidth {8.5in}} +\DeclareOption{executivepaper} + {\setlength\paperheight {10.5in}% + \setlength\paperwidth {7.25in}} +\DeclareOption{landscape} + {\setlength\@tempdima {\paperheight}% + \setlength\paperheight {\paperwidth}% + \setlength\paperwidth {\@tempdima}} +\DeclareOption{fleqn}{\input{fleqn.clo}} +\DeclareOption{ChEqNum}{\global\@DotinEqNumtrue} +\ExecuteOptions{letterpaper} +\ProcessOptions +% +\@twosidetrue\@mparswitchtrue +% +\renewcommand\normalsize{% + % \@setfontsize\normalsize\@ixpt\@xiipt + \@setfontsize\normalsize{10}{13} + \abovedisplayskip 10\p@ \@plus2\p@ \@minus5\p@ + \abovedisplayshortskip \z@ \@plus3\p@ + \belowdisplayshortskip 6\p@ \@plus3\p@ \@minus3\p@ + \belowdisplayskip \abovedisplayskip + \let\@listi\@listI} +\normalsize +\newcommand\small{% + % \@setfontsize\small\@ixpt{11}% + \@setfontsize\small\@ixpt{12}% + \abovedisplayskip 8.5\p@ \@plus3\p@ \@minus4\p@ + \abovedisplayshortskip \z@ \@plus2\p@ + \belowdisplayshortskip 4\p@ \@plus2\p@ \@minus2\p@ + \def\@listi{\leftmargin\leftmargini + \topsep 4\p@ \@plus2\p@ \@minus2\p@ + \parsep 2\p@ \@plus\p@ \@minus\p@ + \itemsep \parsep}% + \belowdisplayskip \abovedisplayskip +} +\newcommand\footnotesize{% + % \@setfontsize\footnotesize\@viiipt{9.5}% + \@setfontsize\footnotesize\@ixpt{10.5}% + \abovedisplayskip 6\p@ \@plus2\p@ \@minus4\p@ + \abovedisplayshortskip \z@ \@plus\p@ + \belowdisplayshortskip 3\p@ \@plus\p@ \@minus2\p@ + \def\@listi{\leftmargin\leftmargini + \topsep 3\p@ \@plus\p@ \@minus\p@ + \parsep 2\p@ \@plus\p@ \@minus\p@ + \itemsep \parsep}% + \belowdisplayskip \abovedisplayskip +} +% \newcommand\scriptsize{\@setfontsize\scriptsize\@viipt\@viiipt} +% \newcommand\tiny{\@setfontsize\tiny\@vpt\@vipt} +% \newcommand\large{\@setfontsize\large\@xiipt{14}} +% \newcommand\Large{\@setfontsize\Large\@xivpt{18}} +% \newcommand\LARGE{\@setfontsize\LARGE\@xviipt{22}} +% \newcommand\huge{\@setfontsize\huge\@xxpt{25}} +% \newcommand\Huge{\@setfontsize\Huge\@xxvpt{30}} +\newcommand\scriptsize{\@setfontsize\scriptsize\@viiipt\@ixpt} +\newcommand\tiny{\@setfontsize\tiny\@vipt\@viipt} +\newcommand\large{\@setfontsize\large{13}{15}} +\newcommand\Large{\@setfontsize\Large{15.4}{19}} +\newcommand\LARGE{\@setfontsize\LARGE\@xviipt{22}} +\newcommand\huge{\@setfontsize\huge\@xxpt{25}} +\newcommand\Huge{\@setfontsize\Huge\@xxvpt{30}} +\setlength\parindent{15\p@} +\setlength\smallskipamount{3\p@ \@plus 1\p@ \@minus 1\p@} +\setlength\medskipamount{6\p@ \@plus 2\p@ \@minus 2\p@} +\setlength\bigskipamount{12\p@ \@plus 4\p@ \@minus 4\p@} +\setlength\headheight{\z@}% +\setlength\headsep{\z@}% +\setlength\topskip{6.9\p@} +\setlength\footskip{24\p@} +\setlength\maxdepth{.5\topskip} +% +\newdimen\typeheight\typeheight47pc% +\newdimen\typewidth\typewidth38pc% +% \newdimen\typewidth\typewidth39pc% +\newlength\extramargin +\setlength\extramargin{5.5pc} +\setlength\textheight{59pc}% +\setlength\textwidth{32.5pc}% +\setlength\marginparsep {10\p@} +\setlength\marginparpush{5\p@} +\setlength\marginparwidth {2.0cm} +% \setlength\oddsidemargin{46.5\p@}% +% \setlength\evensidemargin{28.5\p@}% +\setlength\oddsidemargin{29.5\p@}% +\setlength\evensidemargin{28.5\p@}% + +% +% \setlength\topmargin{15.5\p@}% +\setlength\topmargin{0cm}% +\setlength\footnotesep{7.35\p@} +\setlength{\skip\footins}{19.5\p@ \@plus 4\p@ \@minus 2\p@} +\setlength\floatsep {12\p@ \@plus 2\p@ \@minus 2\p@} +\setlength\textfloatsep{20\p@ \@plus 2\p@ \@minus 4\p@} +\setlength\intextsep {12\p@ \@plus 2\p@ \@minus 2\p@} +\setlength\dblfloatsep {12\p@ \@plus 2\p@ \@minus 2\p@} +\setlength\dbltextfloatsep{20\p@ \@plus 2\p@ \@minus 4\p@} +\setlength\@fptop{0\p@ \@plus 1fil} +\setlength\@fpsep{8\p@ \@plus 2fil} +\setlength\@fpbot{0\p@ \@plus 1fil} +\setlength\@dblfptop{0\p@ \@plus 1fil} +\setlength\@dblfpsep{8\p@ \@plus 2fil} +\setlength\@dblfpbot{0\p@ \@plus 1fil} +\setlength\partopsep{0\p@ \@plus 1\p@ \@minus 1\p@} +\def\@listi{\leftmargin\leftmargini + \parsep 4\p@ \@plus2\p@ \@minus\p@ + \topsep 8\p@ \@plus2\p@ \@minus4\p@ + \itemsep4\p@ \@plus2\p@ \@minus\p@} +\let\@listI\@listi +\@listi +\def\@listii {\leftmargin\leftmarginii + \labelwidth\leftmarginii + \advance\labelwidth-\labelsep + \topsep 4\p@ \@plus2\p@ \@minus\p@ + \parsep 2\p@ \@plus\p@ \@minus\p@ + \itemsep \parsep} +\def\@listiii{\leftmargin\leftmarginiii + \labelwidth\leftmarginiii + \advance\labelwidth-\labelsep + \topsep 2\p@ \@plus\p@\@minus\p@ + \parsep \z@ + \partopsep \p@ \@plus\z@ \@minus\p@ + \itemsep \topsep} +\def\@listiv {\leftmargin\leftmarginiv + \labelwidth\leftmarginiv + \advance\labelwidth-\labelsep} +\def\@listv {\leftmargin\leftmarginv + \labelwidth\leftmarginv + \advance\labelwidth-\labelsep} +\def\@listvi {\leftmargin\leftmarginvi + \labelwidth\leftmarginvi + \advance\labelwidth-\labelsep} +% +\def\@listI{\leftmargin\leftmargini + \parsep 0\p@% \@plus2\p@ \@minus\p@ + \topsep 6\p@ \@plus2\p@% \@minus2\p@ + \itemsep0\p@}% \@plus2\p@ \@minus\p@} +% +\newenvironment{unnumlist}{\list{}{\leftmargin15\p@\itemindent-15\p@}}{\endlist}% +% +\setlength\lineskip{1\p@} +\setlength\normallineskip{1\p@} +\renewcommand\baselinestretch{} +\setlength\parskip{0\p@}% \@plus \p@} +\@lowpenalty 51 +\@medpenalty 151 +\@highpenalty 301 +\setcounter{topnumber}{4} +\renewcommand\topfraction{1} +\setcounter{bottomnumber}{4} +\renewcommand\bottomfraction{1} +\setcounter{totalnumber}{8} +\renewcommand\textfraction{0.0001} +\renewcommand\floatpagefraction{.91} +\setcounter{dbltopnumber}{4} +\renewcommand\dbltopfraction{.9} +\renewcommand\dblfloatpagefraction{.91}% +% +\font\cms=cmsy10 at 5.7\p@ +\def\arblcirc{\lower-.6\p@\hbox{\cms\char'17}}% +% + \def\ps@headings{% + \let\@mkboth\@gobbletwo + \def\@oddfoot{\hbox to\typewidth{\hfill\rhfont\kern3.5\p@{\arblcirc}\kern3.5\p@\rightmark\hbox to \extramargin{\hskip1pc\foliofont\thepage\hfill}}}% + \def\@evenfoot{\hbox to\typewidth{\hspace*{-\extramargin}\hbox to \extramargin{\hfill\foliofont\thepage\hskip1pc}\rhfont\leftmark\hss}} + \def\@evenhead{}% + \def\@oddhead{}% + \def\sectionmark##1{}% + \def\subsectionmark##1{}} +% +\def\ps@plain{% + \let\@mkboth\@gobbletwo% + \def\@oddhead{}% + \let\@evenhead\@oddhead% + \def\@oddfoot{\hbox to\typewidth{\hfill\hbox to \extramargin{\hskip2pc\hfill\foliofont\thepage\hfill}}}% + \def\@evenfoot{\hbox to\typewidth{\hspace*{-\extramargin}\hbox to \extramargin{\hfill\foliofont\thepage\hfill\hskip2pc}}}% + }% +% +\newcommand\maketitle{\par + \begingroup + \renewcommand\thefootnote{}%{\@fnsymbol\c@footnote}% + \def\@makefnmark{\rlap{\@textsuperscript{\normalfont\@thefnmark}}}% + \long\def\@makefntext##1{\sffamily\raggedright\noindent + %\hb@xt@1.8em{\hss\@textsuperscript{\normalfont\@thefnmark}} + ##1\vskip3\p@}% + \newpage + \global\@topnum\z@ % Prevents figures from going at top of page. + \@maketitle% + \thispagestyle{plain}\@thanks\clearpage + \endgroup + \setcounter{footnote}{0}% + \global\let\thanks\relax + \global\let\maketitle\relax + \global\let\@maketitle\relax + \global\let\@thanks\@empty + \global\let\@author\@empty + \global\let\@title\@empty + \global\let\title\relax + \global\let\author\relax + \global\let\date\relax + \global\let\and\relax +} +% +\usepackage{color,ifpdf} + +\definecolor{titlecolor}{cmyk}{0.4,0.4,0.4,0} +\definecolor{headcolor}{cmyk}{0,1.0,1.0,0.30} +\definecolor{shadecolor}{cmyk}{0.03,0.03,0.12,0.0} +\definecolor{fignumcolor}{cmyk}{1.0,0.4,0,0} +\definecolor{marginrulecolor@val}{cmyk}{0,0,0,0.30} +\definecolor{textboxcolor@val}{cmyk}{0.12,0.04,0.08,0.0} + +\def\title@color#1{\textcolor{titlecolor}{#1}} +\def\head@color#1{\textcolor{headcolor}{#1}} +\def\shade@color#1{\textcolor{shadecolor}{#1}} +\def\fignum@color#1{\textcolor{fignumcolor}{#1}} +\def\marginrulecolor#1{\textcolor{marginrulecolor@val}{#1}} +\def\textboxcolor#1{\textcolor{textboxcolor@val}{#1}} + +%\def\title@color#1{\special{color push cmyk 0.4 0.4 0.4 0}#1\special{color pop}} +%\def\head@color#1{\special{color push cmyk 0 1.0 1.0 0.30}#1\special{color pop}}% +%\def\shade@color#1{\special{color push cmyk 0.03 0.03 0.12 0.0}#1\special{color pop}}% +%\def\fignum@color#1{\special{color push cmyk 1.0 0.4 0 0}#1\special{color pop}} +%\def\marginrulecolor#1{\special{color push cmyk 0 0 0 0.30}#1\special{color pop}} +%\def\textboxcolor#1{\special{color push cmyk 0.12 0.04 0.08 0.0}#1\special{color pop}} + +\def\@maketitle{\newpage% + \null% + \begingroup\hsize1.05\typewidth\parindent0\p@% + \let\footnote\thanks + \nointerlineskip% + \vskip-6\p@% + \noindent{\reset@font\titlefont\title@color{\@title}\par} + \vskip15\p@ + % \hspace{-3cm} + \noindent{\authorfont\@author\par\vskip5\p@}% + \vfill% + \global\setbox\z@\vbox{\hsize\typewidth% + % \begin{tabular*}{\typewidth}{@{}p{13.5pc}@{\hskip.75pc}p{22.25pc}@{}} + % \nointerlineskip{\vskip5.4\p@\vbox{\journalinfo}\vrule height0\p@ depth6\p@ width\z@}% + % &\ifvoid\@kwdbox% + % \ifvoid\@absbbox\else{\box\@absbbox}\fi% + % \else% + % {\vskip5.4\p@\box\@kwdbox}\endgraf\nointerlineskip% + % \ifvoid\@absbbox\else\vskip15\p@{\box\@absbbox}\fi% + % \fi\\% + % \end{tabular*} + + % \begin{tabular*}{\typewidth}{@{}|p{3.5pc}|@{\hskip.75pc}p{22.25pc}|@{}} + \begin{tabular*}{\typewidth}{@{}p{1.5pc}@{\hskip.75pc}p{22.25pc}@{}} + \nointerlineskip{\vskip5.4\p@ \vrule height0\p@ depth6\p@ width\z@}% + &\ifvoid\@kwdbox% + \ifvoid\@absbbox\else{\box\@absbbox}\fi% + \else% + {\vskip5.4\p@\box\@kwdbox}\endgraf\nointerlineskip% + \ifvoid\@absbbox\else\vskip15\p@{\box\@absbbox}\fi% + \fi\\% + \end{tabular*} + } + \@tempdima\typewidth\advance\@tempdima77\p@% + \vbox{\shade@color{\hskip-46\p@\vrule height\ht\z@ width\@tempdima depth\dp\z@}\vskip-\ht\z@\vskip-\dp\z@% + {\box\z@}}% + \endgroup} +% +% First page element and layout declaration +% +\def\firstpagenote#1{\gdef\@firstpagenote{#1}}\firstpagenote{}% +\def\journalinfo{{\jinfofont% + \@jname\ \@jyear. \@jvol:\@fstpage--\@endpage\endgraf% + %\ifx\@doi\empty\else\vskip6\p@ This article's doi:\break \@doi\endgraf\vskip6\p@\fi% + \ifx\@doi\empty\else\vskip6\p@ https://doi.org/\@doi\endgraf\vskip6\p@\fi% + Copyright\ \copyright\ \@jyear\ by the author(s).\endgraf All rights reserved\endgraf% + \ifx\@firstpagenote\@empty\else\vskip6\p@\@firstpagenote\endgraf\fi% +}} +% +\def\chk@key{no}% +\def\YES{yes}% +\def\affil#1{\par\ifx\chk@key\YES\else\vskip6\p@\fi{\reset@font\affilfont#1\par}\def\chk@key{yes}} +% +\newbox\@absbbox +\newenvironment{abstract}{\parindent0\p@% + % \global\setbox\@absbbox\vbox\bgroup\hsize23.75pc\abstractfont\noindent\ignorespaces% + \global\setbox\@absbbox\vbox\bgroup\hsize33.75pc\abstractfont\noindent\ignorespaces% + \ifx\abstractname\@empty\else{\head@color{\sffamily\bfseries\abstractname}\endgraf\vskip9\p@}\fi% + } + {\par\egroup} + +% +\newcommand\keywordsname{Keywords} +\newbox\@kwdbox +\newenvironment{keywords}{\parindent0\p@% + % \global\setbox\@kwdbox\vbox\bgroup\hsize23.75pc\keywordsfont\noindent\ignorespaces% + \global\setbox\@kwdbox\vbox\bgroup\hsize33.75pc\keywordsfont\noindent\ignorespaces% + \ifx\keywordsname\@empty\else{\head@color{\sffamily\bfseries\keywordsname}\endgraf\vskip4\p@}\fi% + }{\par\egroup}% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\setcounter{secnumdepth}{3} +\newcounter {part} +\newcounter {section} +\newcounter {subsection}[section] +\newcounter {subsubsection}[subsection] +\newcounter {paragraph}[subsubsection] +\newcounter {subparagraph}[paragraph] +\renewcommand \thepart {\@Roman\c@part} +\renewcommand \thesection {\@arabic\c@section} +\renewcommand\thesubsection {\thesection.\@arabic\c@subsection} +\renewcommand\thesubsubsection{\thesubsection .\@arabic\c@subsubsection} +\renewcommand\theparagraph {\thesubsubsection.\@arabic\c@paragraph} +\renewcommand\thesubparagraph {\theparagraph.\@arabic\c@subparagraph} +\newcommand\part{% + \if@noskipsec \leavevmode \fi + \par + \addvspace{4ex}% + \@afterindentfalse + \secdef\@part\@spart} + +\def\@part[#1]#2{% + \ifnum \c@secnumdepth >\m@ne + \refstepcounter{part}% + \addcontentsline{toc}{part}{\thepart\hspace{1em}#1}% + \else + \addcontentsline{toc}{part}{#1}% + \fi + {\parindent \z@ \raggedright + \interlinepenalty \@M + \normalfont + \ifnum \c@secnumdepth >\m@ne + \Large\bfseries \partname\nobreakspace\thepart + \par\nobreak + \fi + \huge \bfseries #2% + \markboth{}{}\par}% + \nobreak + \vskip 3ex + \@afterheading} +\def\@spart#1{% + {\parindent \z@ \raggedright + \interlinepenalty \@M + \normalfont + \huge \bfseries #1\par}% + \nobreak + \vskip 3ex + \@afterheading} +% +\def\@seccntformat#1{\head@color{\csname the#1\endcsname.}\hskip3\p@} +% +\newcommand\section{\@startsection {section}{1}{\z@}% + {-20\p@ \@plus -3\p@ \@minus -1\p@}% + {4\p@}% + {\sectionfont}} +\newcommand\subsection{\@startsection{subsection}{2}{\z@}% + {-16\p@ \@plus -1\p@ \@minus -.1\p@}% + {4\p@}% + {\subsectionfont}} +\newcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}% + {-12\p@ \@plus -1\p@ \@minus -0.1\p@}% + {-0.5em}% + {\subsubsectionfont}} +\newcommand\paragraph{\@startsection{paragraph}{4}{\z@}% + {13\p@ \@plus 3.25\p@ \@minus 1\p@}% + {-0.5em}% + {\paragraphfont}} +\newcommand\subparagraph{\@startsection{subparagraph}{5}{\parindent}% + {13\p@ \@plus 3.25\p@ \@minus 1\p@}% + {-0.5em}% + {\subparagraphfont}} +% +\def\@sect#1#2#3#4#5#6[#7]#8{% + \ifnum #2>\c@secnumdepth + \let\@svsec\@empty + \else + \refstepcounter{#1}% + \protected@edef\@svsec{\@seccntformat{#1}\relax}% + \fi + \@tempskipa #5\relax + \ifdim \@tempskipa>\z@ + \begingroup + #6{% + \@hangfrom{\hskip #3\relax\@svsec}% + \interlinepenalty \@M \head@color{#8}\@@par}% + \endgroup + \csname #1mark\endcsname{#7}% + \addcontentsline{toc}{#1}{% + \ifnum #2>\c@secnumdepth \else + \protect\numberline{\csname the#1\endcsname}% + \fi + #7}% + \else + \def\@svsechd{% + #6{\hskip #3\relax + \@svsec \head@color{#8.}}% + \csname #1mark\endcsname{#7}% + \addcontentsline{toc}{#1}{% + \ifnum #2>\c@secnumdepth \else + \protect\numberline{\csname the#1\endcsname}% + \fi + #7}}% + \fi + \@xsect{#5}} +% +\def\@ssect#1#2#3#4#5{% + \@tempskipa #3\relax + \ifdim \@tempskipa>\z@ + \begingroup + #4{% + \@hangfrom{\hskip #1}% + \interlinepenalty \@M \head@color{#5}\@@par}% + \endgroup + \else + \def\@svsechd{#4{\hskip #1\relax #5}}% + \fi + \@xsect{#3}} +% +\setlength\leftmargini {2.65em} +\leftmargin \leftmargini +\setlength\leftmarginii {2.2em} +\setlength\leftmarginiii {1.87em} +\setlength\leftmarginiv {1.7em} +\setlength\leftmarginv {1em} +\setlength\leftmarginvi {1em} +\setlength \labelsep {.5em} +\setlength \labelwidth{\leftmargini} +\addtolength\labelwidth{-\labelsep} +\@beginparpenalty -\@lowpenalty +\@endparpenalty -\@lowpenalty +\@itempenalty -\@lowpenalty +\renewcommand\theenumi{\@arabic\c@enumi} +\renewcommand\theenumii{\@alph\c@enumii} +\renewcommand\theenumiii{\@roman\c@enumiii} +\renewcommand\theenumiv{\@Alph\c@enumiv} +\newcommand\labelenumi{\theenumi.} +\newcommand\labelenumii{(\theenumii)} +\newcommand\labelenumiii{\theenumiii.} +\newcommand\labelenumiv{\theenumiv.} +\renewcommand\p@enumii{\theenumi} +\renewcommand\p@enumiii{\theenumi(\theenumii)} +\renewcommand\p@enumiv{\p@enumiii\theenumiii} +\newcommand\labelitemi{\textbullet} +\newcommand\labelitemii{\normalfont\bfseries \textendash} +\newcommand\labelitemiii{\textasteriskcentered} +\newcommand\labelitemiv{\textperiodcentered} +\newenvironment{description} + {\list{}{\labelwidth\z@ \itemindent-\leftmargin + \let\makelabel\descriptionlabel}} + {\endlist} +\newcommand*\descriptionlabel[1]{\hspace\labelsep + \normalfont\bfseries #1} +\newenvironment{verse} + {\let\\\@centercr + \list{}{\itemsep \z@ + \itemindent -1.5em% + \listparindent\itemindent + \rightmargin \leftmargin + \advance\leftmargin 1.5em}% + \item\relax} + {\endlist} +\newenvironment{quotation} + {\list{}{\listparindent 1.5em% + \itemindent \listparindent + \rightmargin \leftmargin + \parsep \z@ \@plus\p@}% + \item\relax} + {\endlist} +\newenvironment{quote} + {\list{}{\rightmargin\leftmargin}% + \item\relax} + {\endlist} +\newenvironment{extract} + {\list{}{\leftmargin1pc\topsep12\p@ plus2\p@\rightmargin\leftmargin}\item[]\extractfont} + {\endlist} +\newcommand\appendix{\par + \setcounter{section}{0}% + \setcounter{subsection}{0}% + \gdef\thesection{\@Alph\c@section}} +\setlength\arraycolsep{2\p@} +\setlength\tabcolsep{6\p@} +\setlength\arrayrulewidth{.4\p@} +\setlength\doublerulesep{2\p@} +\setlength\tabbingsep{\labelsep} +\skip\@mpfootins = \skip\footins +\setlength\fboxsep{3\p@} +\setlength\fboxrule{.4\p@} +\renewcommand \theequation {\@arabic\c@equation} +\newcounter{figure} +\renewcommand \thefigure {\@arabic\c@figure} +\def\fps@figure{tbp} +\def\ftype@figure{1} +\def\ext@figure{lof} +\def\fnum@figure{\figurename\nobreakspace\thefigure} +\newcount\chkfigcnt +\newenvironment{figure} + {\global\advance\chkfigcnt\@ne\@float{figure}} + {\label{chk@figure@\the\chkfigcnt}\end@float} +\newenvironment{figure*} + {\global\advance\chkfigcnt\@ne\@dblfloat{figure}} + {\label{chk@figure@\the\chkfigcnt}\end@dblfloat} +\newcounter{table} +\renewcommand\thetable{\@arabic\c@table} +\def\fps@table{tbp} +\def\ftype@table{2} +\def\ext@table{lot} +\def\fnum@table{\tablename\nobreakspace\thetable} +\newenvironment{table} + {\@float{table}} + {\end@float} +\newenvironment{table*} + {\@dblfloat{table}} + {\end@dblfloat} +\newlength\abovecaptionskip +\newlength\belowcaptionskip +\setlength\abovecaptionskip{-6\p@} +\setlength\belowcaptionskip{0\p@} +% +\def\FigName{figure} +\long\def\@makecaption#1#2{% + \ifx\FigName\@captype + \vskip\abovecaptionskip + \@makefigurecaption{#1}{#2}% + \else + \@maketablecaption{#1}{#2}% + \vskip\belowcaptionskip + \fi} +% +\newdimen\chk@fig@width\chk@fig@width\z@ +% +\def\@makefigurecaption#1#2{\edef\chk@fig@temp{\getpagerefnumber{chk@figure@\the\chkfigcnt}}% + \ifdim\chk@fig@width>\typewidth% + \noindent{\fignum@color{\figcaptionnumfont#1}}\endgraf\vskip-8.5\p@% + \normalcolor\noindent\hbox to \hsize{\hrulefill}\endgraf\vskip-.9\p@% + {\figcaptionfont\raggedright#2\par}% + \else% + \ifdim\chk@fig@width>\textwidth% + \vbox{\par\vskip5\p@\hsize\typewidth\ifodd\chk@fig@temp\raggedright\else\leftskip-7.5pc\rightskip7.5pc plus1fil\fi + \noindent{\fignum@color{\figcaptionnumfont#1}}\endgraf\vskip-8.5\p@% + \normalcolor\noindent\hbox to \hsize{\hrulefill}\endgraf\vskip-.9\p@% + {\figcaptionfont#2\par}\par}% + \else% + \noindent{\fignum@color{\figcaptionnumfont#1}}\endgraf\vskip-8.5\p@% + \normalcolor\noindent\hbox to \hsize{\hrulefill}\endgraf\vskip-.9\p@% + {\figcaptionfont\rightskip0pt plus1fill#2\par}% + \fi + \fi% +} +% +\def\@maketablecaption#1#2{% + \noindent\tablecaptionfont% + \fignum@color{{\tablecaptionnumfont#1\quad}{\tablecaptionfont#2}}\par +}% +\input epsf.sty +% +\def\tabular{\tablefont\let\@halignto\@empty\@tabular} +\newenvironment{tabnote}{\par\tabnotefont}{\par} +% +\DeclareOldFontCommand{\rm}{\normalfont\rmfamily}{\mathrm} +\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf} +\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt} +\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf} +\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit} +\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl} +\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc} +\DeclareRobustCommand*\cal{\@fontswitch\relax\mathcal} +\DeclareRobustCommand*\mit{\@fontswitch\relax\mathnormal} +\newcommand\@pnumwidth{1.75em} +\newcommand\@tocrmarg{6.5em} +\newcommand\@dotsep{1} +\setcounter{tocdepth}{2} +\newcommand\tableofcontents{\par\vspace*{-15.75\p@}% +\definecolor{shadecolor}{cmyk}{0.03,0.03,0.12,0} +\fboxsep12\p@\fboxrule0\p@\ifodd\c@page\moveright3.75pc\else\moveright-3.75pc\fi\vbox\bgroup\begin{shaded}\@nobreaktrue\hsize36pc + \section*{\contentsname} +%% \@mkboth{% +%% \MakeUppercase\contentsname}{\MakeUppercase\contentsname}}% + \@starttoc{toc}% +\end{shaded}\egroup} +\newcommand*\l@part[2]{% + \ifnum \c@tocdepth >-2\relax + \addpenalty\@secpenalty + \addvspace{2.25em \@plus\p@}% + \setlength\@tempdima{3em}% + \begingroup + \parindent \z@ \rightskip \@pnumwidth + \parfillskip -\@pnumwidth + {\leavevmode + \large \bfseries #1\hfil \hb@xt@\@pnumwidth{\hss #2}}\par + \nobreak + \if@compatibility + \global\@nobreaktrue + \everypar{\global\@nobreakfalse\everypar{}}% + \fi + \endgroup + \fi} +\newcommand*\l@section{\@dottedtocline{1}{0em}{1.2em}} +\newcommand*\l@subsection{\@dottedtocline{2}{1.2em}{2em}} +\newcommand*\l@subsubsection{\@dottedtocline{3}{3.2em}{2.8em}} +\newcommand*\l@paragraph{\@dottedtocline{4}{7.0em}{4.1em}} +\newcommand*\l@subparagraph{\@dottedtocline{5}{10em}{5em}} +% +\def\numberline#1{\hb@xt@\@tempdima{#1.\hfil}} +\def\@dottedtocline#1#2#3#4#5{% + \ifnum #1>\c@tocdepth \else + \vskip \z@ \@plus.2\p@ + {\sffamily\small\leftskip #2\relax \rightskip \@tocrmarg \parfillskip -\rightskip + \parindent #2\relax\@afterindenttrue + \interlinepenalty\@M + \leavevmode + \@tempdima #3\relax + \advance\leftskip \@tempdima \null\nobreak\hskip -\leftskip + {#4}\nobreak + \leaders\hbox{$\m@th + \mkern \@dotsep mu\hbox{.}\mkern \@dotsep + mu$} + \hfill + \nobreak + \hb@xt@\@pnumwidth{\hfil#5}% + \par}% + \fi} +% +\newcommand\listoffigures{% + \section*{\listfigurename}% + \@mkboth{\MakeUppercase\listfigurename}% + {\MakeUppercase\listfigurename}% + \@starttoc{lof}% + } +\newcommand*\l@figure{\@dottedtocline{1}{1.5em}{2.3em}} +\newcommand\listoftables{% + \section*{\listtablename}% + \@mkboth{% + \MakeUppercase\listtablename}% + {\MakeUppercase\listtablename}% + \@starttoc{lot}% + } +\let\l@table\l@figure +\newdimen\bibindent +\setlength\bibindent{1.5em} +\def\@biblabel#1{#1.} +\newenvironment{thebibliography}[1] + {\section*{\refname}% + \bibliofont% + \def\@tempa{#1}% + %\@mkboth{\MakeUppercase\refname}{\MakeUppercase\refname}% + \ifx\@tempa\@empty + \list{}% + {\labelwidth0\p@\labelsep0\p@% + \leftmargin12\p@\itemindent-12\p@% + \itemsep\z@ + \@openbib@code + \usecounter{enumiv}% + \let\p@enumiv\@empty + \renewcommand\theenumiv{\@arabic\c@enumiv}% + }% + \else% + \list{\@biblabel{\@arabic\c@enumiv}}% + {\settowidth\labelwidth{\@biblabel{#1}}% + \leftmargin\labelwidth + \itemsep\z@ + \advance\leftmargin\labelsep + \@openbib@code + \usecounter{enumiv}% + \let\p@enumiv\@empty + \renewcommand\theenumiv{\@arabic\c@enumiv}% + \def\@biblabel##1{\@ifundefined{bibnote@\romannumeral\theenumiv}{{\reset@font\normalfont\bibliofont##1.}}{{\bfseries##1.}}} + }% + \fi% + \sloppy + \clubpenalty4000 + \@clubpenalty \clubpenalty + \widowpenalty4000% + \sfcode`\.\@m} + {\def\@noitemerr + {\@latex@warning{Empty `thebibliography' environment}}% + \endlist} +\newcommand\newblock{\hskip .11em\@plus.33em\@minus.07em} +\let\@openbib@code\@empty +\newenvironment{theindex} + {\if@twocolumn + \@restonecolfalse + \else + \@restonecoltrue + \fi + \twocolumn[\section*{\indexname}]% + \@mkboth{\MakeUppercase\indexname}% + {\MakeUppercase\indexname}% + \thispagestyle{plain}\parindent\z@ + \parskip\z@ \@plus .3\p@\relax + \columnseprule \z@ + \columnsep 35\p@ + \let\item\@idxitem} + {\if@restonecol\onecolumn\else\clearpage\fi} +\newcommand\@idxitem{\par\hangindent 40\p@} +\newcommand\subitem{\@idxitem \hspace*{20\p@}} +\newcommand\subsubitem{\@idxitem \hspace*{30\p@}} +\newcommand\indexspace{\par \vskip 10\p@ \@plus5\p@ \@minus3\p@\relax} +\renewcommand\footnoterule{% + \kern-3\p@ + \hrule\@width88.5\p@ height.5\p@ + \kern3\p@} +\newcommand\@makefntext[1]{% + \parindent 1em% + \noindent + \hb@xt@1.8em{\hss\@makefnmark}#1} +\newcommand\contentsname{Contents} +\newcommand\listfigurename{List of Figures} +\newcommand\listtablename{List of Tables} +\newcommand\refname{LITERATURE CITED} +\newcommand\indexname{Index} +\newcommand\figurename{Figure} +\newcommand\tablename{Table} +\newcommand\partname{Part} +\newcommand\appendixname{Appendix} +\newcommand\abstractname{Abstract} +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +\setlength\columnsep{12\p@} +\setlength\columnseprule{0\p@} +% +\pagestyle{headings} +\sloppy +\voffset-1pc +\hoffset-2.75pc +% +\pagenumbering{arabic} +% +%%% Body environment declaration +% +\newcount\marginnotecnt +\newbox\marginnotebox% +\newenvironment{marginnote}[1][\relax] + {\def\@tempa{#1}\global\advance\marginnotecnt\@ne\let\entry\marginnoteentry% + \edef\marginnote@page{\getpagerefnumber{marginnote@\the\marginnotecnt}} + \setbox\marginnotebox\vbox\bgroup\hsize6.5pc\marginnotefont\parindent\z@\if\@tempa\relax\else\vskip\@tempa\fi% + \noindent\hbox to 6.5pc{\marginrulecolor{\hrulefill}}\par% + }% + {\par\vskip-2\p@\noindent\hbox to 6.5pc{\marginrulecolor{\hrulefill}}\egroup% + \ifodd\marginnote@page + \marginpar{\hspace*{2.5\p@}\box\marginnotebox}% + \else + \marginpar{\hspace*{-23\p@}\box\marginnotebox}% + \fi\label{marginnote@\the\marginnotecnt}} +% +\def\marginnoteentry#1#2{\par\addvspace{4\p@}\noindent{\fignum@color{\sffamily\bfseries#1:}}\ #2\par} +% +\newcount\bibnotecnt +\def\bibnote{\@ifnextchar[{\@bibnote}{\@bibnote[\relax]}} +\def\@bibnote[#1]#2{\def\chk@tempa{#1}% + \global\advance\bibnotecnt\@ne% + \edef\bibnote@page{\getpagerefnumber{bibnote@\the\bibnotecnt}}% + \ifodd\bibnote@page + \marginpar{\hbox{\reset@font\hskip2.5\p@\parbox{6.5pc}{\vbox to \z@{\if\chk@tempa\relax\else\vskip\chk@tempa\fi\noindent\hbox to 6.5pc{\marginrulecolor{\hrulefill}}\par{\bibmarginnotefont\ifx\chk@natbib@nonumref\YES\else\ifx\@tempa\@empty\else\theenumiv.\ \fi\fi#2\par}\par\vskip-6.6\p@% + \noindent\hbox to 6.5pc{\marginrulecolor{\hrulefill}}}}}}% + \else% + \marginpar{\hbox{\reset@font\hskip-23\p@\parbox{6.5pc}{\vbox to \z@{\if\chk@tempa\relax\else\vskip\chk@tempa\fi\noindent\hbox to 6.5pc{\marginrulecolor{\hrulefill}}\par{\reset@font\bibmarginnotefont\ifx\chk@natbib@nonumref\YES\else\ifx\@tempa\@empty\else\theenumiv.\ \fi\fi#2\par}\vskip-6.6\p@% + \noindent\hbox to 6.5pc{\marginrulecolor{\hrulefill}}}}}}% +\fi% +\label{bibnote@\the\bibnotecnt} +\immediate\write\@mainaux{\string\gdef\string\bibnote@\romannumeral\theenumiv{}}\bfseries\mathversion{bold}} +% +\def\@bibitem#1{\item\if@filesw \immediate\write\@auxout + {\string\bibcite{#1}{\the\value{\@listctr}}}\fi\ignorespaces\reset@font\normalfont\bibliofont} +% +\def\summaryhead#1{\noindent{\title@color{\sffamily\bfseries#1}\par\vskip4.2\p@}} +\newenvironment{summary}[1][\relax]% + {\par\overfullrule0\p@\let\centerline\leftline% + \definecolor{shadecolor}{cmyk}{0.03,0.03,0.12,0} + \fboxsep10\p@\advance\hsize-2\fboxsep\begin{shaded}\leftmargini12\p@% + \if#1\relax\else\summaryhead{#1}\fi} + {\end{shaded}} +% +\def\issueshead#1{\noindent{\title@color{\sffamily\bfseries#1}\par\vskip4.2\p@}} +\newenvironment{issues}[1][\relax]% + {\par\overfullrule0\p@\let\centerline\leftline% + \definecolor{shadecolor}{cmyk}{0.06,0.02,0.04,0} + \fboxsep10\p@\advance\hsize-2\fboxsep\begin{shaded}\leftmargini12\p@% + \if#1\relax\else\issueshead{#1}\fi} + {\end{shaded}} +% +\gdef\fps@textbox{t} +\def\ftype@textbox{3} +\def\textboxhere{h} +\def\textboxbottom{b} +\newcommand\textboxhead[1]{\noindent{\textboxheadfont\fignum@color{\sffamily#1}\par\vskip6\p@}\noindent\ignorespaces} +\newcommand\textboxsubhead[1]{\par\addvspace{12\p@}\noindent{\textboxheadfont\fignum@color{\sffamily#1}\par\vskip6\p@}\noindent\ignorespaces} +\newcommand\textboxsubsubhead[1]{\par\addvspace{12\p@}\noindent{\textboxheadfont\fignum@color{\sffamily#1.}\hskip6\p@}\noindent\ignorespaces} +\newcount\textboxcnt +\newenvironment{textbox}[1][\relax]% + {\gdef\textboxpos{#1}\global\advance\textboxcnt\@ne% + \edef\textbox@page{\getpagerefnumber{textbox@\the\textboxcnt}} + \setbox\z@\vbox\bgroup\hsize38pc\textboxfont\vskip12\p@\noindent\ignorespaces% + \let\section\textboxhead% + \let\subsection\textboxsubhead% + \let\subsubsection\textboxsubsubhead% + } + {\vskip11\p@\egroup% + \ifx\textboxpos\textboxbottom\@float{textbox}[b]\else\ifx\textboxpos\textboxhere\@float{textbox}[h]\else\@float{textbox}[t]\fi\fi% + \ifodd\textbox@page\else\hskip-7.5pc\fi% + \textboxcolor{\vrule height\ht\z@ width\wd\z@ depth\dp\z@}% + \llap{\box\z@} + \label{textbox@\the\textboxcnt} + \end@float} +% +\AtEndDocument{\immediate\write\@mainaux{\string\endpage{\thepage}}}% +% +\usepackage{graphicx,multicol,refcount,framed}%colortbl,array, +% +\def\Gin@setfile#1#2#3{% + \ifx\\#2\\\Gread@false\fi + \ifGin@bbox\else + \ifGread@ + \csname Gread@% + \expandafter\ifx\csname Gread@#1\endcsname\relax + eps% + \else + #1% + \fi + \endcsname{\Gin@base#2}% + \else + \Gin@nosize{#3}% + \fi + \fi + \Gin@viewport@code + \Gin@nat@height\Gin@ury bp% + \advance\Gin@nat@height-\Gin@lly bp% + \Gin@nat@width\Gin@urx bp% + \advance\Gin@nat@width-\Gin@llx bp% + \Gin@req@sizes + \expandafter\ifx\csname Ginclude@#1\endcsname\relax + \Gin@drafttrue + \expandafter\ifx\csname Gread@#1\endcsname\relax + \@latex@error{Can not include graphics of type: #1}\@ehc + \global\expandafter\let\csname Gread@#1\endcsname\@empty + \fi + \fi + \leavevmode + \ifGin@draft + \hb@xt@\Gin@req@width{% + \vrule\hss + \vbox to \Gin@req@height{% + \hrule \@width \Gin@req@width + \vss + \edef\@tempa{#3}% + \rlap{ \ttfamily\expandafter\strip@prefix\meaning\@tempa}% + \vss + \hrule}% + \hss\vrule}% + \else + \@addtofilelist{#3}% + \ProvidesFile{#3}[Graphic file (type #1)]% + \setbox\z@\hbox{\csname Ginclude@#1\endcsname{#3}}% + \dp\z@\z@ + \ht\z@\Gin@req@height + \wd\z@\Gin@req@width + \global\chk@fig@width\Gin@req@width + \edef\chk@fig@temp{\getpagerefnumber{chk@figure@\the\chkfigcnt}} + \ifdim\Gin@req@width>\typewidth% + \centerline{\box\z@} + \else + \ifdim\Gin@req@width>\textwidth% + \@tempdima\typewidth\advance\@tempdima-\Gin@req@width\divide\@tempdima by 2% + \leftline{\ifodd\chk@fig@temp\hbox{\hspace*{\@tempdima}\box\z@}\else\hbox{\hspace*{-7.5pc}\hspace*{\@tempdima}\box\z@}\fi} + \else% + \centerline{\box\z@} + \fi% + \fi% + \fi} +% +% +\def\chk@natbib@nonumref{no}% +\AtBeginDocument{\immediate\write\@mainaux{\string\fstpage{\thepage}}% + \@ifpackageloaded{natbib}{% + \global\let\theenumiv\theNAT@ctr% + \ifNAT@numbers% + \renewcommand\NAT@open{(}% + \renewcommand\NAT@close{)}% + \gdef\chk@natbib@nonumref{no}% + \gdef\bibnumfmt#1{\@ifundefined{bibnote@\romannumeral\theenumiv}{{\reset@font\normalfont\bibliofont#1.}}{{\bfseries#1.}}}% + \else% + \gdef\chk@natbib@nonumref{yes}% + \gdef\NAT@aysep{}% + \fi% + \let\bibfont\bibliofont% + \global\bibsep\z@% + \def\bibitem@fin{\@ifxundefined\@bibstop{}{\csname bibitem@\@bibstop\endcsname}\reset@font\normalfont\bibliofont}% + }{\def\@cite#1#2{({#1\if@tempswa , #2\fi})}}% +% +\if@DotinEqNum% + \@ifpackageloaded{amsmath}{% + %\def\tagform@#1{\maketag@@@{(\ignorespaces#1\unskip\@@italiccorr)}} + \def\tagform@#1{\maketag@@@{\ignorespaces#1\unskip.}} + }{% + %\def\@eqnnum{{\normalfont \normalcolor (\theequation)}} + \def\@eqnnum{{\normalfont \normalcolor \theequation.}} + }% +\fi% +%% +\@ifpackageloaded{hyperref}{% + \def\@linkcolor{green}% + \def\termkey#1{\hyperlink{key#1}{#1}}% + \def\marginnoteentry#1#2{\par\addvspace{4\p@}\noindent{\fignum@color{\sffamily\bfseries#1:\hypertarget{key#1}{}}}\ #2\par}% +}{% + \def\termkey#1{#1}% +} +% +} +% +\def\@outputpage{% +%\special{color push cmyk 0 0 0 1.0}% +\begingroup % the \endgroup is put in by \aftergroup +\let\firstmark\botmark + \let \protect \noexpand + \@resetactivechars + \@parboxrestore + \shipout \vbox{% + \set@typeset@protect + \aftergroup \endgroup + \aftergroup \set@typeset@protect + % correct? or just restore by ending + % the group? + \if@specialpage + \global\@specialpagefalse\@nameuse{ps@\@specialstyle}% + \fi + \if@twoside + \ifodd\count\z@ \let\@thehead\@oddhead \let\@thefoot\@oddfoot + \let\@themargin\oddsidemargin + \else \let\@thehead\@evenhead + \let\@thefoot\@evenfoot \let\@themargin\evensidemargin + \fi + \fi + \reset@font + \normalsize + \baselineskip\z@skip \lineskip\z@skip \lineskiplimit\z@ + \@begindvi\trimmarks + \vskip \topmargin + \ifodd\c@page\else\advance\@themargin\extramargin\fi% + \moveright\@themargin \vbox {% + \setbox\@tempboxa \vbox to\headheight{% + \vfil + \color@hbox + \normalcolor + \hb@xt@\textwidth {% + \let \label \@gobble + \let \index \@gobble + \let \glossary \@gobble %% 21 Jun 91 + \@thehead + }% + \color@endbox + }% %% 22 Feb 87 + \dp\@tempboxa \z@ +% \box\@tempboxa +% \vskip \headsep + \box\@outputbox + \baselineskip \footskip + \color@hbox + \normalcolor + \hb@xt@\textwidth{% + \let \label \@gobble + \let \index \@gobble %% 22 Feb 87 + \let \glossary \@gobble %% 21 Jun 91 + \@thefoot + }% + \color@endbox + }% + }% +\global \@colht \textheight +\stepcounter{page}% +%\special{color pop} +} +\def\trimmarks{}% +% +\ifpdf% + \setlength{\pdfpagewidth}{8.5in}% + \setlength{\pdfpageheight}{11in}% +\fi% +%% + +%% Journal shortcuts for the bibliography +\newcommand*\aap{A\&A} +\let\astap=\aap +\newcommand*\aapr{A\&A~Rev.} +\newcommand*\aaps{A\&AS} +\newcommand*\actaa{Acta Astron.} +\newcommand*\aj{AJ} +\newcommand*\ao{Appl.~Opt.} +\let\applopt\ao +\newcommand*\apj{ApJ} +\newcommand*\apjl{ApJ} +\let\apjlett\apjl +\newcommand*\apjs{ApJS} +\let\apjsupp\apjs +\newcommand*\aplett{Astrophys.~Lett.} +\newcommand*\apspr{Astrophys.~Space~Phys.~Res.} +\newcommand*\apss{Ap\&SS} +\newcommand*\araa{ARA\&A} +\newcommand*\azh{AZh} +\newcommand*\baas{BAAS} +\newcommand*\bac{Bull. astr. Inst. Czechosl.} +\newcommand*\bain{Bull.~Astron.~Inst.~Netherlands} +\newcommand*\caa{Chinese Astron. Astrophys.} +\newcommand*\cjaa{Chinese J. Astron. Astrophys.} +\newcommand*\fcp{Fund.~Cosmic~Phys.} +\newcommand*\gca{Geochim.~Cosmochim.~Acta} +\newcommand*\grl{Geophys.~Res.~Lett.} +\newcommand*\iaucirc{IAU~Circ.} +\newcommand*\icarus{Icarus} +\newcommand*\jcap{J. Cosmology Astropart. Phys.} +\newcommand*\jcp{J.~Chem.~Phys.} +\newcommand*\jgr{J.~Geophys.~Res.} +\newcommand*\jqsrt{J.~Quant.~Spectr.~Rad.~Transf.} +\newcommand*\jrasc{JRASC} +\newcommand*\memras{MmRAS} +\newcommand*\memsai{Mem.~Soc.~Astron.~Italiana} +\newcommand*\mnras{MNRAS} +\newcommand*\na{New A} +\newcommand*\nar{New A Rev.} +\newcommand*\nat{Nature} +\newcommand*\nphysa{Nucl.~Phys.~A} +\newcommand*\pasa{PASA} +\newcommand*\pasj{PASJ} +\newcommand*\pasp{PASP} +\newcommand*\physrep{Phys.~Rep.} +\newcommand*\physscr{Phys.~Scr} +\newcommand*\planss{Planet.~Space~Sci.} +\newcommand*\pra{Phys.~Rev.~A} +\newcommand*\prb{Phys.~Rev.~B} +\newcommand*\prc{Phys.~Rev.~C} +\newcommand*\prd{Phys.~Rev.~D} +\newcommand*\pre{Phys.~Rev.~E} +\newcommand*\prl{Phys.~Rev.~Lett.} +\newcommand*\procspie{Proc.~SPIE} +\newcommand*\qjras{QJRAS} +\newcommand*\rmxaa{Rev. Mexicana Astron. Astrofis.} +\newcommand*\skytel{S\&T} +\newcommand*\solphys{Sol.~Phys.} +\newcommand*\sovast{Soviet~Ast.} +\newcommand*\ssr{Space~Sci.~Rev.} +\newcommand*\zap{ZAp} + + +\endinput +%% +%% End of file `ar.cls'. diff --git a/theory/SinkParticles/ar-style2.bst b/theory/SinkParticles/ar-style2.bst new file mode 100644 index 0000000000000000000000000000000000000000..f365f2341562e95d139483772f103ecc21e90240 --- /dev/null +++ b/theory/SinkParticles/ar-style2.bst @@ -0,0 +1,1565 @@ +% +% Changed \begin{thebibliography} argument as {} +% Changed format.lab.names to include 3 authors citation. +%% +%% This is file `ar-style2.bst', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% merlin.mbs (with options: `,ay,nat,nm-rvx,nmlm,x6,m5,dt-beg,yr-per,yrp-per,note-yr,jtit-x,vnum-x,pp-last,jnm-x,add-pub,edby,edbyx,fin-bare,ppx,ed,abr,ednx,ord,jabr,amper,and-xcom,xand,em-it,nfss') +%% ---------------------------------------- +%% *** Annual Reviews: Harvard Style (not numbered, excluding titles) *** +%% + %------------------------------------------------------------------- + % The original source file contains the following version information: + % \ProvidesFile{merlin.mbs}[1998/02/25 3.85a (PWD)] + % + % NOTICE: + % This file may be used for non-profit purposes. + % It may not be distributed in exchange for money, + % other than distribution costs. + % + % The author provides it `as is' and does not guarantee it in any way. + % + % Copyright (C) 1994-98 Patrick W. Daly + %------------------------------------------------------------------- + % For use with BibTeX version 0.99a or later + %------------------------------------------------------------------- + % This bibliography style file is intended for texts in ENGLISH + % This is an author-year citation style bibliography. As such, it is + % non-standard LaTeX, and requires a special package file to function properly. + % Such a package is natbib.sty by Patrick W. Daly + % The form of the \bibitem entries is + % \bibitem[Jones et al.(1990)]{key}... + % \bibitem[Jones et al.(1990)Jones, Baker, and Smith]{key}... + % The essential feature is that the label (the part in brackets) consists + % of the author names, as they should appear in the citation, with the year + % in parentheses following. There must be no space before the opening + % parenthesis! + % With natbib v5.3, a full list of authors may also follow the year. + % In natbib.sty, it is possible to define the type of enclosures that is + % really wanted (brackets or parentheses), but in either case, there must + % be parentheses in the label. + % The \cite command functions as follows: + % \citet{key} ==>> Jones et al. (1990) + % \citet*{key} ==>> Jones, Baker, and Smith (1990) + % \citep{key} ==>> (Jones et al., 1990) + % \citep*{key} ==>> (Jones, Baker, and Smith, 1990) + % \citep[chap. 2]{key} ==>> (Jones et al., 1990, chap. 2) + % \citep[e.g.][]{key} ==>> (e.g. Jones et al., 1990) + % \citep[e.g.][p. 32]{key} ==>> (e.g. Jones et al., p. 32) + % \citeauthor{key} ==>> Jones et al. + % \citeauthor*{key} ==>> Jones, Baker, and Smith + % \citeyear{key} ==>> 1990 + %--------------------------------------------------------------------- + +ENTRY + { address + author + booktitle + chapter + edition + editor + howpublished + institution + journal + key + month + note + number + organization + pages + publisher + school + series + title + type + volume + issue + year + } + {} + { label extra.label sort.label short.list } + +INTEGERS { output.state before.all mid.sentence after.sentence after.block } + +FUNCTION {init.state.consts} +{ #0 'before.all := + #1 'mid.sentence := + #2 'after.sentence := + #3 'after.block := +} + +STRINGS { s t } + +FUNCTION {output.nonnull} +{ 's := + output.state mid.sentence = + { ", " * write$ } + { output.state after.block = + { add.period$ write$ + newline$ +% "\newblock " write$ + } + { output.state before.all = + 'write$ + { add.period$ " " * write$ } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +FUNCTION {output} +{ duplicate$ empty$ + 'pop$ + 'output.nonnull + if$ +} + +FUNCTION {output.check} +{ 't := + duplicate$ empty$ + { pop$ "empty " t * " in " * cite$ * warning$ } + 'output.nonnull + if$ +} + +FUNCTION {fin.entry} +{ duplicate$ empty$ + 'pop$ + 'write$ + if$ + newline$ +} + +FUNCTION {new.block} +{ output.state before.all = + 'skip$ + { after.block 'output.state := } + if$ +} + +FUNCTION {new.sentence} +{ output.state after.block = + 'skip$ + { output.state before.all = + 'skip$ + { after.sentence 'output.state := } + if$ + } + if$ +} + +FUNCTION {add.blank} +{ " " * before.all 'output.state := +} + +FUNCTION {add.wsblank} +{ "" * before.all 'output.state := +} + +FUNCTION {date.block} +{ + new.block +} + +FUNCTION {not} +{ { #0 } + { #1 } + if$ +} + +FUNCTION {and} +{ 'skip$ + { pop$ #0 } + if$ +} + +FUNCTION {or} +{ { pop$ #1 } + 'skip$ + if$ +} + +FUNCTION {new.block.checkb} +{ empty$ + swap$ empty$ + and + 'skip$ + 'new.block + if$ +} + +FUNCTION {field.or.null} +{ duplicate$ empty$ + { pop$ "" } + 'skip$ + if$ +} + +FUNCTION {emphasize} +{ duplicate$ empty$ + { pop$ "" } + { "\textit{" swap$ * "}" * } + if$ +} + +FUNCTION {capitalize} +{ "u" change.case$ "t" change.case$ } + +FUNCTION {space.word} +{ " " swap$ * " " * } + + % Here are the language-specific definitions for explicit words. + % Each function has a name bbl.xxx where xxx is the English word. + % The language selected here is ENGLISH +FUNCTION {bbl.and} +{ "and"} + +FUNCTION {bbl.editors} +{ "eds." } + +FUNCTION {bbl.editor} +{ "ed." } + +FUNCTION {bbl.edby} +{ "edited by" } + +FUNCTION {bbl.edition} +{ "ed." } + +FUNCTION {bbl.volume} +{ "vol." } + +FUNCTION {bbl.of} +{ "of" } + +FUNCTION {bbl.number} +{ "no." } + +FUNCTION {bbl.nr} +{ "no." } + +FUNCTION {bbl.in} +{ "in" } + +FUNCTION {bbl.pages} +{ "" } + +FUNCTION {bbl.page} +{ "" } + +FUNCTION {bbl.chapter} +{ "chap." } + +FUNCTION {bbl.techrep} +{ "Tech. Rep." } + +FUNCTION {bbl.mthesis} +{ "Master's thesis" } + +FUNCTION {bbl.phdthesis} +{ "Ph.D. thesis" } + +FUNCTION {bbl.first} +{ "1st" } + +FUNCTION {bbl.second} +{ "2nd" } + +FUNCTION {bbl.third} +{ "3rd" } + +FUNCTION {bbl.fourth} +{ "4th" } + +FUNCTION {bbl.fifth} +{ "5th" } + +FUNCTION {bbl.st} +{ "st" } + +FUNCTION {bbl.nd} +{ "nd" } + +FUNCTION {bbl.rd} +{ "rd" } + +FUNCTION {bbl.th} +{ "th" } + +MACRO {jan} {"Jan."} + +MACRO {feb} {"Feb."} + +MACRO {mar} {"Mar."} + +MACRO {apr} {"Apr."} + +MACRO {may} {"May"} + +MACRO {jun} {"Jun."} + +MACRO {jul} {"Jul."} + +MACRO {aug} {"Aug."} + +MACRO {sep} {"Sep."} + +MACRO {oct} {"Oct."} + +MACRO {nov} {"Nov."} + +MACRO {dec} {"Dec."} + +FUNCTION {eng.ord} +{ duplicate$ "1" swap$ * + #-2 #1 substring$ "1" = + { bbl.th * } + { duplicate$ #-1 #1 substring$ + duplicate$ "1" = + { pop$ bbl.st * } + { duplicate$ "2" = + { pop$ bbl.nd * } + { "3" = + { bbl.rd * } + { bbl.th * } + if$ + } + if$ + } + if$ + } + if$ +} + +MACRO {acmcs} {"ACM Comput. Surv."} + +MACRO {acta} {"Acta Inf."} + +MACRO {cacm} {"Commun. ACM"} + +MACRO {ibmjrd} {"IBM J. Res. Dev."} + +MACRO {ibmsj} {"IBM Syst.~J."} + +MACRO {ieeese} {"IEEE Trans. Softw. Eng."} + +MACRO {ieeetc} {"IEEE Trans. Comput."} + +MACRO {ieeetcad} + {"IEEE Trans. Comput.-Aided Design Integrated Circuits"} + +MACRO {ipl} {"Inf. Process. Lett."} + +MACRO {jacm} {"J.~ACM"} + +MACRO {jcss} {"J.~Comput. Syst. Sci."} + +MACRO {scp} {"Sci. Comput. Programming"} + +MACRO {sicomp} {"SIAM J. Comput."} + +MACRO {tocs} {"ACM Trans. Comput. Syst."} + +MACRO {tods} {"ACM Trans. Database Syst."} + +MACRO {tog} {"ACM Trans. Gr."} + +MACRO {toms} {"ACM Trans. Math. Softw."} + +MACRO {toois} {"ACM Trans. Office Inf. Syst."} + +MACRO {toplas} {"ACM Trans. Prog. Lang. Syst."} + +MACRO {tcs} {"Theoretical Comput. Sci."} + +INTEGERS { nameptr namesleft numnames } + +FUNCTION {format.names} +{ 's := + #1 'nameptr := + s num.names$ 'numnames := + numnames 'namesleft := + { namesleft #0 > } + { s nameptr + "{vv~}{ll}{ jj}{ f{}}" format.name$ + 't := + nameptr #1 > + { + nameptr #6 = + numnames #6 > and + { "others" 't := + #1 'namesleft := } + 'skip$ + if$ + namesleft #1 > + { ", " * t * } + { + "," * + s nameptr "{ll}" format.name$ duplicate$ "others" = + { 't := } + { pop$ } + if$ + t "others" = + { + " et~al." * + } + { " " * t * } + if$ + } + if$ + } + 't + if$ + nameptr #1 + 'nameptr := + namesleft #1 - 'namesleft := + } + while$ +} +FUNCTION {format.names.ed} +{ 's := + #1 'nameptr := + s num.names$ 'numnames := + numnames 'namesleft := + { namesleft #0 > } + { s nameptr + "{f{}~}{vv~}{ll}{ jj}" + format.name$ + 't := + nameptr #1 > + { + namesleft #1 > + { ", " * t * } + { + "," * + s nameptr "{ll}" format.name$ duplicate$ "others" = + { 't := } + { pop$ } + if$ + t "others" = + { + " et~al." * + } + { " " * t * } + if$ + } + if$ + } + 't + if$ + nameptr #1 + 'nameptr := + namesleft #1 - 'namesleft := + } + while$ +} + +FUNCTION {format.key} +{ empty$ + { key field.or.null } + { "" } + if$ +} + +FUNCTION {format.authors} +{ author empty$ + { "" } + { author format.names } + if$ +} + +FUNCTION {format.issues} +{ issue empty$ + { "" } + { "(" * issue ")" * } + if$ +} + +FUNCTION {format.numbers} +{ number empty$ + { "" } + { "(" * number ")" * } + if$ +} + +FUNCTION {format.editors} +{ editor empty$ + { "" } + { editor format.names + editor num.names$ #1 > + { ", " * bbl.editors * } + { ", " * bbl.editor * } + if$ + } + if$ +} + +FUNCTION {format.in.editors} +{ editor empty$ + { "" } + { editor format.names.ed + } + if$ +} + +FUNCTION {format.note} +{ note empty$ + { "" } + { note #1 #1 substring$ + duplicate$ "{" = + 'skip$ + { output.state mid.sentence = + { "l" } + { "u" } + if$ + change.case$ + } + if$ + note #2 global.max$ substring$ * + } + if$ +} + +FUNCTION {format.title} +{ title empty$ + { "" } + { title "t" change.case$ + } + if$ +} + +FUNCTION {format.full.names} +{'s := + #1 'nameptr := + s num.names$ 'numnames := + numnames 'namesleft := + { namesleft #0 > } + { s nameptr + "{vv~}{ll}" format.name$ + 't := + nameptr #1 > + { + nameptr #6 = + numnames #6 > and + { "others" 't := + #1 'namesleft := } + 'skip$ + if$ + namesleft #1 > + { ", " * t * } + { + s nameptr "{ll}" format.name$ duplicate$ "others" = + { 't := } + { pop$ } + if$ + t "others" = + { + " et~al." * + } + { " \& " * t * } + if$ + } + if$ + } + 't + if$ + nameptr #1 + 'nameptr := + namesleft #1 - 'namesleft := + } + while$ +} + +FUNCTION {author.editor.key.full} +{ author empty$ + { editor empty$ + { key empty$ + { cite$ #1 #3 substring$ } + 'key + if$ + } + { editor format.full.names } + if$ + } + { author format.full.names } + if$ +} + +FUNCTION {author.key.full} +{ author empty$ + { key empty$ + { cite$ #1 #3 substring$ } + 'key + if$ + } + { author format.full.names } + if$ +} + +FUNCTION {editor.key.full} +{ editor empty$ + { key empty$ + { cite$ #1 #3 substring$ } + 'key + if$ + } + { editor format.full.names } + if$ +} + +FUNCTION {make.full.names} +{ type$ "book" = + type$ "inbook" = + or + 'author.editor.key.full + { type$ "proceedings" = + 'editor.key.full + 'author.key.full + if$ + } + if$ +} + +FUNCTION {output.bibitem} +{ newline$ + "\bibitem[{" write$ + label write$ + ")" make.full.names duplicate$ short.list = + { pop$ } + { * } + if$ + "}]{" * write$ + cite$ write$ + "}" write$ + newline$ + "" + before.all 'output.state := +} + +FUNCTION {n.dashify} +{ + 't := + "" + { t empty$ not } + { t #1 #1 substring$ "-" = + { t #1 #2 substring$ "--" = not + { "--" * + t #2 global.max$ substring$ 't := + } + { { t #1 #1 substring$ "-" = } + { "-" * + t #2 global.max$ substring$ 't := + } + while$ + } + if$ + } + { t #1 #1 substring$ * + t #2 global.max$ substring$ 't := + } + if$ + } + while$ +} + +FUNCTION {word.in} +{ bbl.in capitalize + " " * } + +FUNCTION {format.date} +{ year duplicate$ empty$ + { "empty year in " cite$ * "; set to ????" * warning$ + pop$ "????" } + 'skip$ + if$ + extra.label * + before.all 'output.state := + after.sentence 'output.state := +} + +FUNCTION {format.btitle} +{ title emphasize +} + +FUNCTION {tie.or.space.connect} +{ duplicate$ text.length$ #3 < + { "~" } + { " " } + if$ + swap$ * * +} + +FUNCTION {either.or.check} +{ empty$ + 'pop$ + { "can't use both " swap$ * " fields in " * cite$ * warning$ } + if$ +} + +FUNCTION {format.bvolume} +{ volume empty$ + { "" } + { bbl.volume volume tie.or.space.connect + series empty$ + 'skip$ + { bbl.of space.word * series emphasize * } + if$ + "volume and number" number either.or.check + } + if$ +} + +FUNCTION {format.number.series} +{ volume empty$ + { number empty$ + { series field.or.null } + { output.state mid.sentence = + { bbl.number } + { bbl.number capitalize } + if$ + number tie.or.space.connect + series empty$ + { "there's a number but no series in " cite$ * warning$ } + { bbl.in space.word * series * } + if$ + } + if$ + } + { "" } + if$ +} + +FUNCTION {is.num} +{ chr.to.int$ + duplicate$ "0" chr.to.int$ < not + swap$ "9" chr.to.int$ > not and +} + +FUNCTION {extract.num} +{ duplicate$ 't := + "" 's := + { t empty$ not } + { t #1 #1 substring$ + t #2 global.max$ substring$ 't := + duplicate$ is.num + { s swap$ * 's := } + { pop$ "" 't := } + if$ + } + while$ + s empty$ + 'skip$ + { pop$ s } + if$ +} + +FUNCTION {convert.edition} +{ edition extract.num "l" change.case$ 's := + s "first" = s "1" = or + { bbl.first 't := } + { s "second" = s "2" = or + { bbl.second 't := } + { s "third" = s "3" = or + { bbl.third 't := } + { s "fourth" = s "4" = or + { bbl.fourth 't := } + { s "fifth" = s "5" = or + { bbl.fifth 't := } + { s #1 #1 substring$ is.num + { s eng.ord 't := } + { edition 't := } + if$ + } + if$ + } + if$ + } + if$ + } + if$ + } + if$ + t +} + +FUNCTION {format.edition} +{ edition empty$ + { "" } + { output.state mid.sentence = + { convert.edition "l" change.case$ " " * bbl.edition * } + { convert.edition "t" change.case$ " " * bbl.edition * } + if$ + } + if$ +} + +INTEGERS { multiresult } + +FUNCTION {multi.page.check} +{ 't := + #0 'multiresult := + { multiresult not + t empty$ not + and + } + { t #1 #1 substring$ + duplicate$ "-" = + swap$ duplicate$ "," = + swap$ "+" = + or or + { #1 'multiresult := } + { t #2 global.max$ substring$ 't := } + if$ + } + while$ + multiresult +} + +FUNCTION {format.pages} +{ pages empty$ + { "" } + { pages multi.page.check + { bbl.pages pages n.dashify tie.or.space.connect } + { bbl.page pages tie.or.space.connect } + if$ + } + if$ +} + +FUNCTION {format.journal.pages} +{ pages empty$ + 'skip$ + { duplicate$ empty$ + { pop$ format.pages } + { + ":" * + pages n.dashify * + } + if$ + } + if$ +} + +FUNCTION {format.vol.num.pages} +{ + volume field.or.null +} + +FUNCTION {format.chapter.pages} +{ chapter empty$ + { "" } + { type empty$ + { bbl.chapter } + { type "l" change.case$ } + if$ + chapter tie.or.space.connect + } + if$ +} + +FUNCTION {format.in.ed.booktitle} +{ booktitle empty$ + { "" } + { editor empty$ + { word.in booktitle emphasize * } + { word.in booktitle emphasize * + ", " * + editor num.names$ #1 > + { bbl.editors } + { bbl.editor } + if$ + * " " * + format.in.editors * + } + if$ + } + if$ +} + +FUNCTION {format.thesis.type} +{ type empty$ + 'skip$ + { pop$ + type "t" change.case$ + } + if$ +} + +FUNCTION {format.tr.number} +{ type empty$ + { bbl.techrep } + 'type + if$ + number empty$ + { "t" change.case$ } + { number tie.or.space.connect } + if$ +} + +FUNCTION {format.article.crossref} +{ + word.in + " \cite{" * crossref * "}" * +} + +FUNCTION {format.book.crossref} +{ volume empty$ + { "empty volume in " cite$ * "'s crossref of " * crossref * warning$ + word.in + } + { bbl.volume capitalize + volume tie.or.space.connect + bbl.of space.word * + } + if$ + " \cite{" * crossref * "}" * +} + +FUNCTION {format.incoll.inproc.crossref} +{ + word.in + " \cite{" * crossref * "}" * +} + +FUNCTION {format.publisher} +{ publisher empty$ + { "empty publisher in " cite$ * warning$ } + 'skip$ + if$ + "" + address empty$ publisher empty$ and + 'skip$ + { + address empty$ + 'skip$ + { address * } + if$ + publisher empty$ + 'skip$ + { address empty$ + 'skip$ + { ": " * } + if$ + publisher * + } + if$ + } + if$ + output +} + +FUNCTION {article} +{ output.bibitem + format.authors "author" output.check + author format.key output + format.date "year" output.check + date.block + crossref missing$ + { journal + emphasize + "journal" output.check + add.blank + format.vol.num.pages output + add.wsblank + format.numbers output + add.wsblank + format.issues output + } + { format.article.crossref output.nonnull + format.pages output + } + if$ + format.journal.pages + new.block + format.note output + fin.entry +} + +FUNCTION {book} +{ output.bibitem + author empty$ + { format.editors "author and editor" output.check + editor format.key output + } + { format.authors output.nonnull + crossref missing$ + { "author and editor" editor either.or.check } + 'skip$ + if$ + } + if$ + format.date "year" output.check + date.block + format.btitle "title" output.check + date.block + crossref missing$ + { format.bvolume output + new.block + format.number.series output + new.sentence + format.publisher + } + { + new.block + format.book.crossref output.nonnull + } + if$ + format.edition output + new.block + format.note output + fin.entry +} + +FUNCTION {booklet} +{ output.bibitem + format.authors output + author format.key output + format.date "year" output.check + date.block + format.title "title" output.check + new.block + howpublished output + address output + new.block + format.note output + fin.entry +} + +FUNCTION {inbook} +{ output.bibitem + author empty$ + { format.editors "author and editor" output.check + editor format.key output + } + { format.authors output.nonnull + crossref missing$ + { "author and editor" editor either.or.check } + 'skip$ + if$ + } + if$ + format.date "year" output.check + date.block + format.btitle "title" output.check + crossref missing$ + { + format.bvolume output + format.chapter.pages "chapter and pages" output.check + new.block + format.number.series output + new.sentence + format.publisher + } + { + format.chapter.pages "chapter and pages" output.check + new.block + format.book.crossref output.nonnull + } + if$ + format.edition output + format.pages "pages" output.check + new.block + format.note output + fin.entry +} + +FUNCTION {incollection} +{ output.bibitem + format.authors "author" output.check + author format.key output + format.date "year" output.check + date.block + crossref missing$ + { format.btitle "title" output.check new.sentence + format.in.ed.booktitle "booktitle" output.check + format.bvolume output + format.number.series output + format.chapter.pages output + new.sentence + format.publisher + format.edition output + } + { format.incoll.inproc.crossref output.nonnull + format.chapter.pages output + } + if$ + format.pages "pages" output.check + new.block + format.note output + fin.entry +} + +FUNCTION {inproceedings} +{ output.bibitem + format.authors "author" output.check + author format.key output + format.date "year" output.check + date.block + crossref missing$ + { format.btitle "title" output.check new.sentence + format.in.ed.booktitle "booktitle" output.check + format.bvolume output + format.number.series output + new.sentence + publisher empty$ + { organization output + address output + } + { organization output + format.publisher + } + if$ + } + { format.incoll.inproc.crossref output.nonnull + format.pages output + } + if$ + new.block + format.note output + fin.entry +} + +FUNCTION {conference} { inproceedings } + +FUNCTION {manual} +{ output.bibitem + format.authors output + author format.key output + format.date "year" output.check + date.block + format.btitle "title" output.check + organization address new.block.checkb + organization output + address output + format.edition output + new.block + format.note output + fin.entry +} + +FUNCTION {mastersthesis} +{ output.bibitem + format.authors "author" output.check + author format.key output + format.date "year" output.check + date.block + format.btitle "title" output.check + new.block + bbl.mthesis format.thesis.type output.nonnull + school "school" output.check + address output + new.block + format.note output + fin.entry +} + +FUNCTION {misc} +{ output.bibitem + format.authors output + author format.key output + format.date "year" output.check + date.block + format.title output + new.block + howpublished output + new.block + format.note output + fin.entry +} + +FUNCTION {phdthesis} +{ output.bibitem + format.authors "author" output.check + author format.key output + format.date "year" output.check + date.block + format.btitle "title" output.check + new.block + bbl.phdthesis format.thesis.type output.nonnull + school "school" output.check + address output + new.block + format.note output + fin.entry +} + +FUNCTION {proceedings} +{ output.bibitem + format.editors output + editor format.key output + format.date "year" output.check + date.block + format.btitle "title" output.check + format.bvolume output + format.number.series output + address output + new.sentence + organization output + publisher output + new.block + format.note output + fin.entry +} + +FUNCTION {techreport} +{ output.bibitem + format.authors "author" output.check + author format.key output + format.date "year" output.check + date.block + format.title "title" output.check + new.block + format.tr.number output.nonnull + institution "institution" output.check + address output + new.block + format.note output + fin.entry +} + +FUNCTION {unpublished} +{ output.bibitem + format.authors "author" output.check + author format.key output + format.date "year" output.check + date.block + format.title "title" output.check + new.block + format.note "note" output.check + fin.entry +} + +FUNCTION {default.type} { misc } + +READ + +FUNCTION {sortify} +{ purify$ + "l" change.case$ +} + +INTEGERS { len } + +FUNCTION {chop.word} +{ 's := + 'len := + s #1 len substring$ = + { s len #1 + global.max$ substring$ } + 's + if$ +} + +FUNCTION {format.lab.names} +{ 's := + s #1 "{vv~}{ll}" format.name$ + s num.names$ duplicate$ + #2 > + { pop$ + " et~al." * + } + { #3 < + { s num.names$ #2 < + 'skip$ + { s #2 "{ff }{vv }{ll}{ jj}" format.name$ "others" = + { + " et~al." * + } + { " \& " * s #2 "{vv~}{ll}" format.name$ + * } + if$ + } + if$ + } + { s #3 "{ff }{vv }{ll}{ jj}" format.name$ "others" = + 'skip$ + { s #3 "{ff }{vv }{ll}{ jj}" format.name$ "others" = + { + " et~al." * + } + { ", " * s #2 "{vv~}{ll}" format.name$ * " \& " * s #3 "{vv~}{ll}" format.name$ + * } + if$ + } + if$ + } + if$ + } + if$ +} + +FUNCTION {author.key.label} +{ author empty$ + { key empty$ + { cite$ #1 #3 substring$ } + 'key + if$ + } + { author format.lab.names } + if$ +} + +FUNCTION {author.editor.key.label} +{ author empty$ + { editor empty$ + { key empty$ + { cite$ #1 #3 substring$ } + 'key + if$ + } + { editor format.lab.names } + if$ + } + { author format.lab.names } + if$ +} + +FUNCTION {editor.key.label} +{ editor empty$ + { key empty$ + { cite$ #1 #3 substring$ } + 'key + if$ + } + { editor format.lab.names } + if$ +} + +FUNCTION {calc.short.authors} +{ type$ "book" = + type$ "inbook" = + or + 'author.editor.key.label + { type$ "proceedings" = + 'editor.key.label + 'author.key.label + if$ + } + if$ + 'short.list := +} + +FUNCTION {calc.label} +{ calc.short.authors + short.list + "(" + * + year duplicate$ empty$ + { pop$ "????" } + 'skip$ + if$ + * + 'label := +} + +FUNCTION {sort.format.names} +{ 's := + #1 'nameptr := + "" + s num.names$ 'numnames := + numnames 'namesleft := + { namesleft #0 > } + { s nameptr + "{vv{ } }{ll{ }}{ f{ }}{ jj{ }}" + format.name$ 't := + nameptr #1 > + { + nameptr #6 = + numnames #6 > and + { "others" 't := + #1 'namesleft := } + 'skip$ + if$ + " " * + namesleft #1 = t "others" = and + { "zzzzz" * } + { t sortify * } + if$ + } + { t sortify * } + if$ + nameptr #1 + 'nameptr := + namesleft #1 - 'namesleft := + } + while$ +} + +FUNCTION {sort.format.title} +{ 't := + "A " #2 + "An " #3 + "The " #4 t chop.word + chop.word + chop.word + sortify + #1 global.max$ substring$ +} + +FUNCTION {author.sort} +{ author empty$ + { key empty$ + { "to sort, need author or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { author sort.format.names } + if$ +} + +FUNCTION {author.editor.sort} +{ author empty$ + { editor empty$ + { key empty$ + { "to sort, need author, editor, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { editor sort.format.names } + if$ + } + { author sort.format.names } + if$ +} + +FUNCTION {editor.sort} +{ editor empty$ + { key empty$ + { "to sort, need editor or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { editor sort.format.names } + if$ +} + +FUNCTION {presort} +{ calc.label + label sortify + " " + * + type$ "book" = + type$ "inbook" = + or + 'author.editor.sort + { type$ "proceedings" = + 'editor.sort + 'author.sort + if$ + } + if$ + #1 entry.max$ substring$ + 'sort.label := + sort.label + * + " " + * + title field.or.null + sort.format.title + * + #1 entry.max$ substring$ + 'sort.key$ := +} + +ITERATE {presort} + +SORT + +STRINGS { last.label next.extra } + +INTEGERS { last.extra.num number.label } + +FUNCTION {initialize.extra.label.stuff} +{ #0 int.to.chr$ 'last.label := + "" 'next.extra := + #0 'last.extra.num := + #0 'number.label := +} + +FUNCTION {forward.pass} +{ last.label label = + { last.extra.num #1 + 'last.extra.num := + last.extra.num int.to.chr$ 'extra.label := + } + { "a" chr.to.int$ 'last.extra.num := + "" 'extra.label := + label 'last.label := + } + if$ + number.label #1 + 'number.label := +} + +FUNCTION {reverse.pass} +{ next.extra "b" = + { "a" 'extra.label := } + 'skip$ + if$ + extra.label 'next.extra := + extra.label + duplicate$ empty$ + 'skip$ + { "{\natexlab{" swap$ * "}}" * } + if$ + 'extra.label := + label extra.label * 'label := +} + +EXECUTE {initialize.extra.label.stuff} + +ITERATE {forward.pass} + +REVERSE {reverse.pass} + +FUNCTION {bib.sort.order} +{ sort.label + " " + * + year field.or.null sortify + * + " " + * + title field.or.null + sort.format.title + * + #1 entry.max$ substring$ + 'sort.key$ := +} + +ITERATE {bib.sort.order} + +SORT + +FUNCTION {begin.bib} +{ preamble$ empty$ + 'skip$ + { preamble$ write$ newline$ } + if$ + "\begin{thebibliography}{}" + write$ newline$ + "\expandafter\ifx\csname natexlab\endcsname\relax\def\natexlab#1{#1}\fi" + write$ newline$ +} + +EXECUTE {begin.bib} + +EXECUTE {init.state.consts} + +ITERATE {call.type$} + +FUNCTION {end.bib} +{ newline$ + "\end{thebibliography}" write$ newline$ +} + +EXECUTE {end.bib} +%% End of customized bst file +%% +%% End of file `ar-style2.bst'. diff --git a/theory/SinkParticles/gear_imf_sampling.tex b/theory/SinkParticles/gear_imf_sampling.tex new file mode 100644 index 0000000000000000000000000000000000000000..4aeb36227d517ac065b17085eebcb096f9ae0092 --- /dev/null +++ b/theory/SinkParticles/gear_imf_sampling.tex @@ -0,0 +1,97 @@ +% Note: The content of this file was taken and adapted from Darwin Roduit's master thesis (June 2024). +% The algorithm was written based on documents of Yves Revaz. +% The latex file was simplified to be part of SWIFT. + +\documentclass[a4paper]{ar-1col-S2O} + +% Math packages +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{amsfonts} + +% Physics package +\usepackage{physics} + +% Algorithm packages +\usepackage{algorithm} +\usepackage{algpseudocodex} + +\begin{document} %------------------------------------------------ +\section{IMF Sampling} +\label{sec:imf_sampling} + +Until now, we haven't explored the details of the IMF sampling and the math behind it. It is time to change that. + +Assume that we have an IMF. We want to sample it correctly to produce star populations. The challenge is to produce a fast algorithm that provides the correct targeted masses. The algorithm must be computationally efficient; otherwise, we may not gain any computational time by creating sinks compared to physically detailed and motivated star formation schemes. + +We do not want to produce too many low-mass stars (low-mass stars dominate the IMF) since it would require immense memory and computational power without significant benefits. Indeed, low-mass stars have a weaker impact on galaxy formation and evolution compared to massive stars that undergo supernovae explosions. To this end, the IMF is split into two parts: continuous and discrete. The separating mass is called $m_t$. In the continuous part, a star particle represents a star population with masses below $m_t$. \\ +Moreover, such particles all have the same mass $m_{\text{SP}}$. In the discrete IMF part, star particles represent individual stars with different masses. Notice that $m_{\text{SP}}$ and $m_t$ are parameters the user sets. Figure \ref{fig:sink_imf} shows the IMF in two parts. + +\begin{figure}[b] + \includegraphics[scale=0.7]{sink_imf} + \caption{This figure shows an IMF split into two parts: the continuous (orange) with mass and the discrete (blue) part with respective mass $M_c$ and $M_d$. The separating mass is called $m_t$. \emph{Source}: Roduit Darwin} + \label{fig:sink_imf} +\end{figure} + +Let us define $M_c$ the mass of the continuous part of the IMF and $M_d$ the mass of the discrete part by the following equation: +% +\begin{equation} + M_c = \int_{m_\text{min}}^{m_t} \Phi(m) \dd m \quad \text{and} \quad M_d = \int_{m_t}^{m_\text{max}} \Phi(m) \dd m \, , +\end{equation} +% +where $\Phi(m)$ is the initial mass function. + +Similarly, we can define $N_c$ as the number of stars in the continuous part of the IMF and $N_d$ as the one in the discrete part: +% +\begin{equation} + N_c = \int_{m_\text{min}}^{m_t} \frac{\Phi(m)}{m} \dd m \quad \text{and} \quad N_d = \int_{m_t}^{m_\text{max}} \frac{\Phi(m)}{m} \dd m \, . +\end{equation} +% +Another important definition is the number of particles (not stars) $N_{\text{SP}}$ of mass $m_{\text{SP}}$ that will populate the continuous part of the IMF. Thus, the total number of stars that the IMF will generate is: +% +\begin{equation} + N_{\text{tot}} = N_{\text{SP}} + N_d \, . +\end{equation} +% +$N_{\text{SP}}$ is obtained by dividing the mass $M_c$ of the continuous part of the IMF by the mass of the stellar particle $m_{\text{SP}}$: +% +\begin{equation} + N_{\text{SP}} = \frac{M_c}{m_{\text{SP}}} \, . +\end{equation} +% +Now, we need to find the probability $P_c$ to spawn a star particle of mass $m_\text{SP}$ and $P_c$ the probability to spawn individual stars representing the discrete part of the IMF. With all the above definitions, it is easy to find those probabilities. Indeed, $P_c$ is given by: +% +\begin{equation} + P_c = \frac{N_{\text{SP}}}{N_{\text{tot}}} = \frac{N_{\text{SP}}}{N_{\text{SP}} + N_d} \quad \text{and} \quad P_d = 1 - P_c \, . +\end{equation} +% +If we assume that only a star particle will contain all stars below $m_t$, i.e. $N_{\text{SP}} = 1$, we find: +% +\begin{equation} + P_c = \frac{1}{1 + N_d} \, . +\end{equation} +% +Therefore, the algorithm of the IMF sampling is presented in Algorithm \ref{algo:imf_sampling}. In this algorithm, we have assumed to have a function \texttt{sample\_IMF\_high()} that correctly samples the IMF for the discrete part. This algorithm is called whenever we need to set the target mass of the sink. So, it is called once a new sink is formed, if a sink exists in the inital conditions or after having spawned a star. \\ + +This algorithm is the same for populations II and III stars. However, the IMF parameters (mainly the minimal IMF mass and maximal IMF mass) and the two free parameters $m_t$ and $m_{SP}$ can vary. + +\begin{algorithm} + \begin{algorithmic} + \State $\mathtt{random\_number} \gets \text{draw an random number in the interval }\left(0, 1 \right]$ + \If{$\mathtt{random\_number} < P_c$} + \State $\mathtt{target\_mass} \gets m_{\text{SP}}$ + \Else + \State $\mathtt{target\_mass} \gets \mathtt{sample\_IMF\_high()}$ + \EndIf + \Return{$\mathtt{target\_mass}$} +\end{algorithmic} +\caption{IMF sampling algorithm, also called \texttt{sink\_update\_target\_mass()}. }\label{algo:imf_sampling} +\end{algorithm} + + +\end{document} %------------------------------------------------ + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: t +%%% End: diff --git a/theory/SinkParticles/plot_imf_image.py b/theory/SinkParticles/plot_imf_image.py new file mode 100644 index 0000000000000000000000000000000000000000..25da38cab2b804a651c5f745063e6ba101b38a52 --- /dev/null +++ b/theory/SinkParticles/plot_imf_image.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Wed Jul 12 13:28 2024 + +This script creates an image of some IMF that we split into the continuous part and discrete part. +This image illustrates the star spawning algorithm with GEAR sink particles + +@author: Darwin Roduit +""" + +import numpy as np +import matplotlib.pyplot as plt +import matplotlib + +# For swift doc, use the following and not the styleheet +plt.rcParams['text.usetex'] = True +plt.rcParams['font.family'] = 'serif' +plt.rcParams['font.serif'] = ['Computer Modern Roman'] + +mmin = 0.5 +mmax = 300 + +matplotlib.rcParams.update({'font.size': 16}) + +figsize = (6.4, 4.8) +fig, ax = plt.subplots(num=1, ncols=1, nrows=1, + figsize=figsize, layout="tight") +ax.set_xlim([0.3, 400]) +ax.set_ylim([1, 5e4]) + +ax.set_xticks([1, 2, 4, 8, 20, 50, 100, 300]) +ax.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) + +ax.set_xlabel("$M_{\star}$ $[M_\odot]$") +ax.set_ylabel("d$N/$d$M$ [arbitrary units]") +ax.set_xscale('log') +ax.set_yscale('log') + +# theoretical imf +s = -1.3 +bins = 10**np.linspace(np.log10(mmin), np.log10(mmax), 100) +n = 0.9*10000*bins**s +ax.plot(bins, n, "k--") + +bins = 10**np.linspace(np.log10(mmin), np.log10(8), 100) +n = 0.9*10000*bins**s +ax.fill_between(bins, 0.1, n, color="red", alpha=0.1) + +bins = 10**np.linspace(np.log10(8), np.log10(mmax), 100) +n = 0.9*10000*bins**s +ax.fill_between(bins, 0.1, n, color="blue", alpha=0.1) + +ax.text(2, 1e2, r"$M_{\rm c}$", horizontalalignment='center') +ax.text(50, 2, r"$M_{\rm d}$", horizontalalignment='center') + +# Add limit +ax.vlines(x=8, ymin=0, ymax=600, color='k', linestyle='-') + +# Add text to the vertical line +ax.text(8, 800, r"$m_{t}$", horizontalalignment='center') + +# fig.patch.set_facecolor('none') # Remove figure background +# ax.set_facecolor('none') # Remove axes background + +plt.savefig('sink_imf.png', dpi=300, bbox_inches='tight') +plt.close() diff --git a/theory/SinkParticles/run.sh b/theory/SinkParticles/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..bafbd115760a1f5e6fd01c079344a8574fe0c907 --- /dev/null +++ b/theory/SinkParticles/run.sh @@ -0,0 +1,6 @@ +# First plot the IMF image +python3 ./plot_imf_image.py + +#Then, run latex to produce the output +pdflatex -jobname=gear_imf_sampling gear_imf_sampling.tex +