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

ckdata.cc

Go to the documentation of this file.
00001 // ckdata.cc
00002 // SuperMix version 1.0  C++ source file
00003 //
00004 // Copyright (c) 1999 California Institute of Technology.
00005 // All rights reserved.
00006 //
00007 // Redistribution and use in source and binary forms for noncommercial
00008 // purposes are permitted provided that the above copyright notice and
00009 // this paragraph are duplicated in all such forms and that any
00010 // documentation and other materials related to such distribution and
00011 // use acknowledge that the software was developed by California
00012 // Institute of Technology. Redistribution and/or use in source or
00013 // binary forms is not permitted for any commercial purpose. Use of
00014 // this software does not include a permitted use of the Institute's
00015 // name or trademark for any purpose.
00016 //
00017 // DISCLAIMER:
00018 // THIS SOFTWARE AND/OR RELATED MATERIALS ARE PROVIDED "AS-IS" WITHOUT
00019 // WARRANTY OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR
00020 // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE OR PURPOSE (AS SET
00021 // FORTH IN UCC 23212-2313) OR FOR ANY PURPOSE WHATSOEVER, FOR THE
00022 // LICENSED PRODUCT, HOWEVER USED.  IN NO EVENT SHALL CALTECH/JPL BE
00023 // LIABLE FOR ANY DAMAGES AND/OR COSTS, INCLUDING BUT NOT LIMITED TO
00024 // INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING ECONOMIC
00025 // DAMAGE OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF
00026 // WHETHER CALTECH/JPL SHALL BE ADVISED, HAVE REASON TO KNOW, OR IN
00027 // FACT SHALL KNOW OF THE POSSIBILITY.  THE USER BEARS ALL RISK
00028 // RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND/OR RELATED
00029 // MATERIALS.
00030 //
00031 // Change history:
00032 // 1/12/00:  Changed calling arguments to ckdata::calc(), ckdata::fillA()
00033 // 12/28/99: Fixed a terrible indexing bug in convC()
00034 // 11/11/98: Changed table access to new syntax
00035 // 7/28/98:  added <math.h> to includes, since SIScmplx.h doesn't
00036 // 7/7/98:   changed sisdevice.h to junction.h
00037 // 6/30/98:  changed ckdata.h to sisdevice.h
00038 
00039 #include "junction.h"
00040 #include "units.h"
00041 #include <math.h>
00042 
00043 // ZEROTOL is the accuracy of the calculations in ckdata
00044 
00045 #ifndef ZEROTOL   /* should be defined in global.h */
00046 #define ZEROTOL (1.0e-6)
00047 #endif
00048 
00049 
00050 // The private variable bessel_values is used as follows:
00051 // bessel_values[0] will hold bessel function values calculated by
00052 // bessels().
00053 // bessel_values[1] will hold the derivatives of the bessel fcns.
00054 // Its size is set by find_max_bessel_n()
00055 
00056 // find_max_bessel_n() will calculate the proper size of bessel_values so
00057 // that all functions with values bigger than ZEROTOL are returned.
00058 // The argument x must be nonnegative; returns: bessel_values.Rmaxindex()
00059 
00060 int ckdata::find_max_bessel_n(const double x)
00061 {
00062   int nmax;  // the maximum order of |Jn(x)| > ZEROTOL
00063   if ( x < ZEROTOL ) {       // for x near zero, J1(x) ~= x/2.0
00064     nmax = 0;
00065   }
00066   else if ( x < 2*sqrt(ZEROTOL) ) { // in this range, J2(x)~=x*x/8.0
00067     nmax = 1;
00068   }
00069   // the following formulas need modification if ZEROTOL != 1.0e-6
00070   else if ( x < 10.0 ) {    // a quadratic fit in this range
00071     nmax = int( 2.165 + 4.678 * sqrt(x) + 0.5786 * x );
00072   }
00073   else {                     // for |x| > 10, a linear fit suffices
00074     nmax = int( 10.0 + 1.25 * x );
00075   }
00076 
00077   // now reallocate bessel_values if necessary, and return
00078   if (nmax > bessel_values.Rmaxindex(nmax))
00079     bessel_values.resize(1,nmax);  // left index range should be {0,1}
00080   return bessel_values.Rmaxindex();
00081 }
00082 
00083 
00084 // bessel() fills bessel_values with Jn(x) and Jn'(x).
00085 // The argument must be nonnegative; returns: bessel_values.Rmaxindex()
00086 //
00087 // Uses the recursion formulas:
00088 //   Jj-1(x) = (2 j/x)*Jj(x) - Jj+1(x)
00089 //   Jj'(x)  = (Jj-1 - Jj+1) / 2
00090 //
00091 // And the Normalization identity:
00092 //   1 = Jo(x) + 2*( J2(x) + J4(x) + J6(x) + ...)
00093 //
00094 // Loosely based on "Numerical Recipes" backwards recursion algorithm.
00095 // See FR notebook, pg 80 for info re derivative calculation
00096 
00097 int ckdata::bessel(const double x)
00098 {
00099   int n,m;             // Jn is max returned order, Jm starts the recursion
00100   double *J, *Jprime;  // will alias the rows in bessel_values
00101 
00102   // size n, m, and bessel_values (using find_max_bessel_n())
00103   n = find_max_bessel_n(x); // Jn, Jn' will be the highest in answers
00104   m = 2 * int(1 + (sqrt( double(n) ) + n)/2);  // an even value for m
00105   J = bessel_values[0];      // could change with each call to
00106   Jprime = bessel_values[1]; // find_max_bessel_n(), because of resize()
00107 
00108   // handle x == 0.0
00109   if ( x == 0.0 ) {
00110     J[0] = 1.0; Jprime[0] = 0.0;   // only Jo and Jo' will be returned
00111     return n;
00112   }
00113 
00114   // x != 0 from here on. Prepare for the backwards recursion loop
00115   double Jj, Jjm, Jjp;  // terms in the recursion: Jj(x), Jj-1(x), Jj+1(x)
00116 
00117   double sum = 0.0;        // the summation used for normalization
00118   int sum_flag = FALSE;    // a boolean toggle for including terms in sum
00119 
00120   const double Limit = 1.0e100;  // don't let unnormalized results get
00121   const double Scale = 1.0e-100; // too big (unlikely). Scale = 1/Limit
00122 
00123   double mult = 2.0 / x;        // x != 0.0
00124   
00125   // the backwards recursion loop
00126   int j;
00127   for ( j = m, Jj = 1.0, Jjp = 0.0; j > 0; --j ) { // remember, m is even
00128 
00129     Jjm = j*mult*Jj - Jjp; // the recursive calculation
00130 
00131     // Save results for Jj and Jj':
00132     if ( j <= n ) {   
00133       J[j] = Jj;
00134       Jprime[j] = (Jjm - Jjp) / 2.0;
00135     }
00136 
00137     // Add the results for j-1 even into normalization sum:
00138     if ( sum_flag == TRUE )
00139       sum += Jjm;
00140     sum_flag = ! sum_flag; // for next trip thru loop
00141 
00142     // Rescale values if results are getting too big:
00143     if ( fabs(Jjm) > Limit ) {
00144       Jjm *= Scale; Jj *= Scale; sum *= Scale;
00145       bessel_values *= Scale;
00146     }
00147 
00148     // step the terms down one
00149     Jjp = Jj; Jj = Jjm;   
00150 
00151   } // end main recursion loop
00152 
00153   // now j = 0; and Jj = Jo(ax), Jjp = J1(ax) (unnormallized)
00154   J[0] = Jj;
00155   Jprime[0] = -Jjp;  // Jo'(x) = - J1(x)
00156 
00157   // normalize the answer
00158   sum = 2.0*sum - Jj;      // now sum = normalization formula
00159   bessel_values /= sum;
00160 
00161   // return the max order calculated
00162   return n;
00163 
00164 } // bessel()
00165   
00166 
00167 // Amj_values will hold the complex Amj coefficients derived from bessel
00168 // function calculations and the phases of the large-signal harmonic
00169 // voltages.  It is sized and filled by fillA().  Only values for nonnegative
00170 // m are in this vector; use Amj(-m) = (-1)^m * conj(Amj(m)) for m < 0.
00171 // See FR notebook, pg 34 for formulas used.
00172 //
00173 // fillA calculates the set of complex Amj = Am(Vj) using:
00174 //   Am(Vj) = Jm(alpha_j) * exp(-I*m*phase(Vj))
00175 //
00176 // Its argument is the peak voltage Vj divided by the photon voltage for
00177 // harmonic j, ie: a complex version of the pumping factor alpha at harmonic j. 
00178 // It returns Amj_values.maxindex(). 
00179 
00180 int ckdata::fillA(const Complex a)    // a is the complex version of alpha, ie: V/Vphoton
00181 {
00182   double  alpha = abs(a);
00183   complex unit  = (alpha == 0.0) ? complex(1.0) : conj(a/alpha);  // unit = exp(-I*arg(a))
00184   int     max   = bessel(alpha);  // calc the bessel fcns
00185 
00186   // now resize Amj_values if required
00187   if (max > Amj_values.maxindex(max))  // then Amj_values is too small
00188     Amj_values.resize(max);
00189  
00190   // lastly fill in the Amj_values
00191   max = Amj_values.maxindex();   // merely a safety precaution ico alloc problem
00192   int m; Complex phase;          // phase will be exp(-I*m*arg(a))
00193   double *J = bessel_values[0];  // the vector of bessel functions
00194   for ( m = 0, phase = 1.0; m <= max; ++m, phase *= unit )
00195     Amj_values[m] = J[m] * phase;
00196   
00197   return max;
00198 }
00199 
00200 
00201 // The public variable Ck is a symmetrically-indexed vector which holds the SIS
00202 // junction large-signal convolution constants (Ck) for +/- values of k.  The
00203 // public variable Tol is approximately the magnitude of the smallest nonzero
00204 // value in Ck.  It is equal to the #define'd value ZEROTOL in global.h, which
00205 // is used by calc() to limit the depth of its calculations, saving time and
00206 // memory.
00207 
00208 // calc() generates the large-signal harmonic voltage convolution
00209 // constants Ck, given a vector of voltages at each harmonic, the
00210 // Local Oscillator frequency, and the gap voltage of the SIS junction.
00211 // Both fLO and VGap must be > 0.0, or calc() will empty Ck.
00212 // calc() also sets Tol to the value of ZEROTOL.
00213 // See FR notebook, pg 65 for derivations of index limits used in calc().
00214 
00215 // this is a helper fcn for calc(); definition follows calc()
00216 static void convC(const int, const int, Matrix &, Vector &);
00217 
00218 ckdata & ckdata::calc(
00219                       const double   fLO,  // The Large-Signal (LO) frequency
00220                       const Vector & V     // The Large-Signal (LO) harmonic voltages
00221                       )
00222 {
00223   const int INITSIZE = 20;   // for temporary table alloc
00224   static Matrix C(2, INITSIZE, Index_C, Index_S);  // will hold intermediate results
00225   C.fillall(0.0);
00226   int curr = 0;   // will be either 0 or 1: picks a row of C
00227   int cmax, pmax; // like individual Rmaxindex() for current and previous C result
00228   int harms = V.maxindex();  // number of harmonics
00229   
00230   // initialize C[curr] for the first iteration
00231   C[curr][0] = 1.0;
00232   cmax = C.Rmaxindex(0);  // note that elements of C beyond cmax are all 0 here
00233   
00234   // loop through V values, convolving C with the previous result
00235   int amax;
00236   double scale = RmsToPeak*VoltToFreq / fLO; // convert Vj to alpha_j
00237 
00238   for ( int j = 1; j <= harms; ++j ) {
00239     // at this point in the loop, all C elements beyond cmax == C.Rmaxindex() are 0
00240     // this will be a loop constant
00241     // if Vj is zero, the Ck don't change
00242     if (V[j] == 0.0) continue;
00243 
00244     // Vj is nonzero here
00245     curr = !curr; pmax = cmax;  // step to other row of C
00246     amax = fillA(scale * V[j] / j);
00247     // now adjust size of C
00248     cmax += (j * amax);  // the new index range for nonzero Ck
00249     if(cmax > C.Rmaxindex(cmax))  // then the capacity of C is too small
00250       // grow capacity of C, plus some extra
00251       cmax = C.resize(1,cmax+INITSIZE).Rmaxindex(cmax);
00252 
00253     // now the previous row, C[!curr], is zero for all elements beyond pmax
00254     // and the size of C is at least cmax, so we can use raw data access for speed
00255     // and C.Rmaxindex() == cmax, so loop constant is still true.
00256 
00257     // do the convolution sum
00258     // this is written as a separate function to support future expandability for
00259     // the harmonic balance routine
00260     convC(j, curr, C, Amj_values);
00261   }
00262   
00263   // C[curr] has the results
00264   Ck = row(curr, C).shrink(ZEROTOL);  //this will reallocate Ck to be big enough
00265   Tol = ZEROTOL;
00266   return *this;
00267 }
00268 
00269 
00270 // convC() calculates the Ck(j) given the Ck(j-1) and the Amj, using the
00271 // formula:
00272 //
00273 // Ck(j) = Sum(m = -oo to m = oo)( C(k-j*m)(j-1) * Amj )
00274 //
00275 // Since we only have Amj for m >= 0, we calculate Amj for m < 0 using:
00276 //
00277 // A(-m)j = ((-1)^m) * conj(Amj)
00278 //
00279 // See FR notebook, pg 32 for formulas used.
00280 //
00281 // Also, since we know the real limits for nonzero Ck(j-1) and for nonzero Amj,
00282 // before calling convC(), C.Rmaxindex() must be set to the limit for nonzero
00283 // Ck(j), which is limit[Ck(j-1)] + j * limit[Amj].  The values stored for Ck(j-1)
00284 // within this new limit but beyond limit[Ck(j-1)] must be all zeroes or convC will
00285 // give the wrong answer.  Also, you must have Amj.maxindex() == limit[Amj], or
00286 // again the answer will be wrong.  ckdata::calc() ensures these conditions are
00287 // met before calling convC(). 
00288 
00289 static void convC(const int j,     // the harmonic number
00290                   const int curr,  // the row of C which will hold the results
00291                   Matrix & C,      // convC will write into the row C[curr]
00292                   Vector & Amj     // the vector of Amj values for harmonic j
00293                   )
00294 {
00295   int k, kmax;         // loop index into C and its limit
00296   int m, amax;         // another loop index (into Amj) and limit
00297   int sign;            // holds +/- 1 for the A(-m)j
00298 
00299   kmax = C.Rmaxindex();
00300   amax = Amj.maxindex();
00301   for ( k = -kmax; k <= kmax; ++k ) {
00302     Complex *pCk = & C[curr][k];  // only do this indexing calculation once
00303     *pCk = C[!curr][k] * Amj[0];
00304     for ( m = 1, sign = -1; m <= amax; ++m, sign *= -1 )
00305       *pCk += C.read(!curr, k - j*m) * Amj[m]                 // term for +m
00306            +  C.read(!curr, k + j*m) * sign * conj(Amj[m]);   // term for -m
00307   }
00308 }
00309 

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