Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
evolution_split.c 14.67 KiB
#include "../binary_c.h"
No_empty_translation_unit_warning;





/*
 * Evolution run splitting algorithm
 *
 * Required EVOLUTION_SPLITTING to be defined (in binary_c_parameters.h)
 * and
 * stardata->preferences->evolution_splitting to be non-zero
 *
 * 1) Detect the end of an evolutionary run, if we have previously
 *    split, restore the previous evolutionary sequence and go again
 *    if required.
 *
 *    The max number of splits is set on the command line
 *    in the preference evolution_splitting_maxdepth, up to a
 *    hard-wired maximum EVOLUTION_SPLITTING_HARD_MAX_DEPTH.
 *    Obviously, too much splitting will cause the population
 *    to grind to a halt.
 *
 *    Returns EVOLUTION_SPLIT_RESTART in this case.
 *
 * 2) Detect when a new split should occur. This is controlled by
 *    the splittest function (below) which returns the number of runs
 *    to be a forked at each split.
 *
 *    Currently, this is implemented for supernovae, but it could
 *    work at any point in the evolutionary run (e.g. common envelope
 *    evolution).
 *
 *    Returns EVOLUTION_SPLIT_NEW_SPLIT in this case.
 *
 * 3) Returns EVOLUTION_SPLIT_CONTINUE otherwise.
 *
 * When a new split is made, a splitinfo struct is created which
 * contains the number of runs remaining, and a copy of the stardata
 * at the point of the split. This is then iterated over until the number
 * of runs left is zero.
 *
 * Note that at the end of evolution, evolution_cleanup is called.
 * Currently this does NOT free memory or close log files, these are
 * allowed to continue, which is probably what you want.
 *
 */

#ifdef EVOLUTION_SPLITTING

#define In_splitdepth(STARDATA)                                 \
    ((STARDATA)->preferences->current_splitdepth <              \
     Min((STARDATA)->preferences->evolution_splitting_maxdepth, \
         EVOLUTION_SPLITTING_HARD_MAX_DEPTH))

// split test function
static int splittest(struct stardata_t * RESTRICT const stardata);

static void modulate_stardata(struct stardata_t * RESTRICT const stardata,
                              const double f);

// debugging output if required
#define SDEBUG Dprint
//#define SDEBUG(...) if(stardata->model.time  > 0) { printf("split : ");printf(__VA_ARGS__); }

#ifdef NUCSYN
#if defined NUCSYN_YIELD_COMPRESSION || defined NUCSYN_LOG_BINARY_DX_YIELDS
#define SAVE_YIELDS
#endif
#endif//NUCSYN

#endif // EVOLUTION_SPLITTING


