/* siminit.c

   written by Don Maszle
   22 November 1991
   
   Copyright (c) 1993.  Don Maszle, Frederic Bois.  All rights reserved.

   -- Revisions -----
     Logfile:  SCCS/s.siminit.c
    Revision:  1.1
        Date:  7/14/93
     Modtime:  19:14:55
      Author:  @a
   -- SCCS  ---------

   Contains initialization routines for the simulation
*/

#ifdef __LOCAL_HDR__
#include "stdlib.h"

#else
#include <stdlib.h>
#endif

#include "sim.h"



/* GetModelInfo
   
   Gets information about the model that we are using for all of the
   simulations.
*/

void GetModelInfo (PMODELINFO pmi)
{
  pmi->nStates = (long) GetNStates();	/* Because difsub() needs variables */
  pmi->nModelVars = (long) GetNModelVars();

  pmi->pdModelVars = GetModelVector ();

}  /* GetModelInfo */



/* InitIntegratorSpec
   
*/

void InitIntegratorSpec (PINTSPEC pis)
{
  pis->dStep    = DSTEP_DEFAULT;
  pis->dStepMin = DSTEP_MIN_DEFAULT;
  pis->dStepMax = DSTEP_MAX_DEFAULT;
  pis->dErrorMax= ERROR_MAX_DEFAULT;
  pis->iMethod  = IM_DEFAULT;
  pis->iMaxDeriv= MAX_DERIV_DEFAULT;

}  /* InitIntegratorSpec */



/* InitOutputSpec
   
*/

void InitOutputSpec (POUTSPEC pos)
{
  pos->szOutfilename = NULL;
  pos->pfileOut = NULL;
  pos->bCommandLineSpec = FALSE;
  
  pos->cDistinctTimes = 0;	/* Init output times list to empty */
  pos->rgdDistinctTimes = NULL;

  pos->nOutputs = 0;		/* Init output list to empty */
  pos->pszOutputNames = NULL;
  pos->phvar = NULL;
  
  pos->plistPrintRecs = NULL;	/* List of Print() records to define ... */
  pos->pcOutputTimes = NULL;	/* ..Count of output times for each var */
  pos->piCurrentOut = NULL;	/* ..Index to current output for each var */
  pos->prgdOutputTimes = NULL;	/* ..Array of output times for each var */
  pos->prgdOutputVals = NULL;	/* ..Array of output values for each var */

}  /* InitOutputSpec */



/*  InitExperiment
   
*/

void InitExperiment (PEXPERIMENT pexp, PMODELINFO pmodelinfo)
{
  pexp->iExp = 0;		    /* Number of current experiment */
  pexp->dT0 = T0_DEFAULT;	    /* Simulation times */
  pexp->dTfinal = TFINAL_DEFAULT;

  pexp->dTime = 0.0;		    /* Current time -- not used for global */
  pexp->iStepFlag = DSSTEP_INIT;    /* difsub flags -- not used for global */
  pexp->iDSFlag = 0;

  pexp->pmodelinfo = pmodelinfo;    /* Same model for all experiments */
  pexp->plistParmMods = InitList(); /* List of parameter modifications */

  InitIntegratorSpec (&pexp->is);	
  InitOutputSpec (&pexp->os);

}  /* InitExperiment */


/* InitMonteCarlo
   
*/

void InitMonteCarlo (PMONTECARLO pmc)
{
  pmc->nRuns = NSIMULATIONS_DEFAULT;
  pmc->bIndependent = TRUE;

  pmc->nPasses = 0;			
  pmc->nFails = 0;
  
  pmc->szMCOutfilename = NULL;		/* File name for Monte Carlo output */
  pmc->pfileMCOut = NULL;		/* Output file for Monte Carlo */

  pmc->szMCPassFilename = NULL;		/* File name for Monte Carlo output */
  pmc->pfileMCPass = NULL;		/* File for Monte Carlo output */

  pmc->szMCFailFilename = NULL;	/* File name for Monte Carlo output */
  pmc->pfileMCFail = NULL;		/* File for Monte Carlo output */

  pmc->szMCBehavFilename = NULL;	/* File name for Monte Carlo output */
  pmc->pfileMCBehav = NULL;		/* File for Monte Carlo output */

  pmc->szMCSumFilename = NULL;		/* File name for Monte Carlo output */
  pmc->pfileMCSum = NULL;		/* File for Monte Carlo output */

  pmc->szSetPointsFilename = NULL;
  pmc->szExtDataFilename = NULL;
  pmc->pfileSetPoints = NULL;
  
  pmc->plistMCVars   = NULL;

}  /* InitMonteCarlo */



