Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

makeikk.annotated.cc

Go to the documentation of this file.
00001 // makeikk.cc
00002 // A simple program to generate the Kramers-Kronig transform of a normalized
00003 // SIS DC IV curve, such as generated by makeiv.cc. It gives good results
00004 // most of the time; Examine a plot of the output to ensure that there are
00005 // no discontinuities introduced by numerical artifacts.
00006 //
00007 // An input file name must be included on the command line;
00008 // the output will be written to the standard output stream.
00009 
00010 #include "supermix.h"
00011 
00012 // integrators and adaptive interpolation building are not included by
00013 // supermix.h, so we must load in the special numerical headers as well.
00014 #include "numerical.h"
00015 
00016 // The comment prefix string to be used in the output header:
00017 const char *comment = "#";
00018 
00019 // ---------------------------------------------------------------------------
00020 // Some parameters you can adjust to control the integration (see integrate.h):
00021 
00022 unsigned   ORDER   = 3;        // the order of the Rhomberg extrapolation (min 1)
00023 double     ABS_TOL = 1.0e-9;   // absolute error tolerance
00024 double     REL_TOL = 1.0e-10;  // relative error tolerance
00025 
00026 // Lowering the tolerances to ~ 1.0e-7 may generate the results more quickly, but
00027 // will only work well if the DC IV curve is not very sharp at the gap. Lowering
00028 // ORDER to 1 may help, but often will not.  If you run into convergence
00029 // problems, this condition will usually be indicated by an error message like
00030 // "WARNING: Recursion limit reached in adaptive interpolator build."
00031 // Ensure that you carefully examine the output in this case; if the input IV
00032 // curve is very sharp, the output may be just fine, even though the warning was
00033 // output. 
00034 // If you do have convergence problems, try reducing ORDER and tightening the
00035 // error tolerances.
00036 
00037 // ---------------------------------------------------------------------------
00038 // Here are the global variables and functions we use to generate the transform:
00039 
00040 // real_interp (defined in real_interp.h) is just an interpolator<double> with
00041 // some useful additional capabilities, like loading its data from a file.
00042 // See makeiv.cc for an introduction to interpolators.
00043 
00044 real_interp IDC;  // will interpolate the normalized DC IV file data 
00045 double Vo;        // will hold the maximum voltage point in IDC table
00046 
00047 
00048 // The next function properly extends the interpolated DC IV data to V < 0. 
00049 // Therefore we can use IDC to get idc(v) for all v. Note how we
00050 // ask the interpolator object for an interpolated value: we simply
00051 // use it like a function which takes a single argument of type double,
00052 // so IDC(v) returns the interpolated current at the voltage v. 
00053 
00054 double idc(double v) { return (v < 0.0) ? -IDC(-v) : IDC(v); }
00055 
00056 
00057 // Now we define the integrand for the Kramers-Kronig calculation.
00058 // This "function" F is really just a type name for a generalized "function
00059 // object", or functional, which has the function operator () defined on it.
00060 // Once we create an object of type F we can use it just like any
00061 // traditional function by appending an argument surrounded by parentheses.
00062 // This is like the interpolator object IDC, which is also a functional.
00063 
00064 struct F {
00065   double x;
00066   F(double xx): x(xx) { }  // supply an initial value for x in the constructor 
00067 
00068   // here's the definition of the () operator which allows F objects to
00069   // mimic the behavior of a function:
00070   double operator()(double v) const
00071     { return (idc(v+x) + idc(v-x) - 2*idc(v))/v; }
00072 };
00073 
00074 // Note: a struct is just a class with all members defaulting to public
00075 // accessibility.
00076 
00077 
00078 // The Kramers-Kronig transform requires a Cauchy Principal Value integration
00079 // of the operand defined by the functional type F. Here's where we perform
00080 // that operation.
00081 //
00082 // First we need an integrator object. Intergrators are objects of a templated
00083 // class defined in integrate.h. When we declare an integrator we must supply
00084 // a template argument which identifies the type of the integrand. The variable
00085 // of integration is always of type double. Integrator objects are functionals
00086 // whose argument list will include the integrand and limits of integration.
00087  
00088 integrator<double> In;  // In is an integrator object which returns a double.
00089 
00090 
00091 // The following function uses the integrator to calculate the Kramers-Kronig
00092 // transform of a voltage x.
00093 
00094 double ikk_f(double x)
00095 {
00096   F f(x);
00097   return (In.open()(f, 0, Vo/3) + In.exp()(f, Vo/3, 1000))/Pi;
00098 }
00099 
00100 // Note a couple of interesting things about the above definition
00101 // which show the power of the C++ functional concept:
00102 //
00103 // The integrator member functions open() and exp() simply set the method used
00104 // to calculate the integral; they return the integrator object itself, so
00105 // "In.open()(...)" works just like calling "In(...)".
00106 //
00107 // About the "f" in the argument list to the integrator call: "F f(x)" constructs
00108 // an object of type F, which is passed as an argument to the integrator.
00109 // The integrator will call this argument using operator (), executing the
00110 // command "f(v)", returning: (idc(v+x) + idc(v-x) - 2*idc(v))/v.
00111 //
00112 // Now we see why we needed the functional F rather than a traditional function.
00113 // The integrator only expects integrands which are functions of the single
00114 // variable of integration; By creating an object of type F we can pass the 2nd
00115 // variable x as a parameter at construction, and the integrator doesn't
00116 // notice.
00117 //
00118 // Finally, "Pi" is a constant defined in global.h.
00119 
00120 
00121 // ---------------------------------------------------------------------------
00122 // the main routine:
00123 
00124 int main(int argc, char **argv) 
00125 {
00126   // Check that a filename was included on the command line; if not,
00127   // generate a usage prompt and exit:
00128   if(argc < 2) {
00129     cerr << "Usage: " << argv[0] << " <idc_file>" << endl;
00130     return 1;
00131   }
00132 
00133   // fetch the file name for the normalized DC IV curve:
00134   const char * name = argv[1];
00135 
00136 
00137   // Now we can set up the DC IV interpolation. file(), defined in real_interp.h,
00138   // loads in the data; quiet() silences complaints about extrapolations.
00139   // See makeiv.cc for a discussion of x() and size().
00140 
00141   IDC.file(name).quiet();
00142   Vo = IDC.x(IDC.size()-1);     // max voltage in the table
00143 
00144   // Set the integrator control parameters:
00145   In.order = ORDER;
00146   In.abs_tolerance  = ABS_TOL;
00147   In.rel_tolerance = REL_TOL;
00148 
00149   // We'll create another interpolator to hold the calculated Kramers-Kronig
00150   // values; this way we can use the adaptive fill algorithm to limit the
00151   // number of points we need to calculate. See makeiv.cc for a description
00152   // of how this works.
00153 
00154   interpolation ikk;  // the same as type interpolator<double> 
00155   ikk.left_slope(0);  // the endpoint slope of the transform for V == 0.
00156 
00157   // Don't set accuracies any tighter in the following, or you may see
00158   // convergence problems. Note that we generate the transform values
00159   // out to voltages double those in the DC IV data set.
00160 
00161   adaptive_fill(ikk, ikk_f, 0, 2*Vo, 1e-6, 1e-3);
00162 
00163   // We're all done, except for displaying the results.
00164 
00165   // Set the output number format:
00166   cout << fixed << setprecision(10);
00167 
00168   // Let's print a header recording the DC IV file name used. Note that
00169   // we prefix the comment identifier to each header line. 
00170   cout << comment << " Normalized SIS Kramers-Kronig IV characteristic curve" << endl;
00171   cout << comment << " The DC IV file for this data:  " << name << endl;
00172   cout << comment << endl;
00173   cout << comment << " V:         " << "\t" << "I:" << endl;
00174 
00175   // output the points actually used in the ikk interpolator:
00176   for (unsigned i = 0; i < ikk.size(); ++i)
00177     cout << ikk.x(i) << "\t" << ikk[i] << endl;
00178 
00179   return 0;
00180 }

Please direct comments and corrections to supermix@submm.caltech.edu
Go to the supermix home page
Generated by doxygen1.2.7