residential/solvers.cpp

00001 // solvers.cpp: general equation solver for compound exponential decays
00002 //
00003 // ETP solver utilities
00004 //
00005 // DP Chassin
00006 // December 2002
00007 // Copyright (C) 2002, Pacific Northwest National Laboratory
00008 // All Rights Reserved
00009 //
00010 // Update with advanced solver in June 2008 by D. Chassin
00011 
00012 #include <stdlib.h>
00013 #include <stdio.h>
00014 #include <float.h>
00015 #include <math.h>
00016 #include <time.h>
00017 #include "gridlabd.h"
00018 
00019 #include "solvers.h"
00020 
00021 #ifndef WIN32
00022 #define _finite isfinite
00023 #endif
00024 static unsigned long _nan[2] = {0xffffffff,0x7fffffff};
00025 static double NaN = *(double*)&_nan;
00026 #define EVAL(t,a,n,b,m,c) (a*exp(n*t) + b*exp(m*t) + c)
00027 
00030 double e2solve( double a,   
00031                 double n,   
00032                 double b,   
00033                 double m,   
00034                 double c,   
00035                 double p,   
00036                 double *e)  
00037 {
00038     double t=0;
00039     double f = EVAL(t,a,n,b,m,c);
00040 
00041     // is there an extremum/inflexion to consider
00042     if (a*b<0)
00043     {
00044         // compute the time t and value fi at which it occurs
00045         double an_bm = -a*n/(b*m);
00046         double tm = log(an_bm)/(m-n);
00047         double fm = EVAL(tm,a,n,b,m,c);
00048         double ti = log(an_bm*n/m)/(m-n);
00049         double fi = EVAL(ti,a,n,b,m,c);
00050         if (tm>0) // extremum in domain
00051         {
00052             if (f*fm<0) // first solution is in range
00053                 t = 0;
00054             else if (c*fm<0) // second solution is in range
00055                 t = ti;
00056             else // no solution is in range
00057                 return NaN;
00058         }
00059         else if (tm<0 && ti>0) // no extremum but inflexion in domain
00060         {
00061             if (fm*c<0) // solution in range
00062                 t = ti;
00063             else // no solution in range
00064                 return NaN;
00065         }
00066         else if (ti<0) // no extremum or inflexion in domain
00067         {
00068             if (fi*c<0) // solution in range
00069                 t = ti;
00070             else // no solution in range
00071                 return NaN;
00072         }
00073         else // no solution possible (includes tm==0 and ti==0)
00074             return NaN;
00075     }
00076 
00077     // solve using Newton's method
00078     int iter = 100;
00079     if (t!=0) // initial t changed to inflexion point
00080         double f = EVAL(t,a,n,b,m,c);
00081     double dfdt = EVAL(t,a*n,n,b*m,m,0); 
00082     while ( fabs(f)>p && isfinite(t) && iter-->0 )
00083     {
00084         t -= f/dfdt;
00085         f = EVAL(t,a,n,b,m,c);
00086         dfdt = EVAL(t,a*n,n,b*m,m,0);
00087     }
00088     if (iter==0)
00089     {
00090         gl_warning("etp::solve(a=%.4f,n=%.4f,b=%.4f,m=%.4f,c=%.4f,prec=%.g) failed to converge",a,n,b,m,c,p);
00091         return NaN; // failed to catch limit condition above
00092     }
00093     if (e!=NULL)
00094         *e = p/dfdt;
00095     return t>0?t:NaN;
00096 }
00097 
00098 #ifdef OLD_SOLVER
00099 #define SGNP(X,P) ((X)<-(P)?-1:((X)>(P)?+1:0))
00100 
00101 int dual_decay_solve(double *ans, double prec, double start, double end, int f, double a, double n, double b, double m, double c)
00102 {
00103 #define EVAL(f,t,a,n,b,m,c) (a*pow(n,f)*exp(n*t) + b*pow(m,f)*exp(m*t) + c)
00104 
00105     // check differential for sign change over range
00106     double df_start = EVAL(f+1,start,a,n,b,m,c);
00107     double df_end = EVAL(f+1,end,a,n,b,m,c);
00108 
00109     if(!_finite(df_start) || !_finite(df_end))
00110         return -1;
00111 
00112     int sa = SGNP(df_start,prec);
00113     int sb = SGNP(df_end,prec);
00114 
00115     // if sign of first differential changed over range
00116     if (sa*sb==-1)
00117     {
00118         // min/max in start-end range implies multiple solutions
00119         double t;
00120         if (!dual_decay_solve(&t, prec, start, end, a*pow(n,f+1),n,b*pow(m,f+1),m,0))
00121 
00122             // error: should have a solution for the inflexion point
00123             return -1;
00124         
00125         // look for solutions right and left of first min/max, keep left result
00126         int n_right = dual_decay_solve(ans, prec, t, end, f, a,n,b,m,c);
00127         if (n_right<0)
00128             return -1; // error; right solution failed
00129         
00130         int n_left = dual_decay_solve(ans, prec, start, t, f, a,n,b,m,c);
00131         if (n_left<0)
00132             return -1; // error: left solution failed
00133 
00134         return n_left+n_right;
00135     }
00136     return dual_decay_solve(ans, prec, start, end, a,n,b,m,c);
00137 #undef EVAL
00138 }
00139 
00140 int dual_decay_solve(double *ans, double prec, double start, double end, double a, double n, double b, double m, double c)
00141 {
00142 #define EVAL(t,a,n,b,m,c) (a*exp(n*t) + b*exp(m*t) + c)
00143     // check for sign change over range
00144     double f_start = EVAL(start,a,n,b,m,c);
00145     double f_end = EVAL(end,a,n,b,m,c);
00146 
00147     if (!_finite(f_start) ||!_finite(f_end))
00148         return -1;
00149 
00150     int sa = SGNP(f_start,prec);
00151     int sb = SGNP(f_end,prec);
00152 
00153     // if sign hasn't change in range
00154     if (sa*sb==1)
00155 
00156         // no solution possible
00157         return 0;
00158 
00159     // check for boundary solutions
00160     else if (sa==0)
00161         *ans = start;
00162     else if (sb==0)
00163         *ans = end;
00164     else // opposite signs
00165     {
00166         // only one solution exists -- use Newton's method to find it
00167         double t=(start+end)/2;
00168         int result = dual_decay_solve(ans,prec,start,t,a,n,b,m,c);
00169         if (result==0)
00170             result = dual_decay_solve(ans,prec,t,end,a,n,b,m,c);
00171         return result;
00172     }   
00173 
00174     return 1;
00175 }
00176 #endif
00177 
00178 #undef SGNP

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