commercial/solvers.cpp

00001 // $Id: solvers.cpp,v 1.3 2008/02/01 06:24:29 d3g637 Exp $
00002 // solver.cpp: general equation solver for compound exponential decays
00003 //
00004 
00005 #include <stdlib.h>
00006 #include <stdio.h>
00007 #include <float.h>
00008 #include <math.h>
00009 #include <time.h>
00010 
00011 #include "solvers.h"
00012 
00013 #ifndef WIN32
00014 #define _finite isfinite
00015 #endif
00016 
00017 #define SGNP(X,P) ((X)<-(P)?-1:((X)>(P)?+1:0))
00018 
00019 int dual_decay_solve(double *ans, double prec, double start, double end, int f, double a, double n, double b, double m, double c)
00020 {
00021 #define EVAL(f,t,a,n,b,m,c) (a*pow(n,f)*exp(n*t) + b*pow(m,f)*exp(m*t) + c)
00022 
00023     // check differential for sign change over range
00024     double df_start = EVAL(f+1,start,a,n,b,m,c);
00025     double df_end = EVAL(f+1,end,a,n,b,m,c);
00026 
00027     if(!_finite(df_start) || !_finite(df_end))
00028         return -1;
00029 
00030     int sa = SGNP(df_start,prec);
00031     int sb = SGNP(df_end,prec);
00032 
00033     // if sign of first differential changed over range
00034     if (sa*sb==-1)
00035     {
00036         // min/max in start-end range implies multiple solutions
00037         double t;
00038         if (!dual_decay_solve(&t, prec, start, end, a*pow(n,f+1),n,b*pow(m,f+1),m,0))
00039 
00040             // error: should have a solution for the inflexion point
00041             return -1;
00042         
00043         // look for solutions right and left of first min/max, keep left result
00044         int n_right = dual_decay_solve(ans, prec, t, end, f, a,n,b,m,c);
00045         if (n_right<0)
00046             return -1; // error; right solution failed
00047         
00048         int n_left = dual_decay_solve(ans, prec, start, t, f, a,n,b,m,c);
00049         if (n_left<0)
00050             return -1; // error: left solution failed
00051 
00052         return n_left+n_right;
00053     }
00054     return dual_decay_solve(ans, prec, start, end, a,n,b,m,c);
00055 #undef EVAL
00056 }
00057 
00058 int dual_decay_solve(double *ans, double prec, double start, double end, double a, double n, double b, double m, double c)
00059 {
00060 #define EVAL(t,a,n,b,m,c) (a*exp(n*t) + b*exp(m*t) + c)
00061     // check for sign change over range
00062     double f_start = EVAL(start,a,n,b,m,c);
00063     double f_end = EVAL(end,a,n,b,m,c);
00064 
00065     if (!_finite(f_start) ||!_finite(f_end))
00066         return -1;
00067 
00068     int sa = SGNP(f_start,prec);
00069     int sb = SGNP(f_end,prec);
00070 
00071     // if sign hasn't change in range
00072     if (sa*sb==1)
00073 
00074         // no solution possible
00075         return 0;
00076 
00077     // check for boundary solutions
00078     else if (sa==0)
00079         *ans = start;
00080     else if (sb==0)
00081         *ans = end;
00082     else // opposite signs
00083     {
00084         // only one solution exists -- use Newton's method to find it
00085         double t=(start+end)/2;
00086         int result = dual_decay_solve(ans,prec,start,t,a,n,b,m,c);
00087         if (result==0)
00088             result = dual_decay_solve(ans,prec,t,end,a,n,b,m,c);
00089         return result;
00090     }   
00091 
00092     return 1;
00093 }
00094 
00095 #undef SGNP

GridLAB-DTM Version 1.0
An open-source project initiated by the US Department of Energy