1 /*
2  * -----------------------------------------------------------------
3  * $Revision:$
4  * $Date:$
5  * -----------------------------------------------------------------
6  * Programmer: Radu Serban  @ LLNL
7  * -----------------------------------------------------------------
8  * Copyright (c) 2006, The Regents of the University of California.
9  * Produced at the Lawrence Livermore National Laboratory.
10  * All rights reserved.
11  * For details, see the LICENSE file.
12  * -----------------------------------------------------------------
13  * This is the implementation for the event detection for CPODES.
14  * -----------------------------------------------------------------
15  */
16 
17 /*
18  * =================================================================
19  * Import Header Files
20  * =================================================================
21  */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 
27 #include "cpodes_private.h"
28 #include <sundials/sundials_math.h>
29 
30 /*
31  * =================================================================
32  * Private Function Prototypes
33  * =================================================================
34  */
35 
36 static booleantype cpRootAlloc(CPodeMem cp_mem, int nrt);
37 static int cpRootfind(CPodeMem cp_mem, realtype ttol);
38 
39 /*
40  * =================================================================
41  * EXPORTED FUNCTIONS
42  * =================================================================
43  */
44 
45 /*
46  * CPodeRootInit
47  *
48  * CPodeRootInit initializes a rootfinding problem to be solved
49  * during the integration of the ODE system.  It loads the root
50  * function pointer and the number of root functions, and allocates
51  * workspace memory.  The return value is CP_SUCCESS = 0 if no errors
52  * occurred, or a negative value otherwise.
53  */
54 
CPodeRootInit(void * cpode_mem,int nrtfn,CPRootFn gfun,void * g_data)55 int CPodeRootInit(void *cpode_mem, int nrtfn, CPRootFn gfun, void *g_data)
56 {
57   CPodeMem cp_mem;
58   booleantype allocOK;
59   int i;
60 
61   /* Check cpode_mem pointer */
62   if (cpode_mem == NULL) {
63     cpProcessError(NULL, CP_MEM_NULL, "CPODES", "CPodeRootInit", MSGCP_NO_MEM);
64     return(CP_MEM_NULL);
65   }
66   cp_mem = (CPodeMem) cpode_mem;
67 
68   /* If called with nrtfn <= 0, then disable rootfinding and return */
69   if (nrtfn <= 0) {
70     cp_mem->cp_doRootfinding = FALSE;
71     return(CP_SUCCESS);
72   }
73 
74   /* Check for legal input parameters */
75   if (gfun == NULL) {
76     cpProcessError(cp_mem, CP_ILL_INPUT, "CPODES", "CPodeRootInit", MSGCP_NULL_G);
77     return(CP_ILL_INPUT);
78   }
79 
80   /* If rerunning CPodeRootInit() with a different number of root
81    * functions (changing number of gfun components), then free
82    * currently held memory resources */
83   if ( (cp_mem->cp_rootMallocDone) && (nrtfn != cp_mem->cp_nrtfn) ) {
84     cpRootFree(cp_mem);
85     cp_mem->cp_rootMallocDone = FALSE;
86   }
87 
88   /* Allocate necessary memory and return */
89   if (!cp_mem->cp_rootMallocDone) {
90     allocOK = cpRootAlloc(cp_mem, nrtfn);
91     if (!allocOK) {
92       cpProcessError(cp_mem, CP_MEM_FAIL, "CPODES", "CPodeRootInit", MSGCP_MEM_FAIL);
93       return(CP_MEM_FAIL);
94     }
95     cp_mem->cp_rootMallocDone = TRUE;
96   }
97 
98   /* Set variable values in CPODES memory block */
99   cp_mem->cp_nrtfn  = nrtfn;
100   cp_mem->cp_gfun   = gfun;
101   cp_mem->cp_g_data = g_data;
102 
103   /* Set default values for rootdir (both directions)
104    * and for gactive (all active) */
105   for(i=0; i<nrtfn; i++) {
106     cp_mem->cp_rootdir[i] = 0;
107     cp_mem->cp_gactive[i] = TRUE;
108   }
109 
110   /* Rootfinding is now enabled */
111   cp_mem->cp_doRootfinding = TRUE;
112 
113   return(CP_SUCCESS);
114 }
115 
116 
117 /*
118  * =================================================================
119  * Readibility Constants
120  * =================================================================
121  */
122 
123 #define uround         (cp_mem->cp_uround)
124 #define tn             (cp_mem->cp_tn)
125 #define h              (cp_mem->cp_h)
126 #define zn             (cp_mem->cp_zn)
127 #define y              (cp_mem->cp_y)
128 #define yp             (cp_mem->cp_yp)
129 
130 #define lrw            (cp_mem->cp_lrw)
131 #define liw            (cp_mem->cp_liw)
132 
133 #define taskc          (cp_mem->cp_taskc)
134 #define toutc          (cp_mem->cp_toutc)
135 
136 #define nrtfn          (cp_mem->cp_nrtfn)
137 #define gfun           (cp_mem->cp_gfun)
138 #define g_data         (cp_mem->cp_g_data)
139 #define nge            (cp_mem->cp_nge)
140 
141 #define gactive        (cp_mem->cp_gactive)
142 #define iroots         (cp_mem->cp_iroots)
143 #define rootdir        (cp_mem->cp_rootdir)
144 #define irfnd          (cp_mem->cp_irfnd)
145 #define tlo            (cp_mem->cp_tlo)
146 #define thi            (cp_mem->cp_thi)
147 #define glo            (cp_mem->cp_glo)
148 #define ghi            (cp_mem->cp_ghi)
149 #define trout          (cp_mem->cp_trout)
150 #define grout          (cp_mem->cp_grout)
151 
152 /*
153  * =================================================================
154  * INTERNAL FUNCTIONS
155  * =================================================================
156  */
157 
158 /*
159  * -----------------------------------------------------------------
160  * Root finding functions
161  *   cpRcheck1    |
162  *   cpRcheck2    |-> interface functions to CPode
163  *   cpRcheck3    |
164  *   cpRootfind
165  * -----------------------------------------------------------------
166  * Memory allocation/deallocation for rootfinding
167  *   cpRootAlloc
168  *   cpRootFree
169  * -----------------------------------------------------------------
170  */
171 
172 /*
173  * cpRcheck1
174  *
175  * This routine completes the initialization of rootfinding memory
176  * information (it is called only once, at the very first step),
177  * and checks whether g has any components that are zero BOTH at AND
178  * very near the initial time of the IVP. Those components of g are
179  * made inactive and will be later reactivated only when they move
180  * away from zero.
181  *
182  * sherm 111125:
183  *  thi (output only)    set to tn
184  *  ghi (output only)    set to g(thi)
185  * where we may have advanced time by smallh to see whether g's that were zero
186  * at tn and deactivated can be reactivated at tn+smallh.
187  *
188  * The return value will be
189  *    CV_RTFUNC_FAIL < 0 if the g function failed
190  *    CP_SUCCESS     = 0 otherwise.
191  */
192 
cpRcheck1(CPodeMem cp_mem)193 int cpRcheck1(CPodeMem cp_mem)
194 {
195   int i, retval;
196   realtype ttol, smallh, hratio;
197   booleantype zroot;
198 
199   for (i = 0; i < nrtfn; i++) iroots[i] = 0;
200 
201   thi = tlo = tn;
202   ttol = (ABS(tn) + ABS(h))*uround*FUZZ_FACTOR;
203 
204   /*
205    * Evaluate g at initial t and check for zero values.
206    * Note that cpRcheck1 is called at the first step
207    * before scaling zn[1] and therefore, y'(t0)=zn[1].
208    */
209 
210   retval = gfun(tlo, zn[0], zn[1], glo, g_data);
211   nge = 1;
212   if (retval != 0) return(CP_RTFUNC_FAIL);
213 
214   /* Assume we won't find a root at the start. */
215   for (i = 0; i < nrtfn; i++) ghi[i] = glo[i];
216 
217   zroot = FALSE;
218   for (i = 0; i < nrtfn; i++) {
219     if (ABS(glo[i]) == ZERO) {
220       zroot = TRUE;
221       gactive[i] = FALSE;
222     }
223   }
224   if (!zroot) return(CP_SUCCESS);
225 
226   /*
227    * Some g_i is zero at t0; look at g at t0+(small increment).
228    * At the initial time and at order 1, we have:
229    * y(t0+smallh) = zn[0] + (smallh/h) * zn[1]
230    * y'(t0+smallh) = zn[1]
231    */
232 
233   hratio = MAX(ttol/ABS(h), PT1);
234   smallh = hratio*h;
235   thi += smallh;
236   N_VLinearSum(ONE, zn[0], hratio, zn[1], y);
237   retval = gfun(thi, y, zn[1], ghi, g_data);
238   nge++;
239   if (retval != 0) return(CP_RTFUNC_FAIL);
240 
241   /*
242    * We check now only the components of g which were exactly
243    * zero at t0 to see if we can 'activate' them.
244    */
245 
246   for (i = 0; i < nrtfn; i++) {
247     if (!gactive[i] && ABS(ghi[i]) != ZERO) {
248       gactive[i] = TRUE;
249     }
250   }
251 
252   return(CP_SUCCESS);
253 }
254 
255 
256 /*
257  * cpRcheck2
258  *
259  * This routine is called at the beginning of a step to find the beginning tlo
260  * of the next root search interval, which is usually the end (thi) of the
261  * previous search interval. But it first checks for exact zeros of any active
262  * g at thi. It then checks for a close pair of zeros (a condition that
263  * would trigger making inactive the corresponding components
264  * of g), and for a new root at a nearby point.
265  * The endpoint thi of the previous search interval is thus adjusted
266  * if necessary to assure that all active g_i are nonzero there,
267  * before returning to do a root search in the interval.
268  *
269  * On entry, thi = tretlast is the last value of tret returned by
270  * CPode.  This may be the previous tn, the previous tout value, or
271  * the last root location.
272  *
273  * This routine returns an int equal to:
274  *      CP_RTFUNC_FAIL < 0 if gfun failed
275  *      RTFOUND        > 0 if a new zero of g was found near tlo, or
276  *      CP_SUCCESS     = 0 otherwise.
277  */
278 
cpRcheck2(CPodeMem cp_mem)279 int cpRcheck2(CPodeMem cp_mem)
280 {
281   int i, retval;
282   realtype ttol, smallh, hratio;
283   booleantype zroot;
284 
285   /* Move tlo up to end of previous search interval in case we find a root
286   here. */
287   tlo = thi;
288   /* Evaluate g(tlo) */
289   (void) cpGetSolution(cp_mem, tlo, y, yp);
290   retval = gfun(tlo, y, yp, glo, g_data);
291   nge++;
292   if (retval != 0) return(CP_RTFUNC_FAIL);
293 
294   /* Assume we won't find a root at the start. */
295   for (i = 0; i < nrtfn; i++) ghi[i] = glo[i];
296 
297   /* Check if any active g function is exactly ZERO at tlo.
298    * If not, simply return CP_SUCCESS. */
299 
300   zroot = FALSE;
301   for (i = 0; i < nrtfn; i++) iroots[i] = 0;
302   for (i = 0; i < nrtfn; i++) {
303     if (!gactive[i]) continue;
304     if (ABS(glo[i]) == ZERO) {
305       zroot = TRUE;
306       iroots[i] = 1;
307     }
308   }
309 
310   /* If no root then tlo==thi, glo==ghi. */
311   if (!zroot) return(CP_SUCCESS);
312 
313   /* One or more g_i has a zero at tlo.
314    * Evaluate g(thi=tlo+smallh). */
315 
316   ttol = (ABS(tn) + ABS(h))*uround*FUZZ_FACTOR;
317   smallh = (h > ZERO) ? ttol : -ttol;
318   thi = tlo+smallh;
319   if ( (thi - tn)*h >= ZERO) {
320     hratio = smallh/h;
321     N_VLinearSum(ONE, y, hratio, zn[1], y);
322   } else {
323     (void) cpGetSolution(cp_mem, thi, y, yp);
324   }
325   retval = gfun(thi, y, yp, ghi, g_data);
326   nge++;
327   if (retval != 0) return(CP_RTFUNC_FAIL);
328 
329   /* Check if any active function is ZERO at thi+smallh.
330    * Make inactive those that were also ZERO at thi.
331    * Report a root for those that only became ZERO at tlo+smallh. */
332 
333   zroot = FALSE;
334   for (i = 0; i < nrtfn; i++) {
335     if (ABS(ghi[i]) == ZERO) {
336       if (!gactive[i]) continue;
337       if (iroots[i] == 1) { iroots[i] = 0; gactive[i] = FALSE; }
338       else                { iroots[i] = 1; zroot = TRUE; }
339     }
340   }
341 
342   if (zroot) return(RTFOUND);
343 
344   return(CP_SUCCESS);
345 }
346 
347 /*
348  * cpRcheck3
349  *
350  * This routine interfaces to cpRootfind to look for a root of g
351  * between tlo and either tn or tout, whichever comes first.
352  * Only roots beyond tlo in the direction of integration are sought.
353  *
354  * On entry, both thi and ghi=g(thi) should have been evaluated. We start by
355  * setting tlo=thi and glo=ghi, shiting the search interval to start at the
356  * end of the previous one.
357  * On return, if there is a root it is in (tlo,thi] which will have been
358  * adjusted to a very narrow bracket around the zero crossing. If there is no
359  * root then thi and ghi are at the end of the search interval, where they can
360  * serve as the start for the next one.
361  *
362  * This routine returns an int equal to:
363  *      CP_RTFUNC_FAIL < 0 if the g function failed,
364  *      RTFOUND        > 0 if a root of g was found, or
365  *      CP_SUCCESS     = 0 otherwise.
366  */
367 
cpRcheck3(CPodeMem cp_mem)368 int cpRcheck3(CPodeMem cp_mem)
369 {
370   int i, retval, ier;
371   realtype ttol;
372 
373   /* Move start of search interval to end of previous one. */
374   tlo = thi;
375   for (i = 0; i < nrtfn; ++i) glo[i] = ghi[i];
376 
377   /* Set thi = tn or tout, whichever comes first. */
378   switch (taskc) {
379   case CP_ONE_STEP:
380     thi = tn;
381     break;
382   case CP_NORMAL:
383     thi = ( (toutc - tn)*h >= ZERO) ? tn : toutc;
384     break;
385   }
386 
387   /* Get y and y' at thi. */
388   (void) cpGetSolution(cp_mem, thi, y, yp);
389 
390   /* Set ghi = g(thi) */
391   retval = gfun(thi, y, yp, ghi, g_data);
392   nge++;
393   if (retval != 0) return(CP_RTFUNC_FAIL);
394 
395   /* Call cpRootfind to search (tlo,thi) for roots, and to modify tlo,thi
396   to create a very narrow bracket around the first root. */
397   ttol = (ABS(tn) + ABS(h))*uround*FUZZ_FACTOR;
398   ier = cpRootfind(cp_mem, ttol);
399 
400   /* If the root function g failed, return now */
401   if (ier == CP_RTFUNC_FAIL) return(CP_RTFUNC_FAIL);
402 
403   /* If any of the inactive components moved away from zero,
404    * activate them now. */
405   for(i=0; i<nrtfn; i++) {
406     if(!gactive[i] && grout[i] != ZERO) gactive[i] = TRUE;
407   }
408 
409   /* If no root found, return CP_SUCCESS. */
410   if (ier == CP_SUCCESS) return(CP_SUCCESS);
411 
412   /* If a root was found, interpolate to get y(trout) and return.  */
413   (void) cpGetSolution(cp_mem, trout, y, yp);
414 
415   return(RTFOUND);
416 }
417 
418 /*
419  * cpRootFind
420  *
421  * This routine solves for a root of g(t) between tlo and thi, if
422  * one exists.  Only roots of odd multiplicity (i.e. with a change
423  * of sign in one of the g_i), or exact zeros, are found.
424  * Here the sign of tlo - thi is arbitrary, but if multiple roots
425  * are found, the one closest to tlo is returned.
426  *
427  * The method used is the Illinois algorithm, a modified secant method.
428  * Reference: Kathie L. Hiebert and Lawrence F. Shampine, Implicitly
429  * Defined Output Points for Solutions of ODEs, Sandia National
430  * Laboratory Report SAND80-0180, February 1980.
431  *
432  * This routine uses the following parameters for communication:
433  *
434  * nrtfn    = number of functions g_i, or number of components of
435  *            the vector-valued function g(t).  Input only.
436  *
437  * gfun     = user-defined function for g(t).  Its form is
438  *            (void) gfun(t, y, gt, g_data)
439  *
440  * gactive  = array specifying whether a component of g should
441  *            or should not be monitored. gactive[i] is initially
442  *            set to TRUE for all i=0,...,nrtfn-1, but it may be
443  *            reset to FALSE if at the first step g[i] is 0.0
444  *            both at the I.C. and at a small perturbation of them.
445  *            gactive[i] is then set back on TRUE only after the
446  *            corresponding g function moves away from 0.0.
447  *
448  * rootdir  = array specifying the direction of zero-crossings.
449  *            If rootdir[i] > 0, search for roots of g_i only if
450  *            g_i is increasing; if rootdir[i] < 0, search for
451  *            roots of g_i only if g_i is decreasing; otherwise
452  *            always search for roots of g_i.
453  *
454  * nge      = cumulative counter for gfun calls.
455  *
456  * ttol     = a convergence tolerance for trout.  Input only.
457  *            When a root at trout is found, it is located in time only to
458  *            within a tolerance of ttol.  Typically, ttol should
459  *            be set to a value on the order of
460  *               100 * UROUND * max (ABS(tlo), ABS(thi))
461  *            where UROUND is the unit roundoff of the machine.
462  *
463  * tlo, thi = endpoints of the interval in which roots are sought.
464  *            On input, they must be distinct, but tlo - thi may
465  *            be of either sign.  The direction of integration is
466  *            assumed to be from tlo to thi.  On return, tlo and thi
467  *            are the endpoints of the final relevant interval (tlo,thi];
468  *            that is, the root has not yet occurred at tlo but has
469  *            definitely occurred by thi. The reported root time trout is
470  *            always the same as thi.
471  *
472  * glo, ghi = arrays of length nrtfn containing the vectors g(tlo)
473  *            and g(thi) respectively.  Input and output.  On input,
474  *            none of the active glo[i] should be zero.
475  *
476  * trout    = root location (same as thi), if a root was found, or the original
477  *            value of thi if not. Output only. trout is the endpoint thi of
478  *            the final interval (tlo,thi] bracketing the root, with |thi-tlo|
479  *            at most ttol.
480  *
481  * grout    = array of length nrtfn containing g(trout) (==ghi) on return.
482  *
483  * iroots   = int array of length nrtfn with root information.
484  *            Output only.  If a root was found, iroots indicates
485  *            which components g_i have a root at trout.  For
486  *            i = 0, ..., nrtfn-1, iroots[i] = 1 if g_i has a root
487  *            and g_i is increasing, iroots[i] = -1 if g_i has a
488  *            root and g_i is decreasing, and iroots[i] = 0 if g_i
489  *            has no roots or g_i varies in the direction opposite
490  *            to that indicated by rootdir[i].
491  *
492  * This routine returns an int equal to:
493  *      CP_RTFUNC_FAIL < 0 if gfun faild
494  *      RTFOUND        > 0 if a root of g was found, or
495  *      CP_SUCCESS     = 0 otherwise.
496  */
cpRootfind(CPodeMem cp_mem,realtype ttol)497 static int cpRootfind(CPodeMem cp_mem, realtype ttol)
498 {
499   realtype alpha, tmid, my_tmid, tmid_saved, thi_saved, fracint, fracsub;
500   int i, retval, side, sideprev;
501   booleantype zroot;
502 
503   /* alpha is a bias weight in the secant method.
504    * On the first two passes, set alpha = 1.  Thereafter, reset alpha
505    * according to the side (low vs high) of the subinterval in which
506    * the sign change was found in the previous two passes.
507    * If the sides were opposite, set alpha = 1.
508    * If the sides were the same, then double alpha (if high side),
509    * or halve alpha (if low side).
510    * The next guess tmid is the secant method value if alpha = 1, but
511    * is closer to tlo if alpha < 1, and closer to thi if alpha > 1.
512    */
513   alpha = ONE;
514 
515   /* First, for each active g function, check whether an event occurred in
516    * (tlo,thi). Since glo != 0 for an active component, this means we check for
517    * a sign change or for ghi = 0 (taking into account rootdir). For each
518    * component that triggers an event, we estimate a "proposal" mid point (by
519    * bisection if ghi=0 or with secant method otherwise) and select the one
520    * closest to tlo. */
521   zroot = FALSE;
522   tmid = thi;
523   for (i = 0;  i < nrtfn; i++) {
524     if(!gactive[i]) continue;
525 
526     if ( (glo[i]*ghi[i] <= ZERO) && (rootdir[i]*glo[i] <= ZERO) ) {
527       zroot = TRUE;
528       if (ghi[i] == ZERO) {
529         my_tmid = thi - HALF * (thi-tlo);
530       } else {
531         my_tmid = thi - (thi - tlo)*ghi[i]/(ghi[i] - alpha*glo[i]);
532       }
533       /* Pick my_tmid if it is closer to tlo than the current tmid. */
534       if ( (my_tmid-tmid)*h < ZERO ) tmid = my_tmid;
535     }
536   }
537 
538   /* If no event was detected, set trout to thi and return CP_SUCCESS */
539   if (!zroot) {
540     trout = thi;
541     for (i = 0; i < nrtfn; i++) grout[i] = ghi[i];
542     return(CP_SUCCESS);
543   }
544 
545   /* An event was detected. Loop to locate nearest root. */
546   side = 0;
547 
548   loop {
549     sideprev = side;
550 
551     /* If tmid is too close to tlo or thi, adjust it inward,
552      * by a fractional distance that is between 0.1 and 0.5. */
553     if (ABS(tmid - tlo) < HALF*ttol) {
554       fracint = ABS(thi - tlo)/ttol;
555       fracsub = (fracint > FIVE) ? PT1 : HALF/fracint;
556       tmid = tlo + fracsub*(thi - tlo);
557     }
558     if (ABS(thi - tmid) < HALF*ttol) {
559       fracint = ABS(thi - tlo)/ttol;
560       fracsub = (fracint > FIVE) ? PT1 : HALF/fracint;
561       tmid = thi - fracsub*(thi - tlo);
562     }
563 
564     /* Get solution at tmid and evaluate g(tmid). */
565     (void) cpGetSolution(cp_mem, tmid, y, yp);
566     retval = gfun(tmid, y, yp, grout, g_data);
567     nge++;
568     if (retval != 0) return(CP_RTFUNC_FAIL);
569 
570     /* Check (tlo, tmid) to see if an event occurred in the "low" side
571      * First make temporary copies of thi and tmid in case the event
572      * turns out to be on the "high" side. */
573     tmid_saved = tmid;
574     thi_saved  = thi;
575     thi = tmid;
576     zroot = FALSE;
577     for (i = 0;  i < nrtfn; i++) {
578       if(!gactive[i]) continue;
579 
580       if ( (glo[i]*grout[i] <= ZERO) && (rootdir[i]*glo[i] <= ZERO) ) {
581         zroot = TRUE;
582         if (grout[i] == ZERO) {
583           my_tmid = thi - HALF * (thi-tlo);
584         } else {
585           my_tmid = thi - (thi - tlo)*grout[i]/(grout[i] - alpha*glo[i]);
586         }
587         if ( (my_tmid-tmid)*h < ZERO ) tmid = my_tmid;
588       }
589     }
590 
591     /* If we detected an event in the "low" side:
592        - accept current value of thi
593        - set ghi <- grout
594        - test for convergence and break from loop if converged
595        - set side=1 (low); if previous side was also 1, scale alpha by 1/2
596        - continue looping to refine root location */
597     if (zroot) {
598       for (i = 0; i < nrtfn; i++) ghi[i] = grout[i];
599       if (ABS(thi - tlo) <= ttol) break;
600 
601       side = 1;
602       if (sideprev == 1) alpha = alpha*HALF;
603       else               alpha = ONE;
604 
605       continue;
606     }
607 
608     /* No event detected in "low" side; event must be in "high" side.
609        - restore previously saved values for thi and set tlo = tmid_saved
610        - set glo <- grout
611        - test for convergence and break from loop if converged
612        - set side=2 (high); if previous side was also 2, scale alpha by 2
613        - continue looping to refine root location */
614     thi = thi_saved;
615     tlo = tmid_saved;
616 
617     for (i = 0; i < nrtfn; i++) glo[i] = grout[i];
618     if (ABS(thi - tlo) <= ttol) break;
619 
620     tmid = thi;
621     for (i = 0;  i < nrtfn; i++) {
622       if(!gactive[i]) continue;
623 
624       if ( (glo[i]*ghi[i] <= ZERO) && (rootdir[i]*glo[i] <= ZERO) ) {
625         if (ghi[i] == ZERO) {
626           my_tmid = thi - HALF * (thi-tlo);
627         } else {
628           my_tmid = thi - (thi - tlo)*ghi[i]/(ghi[i] - alpha*glo[i]);
629         }
630         if ( (my_tmid-tmid)*h < ZERO ) tmid = my_tmid;
631       }
632     }
633 
634     side = 2;
635     if (sideprev == 2) alpha = alpha*TWO;
636     else               alpha = ONE;
637 
638   } /* End of root-search loop */
639 
640   /* Root has been isolated to (tlo,thi] and |thi-tlo| <= ttol. We'll declare
641   that the root was found at trout=thi.
642      - Reset trout and grout
643      - Set iroots
644      - Return RTFOUND. */
645   trout = thi;
646   for (i = 0; i < nrtfn; i++) {
647     grout[i] = ghi[i];
648     iroots[i] = 0;
649     if(!gactive[i]) continue;
650     if ( (glo[i]*ghi[i] <= ZERO) && (rootdir[i]*glo[i] <= ZERO) )
651       iroots[i] = glo[i] > 0 ? -1:1;
652   }
653 
654   return(RTFOUND);
655 }
656 
657 
658 
659 /*
660  * cpRootAlloc allocates memory for the rootfinding algorithm.
661  */
662 
cpRootAlloc(CPodeMem cp_mem,int nrt)663 static booleantype cpRootAlloc(CPodeMem cp_mem, int nrt)
664 {
665   glo = NULL;
666   glo = (realtype *) malloc(nrt*sizeof(realtype));
667 
668   ghi = NULL;
669   ghi = (realtype *) malloc(nrt*sizeof(realtype));
670   if (ghi == NULL) {
671     free(glo); glo = NULL;
672     return(FALSE);
673   }
674 
675   grout = NULL;
676   grout = (realtype *) malloc(nrt*sizeof(realtype));
677   if (grout == NULL) {
678     free(glo); glo = NULL;
679     free(ghi); ghi = NULL;
680     return(FALSE);
681   }
682 
683   iroots = NULL;
684   iroots = (int *) malloc(nrt*sizeof(int));
685   if (iroots == NULL) {
686     free(glo); glo = NULL;
687     free(ghi); ghi = NULL;
688     free(grout); grout = NULL;
689     return(FALSE);
690   }
691 
692   rootdir = NULL;
693   rootdir = (int *) malloc(nrt*sizeof(int));
694   if (rootdir == NULL) {
695     free(glo); glo = NULL;
696     free(ghi); ghi = NULL;
697     free(grout); grout = NULL;
698     free(iroots); iroots = NULL;
699   }
700 
701   gactive = NULL;
702   gactive = (booleantype *) malloc(nrt*sizeof(booleantype));
703   if (gactive == NULL) {
704     free(glo); glo = NULL;
705     free(ghi); ghi = NULL;
706     free(grout); grout = NULL;
707     free(iroots); iroots = NULL;
708     free(rootdir); rootdir = NULL;
709   }
710 
711   lrw += 3*nrt;
712   liw += 3*nrt;
713 
714   return(TRUE);
715 }
716 
717 
718 /*
719  * cpRootFree frees the memory allocated in cpRootAlloc.
720  */
721 
cpRootFree(CPodeMem cp_mem)722 void cpRootFree(CPodeMem cp_mem)
723 {
724   free(glo); glo = NULL;
725   free(ghi); ghi = NULL;
726   free(grout); grout = NULL;
727   free(gactive); gactive = NULL;
728   free(iroots); iroots = NULL;
729   free(rootdir); rootdir = NULL;
730 
731   lrw -= 3 * nrtfn;
732   liw -= 3 * nrtfn;
733 }
734 
735