int evolution_split(struct stardata_t * RESTRICT const stardata,
                     int * RESTRICT const status)
{
    int retval = EVOLUTION_SPLIT_CONTINUE;

#ifdef EVOLUTION_SPLITTING
    if(stardata->preferences->evolution_splitting)
    {
#define Break_evolution_Q                               \
        (abs(*status) == EVOLUTION_BREAK ||             \
         abs(*status) == EVOLUTION_STOP)

        SDEBUG("splitting is on at t=%g : status = %d cf %d %d (break? %s): stellar types %d %d : splittest = %d, doingsplit = %d, In_splitdepth = %d (current depth %d vs Min(%d,%d))\n",
               stardata->model.time,
               *status,
               EVOLUTION_BREAK,
               EVOLUTION_STOP,
               Yesno(Break_evolution_Q),
               stardata->star[0].stellar_type,
               stardata->star[1].stellar_type,
               splittest(stardata),
               stardata->model.doingsplit,
               In_splitdepth(stardata),
               stardata->preferences->current_splitdepth,
               stardata->preferences->evolution_splitting_maxdepth,
               EVOLUTION_SPLITTING_HARD_MAX_DEPTH
            );

        if(Break_evolution_Q)
        {
            /*
             * flush yield logs of previous evolution, except
             * the very last time which is handled by evolve_system_*
             */
            if(!(stardata->preferences->current_splitdepth==1 &&
                 stardata->preferences->splitinfo[stardata->preferences->current_splitdepth - 1]->count==0))
            {
                /* do logging and cleanup */
                SDEBUG("Split do logging and cleanup\n");
                Evolution_logging;
                evolution_cleanup(stardata,FALSE);
            }

            while(stardata->preferences->current_splitdepth > 0)
            {
                struct splitinfo_t * p = stardata->preferences->splitinfo[stardata->preferences->current_splitdepth - 1];
                /*
                 * Reached end of evolution
                 *
                 * Check if we need to rerun from a previous
                 * splitpoint
                 */
                SDEBUG("SPLIT status is evolution_break : currently working on split-info struct %d at %p, of which count = %d\n",
                       stardata->preferences->current_splitdepth-1,
                       (void*)p,
                       p->count);

                if(p->count > 0)
                {
                    /*
                     * yes, need to, restore almost everything
                     * and lower the count
                     *
                     * NB save random number seed information: this
                     * should not be reset otherwise all the post-split stars
                     * will be identical to the pre-split stars
                     */
                    long random_seed = stardata->common.random_seed;
                    struct binary_c_random_buffer_t random_buffer = stardata->common.random_buffer;

                    p->count--;

                    erase_events(stardata);
                    free_previous_stardatas(stardata);

                    copy_stardata(p->stardata,
                                  stardata,
                                  COPY_STARDATA_PREVIOUS_COPY,
                                  COPY_STARDATA_PERSISTENT_FROM_POINTER);

                    stardata->common.random_seed = random_seed;
                    stardata->common.random_buffer = random_buffer;

                    *status = EVOLUTION_CONTINUE;
                    SDEBUG("SPLIT restarting depth=%d, count=%d at t=%g, stellar types %d %d\n",
                           stardata->preferences->current_splitdepth - 1,
                           p->count,
                           stardata->model.time,
                           stardata->star[0].stellar_type,
                           stardata->star[1].stellar_type
                        );
                    Append_logstring(LOG_SPLIT_EVOLUTION,
                                     "Evolution restart @ t=%g, %d.%d",
                                     stardata->model.time,
                                     stardata->preferences->current_splitdepth-1,
                                     p->count);
#ifdef LINUX
                    reset_binary_c_timeout();
#endif
                    retval = EVOLUTION_SPLIT_RESTART;
                    break;
                }
                else
                {
                    stardata->preferences->current_splitdepth--;
                    SDEBUG("SPLIT last done (count=%d) : next depth is %d\n",
                           p->count,
                           stardata->preferences->current_splitdepth-1);

                    /* clean up memory */
                    struct stardata_t * s = stardata->preferences->splitinfo[stardata->preferences->current_splitdepth]->stardata;

                    free_previous_stardatas(s);
                    Safe_free(s);
                    Safe_free(stardata->preferences->splitinfo[stardata->preferences->current_splitdepth]);
                    retval = EVOLUTION_SPLIT_CONTINUE;
                }
            }
            stardata->model.split_last_time = FALSE;

        }
        else if(stardata->model.doingsplit == FALSE &&
                In_splitdepth(stardata))
        {
            /*
             * check for a new split case in this timestep
             */
            int split = splittest(stardata);
            SDEBUG("Splittest : %d\n",split);

            if(split>=1)
            {
                /*
                 * Found a new split case
                 */
                SDEBUG("SPLIT AT t = %g : current_split_depth = %d\n",
                       stardata->model.time,
                       stardata->preferences->current_splitdepth);

                /* add new struct */
                struct splitinfo_t * p
                    = stardata->preferences->splitinfo[stardata->preferences->current_splitdepth]
                    = Malloc(sizeof(struct splitinfo_t));

                SDEBUG("malloced new split-info struct at %p\n",(void*)p);

                p->depth = stardata->preferences->current_splitdepth;

                /* should be rerun this many times */
                p->count = split - 1;

                /*
                 * allocate space
                 */
                p->stardata = new_stardata(stardata->preferences);

                /*
                 * Copy previous stardata structure to p->stardata
                 */
                copy_stardata(
                    stardata->previous_stardata,
                    p->stardata,
                    COPY_STARDATA_PREVIOUS_COPY,
                    COPY_STARDATA_PERSISTENT_FROM_POINTER
                    );

                /*
                 * Keep preferences
                 */
                p->stardata->preferences = stardata->preferences;

                SDEBUG("starting post-split %d at t=%g\n",
                       p->count,
                       p->stardata->model.time);

                /*
                 * modulate probability, yields, ensemble etc. which
                 * have been saved up until now
                 */
                modulate_stardata(p->stardata,1.0/(double)split);

                SDEBUG("Rerunning from time stardata = %g, p->stardata = %g; restore stellar types to %d %d\n",
                       stardata->model.time,
                       p->stardata->model.time,
                       p->stardata->star[0].stellar_type,
                       p->stardata->star[1].stellar_type
                    );

                /* set the split_last_time flag to avoid a double split */
                p->stardata->model.split_last_time = TRUE;

                /* set the doingsplit number */
                p->stardata->model.doingsplit = TRUE;

                /* next split should be one level up */
                p->stardata->preferences->current_splitdepth++;
                SDEBUG("SPLIT set current_splitdepth=%d\n",stardata->preferences->current_splitdepth);

                /*
                 * Free stardata's previous_stardatas, events etc.
                 * so we can replace stardata
                 */
                free_previous_stardatas(stardata);
                erase_events(stardata);
                /*
                 * put back in stardata for restart
                 */

                copy_stardata(p->stardata,
                              stardata,
                              COPY_STARDATA_PREVIOUS_COPY,
                              COPY_STARDATA_PERSISTENT_FROM_POINTER);

                stardata->model.doingsplit = TRUE;

                Append_logstring(LOG_SPLIT_EVOLUTION,
                                 "Evolution split and restart at t=%g, %d.%d",
                                 stardata->model.time,
                                 stardata->preferences->current_splitdepth-1,
                                 p->count);

#ifdef LINUX
                reset_binary_c_timeout();
#endif

                /* return and restart this timestep */
                retval = EVOLUTION_SPLIT_NEW_SPLIT;
            }
        }
    }
#endif // EVOLUTION_SPLITTING

    /*
     * We're not doing a split if we're just
     * continuing
     */
    if(retval == EVOLUTION_SPLIT_CONTINUE)
    {
        stardata->model.doingsplit = FALSE;
    }

    return retval;
}

