Skip to content
Snippets Groups Projects
kira_parti.cc 51.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Interface to Kira Starlab (www.manybody.org) library for partiview.
     *
     * Stuart Levy, slevy@ncsa.uiuc.edu
     * National Center for Supercomputing Applications,
     * University of Illinois 2001.
    
     * This file is part of partiview, released under the
     * Illinois Open Source License; see the file LICENSE.partiview for details.
    
     * Revision 1.51  2004/04/19 21:04:12  slevy
     * Un-CONST-ify most of what had been constified.  It's just too messy.
     *
    
     * Revision 1.50  2004/04/19 17:42:41  slevy
     * Use AC_FUNC_ALLOCA and the autoconf-standard alloca boilerplate
     * in each of the files that uses alloca.
     *
    
     * Revision 1.49  2003/07/29 21:15:37  steve
     * Changed Starlab vectors to vec.  Defined NEWSTDIO to allow compilatin
     * under gcc 3.2 (not sure if 2.9x is broken...).
     *
    
     * Revision 1.48  2003/02/15 06:57:45  slevy
     * Initialize new <dyndata>.slvalid field.
     *
    
     * Revision 1.47  2002/06/18 21:25:25  slevy
     * Add reference to LICENSE.partiview file.
     * Where appropriate, update references to geomview license too,
     * now called COPYING.geomview.
     *
    
    slevy's avatar
    slevy committed
     * Revision 1.46  2002/06/12 23:30:26  slevy
     * Need winjunk.h here too.
     * Scrap unix-specific debug code.
     *
    
     * Revision 1.45  2002/05/06 19:44:34  slevy
     * Avoid Fl:: references if -DUSE_PLOT=0.
     *
    
     * Revision 1.44  2002/04/17 20:47:58  slevy
     * Add a not-quite-proprietary notice to all source files.
     * Once we pick a license this might change, but
     * in the mean time, at least the NCSA UIUC origin is noted.
     *
    
     * Revision 1.43  2002/03/11 22:28:21  slevy
     * Allow -DUSE_PLOT=0 to disable FLTK H-R plot widget.
     *
    
     * Revision 1.42  2002/01/27 00:37:48  slevy
     * No need to bogusly import getfloats() any more.
     *
    
    slevy's avatar
    slevy committed
     * Revision 1.41  2001/12/28 07:18:36  slevy
     * Convert to new plugin style.
     *
    
     * Revision 1.40  2001/08/29 20:58:14  slevy
     * Try (?) to handle selection (leafsel/bufsl->sel) properly -- it seems
     * to interact with double-buffering.
     * Use preload_pdyn() to precompute trees for each worldbundle.
     *
    
     * Revision 1.39  2001/08/28 18:42:07  slevy
     * Yeow, refer to the proper ww->bufleafsel[]!
     *
    
     * Revision 1.38  2001/08/28 18:28:18  slevy
     * Switch (I hope) to double-buffered graphics data, flagged by ww->bufno.
     *
    
     * Revision 1.37  2001/08/26 17:36:46  slevy
     * Parenthesize ambiguous expressions...
     *
    
     * Revision 1.36  2001/08/26 02:11:07  slevy
     * Oops, toss extra glBegin(GL_POINTS).
     *
    
     * Revision 1.35  2001/08/21 03:11:05  slevy
     * Use Steve's new family of center-points -- "kira center" command.
     * Add new per-particle field, "ismember", to show escapees.
     *
    
     * Revision 1.34  2001/07/19 20:12:47  slevy
     * Reprocess particles after changing parameters.
     * (Some "kira" commands use kira_set(), which already does this.
     * Just set "changed" for those which don't.)
     * Impose a max trail gap (delta time).  Break up trails at time-gaps
     * exceeding this threshold.
     *
    
     * Revision 1.33  2001/07/18 22:41:45  slevy
     * Add "kira center" command, to report all positions relative to
     * the kira-computed smoothed cluster center.
     *
    
     * Revision 1.32  2001/07/18 19:45:34  slevy
     * Picked stars now report their type and spectral/luminosity classes too.
     * New "kira hrdiag" command allows turning it on/off, setting plot ranges.
     * "kira int x" is a synonym for "kira int x=x", the common case.
     * New "sqrtmass" attribute.  "mass" is again in linear, not log units.
     * Disable kira display if we don't get a valid input file.
     * XXX Bogusly import getfloats() from partibrains.c; should go in a separate
     * utility .c/.h file.
     *
     * Revision 1.31 2001/07/17 22:19:37  slevy
     * Use type_string(stellar_type) to get readable names for stellar types
     * of picked stars.
     * 
     * Revision 1.30 2001/07/17 17:29:36  slevy
     * Use new mass_scale_factor() to get starlab's idea of
     * dynamical->solar mass unit conversion.
     * If it knows, then "kira massscale" only allows user
     * to override if user-supplied value ends with "!",
     * e.g. "kira mscale 600!".
     * 
     * Revision 1.29 2001/07/16 19:08:30  slevy
     * #if(def)'s for CAVE, where we have no plot widget.
     * 
     * Revision 1.28 2001/07/16 17:55:55  slevy
     * Report log10(mass*massscale) -- log is handier than linear units.
     * 
     * Revision 1.27 2001/07/12 21:37:02  slevy
     * Lots of changes.  Add trails.  Move tracking code so we'll be
     * able to track collections rather than single particles (not yet though).
     * Trails are relative to tracked-point position, so can be non-inertial.
     * 
     * Revision 1.26 2001/07/10 17:15:50  slevy
     * Add massscale (mscale) for convenient mass presentation.
     * Compute fake aggregate temp/luminosity for CM nodes.
     * Oops, marks need their own sel[] array!
     * Allow adjusting H-R diagram alpha values too.
     * 
     * Revision 1.25 2001/07/09 19:57:22  slevy
     * Add H-R diagram zoom (b/B keys).
     * Pay attention to emph/see settings.
     * Get selection working properly.
     * 
     * Revision 1.24 2001/07/07 15:27:57  slevy
     * Save/restore positions of subparticles, so we can reuse interpolated_tree safely.
     * Only use SEL_DEST-initialized pick etc. info.
     * 
     * Revision 1.23 2001/07/04 03:28:03  slevy
     * Add picking support: kira_picked() gets all pick callbacks.
     * Add set-selection features, including interaction tracing.
     * 
     * Revision 1.22 2001/07/02 16:17:24  slevy
     * Cast kira_HRplot to avoid complaints.
     * Hack for gcc 3.0's stdio (#ifdef NEWSTDIO).
     * 
     * Revision 1.21 2001/07/01 14:30:43  slevy
     * Do more plot setup here in kira_parti.  Set axis titles too.
     * 
     * Revision 1.20 2001/07/01 07:31:45  slevy
     * Hmm, make default logT range a bit smaller.
     * 
     * Revision 1.19 2001/07/01 06:42:01  slevy
     * Oops, get the sense of the log-T scale correct!
     * 
     * Revision 1.18 2001/06/30 18:09:15  slevy
     * Add kira_HRplot for drawing in H-R diagram.
     * 
     * Revision 1.17 2001/06/30 07:01:47  slevy
     * Yes, call create_interpolated_tree2() if not OLDTREE!
     * 
     * Revision 1.16 2001/06/06 17:22:55  slevy
     * get_stellar_type() now returns int (enum from starlab/inc/star_support.h),
     * rather than a string.
     * 
     * Revision 1.15 2001/05/30 14:31:16  slevy
     * Add Tlog, Lum, and stellar-type fields.
     * Star type is number-encoded, sigh:
     * 	0 = unknown
     * 	1 = ms	Main Sequence
     * 	2 = cd	Compact Dwarf??
     * 	3 = gs	??
     * 	4 = bh	Black Hole
     * 	5 = sg	Supergiant?
     * 	6 = hb	Horizontal Branch
     * 
     * Revision 1.14 2001/05/15 12:18:57  slevy
     * New interface to dynamic-data routines.
     * Now, the only #ifdef KIRA/WARP needed in partibrains.c are
     * the data-command initialization routines.  All others,
     * including control-command parsing and specialized drawing,
     * is now via a function table.
     * 
     * Revision 1.13 2001/05/14 15:47:59  slevy
     * Invalidate color/size/threshold sequence numbers so main code will recalc.
     * 
     * Revision 1.12 2001/05/14 08:40:06  slevy
     * kira_draw(): don't do anything if the dynamic data isn't ours.
     * 
     * Revision 1.11 2001/05/12 07:23:05  slevy
     * New generic get-time-range function for dynamic data.
     * 
     * Revision 1.10 2001/04/04 19:23:05  slevy
     * Use portable form of is_open().  Other tidiness.
     * 
     * Revision 1.9 2001/03/30 13:56:07  slevy
     * Rename functions to kira_*.
     * Add framework for incrementally reading paragraphs of input:
     * kira_open(), kira_read_more().
     * Add scraps for maintaining trails, but not enough to work yet.
     * 
     * Revision 1.8 2001/02/04 16:52:38  slevy
     * Use default speck-field names if user doesn't override them
     * (either before or after "kira" reader).
     * 
     * Revision 1.7 2001/02/03 23:37:51  slevy
     * 
     * Oops, now kira_draw needs to ensure we really have something to draw!
     * 
     * Revision 1.6 2001/02/03 15:26:16  slevy
     * 
     * OK, toss those extra 'break's.
     * 
     * Revision 1.5 2001/02/03 15:20:34  slevy
     * Add "kira tree" support -- tree arcs, with crosswise tick-marks
     * at center-of-mass points.  Tickmarks lie in screen plane, with
     * length of <tickscale> times true separation.
     * 
     * Revision 1.4 2000/12/31 00:25:05  slevy
     * Re-extract whenever kira_set changes something.
     * Add particle tracking.
     * Compute ring sizes correctly, and draw rounder rings.
     * 
     * Revision 1.3 2000/12/30 02:19:30  slevy
     * Add ring markers for non-isolated stars.  Not finished yet, but seems usable.
     * No GUI yet, but new commands:
     *    kiractl nodes  {on|off|roots}
     *    kiractl rings  {on|off|roots}
     *    kiractl ringsize {sep|semi}   (ring size from curr. separation or semimajor axis)
     *    kiractl scale  <scalefactor>
     *    kiractl span   <minpixels> <maxpixels>   (allowed range of ring radii)
     * 
     * Change encoding of nclump: n for leaves, -n for center-of-mass nodes,
     *    so colormap can make them distinguishable.
     * 
     * Bug: after kiractl command, must change timestep before it takes effect.
     * Bug: ring markers aren't colored yet for some reason.
     * 
     * Revision 1.2 2000/12/22 01:11:09  slevy
     * Recode to be speedier (use recursion) and to provide more information to partiview:
     * 	[0] worldline index (= kira index for singles, - and unique for others)
     * 	[1] mass
     * 	[2] number of stars (leaf nodes) in clump
     * 		+ 100 if this is a member of an unperturbed binary
     * 	[3] top-level name (small integer, = worldline index for single stars)
     * 	[4] tree address in clump (0=single, 1=root, children of <i> are <2i>,<2i+1>)
     * 
     * Revision 1.1 2000/12/20 16:50:54  slevy
     * Glue code to read starlab (kira) files and interpolate particle positions
     * into partiview internal form.
     */
    
    
    slevy's avatar
    slevy committed
    #ifdef USE_KIRA
    
    
    slevy's avatar
    slevy committed
    #ifdef _WIN32
    # include "winjunk.h"
    #endif
    
    
    #ifdef NEWSTDIO
    
    #include <ostream.h>
    #include <istream.h>
    
    #endif /*NEWSTDIO*/
    
    
    slevy's avatar
    slevy committed
    #include "worldline.h"
    
    #include "shmem.h"
    #include "specks.h"
    #include "partiviewc.h"
    
    slevy's avatar
    slevy committed
    #include "findfile.h"
    
    slevy's avatar
    slevy committed
    
    #include "kira_parti.h"		// declare following as extern "C"
    
    #if !defined(CAVE) && !defined(USE_PLOT)
    
    # define USE_PLOT 1
    #endif
    
    #if USE_PLOT
    # include "Plot.H"
    #endif
    
    slevy's avatar
    slevy committed
    #include <sys/types.h>
    #include <sys/time.h>
    
    slevy's avatar
    slevy committed
    
    
    #include <ctype.h>
    #undef isdigit		/* irix 6.5 back-compat hack */
    
    
    slevy's avatar
     
    slevy committed
    static char local_id[] = "$Id$";
    
    slevy's avatar
    slevy committed
    
    
    slevy's avatar
    slevy committed
    extern struct specklist *kira_get_parti( struct stuff *st, double realtime );
    
    extern int kira_parse_args( struct dyndata *dd, struct stuff *st, int argc, char **argv );
    extern int kira_draw( struct dyndata *dd, struct stuff *st, struct specklist *slhead,
    		Matrix *Tc2w, float radperpix );
    
    extern void kira_HRplot( Fl_Plot *, struct stuff *st, struct dyndata *dd );
    
    slevy's avatar
    slevy committed
    extern int kira_HRplot_events( Fl_Plot *plot, int ev );
    
    extern int kira_picked( struct stuff *st, GLuint *hit, struct specklist *sl, int speckno );
    
    extern void kira_maxtrail( struct dyndata *dd, struct stuff *st, int newmax );
    
    slevy's avatar
    slevy committed
    
    typedef worldbundle *worldbundleptr;
    
    slevy's avatar
     
    slevy committed
    
    
    enum speckfields {
    	SPECK_ID = 0,		// worldline index (= kira index for single stars,
    
    				//	unique small negative int for others)
    
    	SPECK_NCLUMP = 2,	// number of stars in clump
    	SPECK_TLOG = 3,		// log10(Teff)
    	SPECK_LUM = 4,		// L/Lsun?
    	SPECK_STYPE = 5,	// star type: 1ms 2cd 3gs 4bh 5sg 6hb
    
    	SPECK_ISMEMBER = 6,	// is member of cluster?
    	SPECK_ROOTID = 7,	// worldline index of root of clump
    	SPECK_TREEADDR = 8,	// bit-encoded tree address within our clump (0 for isolated stars)
    	SPECK_RINGSIZE = 9,	// size of ring marker
    	SPECK_SQRTMASS = 10,	// square root of mass, for handy brightness factor
    	SPECK_MU = 11,		// mass ratio; = 0 for leaf nodes
    
    	SPECK_SEPVEC = 12,	// separation vec[3]
    
    };
    
    static char *fieldnames[] = {  // Must match ``enum speckfields'' !!
    	"id",		// worldline index (=? kira index for singles), unique <0 for others
    
    	"nclump",	// number of stars in clump, = 1 for singles
    	"Tlog",		// log10( Teff )
    	"Lum",		// L/Lsun?
    	"stype",	// stellar type index
    
    	"ismember",	// is member of cluster? (0/1)
    
    	"rootid",	// id of root of clump, = our id for singles
    	"treeaddr",	// binary-coded address in clump
    	"ringsize",	// size of ring
    
    	"sqrtmass",	// sqrt(mass/Msun)
    
    	"mu",		// mass ratio for nonleaf nodes
    	NULL
    };
    
    struct vald {
    	float min, max, sum;
    };
    
    
    struct trailhead {
        int maxtrail, ntrails;
        int next;		/* ring buffer next-slot-to-use */
    
        real lasttime;
    
    slevy's avatar
    slevy committed
    struct worldstuff {
    
    slevy's avatar
    slevy committed
        int nh, maxnh;
    
    slevy's avatar
    slevy committed
        worldbundleptr *wh;
    
    slevy's avatar
    slevy committed
        ifstream *ins;
    
    slevy's avatar
    slevy committed
        int ih;			// current worldbundle index
        real tmin, tmax;
        real tcur;			// current time
        real treq;			// requested time
    
    slevy's avatar
    slevy committed
        int readflags;		// KIRA_VERBOSE | KIRA_READLATER
    
    slevy's avatar
     
    slevy committed
        int treenodes;		// KIRA_{OFF|ON|ROOTS}
        int treerings;		// KIRA_{OFF|ON|ROOTS}
    
    slevy's avatar
     
    slevy committed
        int treearcs;		// KIRA_{OFF|ON|CROSS|TICK}
        float tickscale;		// size of treearc cross mark (frac of sep)
    
    slevy's avatar
     
    slevy committed
        int ringsizer;		// KIRA_RINGSEP, KIRA_RINGA
        float ringscale;		// multiplier for above
        float ringmin, ringmax;	// range of pixel sizes for ring markers
    
    slevy's avatar
     
    slevy committed
        int tracking, wastracking;	// id of particle we're tracking, or zero
        Point trackpos;		// last known position of tracked particle
    
        float massscale;		// scale-factor for masses (conv to Msun)
        int truemassscale;		// Did massscale come from kira itself?
    
    slevy's avatar
     
    slevy committed
    
    
    slevy's avatar
     
    slevy committed
        int maxstars, maxmarks;	// room allocated for each
    
    slevy's avatar
     
    slevy committed
        struct specklist *sl;
    
    slevy's avatar
     
    slevy committed
        struct specklist *marksl;
    
    slevy's avatar
     
    slevy committed
        int slvalid;
    
        struct specklist *bufsl[2], *bufmarksl[2];  // double-buffers
        int bufno;
    
        vec center_pos;
        vec center_vel;
    
    
        int myselseq;		// sequence number of ww->sel
        int nleafsel;
    
    
    				// selection mapping
        SelOp intsrc;		// for all particles matching intsrc,
        SelOp intdest;		//   then turn on intdest bit(s).
    
    slevy's avatar
    slevy committed
    
    
        struct trailhead *trails;	// per-star specklist of recent history
        int maxtrail;
        int maxtrailno;
    
        float trailalpha;
        float trailpsize;
        int trailonly;
    
        real maxtrailgap;
    
    
        SelOp picksel;		// what to do when a star is picked
    
    slevy's avatar
    slevy committed
    
    
    slevy's avatar
     
    slevy committed
        struct speck *marksp;	// current pointer, updated by add_speck
        int leafcount;		// temp, used in recursion only
    
        int interactsel, unionsel;	// ditto
        int pickcount;		// temp, used in kira_picked
    
        struct vald vd[SPECK_NDATAFIELDS];
    
    slevy's avatar
    slevy committed
    
    
    slevy's avatar
    slevy committed
        worldstuff( struct stuff *st ) {
    	this->init( st );
    
    slevy's avatar
    slevy committed
        void init( struct stuff *st ) {
    
    slevy's avatar
     
    slevy committed
    	nh = 0;
    	wh = NULL;
    	ih = 0;
    	tmin = 0, tmax = 1;
    	tcur = treq = 0;
    
    	treenodes = KIRA_ON;
    	treerings = KIRA_OFF;
    
    slevy's avatar
     
    slevy committed
    	treearcs = KIRA_ON;
    
    slevy's avatar
     
    slevy committed
    	ringsizer = KIRA_RINGA;
    	ringscale = 1.5;
    
    	truemassscale = 0;
    
    slevy's avatar
     
    slevy committed
    
    	ringmin = 2;
    	ringmax = 50;
    
    
    slevy's avatar
     
    slevy committed
    	tickscale = 0.25;
    
    slevy's avatar
     
    slevy committed
    	tracking = wastracking = 0;
    
    	center_pos[0] = center_pos[1] = center_pos[2] = 0;
    	center_vel[0] = center_vel[1] = center_vel[2] = 0;
    
    slevy's avatar
     
    slevy committed
    
    
    slevy's avatar
     
    slevy committed
    	sl = marksl = NULL;
    
    	bufsl[0] = bufsl[1] = NULL;
    	bufmarksl[0] = bufmarksl[1] = NULL;
    	bufno = 0;
    
    slevy's avatar
     
    slevy committed
    	maxstars = maxmarks = 0;
    
    slevy's avatar
     
    slevy committed
    	slvalid = 0;
    
    slevy's avatar
    slevy committed
    
    
    	trails = NULL;
    	maxtrail = 50;
    	maxtrailno = 0;
    	trailonly = 0;
    	trailalpha = 0.6;
    	trailpsize = 1.0;
    
    	maxtrailgap = 0.1;
    
    slevy's avatar
    slevy committed
    
    
    slevy's avatar
     
    slevy committed
    	marksp = NULL;
    	leafcount = 0;
    
    
    	myselseq = 0;
    	leafsel = NULL;
    
    	bufleafsel[0] = bufleafsel[1] = NULL;
    
    slevy's avatar
    slevy committed
    	parse_selexpr( st, "pick", &picksel, NULL, NULL );
    
    	selinit( &trailsel );
    	selinit( &intsrc );
    	selinit( &intdest );
    
    slevy's avatar
     
    slevy committed
        }
    };
    
    
    extern void kira_add_trail( struct stuff *st, struct worldstuff *, int id, struct speck * );
    extern void kira_erase_trail( struct stuff *st, struct worldstuff *, int id );
    extern void kira_track_break( struct worldstuff *ww, Point *newtrack );
    
    slevy's avatar
     
    slevy committed
    
    
    void kira_invalidate( struct dyndata *dd, struct stuff *st ) {
    
    slevy's avatar
     
    slevy committed
        if(st->sl) st->sl->used = -1000000;
    
        worldstuff *ww = (worldstuff *)dd->data;
    
    slevy's avatar
     
    slevy committed
        if(ww) ww->slvalid = 0;
    
    slevy's avatar
     
    slevy committed
    }
    
    
    slevy's avatar
    slevy committed
    int kira_read( struct stuff **stp, int argc, char *argv[], char *fname, void * ) {
    
        if(argc < 1 || strcmp(argv[0], "kira") != 0)
    	return 0;
    
        struct stuff *st = *stp;
        st->clk->continuous = 1;
        char *realfile = findfile(fname, argv[1]);
        kira_open( &st->dyn, st, realfile ? realfile : argv[1], argc>2 ? atoi(argv[2]) : 0 );
        return 1;
    }
    
    void kira_parti_init() {
        parti_add_reader( kira_read, "kira", NULL );
    }
    
    
    int kira_set( struct dyndata *dd, struct stuff *st, int what, double val )
    
    slevy's avatar
     
    slevy committed
    {
    
        struct worldstuff *ww = (struct worldstuff *)dd->data;
    
    slevy's avatar
     
    slevy committed
        if(ww == NULL) return 0;
    
    
        if(kira_get(dd, st, what) == val)
    
    slevy's avatar
     
    slevy committed
    	return 2;
    
        switch(what) {
        case KIRA_NODES: ww->treenodes = (int)val; break;
        case KIRA_RINGS: ww->treerings = (int)val; break;
    
    slevy's avatar
     
    slevy committed
        case KIRA_TREE: ww->treearcs = (int)val; break;
        case KIRA_TICKSCALE: ww->tickscale = val; break;
    
    slevy's avatar
     
    slevy committed
        case KIRA_RINGSIZE: ww->ringsizer = (int)val; break;
        case KIRA_RINGSCALE: ww->ringscale = val; break;
        case KIRA_RINGMIN: ww->ringmin = val; break;
        case KIRA_RINGMAX: ww->ringmax = val; break;
    
    slevy's avatar
     
    slevy committed
        case KIRA_TRACK: if(ww->tracking != (int)val) ww->wastracking = 0;
    		     ww->tracking = (int)val; break;
    
    slevy's avatar
     
    slevy committed
        default: return 0;
        }
    
        kira_invalidate( dd, st );
    
    slevy's avatar
     
    slevy committed
        specks_set_timestep( st );
        parti_redraw();
        return 1;
    }
    
    
    double kira_get( struct dyndata *dd, struct stuff *st, int what )
    
    slevy's avatar
     
    slevy committed
    {
    
        struct worldstuff *ww = (struct worldstuff *)dd->data;
    
    slevy's avatar
     
    slevy committed
        if(ww == NULL) return 0;
    
        switch(what) {
    
    slevy's avatar
     
    slevy committed
        case KIRA_NODES: return ww->treenodes;
        case KIRA_RINGS: return ww->treerings;
        case KIRA_TREE:  return ww->treearcs;
        case KIRA_TICKSCALE: return ww->tickscale;
        case KIRA_RINGSIZE: return ww->ringsizer;
        case KIRA_RINGSCALE: return ww->ringscale;
        case KIRA_RINGMIN: return ww->ringmin;
        case KIRA_RINGMAX: return ww->ringmax;
        case KIRA_TRACK: return ww->tracking;
    
    slevy's avatar
     
    slevy committed
        }
        return 0;
    }
    
    
    slevy's avatar
    slevy committed
    int hasdata( ifstream *s, double maxwait ) {
    
    #ifdef NEWSTDIO
        return 1;
    #else /*old stdio, where filebuf::fd() still existed*/
    
    slevy's avatar
    slevy committed
        struct timeval tv;
        fd_set fds;
        if(s == NULL) return 0;
        if(s->rdbuf()->in_avail() || s->eof()) return 1;
        if(maxwait < 0) maxwait = 0;
        tv.tv_sec = (int)maxwait;
        tv.tv_usec = (int) (1000000 * (maxwait - tv.tv_sec));
        FD_ZERO(&fds);
        FD_SET(s->rdbuf()->fd(), &fds);
        return(select(s->rdbuf()->fd() + 1, &fds, NULL, NULL, &tv) > 0);
    
    int kira_help( struct dyndata *dd, struct stuff *st, int verbose )
    {
    
        msg(" kira {node|ring|size|scale|span|track|trail}  starlab controls; try \"kira ?\"");
    
        return 1;
    }
    
    int kira_read_more( struct dyndata *dd, struct stuff *st, int nmax, double tmax, double realdtmax )
    
    slevy's avatar
    slevy committed
    {
    
        struct worldstuff *ww = (struct worldstuff *)dd->data;
    
    slevy's avatar
    slevy committed
        if(ww == NULL) return -1;
        ifstream *ins = ww->ins;
    
        if(ins == NULL || !ins->rdbuf()->is_open()) return -1;
    
    slevy's avatar
    slevy committed
    
        int paras = 0;
        int timelimit = (realdtmax < 1e6);
        double wallstop = realdtmax + wallclock_time();
        while( paras < nmax && (ww->nh == 0 || ww->wh[ww->nh-1]->get_t_max() < tmax) ) {
    
    	if(timelimit) {
    	    double dt = wallstop - wallclock_time();
    	    if(!hasdata(ins, dt))
    		break;
    	    if(dt <= 0)
    		break;
    	}
    
    slevy's avatar
    slevy committed
    
    
    slevy's avatar
    slevy committed
    	if(ww->nh >= ww->maxnh) {
    	    ww->maxnh = (ww->nh > ww->maxnh*2) ? ww->nh + 1 : ww->maxnh*2;
    	    ww->wh = RenewN( ww->wh, worldbundleptr, ww->maxnh );
    	}
    
    slevy's avatar
    slevy committed
    
    
    slevy's avatar
    slevy committed
    	worldbundle *wb;
    
    	wb = read_bundle(*ins, ww->readflags & KIRA_VERBOSE);
    	if(wb == NULL) {
    	    ins->close();
    	    if(paras == 0) paras = -1;
    	    break;
    	}
    
    slevy's avatar
    slevy committed
    	ww->wh[ww->nh++] = wb;
    
    slevy's avatar
    slevy committed
    	paras++;
        }
    
        if(paras > 0) {
    	ww->tmin = ww->wh[0]->get_t_min();
    	ww->tmax = ww->wh[ww->nh-1]->get_t_max();
        }
        return paras;
    }
    
    slevy's avatar
    slevy committed
    
    
    int kira_get_trange( struct dyndata *dd, struct stuff *st, double *tminp, double *tmaxp )
    
        struct worldstuff *ww = (struct worldstuff *)dd->data;
    
        if(ww == NULL) {
    	*tminp = *tmaxp = 0;
    	return 0;
        }
        *tminp = ww->tmin;
        *tmaxp = ww->tmax;
        return 1;
    }
    
    
    int kira_open( struct dyndata *dd, struct stuff *st, char *filename, int readflags)
    
    slevy's avatar
    slevy committed
    {
    
        // If data field names aren't already assigned, set them now.
        for(int i = 0; fieldnames[i] != NULL; i++) {
    	if(st->vdesc[st->curdata][i].name[0] == '\0')
    	    strcpy(st->vdesc[st->curdata][i].name, fieldnames[i]);
        }
    
        if(filename == NULL || !strcmp(filename, "-init")) {
    	return 0;	// "kira -init" sets up datafield names w/o complaint
        }
    
    
    slevy's avatar
    slevy committed
        ifstream *ins = new ifstream(filename);
        if (!ins || !*ins) {
    	msg("Kira/starlab data file %s not found.", filename);
    	delete ins;
    
    slevy's avatar
    slevy committed
    	return 0;
        }
    
    
    slevy's avatar
    slevy committed
        struct worldstuff *ww = NewN( worldstuff, 1 );	// from shmem -- avoid "new"
    
    slevy's avatar
    slevy committed
        ww->init( st );
    
    slevy's avatar
    slevy committed
        ww->maxnh = 1024;
        ww->wh = NewN( worldbundleptr, ww->maxnh );
        ww->ins = ins;
        ww->tmin = ww->tmax = 0;
    
        ww->tcur = ww->treq = -1.0;		/* invalidate */
    
    slevy's avatar
    slevy committed
        ww->readflags = readflags;
    
    slevy's avatar
    slevy committed
    
    
        memset( dd, 0, sizeof(*dd) );
        dd->data = ww;
        dd->getspecks = kira_get_parti;
        dd->trange = kira_get_trange;
        dd->ctlcmd = kira_parse_args;
        dd->draw = kira_draw;
        dd->help = kira_help;
        dd->free = NULL;			/* XXX create a 'free' function someday */
        dd->enabled = 1;
    
        dd->slvalid = 0;
    
    slevy's avatar
    slevy committed
    
    
    slevy's avatar
    slevy committed
        ww->nh = 0;
        if(! (readflags & KIRA_READLATER)) {
    
    	kira_read_more( dd, st, ww->maxnh, 1e38, 1e38 );
    
    slevy's avatar
    slevy committed
    
    
    slevy's avatar
    slevy committed
    	if (ww->nh == 0) {
    	    msg("Data file %s not in worldbundle format.", filename);
    
    slevy's avatar
    slevy committed
    	    return 0;
    	}
    
    	preload_pdyn(ww->wh, ww->nh, false);
    
        float mscale = mass_scale_factor();
        if(mscale > 0) {
    	ww->massscale = mscale;
    	ww->truemassscale = 1;
        }
    
    
    slevy's avatar
    slevy committed
        ww->ih = 0;
        ww->sl = NULL;
    
    
    
        // Register for H-R diagram display
    
        static float xrange[] = { 5, 3 };	// initial range of log(T)
        static float yrange[] = { -4, 6 };	// initial range of log(L)
        static char xtitle[] = "log T";
        static char ytitle[] = "log L";
    
    
        plot = parti_register_plot( st, (void (*)(Fl_Plot*,void*,void*))kira_HRplot, dd );
    
        if(plot) {
    	if(plot->x0() == plot->x1())
    	    plot->xrange( xrange[0], xrange[1] );
    	if(plot->y0() == plot->y1())
    	    plot->yrange( yrange[0], yrange[1] );
    	if(plot->xtitle() == NULL) plot->xtitle( xtitle );
    	if(plot->ytitle() == NULL) plot->ytitle( ytitle );
    
    slevy's avatar
    slevy committed
    	plot->eventhook = kira_HRplot_events;
    
    #endif /*USE_PLOT*/
    
    slevy's avatar
    slevy committed
        return 1;
    }
    
    
    double get_parti_time( struct dyndata *dd, struct stuff *st ) {
        return dd->data ? ((struct worldstuff *)(dd->data))->tcur : 0.0;
    
    slevy's avatar
    slevy committed
    }
    
    
    void set_parti_time( struct dyndata *dd, struct stuff *st, double reqtime ) {
        if(dd->data == NULL)
    
    slevy's avatar
    slevy committed
    	return;
        // otherwise please seek to requested time ...
    }
    
    
    static float log10_of( float v ) {
        return v <= 0 ? 0 : log10f( v );
    }
    
    slevy's avatar
     
    slevy committed
    
    
    static float netTL( pdyn *b, float *totlum ) {	// temp-weighted lum
        if(b == NULL) return 0;
        if(b->is_leaf()) {
    	*totlum += b->get_luminosity();
    	return b->get_temperature() * b->get_luminosity();
        }
        float totTL = 0;
        for_all_daughters(pdyn, b, bb)
    	totTL += netTL( bb, totlum );
        return totTL;
    }
    
    
    speck *add_speck(pdyn *b, pdyn *top, int addr, speck *sp, specklist *sl, worldstuff *ww)
    
    slevy's avatar
    slevy committed
    {
    
    slevy's avatar
     
    slevy committed
        sp->p.x[0] = b->get_pos()[0];
        sp->p.x[1] = b->get_pos()[1];
        sp->p.x[2] = b->get_pos()[2];
    
    
        if(ww->centered) {
    	sp->p.x[0] -= ww->center_pos[0];
    	sp->p.x[1] -= ww->center_pos[1];
    	sp->p.x[2] -= ww->center_pos[2];
        }
    
    
        int id = b->get_index();
        if(b->is_leaf()) {
    	if(id < ww->nleafsel) {
    	    ww->unionsel |= ww->leafsel[id];
    
    slevy's avatar
    slevy committed
    	    if(SELECTED(ww->leafsel[id], &ww->intsrc))
    
    		ww->interactsel = ww->intdest.wanton;
    	}
    	sp->val[SPECK_ID] = id;
    
    	sp->val[SPECK_TLOG] = log10_of( b->get_temperature() );
    	sp->val[SPECK_LUM] = b->get_luminosity();
    
    	id = -b->get_worldline_index();
    	if(id >= 0 || id + ww->maxstars < ww->maxleaves) {
    	    static int yikes = 1;
    	    if(yikes++ % 256 == 0) printf("OOPS: worldline_index %d not in range 1..%d\n",
    		    -id, 2*ww->maxleaves);
    
    	    int slotno = ww->maxstars + id;
    	    ww->unionsel |= ww->leafsel[slotno];
    
    	sp->val[SPECK_ID] = id;		// distinguish CM nodes
    
    
    	float totL = 0, totTL;
    	totTL = netTL( b, &totL );
    	sp->val[SPECK_LUM] = totL;
    	sp->val[SPECK_TLOG] = log10_of( totL == 0 ? 0 : totTL / totL );
    
    slevy's avatar
     
    slevy committed
    
    
        sp->val[SPECK_MASS] = b->get_mass() * ww->massscale;
        sp->val[SPECK_SQRTMASS] = sqrtf( sp->val[SPECK_MASS] );
    
        sp->val[SPECK_STYPE] = b->get_stellar_type();	/* see starlab/inc/star_support.h */
    
    slevy's avatar
     
    slevy committed
    
    
        sp->val[SPECK_ISMEMBER] = is_member( ww->wh[ww->ih], b );
    
    slevy's avatar
    slevy committed
        sp->val[SPECK_NCLUMP] = 0;		// complete (add n_leaves) in kira_to_parti
    
    slevy's avatar
     
    slevy committed
    
    
        // DON'T offset unperturbed binaries by 100 in "nclump" field.
    
    slevy's avatar
     
    slevy committed
    
    
        //if (b != top) {
        //	pdyn *bb = b;
        //	while (bb->get_elder_sister())
        //	    bb = bb->get_elder_sister();
        //	if (bb->get_kepler()) sp->val[SPECK_NCLUMP] += 100;
        //}
    
    slevy's avatar
     
    slevy committed
    
    
        sp->val[SPECK_ROOTID] = (top->is_leaf())
    			? top->get_index()
    			: -top->get_worldline_index();
    
    slevy's avatar
     
    slevy committed
    
        // Addr is 0 for top-level leaf, 1 for top-level CM,
        // constructed from parent addr otherwise.
    
    
    slevy's avatar
     
    slevy committed
        sp->val[SPECK_TREEADDR] = addr;
    
        sp->val[SPECK_RINGSIZE] = 0;
    
    slevy's avatar
     
    slevy committed
    
    
        if(id != 0 && ww->tracking == id) {
    	Point newpos;
    	newpos.x[0] = b->get_pos()[0];
    	newpos.x[1] = b->get_pos()[1];
    	newpos.x[2] = b->get_pos()[2];
    	if(ww->wastracking) {
    	    Point delta;
    	    vsub( &delta, &newpos,&ww->trackpos );
    	    parti_nudge_camera( &delta );
    	} else {
    	    kira_track_break( ww, &newpos );
    	}
    	ww->trackpos = newpos;
    	ww->wastracking = 1;
        }
    
    
    slevy's avatar
     
    slevy committed
        sl->nspecks++;
        sp = NextSpeck(sp, sl, 1);
    
    slevy's avatar
     
    slevy committed
        return sp;
    }
        
    
    speck *assign_specks(pdyn *b, pdyn *top, int addr, speck *sp, specklist *sl, worldstuff *ww)
    
    slevy's avatar
     
    slevy committed
    {
        // Convert relative coordinates to absolute.
    
    slevy's avatar
     
    slevy committed
    
        if (b != top)
    	b->inc_pos(b->get_parent()->get_pos());
    
    
    slevy's avatar
     
    slevy committed
    
    
    slevy's avatar
     
    slevy committed
        if(b->is_leaf()
    		|| ww->treenodes == KIRA_ON
    		|| (ww->treenodes == KIRA_ROOTS && b == top)) {
    	sp = add_speck(b, top, addr, sp, sl, ww);
    
    	if(b->is_leaf())
    	    ww->leafcount++;
        }
    
        if(!b->is_leaf() &&
    
    slevy's avatar
     
    slevy committed
    		(ww->treerings == KIRA_ON || ww->treearcs != KIRA_OFF
    
    slevy's avatar
     
    slevy committed
    		 || (ww->treerings == KIRA_ROOTS && b == top))) {
    
    	struct speck *marksp = ww->marksp;
    	ww->marksp = add_speck(b, top, addr, marksp, ww->marksl, ww);
    
    	// find our two children
    
    
    	pdyn *b1 = b->get_oldest_daughter();
    	pdyn *b2 = b1 ? b1->get_younger_sister() : NULL;
    
    slevy's avatar
     
    slevy committed
    	if(b2) {
    
    	    vec sep = b1->get_pos() - b2->get_pos();
    
    slevy's avatar
     
    slevy committed
    	    real dist = sqrt(sep * sep);
    
    slevy's avatar
     
    slevy committed
    	    real mass = b->get_mass();	// = sum of b1&b2 masses.
    
    slevy's avatar
     
    slevy committed
    
    	    real size = 0;
    	    switch(ww->ringsizer) {
    	    case KIRA_RINGSEP:
    
    slevy's avatar
     
    slevy committed
    		size = 0.5 * dist;
    
    slevy's avatar
     
    slevy committed
    		break;
    	    case KIRA_RINGA:
    	      {
    
    		vec vel = b1->get_vel() - b2->get_vel();
    
    slevy's avatar
     
    slevy committed
    		real speed2 = vel * vel;
    		real E = 0.5 * speed2 - mass / dist;
    		size = -mass / (2*E);	// semimajor axis
    	      }
    	    }
    
    	    marksp->val[SPECK_RINGSIZE] = size;
    
    slevy's avatar
     
    slevy committed
    	    marksp->val[SPECK_MU] = b1->get_mass() / mass;
    	    marksp->val[SPECK_SEPVEC] = sep[0];
    	    marksp->val[SPECK_SEPVEC+1] = sep[1];
    	    marksp->val[SPECK_SEPVEC+2] = sep[2];
    
    slevy's avatar
     
    slevy committed
    	}
        }
    
    slevy's avatar
     
    slevy committed
    
        // Recursion.
    
        addr *= 2;
    
        for_all_daughters(pdyn, b, bb)
    
    slevy's avatar
     
    slevy committed
    	sp = assign_specks(bb, top, addr++, sp, sl, ww);
    
    slevy's avatar
     
    slevy committed
    
    
    slevy's avatar
     
    slevy committed
        return sp;
    
    slevy's avatar
    slevy committed
    }
    
    
    struct specklist *kira_to_parti(pdyn *root, struct dyndata *dd, struct stuff *st, struct worldstuff *ww)
    
    slevy's avatar
    slevy committed
    {
    
    slevy's avatar
    slevy committed
    
        if(ww == NULL || ww->nh == 0) return NULL;
    
    
    slevy's avatar
     
    slevy committed
    
    
        struct specklist *sl = ww->bufsl[ww->bufno];
        struct specklist *marksl = ww->bufmarksl[ww->bufno];
    
        if(sl == NULL) {
    
    slevy's avatar
    slevy committed
    	// Use NewN to allocate these in (potentially) shared memory,
    	// rather than new which will allocate in local malloc pool.
    	// Needed for virdir/cave version.
    
    slevy's avatar
     
    slevy committed
    
    
    	int w, wmax = 0;
    	for(int k = 0; k < ww->nh; k++) {
    	    if(ww->wh[k] && wmax < (w = ww->wh[k]->get_nw()))
    		wmax = w;
    	}
    
    slevy's avatar
     
    slevy committed
    	int smax = root->n_leaves();
    
    	ww->maxstars = smax + wmax + smax;	// leave a bit of room for growth
    
    slevy's avatar
     
    slevy committed
    	ww->maxmarks = 1+smax;
    
    	ww->maxleaves = smax;
    
    slevy's avatar
     
    slevy committed
    
    
    slevy's avatar
    slevy committed
            sl = NewN( struct specklist, 1 );
    	memset(sl, 0, sizeof(*sl));
    
    slevy's avatar
     
    slevy committed
    	sl->bytesperspeck = SMALLSPECKSIZE( SPECK_NDATAFIELDS );
    	sl->specks = NewNSpeck(sl, ww->maxstars);
    
    slevy's avatar
    slevy committed
    	sl->scaledby = 1;
    
    slevy's avatar
     
    slevy committed
    	marksl = NewN( struct specklist, 1 );
    	*marksl = *sl;
    	marksl->specks = NewNSpeck(marksl, ww->maxmarks);
    	marksl->special = MARKERS;
    
    
    	sl->nsel = ww->maxstars;
    	marksl->nsel = ww->maxmarks;
    	if(ww->bufsl[1 - ww->bufno]) {
    	    sl->sel = ww->bufsl[1-ww->bufno]->sel;
    	    marksl->sel = ww->bufmarksl[1-ww->bufno]->sel;
    	} else {
    	    sl->sel = NewN( SelMask, ww->maxstars );
    	    memset(sl->sel, 0, sl->nsel*sizeof(SelMask));
    	    marksl->sel = NewN( SelMask, ww->maxmarks );
    	    memset(marksl->sel, 0, marksl->nsel*sizeof(SelMask));
    	}
    
    
    slevy's avatar
     
    slevy committed
    	sl->next = marksl;
    	marksl->next = NULL;
    
    
    	if(ww->bufleafsel[1 - ww->bufno] != NULL) {
    	    ww->bufleafsel[ww->bufno] = ww->bufleafsel[1 - ww->bufno];
    	} else {		// Only one bufleafsel
    	    ww->bufleafsel[ww->bufno] = NewN( SelMask, ww->maxstars );
    	    ww->nleafsel = ww->maxstars;
    	    memset(ww->bufleafsel[ww->bufno], 0, ww->maxstars*sizeof(SelMask));
    	}
    
    	ww->trails = NewN( trailhead, ww->maxstars );
    	memset(ww->trails, 0, ww->maxstars*sizeof(trailhead));
    
    
    	selinit( &ww->intdest );
    	selinit( &ww->intsrc );
    
    
    	ww->bufsl[ww->bufno] = sl;
    	ww->bufmarksl[ww->bufno] = marksl;
    
    slevy's avatar
     
    slevy committed
    
    
    	specks_all_picks( st, kira_picked, dd );
    
    
    slevy's avatar
    slevy committed
        }
    
        ww->sl = sl;
        ww->marksl = marksl;
    
    slevy's avatar
     
    slevy committed
    
    
        if(ww->myselseq < sl->selseq && sl->sel != NULL) {
    	// somebody changed something in the global select bits, so
    	// gather all stars' sel-bits back into our local copy.
    	struct speck *tsp = sl->specks;
    
    	SelMask *leafsel = ww->bufleafsel[1 - ww->bufno];
    
    	for(int k = 0; k < sl->nsel; k++) {
    	    int id = (int)tsp->val[SPECK_ID];
    
    slevy's avatar
    slevy committed
    	    if(id == 0) break;
    
    	    if(id > 0 && id < ww->nleafsel) {
    
    	    } else if(id < 0 && id + ww->maxstars >= ww->maxleaves) {
    
    		leafsel[ww->maxstars + id] = sl->sel[k];
    
    	    tsp = NextSpeck(tsp, sl, 1);
    	}
    
    slevy's avatar
    slevy committed
    	ww->myselseq = sl->selseq;
    
    slevy's avatar
    slevy committed
        struct speck *sp = sl->specks;
    
    
    slevy's avatar
     
    slevy committed
        sl->nspecks = 0;
    
        sl->colorseq = sl->sizeseq = sl->threshseq = 0;
    
        marksl->colorseq = marksl->sizeseq = marksl->threshseq = 0;
    
    slevy's avatar
     
    slevy committed
        marksl->nspecks = 0;
        ww->marksp = marksl->specks;
        ww->leafcount = 0;
    
        ww->leafsel = ww->bufleafsel[ww->bufno];
    
    slevy's avatar
     
    slevy committed
    
    
        int i;
        struct vald *vd = ww->vd;
        for(i = 0, vd = ww->vd; i < SPECK_NDATAFIELDS; i++, vd++) {
    	vd->min = 1e9;
    	vd->max = -1e9;
    	vd->sum = 0;
        }
        int ntotal = 0;
    
    
    slevy's avatar
     
    slevy committed
        if(ww->wastracking) ww->wastracking = -1;
    
    
        for_all_daughters(pdyn, root, b) {
    
    slevy's avatar
     
    slevy committed
    	int ns = sl->nspecks;
    
    slevy's avatar
     
    slevy committed
    	int mns = marksl->nspecks;
    	int nl = ww->leafcount;
    
    slevy's avatar
     
    slevy committed
    	speck *tsp = sp;
    
    slevy's avatar
     
    slevy committed
    	speck *msp = ww->marksp;
    
    slevy's avatar
     
    slevy committed
    
    
    	ww->interactsel = ww->unionsel = 0;
    
    slevy's avatar
     
    slevy committed
    	sp = assign_specks(b, b, !b->is_leaf(), sp, sl, ww);