/* SetCmdLineOptions

   overrides the default options with those from the command-line.
   First set the desired options from the command line, then inhibits
   those that are asked to be turned off.
*/

void SetCmdLineOptions (PANALYSIS panal)
{
  panal->fOptions |= panal->fCmdOptions;  /* Use these */
  panal->fOptions &= ~panal->fNotOptions; /* Inhibit these */
}  /* SetCmdLineOptions */


/* SetOptions

   chooses various default options for the analysis, overriding with
   any values that were given on the command line.
*/

void SetOptions (PANALYSIS panal)
{
  panal->fOptions = 0;

  switch (panal->iType) {
    case AT_DEFAULTSIM:
      panal->fOptions |= (OF_VARNAMES | OF_VERT_OUTPUT);
      break;

    case AT_SETPOINTS:
      panal->fOptions |= (OF_PARMLIST | OF_MCOUTPUTS | OF_MCRESULT);
      break;

    case AT_MONTECARLO:
      panal->fOptions |= (OF_PARMLIST | OF_MCRESULT);
      break;

  } /* switch */

  SetCmdLineOptions (panal);		/*-- Maybe override the defaults */

} /* SetOptions */


/* InitAnalysis
   
   Initializes an analysis structure and its associated
   sub-structures for integration, output, experiments, and
   Monte Carlo.
*/

void InitAnalysis (PANALYSIS panal)
{
  int i;
  
  if (!panal)
    return;

  panal->iType = AT_NOTSPECD;		/* Type of analysis */
  panal->fOptions = 0;                  /* Options will be set by SetOptions */
  panal->fCmdOptions = 0;		/* CmdLine opts: overwrites defaults */
  panal->fNotOptions = 0;		/* CmdLine opts: inhibits defaults */

  panal->dSeed  = SEED_DEFAULT;		/* The seed for everybody */

  panal->wContext = CN_GLOBAL;		/* Begin in global context */  
  panal->pexpCurrent = &panal->expGlobal;

  GetModelInfo (&panal->modelinfo);	/* The model we are using */
				 	/* Global experiment settings */
  InitExperiment (&panal->expGlobal, &panal->modelinfo);

	/* Init all experiments NULL */
  
  for (i = 0; i < MAX_EXPERIMENTS; i++)
    panal->rgpExps[i] = NULL;
  InitMonteCarlo (&panal->mc);		/* Monte Carlo specifications */

}  /* InitAnalysis */



/* InitOutputs
   
   Returns TRUE if ok, FALSE otherwise.
*/
#ifdef ndef
BOOL InitOutputs (PEXPERIMENT pexp, PINT piOut, PDOUBLE pdTout)
{
  BOOL bReturn = FALSE;
  
  if (!pexp->os.nOutputs)
    ReportError (NULL, RE_NOOUTPUTS, (PSTR) &pexp->iExp, NULL);

  else if (!pexp->os.pdOutputTimes)
    ReportError (NULL, RE_NOOUTTIMES, (PSTR) &pexp->iExp, NULL);

  else {
    if (!(pexp->os.pdOutputVals = (PDOUBLE) malloc( sizeof(double)
                                 * pexp->os.nOutputs * pexp->os.nOutputTimes)))
      ReportError (NULL, RE_OUTOFMEM | RE_FATAL, "InitOutputs()", NULL);
    
    else {
      *piOut = 0;			      /* Keep track of out times */
      *pdTout = pexp->os.pdOutputTimes[0];
      bReturn = TRUE;
    }  /* else */
  }  /* else */

  return (bReturn);
}  /* InitOutputs */
#endif

