def inv_f(f): """ Solve f^(-1)(i_f(r)) to get i_f and i_r using Newton's method Uses a fixed number of iterations for easy taping and a different formulation for f>0 and f<0 to ensure convergence in few iterations. Solution has good accuracy for all values. """ # Obtain a good guess first using EKV's simple interpolation function i1 = 4. * f_simple(f) i2 = i1 for counter in xrange(4): # (f > 0) => (i1 >= 3.) The 1e-15 term needed to prevent AD # library from choking as without it we have log(0) later sqi1p = np.sqrt(1. + i1 + 1e-15) sqi11 = sqi1p - 1. fodfp = (sqi1p - 2. + np.log(sqi11) - f) * 2. * sqi11 i1 = abs(i1 - fodfp) # f < 0 sqi1 = np.sqrt(1. + i2) fodfn = (sqi1 - 1. - np.exp(f - sqi1 + 2.)) * 2. * sqi1 \ / (np.exp(f - sqi1 + 2.) + 1.) i2 = i2 - fodfn return ad.condassign(f, i1, i2)
def inv_f1(f): """ Approximately solve f^(-1)(i_f(r)) to get i_f and i_r using relaxation """ # Obtain a good guess first using EKV's simple interpolation function i_f = 4. * f_simple(f) # Use fixed number of iterations for easy taping for i in xrange(30): sqi1 = np.sqrt(1. + i_f + 1e-15) # Uses different formulation for f>0 and f<0 for convergence i_fnew = ad.condassign(f, (f + 2. - np.log(sqi1 - 1.))**2 - 1., (np.exp(f - sqi1 + 2.) + 1.)**2 - 1.) i_f = .5 * i_f + .5 * i_fnew return i_f