residential/solvers.cpp

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

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