BOOL InitOutputs (PEXPERIMENT pexp, PINT piOut, PDOUBLE pdTout)
{
  int j;
  BOOL bReturn = FALSE;
  
  if (!pexp->os.nOutputs)
    ReportError (NULL, RE_NOOUTPUTS, (PSTR) &pexp->iExp, NULL);

  else {
    *piOut = 0;			      /* Keep track of out times */
    *pdTout = pexp->os.rgdDistinctTimes[0];
    for (j = 0; j < pexp->os.nOutputs; j++)
      pexp->os.piCurrentOut[j] = 0;

    bReturn = TRUE;
  }  /* else */

  return (bReturn);
}  /* InitOutputs */


/* InitOneOutVar
   
   Initializes information for one output to be printed.  Creates space
   for outputs.
*/

int InitOneOutVar (PVOID pData, PVOID pInfo)
{
  PPRINTREC ppr = (PPRINTREC) pData;
  POUTSPEC pos = (POUTSPEC) pInfo;
  int i = pos->nOutputs++;		/* Index of current cell */

  pos->pszOutputNames[i] = ppr->szOutputName;
  pos->phvar[i]          = ppr->hvar;
  pos->pcOutputTimes[i]  = ppr->cTimes;
  pos->piCurrentOut[i]   = 0;

  pos->prgdOutputTimes[i] = ppr->pdTimes;
  pos->prgdOutputVals[i] = (PDOUBLE) malloc(ppr->cTimes * sizeof(double));

/*. fprintf (stderr,  "* Allocated %d bytes for times and outputs\n", */
/*. 	 ppr->cTimes * 2 * sizeof(double)); */

  if (!pos->prgdOutputTimes[i] || !pos->prgdOutputVals[i])
    ReportError (NULL, RE_OUTOFMEM | RE_FATAL, "InitOneOutVar()", NULL);    

  return 0;
}  /* InitOneOutVar */


/* CreateOutputSchedule
   
   Creates a single output time array from the individual times.  This
   will be used to efficiently schedule the outputs.
   
   This routines sorts n sorted arrays into 1 array of undetermined
   length omitting duplicate entries.
   
   The algorithm works as follows.  A counter is kept into each
   array.  A current test "point" is found as the element of the
   first array whose counter is not past the end.  Thus, we start
   with the point at the first element of the first array.

   Looping through the list of arrays, if another array is found
   whose counter element is less than the point, then the point is
   switched to this element.  If no such element is found, the point
   is added to the new list.  If an array element found which is equal
   to the point the counter for that array is incremented.
   
   The pos->piCurrentOut fields are used for counters.  They are not
   reset, since InitOutputs() must be called before running the
   simulation anyway.
*/


/* FindNewPoint
   
   Finds the new point.  Return FALSE if the sort is finished, i.e. no
   new point can be found.
*/

BOOL FindNewPoint (POUTSPEC pos, PINT piPoint)
{
  for (*piPoint = 0; *piPoint < pos->nOutputs; (*piPoint)++)
    if (pos->piCurrentOut[*piPoint] < pos->pcOutputTimes[*piPoint])
      break;
    
  return (*piPoint < pos->nOutputs ? TRUE : FALSE);
}  /* FindNewPoint */


