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

sisdevice.cc

Go to the documentation of this file.
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 doxygen1.2.7