Main Page | Namespace List | Class Hierarchy | Class List | File List | Class Members | File Members | Related Pages

distrib_base.cpp

Go to the documentation of this file.
00001 /*
00002  * ******** fete: From ENDF To ENDL *********
00003  * 
00004  * Copyright (c) 2006, The Regents of the University of California. 
00005  * All rights reserved.
00006  * 
00007  * Produced at the Lawrence Livermore National Laboratory. 
00008  * Written by David A. Brown, Gerry Hedstrom, Tony Hill
00009  * 
00010  * This file is part of fete v1.0  (UCRL-CODE-218718)
00011  * 
00012  * Please read the COPYING file for "Our Notice and GNU General 
00013  * Public License" in the root of this software distribution.  
00014  * 
00015  * This program is free software; you can redistribute it and/or modify 
00016  * it under the terms of the GNU General Public License (as published by 
00017  * the Free Software Foundation) version 2, dated June 1991. 
00018  * 
00019  * This program is distributed in the hope that it will be useful, 
00020  * but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF 
00021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms 
00022  * and conditions of the GNU General Public License for more details. 
00023  * 
00024  * You should have received a copy of the GNU General Public License along 
00025  * with this program; if not, write to the Free Software Foundation, Inc., 
00026  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
00027  * 
00028  * $Revision: 1849 $
00029  * $Date: 2006-04-21 16:08:09 -0700 (Fri, 21 Apr 2006) $
00030  * $Author: hedstrom $
00031  * $Id: distrib_base.cpp 1849 2006-04-21 23:08:09Z hedstrom $
00032  * 
00033  * ******** fete: From ENDF To ENDL *********
00034  */
00035 
00036 // implementation of the distrib_base class
00037 
00038 #include "distrib_base.hpp"
00039 #include "messaging.hpp"
00040 #include "endl_formats.hpp"
00041 #include "endl_precision.hpp"
00042 
00043 extern ENDLClass ENDL;
00044 
00045 // *********** distrib_base class ***********************
00046 // ----------- distrib_base::print -----------------
00047 //! to print the list
00048 void distrib_base::print()
00049 {
00050   cout.setf(ios::scientific,ios::floatfield);
00051 
00052   cout << "E_in: " << ENDL.data( E_in() ) << endl;
00053   cout << "weight: " << weight << endl;
00054   dd_list::print();
00055 }
00056 // ----------- distrib_base::thicken -----------------
00057 //! Expand the entire list to achieve the tolerance
00058 void distrib_base::thicken( )
00059 {
00060   // point to the the last true node
00061   dd_list::iterator last = end();
00062   --last;
00063   thicken( begin(), last );
00064 }
00065 
00066 // ----------- distrib_base::thicken -----------------
00067 //! add links between first and last until linear interpolation
00068 //! is good to within tol_1d
00069 void distrib_base::thicken( dd_list::iterator first,
00070   dd_list::iterator last )
00071 {
00072   int Max_List_Size = static_cast<int>( Global.Value("max_list_size") );
00073   double abs_tol;
00074   double biggest = 0.0;
00075 
00076   // set the noise level
00077   dd_list::iterator this_link;
00078   for(this_link = first; this_link != last; ++this_link)
00079   {
00080     if(abs(this_link->y) > biggest)
00081     {
00082       biggest = abs(this_link->y);
00083     }
00084   }
00085   // they could be all zeros
00086   if( biggest == 0.0 )
00087   {
00088     return;
00089   }
00090   abs_tol = cut_off*biggest;
00091 
00092   double E_mid;
00093   double p_mid;
00094   double true_p;
00095 
00096   dd_link new_link;  // for creating new links
00097 
00098   // *** In this version we test only the accuracy of the    ***
00099   // *** linear interpolation at the midpoint energy between ***
00100   // *** two consecutive links.                              ***
00101 
00102   // loop through the list
00103   for(this_link = first; this_link != last; ++this_link)
00104   {
00105     dd_list::iterator next_link = this_link;
00106     ++next_link;
00107     // keep checking until the interval passes the test
00108     for(;;)
00109     {
00110       // get the local energy epsilon (smallest dE can see in output file)
00111       double local_eps = ENDL_EPSILON( this_link->E_out() );
00112       if( next_link->E_out() - this_link->E_out() < local_eps )
00113       {
00114         break;
00115       }
00116 
00117       // the midpoint energy
00118       E_mid = 0.5*(this_link->E_out() + next_link->E_out());
00119 
00120       // the linear interpolation
00121       dd_link test_link(this_link->x, this_link->y);
00122       p_mid = linlin_interp(E_mid, test_link, *next_link);
00123       // the true probability
00124       true_p = f(E_mid);
00125 
00126       // test against relative and absolute errors
00127       if(abs(p_mid - true_p) < tol_1d*abs(true_p) ||
00128          abs(p_mid - true_p) < abs_tol  )
00129       {
00130         break;
00131       }
00132       else
00133       {
00134         // make a new link and insert it before next_link
00135         new_link = dd_link(E_mid, true_p);
00136         insert(next_link, new_link);
00137         --next_link;  // the new link is now the next link
00138       }
00139       // *** a safely check ***
00140       if( size() >= Max_List_Size )
00141       {
00142         print();
00143         SevereError("distrib_base::thicken",pastenum("got ",
00144              Max_List_Size)+" links in 1-d thicken routine");
00145       }
00146     }
00147   }
00148 }
00149 
00150 // ----------- distrib_base::get_norm -----------------
00151 //! get the probability for this list.
00152 //! This routine is used for double differential data
00153 double distrib_base::get_norm()
00154 {
00155   dd_list::iterator link = begin();  // the first link
00156 
00157   double norm = 0.0;
00158   double last_E = link->E_out();
00159   double last_P = link->y;
00160   double next_E;
00161   double next_P;
00162 
00163   // catch bad calls to routine
00164   if (empty()) 
00165   {
00166     Warning("distrib_base::get_norm",
00167       "Can't get norm with no elements in distrib_base!");
00168     return 0.0;
00169   }
00170   
00171   // if there is only one link, the norm is easy to do, in a sense
00172   if (size()==1) 
00173   {
00174     Warning("distrib_base::get_norm", "Only one element in distrib_base!");
00175     return link->y;
00176   }
00177   
00178   // loop through the list
00179   for(++link; link != end(); ++link)
00180   {
00181     // get the next energy and probability
00182     next_E = link->E_out();
00183     next_P = link->y;
00184 
00185     // accumulate the probability
00186     norm += 0.5*(next_P + last_P)*(next_E - last_E);
00187 
00188     // save the values for the next interval
00189     last_E = next_E;
00190     last_P = next_P;
00191   }
00192 
00193   return norm;
00194 }
00195 
00196 // ----------- distrib_base::renorm -----------------
00197 //! ensure that the total probability is 1
00198 void distrib_base::renorm()
00199 {
00200   double norm = get_norm();
00201   dd_list::iterator link = begin();
00202 
00203   // error check
00204   if(norm == 0.0)
00205   {
00206     Warning("distrib_base::renorm",pastenum("Zero norm in renorm routine for E_in: ",E_in( )));
00207     // fudge something
00208     link->y = 1.0;
00209     max_P = 1.0;
00210   }
00211   else
00212   {
00213     // Now, renormalize and find the maximum y
00214     max_P = 0.0;
00215     for( ; link != end(); ++link)
00216     {
00217       link->y /= norm;
00218       if( link->y > max_P )
00219       {
00220         max_P = link->y;
00221       }
00222     }
00223   }
00224 }
00225 
00226 // ----------- distrib_base::renorm -----------------
00227 //! ensure that the total probability is 1, with the norm given
00228 void distrib_base::renorm(double Norm)
00229 {
00230   // error check
00231   if(Norm == 0.0)
00232   {
00233     SevereError("distrib_base::renorm(double Norm)","Zero norm in renorm(double Norm) routine");
00234   }
00235   // Now, normalize
00236   *this *= 1.0/Norm;
00237 }
00238 
00239 // ----------- distrib_base::get_bins -----------------
00240 //! make the equally probable energy bins
00241 void distrib_base::get_bins()
00242 {
00243   // for double-differential data the norm may not be 1
00244   double Norm = get_norm( );
00245   if( Norm <= 0.0 )
00246   {
00247     print( );
00248     SevereError("distrib_base::get_bins","zero norm");
00249   }
00250   double d_prob = Norm/num_bins;
00251   double cum_prob = 0.0;
00252 
00253   double prev_x = begin()->x;
00254   double prev_y = begin()->y;
00255   equi_prob.push_back( prev_x );  // [0]-th entry
00256 
00257   dd_list::iterator link = begin();
00258   ++link;
00259   double next_x = link->x;
00260   double next_y = link->y;
00261 
00262   // find the j-th bin
00263   for(int j = 1; j < num_bins; ++j)
00264   {
00265     double target = j*d_prob;
00266     double trapezoid = 0.5*(next_y + prev_y)*(next_x - prev_x);
00267     while(cum_prob + trapezoid < target)
00268     {
00269       cum_prob += trapezoid;
00270       prev_x = next_x;
00271       prev_y = next_y;
00272       ++link;
00273       if(link == end())
00274       {
00275         SevereError("distrib_base::get_bins","failure in get_bins");
00276       }
00277       next_x = link->x;
00278       next_y = link->y;
00279       trapezoid = 0.5*(next_y + prev_y)*(next_x - prev_x);
00280     }
00281     // solve the quadratic equation for x:
00282     //   target = cum_prob + 
00283     //      {integral from prev_x to x}{prev_y + slope*(u - prev_x)} du
00284     // with slope = (next_y - prev_y)/(next_x - prev_x)
00285     // {actually, it's better to solve for 1/(x - prev_x) and invert}
00286     double slope = (next_y - prev_y)/(next_x - prev_x);
00287     equi_prob.push_back( prev_x + 2*( target - cum_prob )/
00288       ( prev_y + sqrt( prev_y*prev_y + 2*slope*( target - cum_prob ) ) ) );
00289   }
00290   // the last bin boundary
00291   link = end();
00292   --link;
00293   equi_prob.push_back( link->x );
00294 }
00295 
00296 // ----------- distrib_base::mirror -----------------
00297 //! Reverse the order of the links with x replaced by -x.
00298 //! This routine is for angular distributions of the residual
00299 //! in elastic scattering (mu, probability) -> (-mu, probability).
00300 void distrib_base::mirror()
00301 {
00302   // first, reverse the list
00303   reverse();
00304 
00305   // replace mu by -mu
00306   for(dd_list::iterator link = begin(); link != end(); ++link)
00307   {
00308     link->mu() *=-1.0;  // Mirror the "x" axis value about zero
00309   }
00310 }
00311 
00312 // ----------- distrib_base::widen_delta -----------------
00313 //! This routine widens the delta functions
00314 void distrib_base::widen_delta( )
00315 {
00316   dd_list::iterator next_link;
00317   dd_list::iterator prev_link;
00318   dd_list::iterator link = begin();
00319 
00320   // for a single line with multiplicity 0 we need to keep the line
00321   if( ( size( ) == 1 ) && ( begin()->y == 0.0 ) ){
00322     begin()->y = 1.0;
00323   }
00324 
00325   // loop through the discrete lines
00326   for(; link != end( ); link = next_link)
00327   {
00328     next_link = link;
00329     ++next_link;
00330     prev_link = link;
00331     --prev_link;
00332 
00333     // do not widen null delta-functions
00334     if( link->y > 0.0)
00335     {
00336       // what is the separation between states?
00337       double dE_L = ( link == begin( ) ) ? link->E_out() :
00338         link->E_out() - prev_link->E_out();
00339 
00340       // set width of delta function (basically smallest dE we
00341       // can see in output file)
00342       double dE = 0.5*DELTA_WIDTH( link->E_out() );
00343 
00344       if( dE_L <= dE )
00345       {
00346         SevereError("distrib_base::widen_delta", 
00347             pastenum("Spikes out of order left at ",link->E_out()));
00348       }
00349 
00350       // insert the left base of the triangle
00351       dd_link new_link = dd_link(link->E_out() - dE, 0.0);
00352       insert(link, new_link);
00353       ++prev_link;
00354 
00355       double dE_R = ( next_link == end( ) ) ? link->E_out( ) :
00356         next_link->E_out( ) - link->E_out( );
00357 
00358       if( dE_R <= 0.0 )
00359       {
00360         SevereError("distrib_base::widen_delta", 
00361             pastenum("Spikes out of order right at ",link->E_out()));
00362       }
00363 
00364       // check for indistinguishability of lines
00365       while( ( dE_R <= 3*dE ) && ( next_link != end( ) ) )
00366       {
00367         // combine the lines
00368         next_link->y += link->y;
00369         erase( link );
00370         link = next_link;
00371         ++next_link;
00372         if( next_link == end( ) )
00373         {
00374           break;
00375         }
00376         else
00377         {
00378           dE_R = next_link->E_out( ) - link->E_out( );
00379         }
00380       }
00381 
00382       // insert the base of the triangle
00383       new_link = dd_link(link->E_out() + dE, 0.0);
00384       insert(next_link, new_link);
00385 
00386       // get the width of the triangle
00387       dE = link->E_out( ) + dE - prev_link->E_out( );
00388 
00389       // scale it
00390       link->y /= dE/2;
00391 
00392     }
00393   }
00394 }
00395 // ----------- distrib_base::list_interp -----------------
00396 //! Interpolate this list between left_list and right_list
00397 //! using the equiprobable bins.
00398 void distrib_base::list_interp(double e_in, distrib_base& left_list,
00399   distrib_base& right_list)
00400 {
00401   Warning("","You are using distrib_base::list_interp, not a good idea");
00402   if(!empty())
00403   {
00404     SevereError("distrib_base::list_interp",
00405         "Trying to fill a distrib_base which has already been made");
00406   }
00407   double alpha = (e_in - left_list.E_in())/
00408                  (right_list.E_in() - left_list.E_in());
00409   if((alpha <= 0.0) || (alpha >= 1.0))
00410   {
00411     SevereError("distrib_base::list_interp","Attempt to extrapolate");
00412   }
00413   E_in() = e_in;
00414 
00415   // fill the equiprob able bins
00416   for(int j = 0; j <= num_bins; ++j)
00417   {
00418     equi_prob.push_back( (1.0 - alpha)*left_list.equi_prob[j] +
00419       alpha*right_list.equi_prob[j] );
00420   }
00421   // now make the intermediate list (piecewise constant)
00422   double dp = 1.0/num_bins;
00423   for(int j = 0; j < num_bins; ++j)
00424   {
00425     double dE = equi_prob[j+1] - equi_prob[j];
00426     if(dE == 0.0) SevereError("distrib_base::list_interp"," dE = 0");
00427     dd_link new_link = dd_link(equi_prob[j], dp/dE);
00428     insert(end(), new_link);
00429     new_link.x = equi_prob[j+1];
00430     insert(end(), new_link);
00431   }
00432   widen_jumps();
00433 }
00434 // ----------- distrib_base::check_equiprob -----------------
00435 //! check the accuracy of 2d linear interpolation based on the
00436 //! equally probable bins
00437 bool distrib_base::check_equiprob(distrib_base& left_list,
00438   distrib_base& right_list, double tol_2d)
00439 {
00440   bool is_OK = true;
00441   // Use the maximum energy as a scale for the relative error.
00442   double E_max = equi_prob[num_bins];
00443 
00444   // location of this list relative to the others
00445   double alpha = (E_in() - left_list.E_in())/
00446     (right_list.E_in() - left_list.E_in());
00447   if((alpha <= 0.0) || (alpha >= 1.0))
00448   {
00449     SevereError("distrib_base::check_equiprob","bad interpolation encountered");
00450   }
00451   for(int j = 0; j <= num_bins; ++j)
00452   {
00453     // the average
00454     double average = (1.0 - alpha)*left_list.equi_prob[j] +
00455       alpha*right_list.equi_prob[j];
00456     if(abs(equi_prob[j] - average) > tol_2d*E_max)
00457     {
00458       is_OK = false;
00459     }
00460   }
00461   return is_OK;
00462 }
00463 // ----------- distrib_base::check_interp_2d -----------------
00464 //! This routine was once used to check unit-based interpolation.
00465 //! check the accuracy of 2d linear interpolation
00466 //! We check the values at the links because they were interpolated
00467 //! We also check half way between the links.
00468 //! These checks are to within tol_2d; if they fail, then thicken and
00469 //! thin this list to within tol_1d
00470 //! But values below cut_off_2d times the maximum are ignored
00471 bool distrib_base::check_interp_2d(double tol_2d, double cut_off_2d)
00472 {
00473   SevereError("distrib_base::check_interp_2d",
00474     "Write code in distrib_base for checking unit-based interpolation");
00475   return false;
00476   /*  ***  What follows is some old code ***
00477    *  bool is_OK = true;
00478    *  double noise = 0.0;
00479    *  double E_out;
00480    *  dd_link good_link;
00481    *  dd_list::iterator left_link;
00482    *  dd_list::iterator next_link;
00483 
00484    *  //< set up a one_d_distrib of good values
00485    *  dd_list good_list();
00486 
00487    *  if(debug_on)
00488    *  {
00489    *    cout<< "Interpolated:\n";
00490    *    print();
00491    *  }
00492 
00493    *  //< make the good intermediate list
00494    *  left_link = begin();
00495    *  next_link = left_link;
00496    *  ++next_link;
00497    *  for(; next_link != end(); ++left_link, ++next_link)
00498    *  {
00499    *    // for the link at the node
00500    *    E_out = left_link->E_out();
00501    *    good_link.x = E_out;
00502    *    good_link.y = f(E_out);
00503    *    if(abs(good_link.y) > noise)
00504    *    {
00505    *      noise = good_link.y;
00506    *    }
00507    *    good_list.insert(good_list.end(), good_link);
00508 
00509    *    // for the link in the middle
00510    *    E_out = 0.5*(E_out + next_link->E_out());
00511    *    good_link.x = E_out;
00512    *    good_link.y = f(E_out);
00513    *    if(abs(good_link.y) > noise)
00514    *    {
00515    *      noise = good_link.y;
00516    *    }
00517    *    good_list.insert(good_list.end(), good_link);
00518    *  }
00519 
00520    *  // the last good link
00521    *  E_out = left_link->E_out();
00522    *  good_link.x = E_out;
00523    *  good_link.y = f(E_out);
00524    *  if(abs(good_link.y) > noise)
00525    *  {
00526    *    noise = good_link.y;
00527    *  }
00528    *  good_list.insert(good_list.end(), good_link);
00529 
00530    *  if(debug_on)
00531    *  {
00532    *    cout << "Good intermediate values\n";
00533    *    good_list.print();
00534    *  }
00535 
00536    *  // make the total probability be 1
00537    *  good_list.renorm();
00538 
00539    *  if(debug_on)
00540    *  {
00541    *    cout << "renormed intermediate values\n";
00542    *    good_list.print();
00543    *  }
00544 
00545    *  // set the level below which accuracy is ignored
00546    *  noise *= cut_off_2d;
00547 
00548    *  // check the midpoint values
00549    *  dd_list::iterator good_ptr = good_list.begin();
00550    *  left_link = begin();
00551    *  next_link = left_link;
00552    *  ++next_link;
00553    *  for( ; next_link != end();
00554    *    ++left_link, ++next_link, ++good_ptr)
00555    *  {
00556    *    // test the left link
00557    *    if((abs(good_ptr->y - left_link->y) > tol_2d*good_ptr->y) &&
00558    *       (abs(good_ptr->y) >= noise))
00559    *    {
00560    *      is_OK = false;
00561    *    }
00562 
00563    *    // go on to the middle values
00564    *    ++good_ptr;
00565 
00566    *    // are we still OK?
00567    *    is_OK &= check_interp(left_link, good_ptr,
00568    *      next_link, tol_2d, noise);
00569    *  }
00570    *  // test the last link
00571    *  is_OK &= (abs(good_ptr->y - left_link->y) <=
00572    *               tol_2d*abs(good_ptr->y)) ||
00573    *           (abs(good_ptr->y) <= noise);
00574 
00575    *  if(!is_OK)
00576    *  {
00577    *    // get the unnormalized values again
00578    *    for(left_link = begin(); left_link != end();
00579    *      ++left_link)
00580    *    {
00581    *      left_link->y = f(left_link->E_out());
00582    *    }
00583    *    thicken();
00584    *    //    cout << "mid_list:" << endl;
00585    *    //    print();
00586    *    thinit();
00587    *    renorm();
00588    *  }
00589    *  return is_OK;
00590    */
00591 }

Generated on Thu Sep 7 10:30:03 2006 for fete -- From ENDFB6 To ENDL by doxygen 1.3.4