// Draws the first passage time from the propensity function. // Uses the help routine drawT_f and structure drawT_params for some technical // reasons related to the way to input a function and parameters required by // the GSL library. Real GreensFunction1DAbsAbs::drawTime (Real rnd) const { THROW_UNLESS( std::invalid_argument, 0.0 <= rnd && rnd < 1.0 ); const Real a(this->geta()); const Real sigma(this->getsigma()); const Real L(this->geta() - this->getsigma()); const Real r0(this->getr0()); const Real D(this->getD()); const Real v(this->getv()); if (D == 0.0 ) { return INFINITY; } else if ( L < 0.0 || fabs(a-r0) < EPSILON*L || fabs(r0-sigma) > (1.0 - EPSILON)*L ) { // if the domain had zero size return 0.0; } const Real expo(-D/(L*L)); const Real r0s_L((r0-sigma)/L); // some abbreviations for terms appearing in the sums with drift<>0 const Real sigmav2D(sigma*v/2.0/D); const Real av2D(a*v/2.0/D); const Real Lv2D(L*v/2.0/D); // exponent of the prefactor present in case of v<>0; has to be split because it has a t-dep. and t-indep. part const Real vexpo_t(-v*v/4.0/D); const Real vexpo_pref(-v*r0/2.0/D); // the structure to store the numbers to calculate the numbers for 1-S struct drawT_params parameters; Real Xn, exponent, prefactor; Real nPI; // Construct the coefficients and the terms in the exponent and put them // into the params structure int n = 0; // a simpler sum has to be computed for the case w/o drift, so distinguish here if(v==0) { do { nPI = ((Real)(n+1))*M_PI; // why n+1 : this loop starts at n=0 (1st index of the arrays), while the sum starts at n=1 ! Xn = sin(nPI*r0s_L) * (1.0 - cos(nPI)) / nPI; exponent = nPI*nPI*expo; // store the coefficients in the structure parameters.Xn[n] = Xn; // also store the values for the exponent parameters.exponent[n]=exponent; n++; } // TODO: Modify this later to include a cutoff when changes are small while (n<MAX_TERMS); } else // case with drift<>0 { do { nPI = ((Real)(n+1))*M_PI; // why n+1 : this loop starts at n=0 (1st index of the arrays), while the sum starts at n=1 ! Xn = (exp(sigmav2D) - cos(nPI)*exp(av2D)) * nPI/(Lv2D*Lv2D+nPI*nPI) * sin(nPI*r0s_L); exponent = nPI*nPI*expo + vexpo_t; // store the coefficients in the structure parameters.Xn[n] = Xn; // also store the values for the exponent parameters.exponent[n]=exponent; n++; } // TODO: Modify this later to include a cutoff when changes are small while (n<MAX_TERMS); } // the prefactor of the sum is also different in case of drift<>0 : if(v==0) prefactor = 2.0*exp(vexpo_pref); else prefactor = 2.0; parameters.prefactor = prefactor; parameters.rnd = rnd; parameters.terms = MAX_TERMS; parameters.tscale = this->t_scale; gsl_function F; F.function = &drawT_f; F.params = ¶meters; // Find a good interval to determine the first passage time in const Real dist( std::min(r0-sigma, a-r0) ); // construct a guess: MSD = sqrt (2*d*D*t) Real t_guess( dist * dist / ( 2.0 * D ) ); // A different guess has to be made in case of nonzero drift to account for the displacement due to it // When drifting towards the closest boundary... if( ( r0-sigma >= L/2.0 && v > 0.0 ) || ( r0-sigma <= L/2.0 && v < 0.0 ) ) t_guess = sqrt(D*D/(v*v*v*v)+dist*dist/(v*v)) - D/(v*v); // When drifting away from the closest boundary... if( ( r0-sigma < L/2.0 && v > 0.0 ) || ( r0-sigma > L/2.0 && v < 0.0 ) ) t_guess = D/(v*v) - sqrt(D*D/(v*v*v*v)-dist*dist/(v*v)); Real value( GSL_FN_EVAL( &F, t_guess ) ); Real low( t_guess ); Real high( t_guess ); if( value < 0.0 ) { // scale the interval around the guess such that the function // straddles if the guess was too low do { // keep increasing the upper boundary until the // function straddles high *= 10.0; value = GSL_FN_EVAL( &F, high ); if( fabs( high ) >= t_guess * 1e6 ) { std::cerr << "Couldn't adjust high. F(" << high << ") = " << value << std::endl; throw std::exception(); } } while ( value <= 0.0 ); } else { // if the guess was too high initialize with 2 so the test // below survives the first iteration Real value_prev( 2.0 ); do { if( fabs( low ) <= t_guess * 1.0e-6 || fabs(value-value_prev) < EPSILON*this->t_scale ) { std::cerr << "Couldn't adjust low. F(" << low << ") = " << value << " t_guess: " << t_guess << " diff: " << (value - value_prev) << " value: " << value << " value_prev: " << value_prev << " t_scale: " << this->t_scale << std::endl; return low; } value_prev = value; // keep decreasing the lower boundary until the // function straddles low *= 0.1; // get the accompanying value value = GSL_FN_EVAL( &F, low ); } while ( value >= 0.0 ); } // find the intersection on the y-axis between the random number and // the function // define a new solver type brent const gsl_root_fsolver_type* solverType( gsl_root_fsolver_brent ); // make a new solver instance // TODO: incl typecast? gsl_root_fsolver* solver( gsl_root_fsolver_alloc( solverType ) ); const Real t( findRoot( F, solver, low, high, EPSILON*t_scale, EPSILON, "GreensFunction1DAbsAbs::drawTime" ) ); // return the drawn time return t; }
// Draws the first passage time from the survival probability, // using an assistance function drawT_f that casts the math. function // into the form needed by the GSL root solver. Real GreensFunction1DRadAbs::drawTime (Real rnd) const { THROW_UNLESS( std::invalid_argument, 0.0 <= rnd && rnd < 1.0 ); const Real sigma(this->getsigma()); const Real a(this->geta()); const Real L(this->geta()-this->getsigma()); const Real r0(this->getr0()); const Real k(this->getk()); const Real D(this->getD()); const Real v(this->getv()); const Real h((this->getk()+this->getv()/2.0)/this->getD()); if ( D == 0.0 || L == INFINITY ) { return INFINITY; } if ( rnd <= EPSILON || L < 0.0 || fabs(a-r0) < EPSILON*L ) { return 0.0; } const Real v2D(v/2.0/D); const Real exp_av2D(exp(a*v2D)); const Real exp_sigmav2D(exp(sigma*v2D)); // exponent of the prefactor present in case of v<>0; has to be split because it has a t-dep. and t-indep. part const Real vexpo_t(-v*v/4.0/D); const Real vexpo_pref(-v*r0/2.0/D); // the structure to store the numbers to calculate the numbers for 1-S struct drawT_params parameters; // some temporary variables double root_n = 0; double root_n2, root_n_r0_s, root_n_L, h_root_n; double Xn, exponent, prefactor; // produce the coefficients and the terms in the exponent and put them // in the params structure. This is not very efficient at this point, // coefficients should be calculated on demand->TODO for (int n=0; n<MAX_TERMS; n++) { root_n = this->root_n(n+1); // get the n-th root of tan(root*a)=root/-h (Note: root numbering starts at n=1) root_n2 = root_n * root_n; root_n_r0_s = root_n * (r0-sigma); root_n_L = root_n * L; h_root_n = h / root_n; if(v==0) Xn = (h*sin(root_n_r0_s) + root_n*cos(root_n_r0_s)) / (L*(root_n2+h*h)+h) * ( h_root_n + sin(root_n_L) - h_root_n*cos(root_n_L) ); else Xn = (h*sin(root_n_r0_s) + root_n*cos(root_n_r0_s)) / (L*(root_n2+h*h)+h) * (exp_sigmav2D*h*k/D - exp_av2D*(root_n2+h*h)*cos(root_n_L)) / (h_root_n * (root_n2 + v2D*v2D)); exponent = -D*root_n2 + vexpo_t; // store the coefficients in the structure parameters.Xn[n] = Xn; // also store the values for the exponent parameters.exponent[n] = exponent; } // the prefactor of the sum is also different in case of drift<>0 : if(v==0) prefactor = 2.0; else prefactor = 2.0*exp(vexpo_pref); parameters.prefactor = prefactor; // store the random number for the probability parameters.rnd = rnd; // store the number of terms used parameters.terms = MAX_TERMS; parameters.tscale = this->t_scale; // Define the function for the rootfinder gsl_function F; F.function = &GreensFunction1DRadAbs::drawT_f; F.params = ¶meters; // Find a good interval to determine the first passage time in // get the distance to absorbing boundary (disregard rad BC) const Real dist(fabs(a-r0)); //const Real dist( std::min(r0, a-r0)); // for test purposes // construct a guess: MSD = sqrt (2*d*D*t) Real t_guess( dist * dist / ( 2.0*D ) ); // A different guess has to be made in case of nonzero drift to account for the displacement due to it // TODO: This does not work properly in this case yet, but we don't know why... // When drifting towards the closest boundary //if( (r0 >= a/2.0 && v > 0.0) || (r0 <= a/2.0 && v < 0.0) ) t_guess = sqrt(D*D/(v*v*v*v)+dist*dist/(v*v)) - D/(v*v); // When drifting away from the closest boundary //if( ( r0 < a/2.0 && v > 0.0) || ( r0 > a/2.0 && v < 0.0) ) t_guess = D/(v*v) - sqrt(D*D/(v*v*v*v)-dist*dist/(v*v)); Real value( GSL_FN_EVAL( &F, t_guess ) ); Real low( t_guess ); Real high( t_guess ); // scale the interval around the guess such that the function straddles if( value < 0.0 ) { // if the guess was too low do { // keep increasing the upper boundary until the // function straddles high *= 10; value = GSL_FN_EVAL( &F, high ); if( fabs( high ) >= t_guess * 1e6 ) { std::cerr << "GF1DRad: Couldn't adjust high. F(" << high << ") = " << value << std::endl; throw std::exception(); } } while ( value <= 0.0 ); } else { // if the guess was too high // initialize with 2 so the test below survives the first // iteration Real value_prev( 2 ); do { if( fabs( low ) <= t_guess * 1e-6 || fabs(value-value_prev) < EPSILON*1.0 ) { std::cerr << "GF1DRad: Couldn't adjust low. F(" << low << ") = " << value << " t_guess: " << t_guess << " diff: " << (value - value_prev) << " value: " << value << " value_prev: " << value_prev << " rnd: " << rnd << std::endl; return low; } value_prev = value; // keep decreasing the lower boundary until the function straddles low *= 0.1; // get the accompanying value value = GSL_FN_EVAL( &F, low ); } while ( value >= 0.0 ); } // find the intersection on the y-axis between the random number and // the function // define a new solver type brent const gsl_root_fsolver_type* solverType( gsl_root_fsolver_brent ); // make a new solver instance // TODO: incl typecast? gsl_root_fsolver* solver( gsl_root_fsolver_alloc( solverType ) ); const Real t( findRoot( F, solver, low, high, t_scale*EPSILON, EPSILON, "GreensFunction1DRadAbs::drawTime" ) ); // return the drawn time return t; }