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
1.2.7