00001 // sisdevice.cc 00002 // SuperMix version 1.0a 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 // 6/12/00: Added a constructor; check for uninitialized iv curve 00033 // 4/29/00: Changed _Vn to Vn_, etc., part of a Linux bug fix; 00034 // changed logic for Vn == Vn_ comparisons, etc., as well 00035 // 3/9/00: Fixed behavior of noise() for certain arguments near 0 00036 // 1/12/00: Changed calling arguments to sis_basic_device::C.calc() 00037 // 12/28/99: Removed tosymm(), fromsymm() 00038 // 4/15/98: Changed to use new ivcurve syntax 00039 // 11/11/98: Changed table access to new syntax 00040 // 11/10/98: Minor changes to support new vector accessing 00041 // 8/19/98: Fixed another bug in noise(): the coth(0)*Idc(0) calculation 00042 // 8/18/98: Fixed a bug in noise(), took out 1/2 factor in normalization 00043 // 7/16/98: Added RMS correction of 1/2 to Hmn; major bug fixes 00044 // 7/15/98: Changed junction state vectors to Index_C vice Index_S 00045 // 7/14/98: Many corrections to the code 00046 // 7/10/98: Now it can handle IF freq == 0 00047 // 7/10/98: Characteristics are now parameters vice doubles 00048 // 7/8/98: updated to incorporate interface changed in junction 00049 // 7/7/98: fixed the sign error in capacitor code for large_signal() 00050 // 7/2/98: small_signal() can't handle IF freq == 0 case yet. 00051 00052 #include "sisdevice.h" 00053 #include "error.h" 00054 #include "units.h" 00055 #include <math.h> // for double tanh() 00056 00057 static const double deps = 1.0e-6; // normalized voltage close to 0.0 00058 00059 // -------------------------------------------------------------------- 00060 // The constructor sets up things so that large_signal() will throw an 00061 // error if all set-up is incomplete. 00062 00063 sis_basic_device::sis_basic_device() 00064 : Vn(0), Rn(0), Cap(0), piv(0), iv_data_ok(0) 00065 { } 00066 00067 00068 // -------------------------------------------------------------------- 00069 // The large-signal analysis routine calculates a vector of currents 00070 // from the supplied vector of voltages. Only currents with frequencies 00071 // <= max_harmonics * fLO will be returned. Calculates the convolution 00072 // constants Ck, then convolves them using the SIS IV curve to get the 00073 // currents. Many of sis_basic_device's private variables are set by this 00074 // function. 00075 00076 const Vector & sis_basic_device::large_signal( 00077 const Vector & V, // the vector of input voltages 00078 double fLO, // the LO frequency 00079 int max_harmonics ) // the max number of harmonics 00080 { 00081 if (piv == 0) 00082 error::fatal("Uninitialized sis iv curve reference."); 00083 00084 if (Vn <= 0 || Rn <= 0 || Cap < 0) // Cap == 0 is o.k. 00085 error::fatal("Bad or uninitialized sis junction parameters."); 00086 00087 if (fLO <= 0.0 || max_harmonics < 0) 00088 error::fatal("Invalid argument in call to junction::large_signal()."); 00089 00090 // If we get here, everything is ready to do the calculation: 00091 00092 Voltages = V; 00093 LO_freq = fLO; 00094 Vn_ = Vn; Rn_ = Rn; Cap_ = Cap; // record parameter values 00095 C.calc(fLO, V); // now the ckdata, C.Ck, are calculated 00096 00097 double V0 = V.read(0).real/Vn_; // the normalized bias voltage 00098 double VLO = fLO / (Vn_ * VoltToFreq); // normalized LO photon voltage 00099 int limit = C.Ck.maxindex(); // loop limit, used below 00100 00101 // Fill the table of -j/Rn * ivcurve::I(V0 + n*VLO) and derivatives 00102 I_VLO.reallocate(limit,Index_S); 00103 I_VLO_prime.reallocate(limit,Index_S); 00104 { // limit scope of next lines 00105 complex current, current_p; 00106 register complex I_norm(0.0,-1/Rn_); // = -J/Rn 00107 for(register int k = -limit; k <= limit; ++k) { 00108 piv->Iprime(V0 + k*VLO, current, current_p); 00109 I_VLO[k] = current * I_norm; 00110 I_VLO_prime[k] = current_p * I_norm; 00111 } // release register variable 00112 } 00113 00114 // Convolve the Ck to get the harmonic currents: 00115 // See FR notebook, pg 104 for formulas 00116 Currents.reallocate(max_harmonics+1,Index_C).fill(0.0); 00117 for(register int k = -limit; k <= limit; ++k) { 00118 register complex f = C.Ck[k]; complex Ik = I_VLO[k]; 00119 Currents[0].real += norm(f) * Ik.real; // C[k]*conj(C[k])*im(I(V0+kVLO))/Rn 00120 f *= Ik; // now f = -j*C[k]*I(V0 + k VLO)/Rn 00121 for(register int p = 1; p <= max_harmonics; ++p) 00122 // I = I(p) + conj(I(-p)) , for I with nonnegative harmonic indexing: 00123 Currents[p] += f * conj(C.Ck.read(k+p)) + conj(f) * C.Ck.read(k-p); 00124 } 00125 00126 // finally convert normalized currents to real RMS (already included 1/Rn above) 00127 Currents *= Vn_/RmsToPeak; 00128 Currents[0] *= RmsToPeak; // don't RMS correct DC current 00129 00130 // Capacitor effects (FR notebook, pg 106): 00131 complex Xlo(0.0, 2*Pi*fLO*Cap_); // imaginary admittance at LO freq 00132 limit = (Currents.maxindex() < V.maxindex()) ? 00133 Currents.maxindex() : V.maxindex(); 00134 for(register int k = 1; k <= limit; ++k) 00135 // current thru capacitor at DC vanishes 00136 Currents[k] += k * Xlo * V[k]; // capacitor current 00137 00138 IF_freq = 0.0; // this tells small_signal() and noise() to rebuild I_pVIF, I_mVIF 00139 iv_data_ok = 1; 00140 return Currents; 00141 } // large_signal() 00142 00143 00144 // -------------------------------------------------------------------- 00145 // The small-signal analysis routine calculates a symmetrical harmonic 00146 // admittance matrix using several private variables set by the large 00147 // signal analysis; it also may fill IF_freq, I_pVIF, and I_mVIF, for 00148 // later use by noise(); conversely, it will use values set by noise(). 00149 // large_signal() must be called at least once before calls to this 00150 // routine, but then many calls may be made to this routine as long as 00151 // the balance remains valid. 00152 00153 const Matrix & sis_basic_device::small_signal( 00154 double fIF, // the IF frequency 00155 int max_harmonics ) // the max number of harmonics 00156 { 00157 if (fabs(Vn - Vn_) > deps*mVolt || 00158 fabs(Rn - Rn_) > deps*Ohm || 00159 fabs(Cap - Cap_) > deps*fFarad) 00160 error::fatal("SIS junction parameters changed since last large_signal() call."); 00161 00162 if (fIF < 0.0 || max_harmonics < 0 || fIF >= LO_freq) 00163 error::fatal("Invalid argument in call to junction::small_signal()."); 00164 00165 // If we get here, everything is ready to do the calculation: 00166 00167 double Vif = fIF/(Vn_ * VoltToFreq); // normalized IF photon voltage 00168 double VLO = LO_freq/(Vn_ * VoltToFreq); // normalized LO photon voltage 00169 int limit = C.Ck.maxindex()+max_harmonics; // required range of I(V) data 00170 00171 // fill in the ivcurve lookup values, if necessary, and calculate Vif: 00172 if (IF_freq == 0.0 || IF_freq != fIF || limit > I_pVIF.maxindex()) { 00173 IF_freq = fIF; 00174 00175 // Fill the tables of -j/Rn * ivcurve::I(V0 + n*VLO +/- Vif) 00176 I_pVIF.reallocate(limit, Index_S); 00177 I_mVIF.reallocate(limit, Index_S); 00178 register double V0 = Voltages[0].real/Vn_; // the normalized bias voltage 00179 complex I_norm(0.0,-1/Rn_); // -J/Rn 00180 register double Vl = VLO; // keep value of VLO in a register 00181 if (Vif != 0.0) { 00182 register double Vi = Vif; 00183 for(register int k = -limit; k <= limit; ++k) { 00184 I_pVIF[k] = I_norm * (*piv)((V0 + k*Vl) + Vi); 00185 I_mVIF[k] = I_norm * (*piv)((V0 + k*Vl) - Vi); 00186 } 00187 } 00188 else { // (Vif == 0.0) 00189 for(register int k = -limit; k <= limit; ++k) { 00190 I_pVIF[k] = I_norm * (*piv)(V0 + k*Vl); 00191 I_mVIF[k] = I_pVIF[k]; 00192 } 00193 } 00194 } 00195 00196 // Convolve the Ck and the ivcurve to get the Ymn: 00197 // Formulas used are in pp following pg 102 in FR notebook. Note that the local 00198 // I(V) lookup tables already include a factor -j/Rn. This affects the conj(I), 00199 // since -j*conj(I) = conj(j*I) = -conj(-j*I); so all conj(I) terms change sign. 00200 00201 Y.reallocate(max_harmonics,max_harmonics,Index_S,Index_S).fill(0.0); 00202 limit = C.Ck.maxindex(); // reusing the variable defined above 00203 00204 if (fIF != 0.0) { 00205 // Normal small signal analysis condition 00206 double Vi2 = 1/(2*Vif); // used inside the loops 00207 for(register int k = -limit; k <= limit; ++k) { 00208 complex Ck = C.Ck[k]; // C(k) 00209 complex Ik = I_VLO[k]; // -j/Rn I(V0 + kVLO) 00210 complex Ikmif = I_mVIF[k]; // -j/Rn I(V0 + kVLO - Vif) 00211 complex Ikpif = conj(I_pVIF[k]); // conj(-j/Rn I(V0 + kVLO + Vif)) 00212 Y[0][0] += (norm(Ck)*Vi2)*(Ik - Ikmif + Ikpif - conj(Ik)); 00213 00214 complex Co; // will be C(k)C(k+m-n)* in loops below 00215 for(register int n = 1; n <= max_harmonics; ++n) { 00216 register int kpn = k+n, kmn = k-n; 00217 register double fp = 1/(2*(Vif+n*VLO)), fm = 1/(2*(Vif-n*VLO)); 00218 complex Ikpn = Ik - I_mVIF[kpn]; // will be used in loop over m 00219 complex Ikmn = Ik - I_mVIF[kmn]; 00220 if (kpn <= limit) { // so C(k+n) is nonzero 00221 Co = Ck * conj(C.Ck[kpn]); 00222 complex Itemp = conj(I_VLO[kpn]); 00223 Y[0][-n] += (fm*Co)*(Ikpn + Ikpif - Itemp); 00224 Y[n][0] += (Vi2*Co)*(Ik - Ikmif + conj(I_pVIF[kpn]) - Itemp); 00225 } 00226 if (kmn >= -limit) { // so C(k-n) is nonzero 00227 Co = Ck * conj(C.Ck[kmn]); 00228 complex Itemp = conj(I_VLO[kmn]); 00229 Y[0][n] += (fp*Co)*(Ikmn + Ikpif - Itemp); 00230 Y[-n][0] += (Vi2*Co)*(Ik - Ikmif + conj(I_pVIF[kmn]) - Itemp); 00231 } 00232 00233 for(register int m = 1; m <= max_harmonics; ++m) { 00234 register int kpm = k+m, kmm = k-m; 00235 complex Ikpm = conj(I_pVIF[kpm]); 00236 complex Ikmm = conj(I_pVIF[kmm]); 00237 register int kk = k+m-n; // +m,+n 00238 if((kk <= limit)&&(kk >= -limit)) { //C(k+m-n) nonzero 00239 Co = Ck * conj(C.Ck[kk]); 00240 Y[m][n] += (fp*Co)*(Ikmn + Ikpm - conj(I_VLO[kk])); 00241 } 00242 kk = k+m+n; // +m,-n 00243 if((kk <= limit)&&(kk >= -limit)) { //C(k+m+n) nonzero 00244 Co = Ck * conj(C.Ck[kk]); 00245 Y[m][-n] += (fm*Co)*(Ikpn + Ikpm - conj(I_VLO[kk])); 00246 } 00247 kk = k-m-n; // -m,+n 00248 if((kk <= limit)&&(kk >= -limit)) { //C(k-m-n) nonzero 00249 Co = Ck * conj(C.Ck[kk]); 00250 Y[-m][n] += (fp*Co)*(Ikmn + Ikmm - conj(I_VLO[kk])); 00251 } 00252 kk = k-m+n; // -m,-n 00253 if((kk <= limit)&&(kk >= -limit)) { //C(k-m+n) nonzero 00254 Co = Ck * conj(C.Ck[kk]); 00255 Y[-m][-n] += (fm*Co)*(Ikpn + Ikmm - conj(I_VLO[kk])); 00256 } 00257 00258 }}} // for m,n,k loops 00259 } 00260 00261 else { 00262 // fIF = 0; we need to look at derivatives of the I(V) curve (notes, pp 110-113) 00263 for(register int k = -limit; k <= limit; ++k) { 00264 complex Ck = C.Ck[k]; // C(k) 00265 complex Ik = I_VLO[k]; // -j/Rn I(V0 + kVLO) 00266 complex Ikprime = I_VLO_prime[k]; // -j/Rn I'(V0 + kVLO) 00267 Y[0][0] += norm(Ck) * Ikprime.real; // |Ck|^2 * Idc' 00268 00269 complex Co; // will be C(k)C(k+m-n)* in loops below 00270 for(register int n = 1; n <= max_harmonics; ++n) { 00271 register int kpn = k+n, kmn = k-n; 00272 register double f = 1/(2*n*VLO); 00273 complex Ikpn = Ik - I_mVIF[kpn]; // will be used in loop over m 00274 complex Ikmn = Ik - I_mVIF[kmn]; 00275 if(kpn <= limit) { // C(k+n) is nonzero 00276 Co = Ck*conj(C.Ck[kpn]); 00277 Y[n][0] += (0.5/RmsToPeak)*Co*(Ikprime + conj(I_VLO_prime[kpn])); 00278 Y[0][-n] -= Co*(2*f*RmsToPeak*Ikpn.real); // note -= vice += 00279 } 00280 00281 for(register int m = 1; m <= max_harmonics; ++m) { 00282 complex Ikpm = conj(I_pVIF[k+m]); 00283 register int kk = kmn+m; // k+m-n 00284 if((kk <= limit)&&(kk >= -limit)) { //C(k+m-n) nonzero 00285 Co = Ck * conj(C.Ck[kk]); 00286 Y[m][n] += (f*Co)*(Ikmn + Ikpm - conj(I_VLO[kk])); 00287 } 00288 kk = kpn+m; // +m,-n 00289 if((kk <= limit)&&(kk >= -limit)) { //C(k+m+n) nonzero 00290 Co = Ck * conj(C.Ck[kk]); 00291 Y[m][-n] -= (f*Co)*(Ikpn + Ikpm - conj(I_VLO[kk])); 00292 } 00293 }}} // for n,m,k loops 00294 00295 // now fill in the other elements 00296 for(register int i = 0; i <= max_harmonics; ++i) 00297 for(register int j = 1; j <= max_harmonics; ++j) { 00298 Y[-j][-i] = conj(Y[j][i]); 00299 Y[-i][j] = conj(Y[i][-j]); 00300 } 00301 } // else 00302 00303 // Capacitor effects (FR notebook, pg 106): 00304 { // limit scope of the following constant 00305 register const double c = 2*Pi*Cap; 00306 for(register int k = -max_harmonics; k <= max_harmonics; ++k) 00307 Y[k][k].imaginary += c * (fIF + k*LO_freq); // only diagonal terms 00308 } 00309 00310 return Y; 00311 } // small_signal() 00312 00313 00314 // -------------------------------------------------------------------- 00315 // The routine that returns the symmetrical harmonic noise correlation 00316 // matrix using several private variables set by the large signal 00317 // analysis; it also may fill IF_freq, I_pVIF, and I_mVIF, for later 00318 // use by small_signal(); conversely, it will use values set by that 00319 // routine, to speed up the calculation for calls to both routines. 00320 // large_signal() must be called at least once before calls to this 00321 // routine, but then many calls may be made to this routine as long as 00322 // the balance remains valid. 00323 00324 const Matrix & sis_basic_device::noise( 00325 double fIF, // the IF frequency 00326 double T, // the temperature 00327 int max_harmonics ) // the max number of harmonics 00328 { 00329 if (fabs(Vn - Vn_) > deps*mVolt || 00330 fabs(Rn - Rn_) > deps*Ohm || 00331 fabs(Cap - Cap_) > deps*fFarad) 00332 error::fatal("SIS junction parameters changed since last large_signal() call."); 00333 00334 if (fIF < 0.0 || max_harmonics < 0 || fIF >= LO_freq || T <= 0.0) 00335 error::fatal("Invalid argument in call to junction::noise()."); 00336 00337 // If we get here, everything is ready to do the calculation: 00338 00339 double Vif = fIF/(Vn_ * VoltToFreq); // normalized IF photon voltage 00340 double VLO = LO_freq/(Vn_ * VoltToFreq); // normalized LO photon voltage 00341 double V0 = Voltages[0].real/Vn_; // the normalized bias voltage 00342 int limit = C.Ck.maxindex()+max_harmonics; // required range of I(V) data 00343 00344 // fill in the ivcurve lookup values, if necessary, and calculate Vif: 00345 if (IF_freq == 0.0 || IF_freq != fIF || limit > I_pVIF.maxindex()) { 00346 IF_freq = fIF; 00347 00348 // Fill the tables of -j/Rn * ivcurve::I(V0 + n*VLO +/- Vif) 00349 I_pVIF.reallocate(limit, Index_S); 00350 I_mVIF.reallocate(limit, Index_S); 00351 register double Vo = V0; // save in a register 00352 complex I_norm(0.0,-1/Rn_); // -J/Rn 00353 register double Vl = VLO; // another register copy 00354 if (Vif != 0.0) { 00355 register double Vi = Vif; 00356 for(register int k = -limit; k <= limit; ++k) { 00357 I_pVIF[k] = I_norm * (*piv)((V0 + k*Vl) + Vi); 00358 I_mVIF[k] = I_norm * (*piv)((V0 + k*Vl) - Vi); 00359 } 00360 } 00361 else { // (Vif == 0.0) 00362 for(register int k = -limit; k <= limit; ++k) { 00363 I_pVIF[k] = I_norm * (*piv)(Vo + k*Vl); 00364 I_mVIF[k] = I_pVIF[k]; 00365 } 00366 } 00367 } 00368 00369 // a table of coth()'s times Idc's: 00370 // the formulas are in FR notebook, pp 104-105 00371 00372 real_vector cothp(limit,Index_S); 00373 real_vector cothm(limit,Index_S); 00374 { // this block limits the scope of the following variables 00375 register double f1 = eCharge*Vn_; // the result normalization 00376 register double f2 = (2*BoltzK*T)/f1; // normalize coth() arg, even if T == 0 00377 for(register int k = -limit; k <= limit; ++k) { 00378 // note that because of the I_norm factor, I_pVIF[k].real == Idc[k]/Rn, etc. 00379 // we will handle the case where the coth argument is 0 using Idc'(V) 00380 double arg, arg_f2; 00381 complex current, current_p; // hold results of ivcurve::Iprime() 00382 00383 arg = (V0 + k*VLO) + Vif; 00384 if(fabs(arg) < deps) arg = 0.0 ; // very close to 0.0 00385 00386 if(f2 == 0.0 || fabs((arg_f2 = arg/f2)) > 24.0) { 00387 // then |tanh(arg/f2)| == 1.0 to 20 decimal places 00388 cothp[k] = ((arg < 0.0)? -1.0 : 1.0)*f1*I_pVIF[k].real; 00389 } 00390 else if(arg != 0.0) { 00391 cothp[k] = f1/tanh(arg_f2)*I_pVIF[k].real; 00392 } 00393 else { 00394 // lim(arg->0) = (f1*f2)*Idc'(0)/Rn 00395 piv->Iprime(0,current,current_p); 00396 cothp[k] = (2*BoltzK*T/Rn_)*current_p.imaginary; 00397 } 00398 00399 arg = (V0 + k*VLO) - Vif; 00400 if(fabs(arg) < deps) arg = 0.0 ; 00401 00402 if(f2 == 0.0 || fabs((arg_f2 = arg/f2)) > 24.0) { 00403 // then |tanh(arg/f2)| == 1.0 to 20 decimal places 00404 cothm[k] = ((arg < 0.0)? -1.0 : 1.0)*f1*I_mVIF[k].real; 00405 } 00406 else if(arg != 0.0) { 00407 cothm[k] = f1/tanh(arg_f2)*I_mVIF[k].real; 00408 } 00409 else { 00410 // lim(arg->0) = (f1*f2)*Idc'(0)/Rn 00411 piv->Iprime(0,current,current_p); 00412 cothm[k] = (2*BoltzK*T/Rn_)*current_p.imaginary; 00413 } 00414 } 00415 } 00416 00417 // Convolve the Ck and the coth's to get the Hmn: 00418 // See FR notebook, pp 104-105 for formulas 00419 00420 H.reallocate(max_harmonics,max_harmonics,Index_S,Index_S).fill(0.0); 00421 limit = C.Ck.maxindex(); 00422 for(int n = -max_harmonics; n <= max_harmonics; ++n) 00423 for(register int m = -max_harmonics; m <= max_harmonics; ++m) { 00424 register complex *pHmn = & H[m][n]; 00425 for(register int k = -limit; k <= limit; ++k) 00426 *pHmn += 00427 (C.Ck[k] * conj(C.Ck.read(k+m-n))) * 00428 (cothp[k+m] + cothm[k-n]); 00429 } 00430 00431 return H; 00432 } // noise() 00433 00434 00435 // -------------------------------------------------------------------- 00436 00437 int sis_basic_device::call_large_signal() const 00438 { 00439 // if parameters have changed since the last call to large_signal, 00440 // then state data is assumed bad. 00441 return (!iv_data_ok || 00442 fabs(Vn - Vn_) > deps*mVolt || 00443 fabs(Rn - Rn_) > deps*Ohm || 00444 fabs(Cap - Cap_) > deps*fFarad); 00445 }
Please direct comments and corrections to
supermix@submm.caltech.edu
Go to the supermix home page
Generated by
1.2.7