void CreateOutputSchedule (POUTSPEC pos)
{
  int i, cTimes = 0, iPoint, iTmp;
  BOOL bCont = TRUE;
  
  for (i = 0; i < pos->nOutputs; i++)		/* Find max size of array */
    cTimes += pos->pcOutputTimes[i];
  
  iTmp = cTimes;
  if (!(pos->rgdDistinctTimes = (PDOUBLE) malloc(sizeof(double) * cTimes)))
    ReportError (NULL, RE_OUTOFMEM | RE_FATAL, "CreateOutputSchedule()",NULL);

  cTimes = 0;
  FindNewPoint (pos, &iPoint);  
  while (bCont) {

    for (i = 0; i < pos->nOutputs; i++)
      if (i != iPoint && pos->piCurrentOut[i] < pos->pcOutputTimes[i]) {

	if (pos->prgdOutputTimes[i][pos->piCurrentOut[i]]
	    < pos->prgdOutputTimes[iPoint][pos->piCurrentOut[iPoint]])
	  iPoint = i;

	else if (pos->prgdOutputTimes[i][pos->piCurrentOut[i]]
		 == pos->prgdOutputTimes[iPoint][pos->piCurrentOut[iPoint]])
	  pos->piCurrentOut[i]++;
      }  /* if */
    pos->rgdDistinctTimes[cTimes++] =
	pos->prgdOutputTimes[iPoint][pos->piCurrentOut[iPoint]];

    if (++pos->piCurrentOut[iPoint] >= pos->pcOutputTimes[iPoint])
      bCont = FindNewPoint (pos, &iPoint);  

  }  /* while */

  pos->cDistinctTimes = cTimes;		/* Save number of distinct times */

/* pos->rgdDistinctTimes = realloc ();*/  /* Reallocate for smaller block */

}  /* CreateOutputSchedule */


/* PrepareOutSpec
   
   Prepares an output spec determined by the plistPrintRecs
   specification.  
*/

BOOL PrepareOutSpec (PEXPERIMENT pexp)
{
  POUTSPEC pos = &pexp->os;
  BOOL bReturn = FALSE;
  int cOut = ListLength(pexp->os.plistPrintRecs);

  if (!cOut)
    ReportError (NULL, RE_NOOUTPUTS, (PSTR) &pexp->iExp, NULL);

  else {
    pos->pszOutputNames = (PSTR *) malloc(cOut * sizeof(PSTR));
    pos->phvar          = (HVAR *) malloc(cOut * sizeof(HVAR));
    pos->pcOutputTimes  = (PINT) malloc(cOut * sizeof(int));
    pos->piCurrentOut   = (PINT) malloc(cOut * sizeof(int));
    pos->prgdOutputTimes= (PDOUBLE *) malloc(cOut * sizeof(PDOUBLE));
    pos->prgdOutputVals = (PDOUBLE *) malloc(cOut * sizeof(PDOUBLE));

/*. fprintf (stderr, "* Allocated %d bytes for output spec\n", */
/*. 	 cOut * (4 * sizeof(PSTR) + 2 * sizeof(int))); */
    
    if (!pos->pszOutputNames  || !pos->phvar 
	|| !pos->pcOutputTimes   || !pos->piCurrentOut
	|| !pos->prgdOutputTimes || !pos->prgdOutputVals)

      ReportError (NULL, RE_OUTOFMEM | RE_FATAL, "PrepareOutSpec()", NULL);
    
    else {
      pos->nOutputs = 0;		/* Current cell counter in callback */
      ForAllList (pos->plistPrintRecs, InitOneOutVar, (PVOID) &pexp->os);
      pos->nOutputs = cOut;		/* Set count of outputs for real */

      FreeList (&pos->plistPrintRecs, NULL, TRUE);
      CreateOutputSchedule (pos);	/* Create single output time list */
      bReturn = TRUE;
    }  /* else */
  }  /* else */

  return (bReturn);
}  /* PrepareOutSpec */


BOOL PrintOutSpec (PEXPERIMENT pexp)
{
  POUTSPEC pos = &pexp->os;
  int j, i, cOut = pos->nOutputs;

  fprintf (stderr, "%d Outputs:\n", cOut);
  for (i = 0; i < cOut; i++) {
    fprintf (stderr, "  %#0x  %s: ", pos->phvar[i], pos->pszOutputNames[i]);
    for (j = 0; j < pos->pcOutputTimes[i]; j++)
      fprintf (stderr, "%g ", pos->prgdOutputTimes[i][j]);
    fprintf (stderr, "\n");
  }  /* for */
  return 0;
}  /* PrintOutSpec */