#ifdef EVOLUTION_SPLITTING

static int splittest(struct stardata_t * RESTRICT const stardata)
{
    struct stardata_t * previous_stardata = stardata->previous_stardata;
    int split = 0;
    Star_number i;

    /*
     * Detect new split cases after stellar evolution has
     * been done in this system. You can use stardata and
     * stardata->previous_stardata to do this.
     *
     * Returns a number saying with which resolution the split
     * should act. e.g. returning 10 means the evolution is split into
     * 10 new sub-runs.
     *
     * Returns zero if there should be no split.
     */

    Starloop(i)
    {
        if(!split)
        {
            // Supernova
            SDEBUG("check SN %d : type %d\n",
                   stardata->model.supernova,
                   stardata->star[i].stellar_type);

            if(stardata->model.supernova ||
               ((stardata->star[i].stellar_type == BLACK_HOLE ||
                 stardata->star[i].stellar_type == NEUTRON_STAR) &&
                (previous_stardata->star[i].stellar_type<=HeGB)))
            {
                split = stardata->preferences->evolution_splitting_sn_n;
            }
        }
    }
    return split;
}

static void modulate_stardata(struct stardata_t * RESTRICT const stardata,
                              const double f)
{

    /*
     * Function to "modulate" a stardata, i.e. multiply some integrals
     * by a factor f to represent an evolution split into 1/f runs.
     *
     * Not all things will want to be modulated, e.g. stellar structure
     * variables. However, some - like integrated yields which are output
     * on a timestep-by-timestep basis, or at the end of evolution
     * (which is really just the sum of the timestep-by-timestep basis data)
     * will need to be modulated, as will the probability. The reason
     * is that the subsequent evolution will be repeated 1/f times,
     * and we don't want to overcount.
     */

    /* probability should be reduced for rerun */
    stardata->model.probability *= f;

    /* various arrays also need modulating */
#ifdef NUCSYN
    {
        Star_number k;
#ifdef NUCSYN_ID_SOURCES
        Source_number j;
#endif
#ifdef STELLAR_POPULATIONS_ENSEMBLE
        Ensemble_index i;
#endif

        Starloop(k)
        {
            SETstar(k);

            Xmult(star->Xyield,f);

#ifdef LOG_BINARY_MPYIELDS
            Xmult(star->mpyield,f);
#endif

#ifdef SAVE_YIELDS
            if(k==0) Xmult(stardata->model.oldyields,f);

#ifdef NUCSYN_ID_SOURCES
            Sourceloop(j)
            {
                Xmult(&(stardata->model.oldyieldsources[k][j][0]),f);
            }

#endif //NUCSYN_ID_SOURCES
#endif // NUCSYN_YIELD_COMPRESSION || NUCSYN_LOG_BINARY_DX_YIELDS

#ifdef NUCSYN_ID_SOURCES
            Sourceloop(j)
            {
                Xmult(&(star->Xsource[j][0]),f);
            }
#endif

#ifdef STELLAR_POPULATIONS_ENSEMBLE

            for(i=0;i<ENSEMBLE_NUMBER;i++)
            {
                stardata->model.ensemble_store[k][i] *= f;
            }
#endif
        }
    }
#endif //NUCSYN

}

#endif // EVOLUTION_SPLITTING