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
1.2.7