MTTensor.h

Go to the documentation of this file.
00001 #ifndef CMTTENSOR_H_
00002 #define CMTTENSOR_H_
00003 #include <complex>
00004 #include "NumUtil.h"
00005 #include "miscfunc.h"
00006 
00007 namespace gplib
00008   {
00009     //! Stores MT-Tensor components at a single frequency, calculates derived quantities
00010     class MTTensor
00011       {
00012     private:
00013       //the impedance elements
00014       std::complex<double> Zxx;
00015       std::complex<double> Zxy;
00016       std::complex<double> Zyx;
00017       std::complex<double> Zyy;
00018       //and their errors
00019       double dZxx;
00020       double dZxy;
00021       double dZyx;
00022       double dZyy;
00023       double frequency;
00024       double rotangle;
00025       //! Coherency for the x direction
00026       double Rx;
00027       //! Coherency for the y direction
00028       double Ry;
00029       //! Number of degrees of freedom
00030       double Nu;
00031       //! Calculate apparent resistivity
00032       double CalcRho(const std::complex<double> &Z) const
00033         {
00034           return mu / (2 * PI * frequency) * (Z.real() * Z.real() + Z.imag()
00035               * Z.imag()) * 1000000.0;
00036         }
00037       //! Calculate Phase
00038       double CalcPhase(const std::complex<double> &Z) const
00039         {
00040           if (fcmp(Z.real(), 0.0, std::numeric_limits<double>::epsilon()) != 0) //if real part significantly different from 0.
00041             return atan2(Z.imag(), Z.real()) / PI * 180;
00042           else
00043             return 0.0;
00044         }
00045       //! Calculate Phase restricted to 0 to 90 degree range
00046       double CalcPhase90(const std::complex<double> &Z) const
00047         {
00048           if (fcmp(Z.real(), 0.0, std::numeric_limits<double>::epsilon()) != 0) //if real part significantly different from 0.
00049             return atan(Z.imag() / Z.real()) / PI * 180; // here we use atan instead of atan2
00050           else
00051             return 0.0;
00052         }
00053       //! Calculate Apparent Resistivity Error
00054       double CalcdRho(const std::complex<double> &Z, const double dZ) const
00055         {
00056           return mu / (PI * frequency) * abs(Z) * dZ * 1000000.0;
00057         }
00058       //! Calculate Phase Error
00059       double CalcdPhase(const std::complex<double> &Z, const double dZ) const
00060         {
00061           if (fcmp(abs(Z), 0.0, std::numeric_limits<double>::epsilon()) != 0) //if Z significantly different from 0.
00062             return dZ / abs(Z) / PI * 180;
00063           else
00064             return 0.0;
00065         }
00066       //! Calculate Schmucker's Z*
00067       double CalcZStar(const std::complex<double> &Z) const
00068         {
00069           return std::abs((I / (2.0 * PI * frequency) * Z * 1000.0).real());
00070         }
00071       //! Calculate Schmucker's Rho*
00072       double CalcRhoStar(const std::complex<double> &Z) const
00073         {
00074           double phase = CalcPhase(Z) / 180.0 * PI;
00075           if (phase > 45.0)
00076             return 2.0 * pow2(cos(phase)) * CalcRho(Z);
00077           else
00078             return 0.5 * 1. / pow2(sin(phase)) * CalcRho(Z);
00079         }
00080     public:
00081       //! Function for Errors that cannot be calculated analytically when we don't want Jacknife errors
00082       double GetdZero() const
00083         {
00084           return 0.0;
00085         }
00086       //! Set the errors for the impedance elements
00087       void SetErrors(double dxx, double dxy, double dyx, double dyy)
00088         {
00089           dZxx = dxx;
00090           dZxy = dxy;
00091           dZyx = dyx;
00092           dZyy = dyy;
00093         }
00094       //!Rotate by the given angle in radian
00095       void Rotate(double angle);
00096       ///!return the current angle in radian
00097       double GetRotangle() const
00098         {
00099           return rotangle;
00100         }
00101       //! Set the rotation angle, without performing the corresponding rotation
00102       double &SetRotangle()
00103         {
00104           return rotangle;
00105         }
00106       //! Get the frequency for the impedance
00107       double GetFrequency() const
00108         {
00109           return frequency;
00110         }
00111       //! Return tensor elements
00112       std::complex<double> GetZyy() const
00113         {
00114           return Zyy;
00115         }
00116       std::complex<double> GetZxx() const
00117         {
00118           return Zxx;
00119         }
00120       std::complex<double> GetZxy() const
00121         {
00122           return Zxy;
00123         }
00124       std::complex<double> GetZyx() const
00125         {
00126           return Zyx;
00127         }
00128       //! Write access to tensor elements
00129       std::complex<double> &SetZyy()
00130         {
00131           return Zyy;
00132         }
00133       std::complex<double> &SetZxx()
00134         {
00135           return Zxx;
00136         }
00137       std::complex<double> &SetZxy()
00138         {
00139           return Zxy;
00140         }
00141       std::complex<double> &SetZyx()
00142         {
00143           return Zyx;
00144         }
00145       //! Return tensor element errors
00146       double GetdZxx() const
00147         {
00148           return dZxx;
00149         }
00150       double GetdZxy() const
00151         {
00152           return dZxy;
00153         }
00154       double GetdZyx() const
00155         {
00156           return dZyx;
00157         }
00158       double GetdZyy() const
00159         {
00160           return dZyy;
00161         }
00162       //! Write access to errors
00163       double &SetdZyy()
00164         {
00165           return dZyy;
00166         }
00167       double &SetdZxx()
00168         {
00169           return dZxx;
00170         }
00171       double &SetdZxy()
00172         {
00173           return dZxy;
00174         }
00175       double &SetdZyx()
00176         {
00177           return dZyx;
00178         }
00179       //! Return apparent resistivity
00180       double GetRhoxx() const
00181         {
00182           return CalcRho(Zxx);
00183         }
00184       double GetRhoxy() const
00185         {
00186           return CalcRho(Zxy);
00187         }
00188       double GetRhoyx() const
00189         {
00190           return CalcRho(Zyx);
00191         }
00192       double GetRhoyy() const
00193         {
00194           return CalcRho(Zyy);
00195         }
00196       //! Return phase
00197       double GetPhixx() const
00198         {
00199           return CalcPhase(Zxx);
00200         }
00201       double GetPhixy() const
00202         {
00203           return CalcPhase(Zxy);
00204         }
00205       double GetPhiyx() const
00206         {
00207           return CalcPhase(Zyx);
00208         }
00209       double GetPhiyy() const
00210         {
00211           return CalcPhase(Zyy);
00212         }
00213       //! Return phase restricted to 0 to 90 degree range
00214       double GetPhi90xx() const
00215         {
00216           return CalcPhase90(Zxx);
00217         }
00218       double GetPhi90xy() const
00219         {
00220           return CalcPhase90(Zxy);
00221         }
00222       double GetPhi90yx() const
00223         {
00224           return CalcPhase90(Zyx);
00225         }
00226       double GetPhi90yy() const
00227         {
00228           return CalcPhase90(Zyy);
00229         }
00230       //! Return Rho Error for tensor elements
00231       double GetdRhoxx() const
00232         {
00233           return CalcdRho(Zxx, dZxx);
00234         }
00235       double GetdRhoxy() const
00236         {
00237           return CalcdRho(Zxy, dZxy);
00238         }
00239       double GetdRhoyx() const
00240         {
00241           return CalcdRho(Zyx, dZyx);
00242         }
00243       double GetdRhoyy() const
00244         {
00245           return CalcdRho(Zyy, dZyy);
00246         }
00247       //! return phase error for tensor elements
00248       double GetdPhixx() const
00249         {
00250           return CalcdPhase(Zxx, dZxx);
00251         }
00252       double GetdPhixy() const
00253         {
00254           return CalcdPhase(Zxy, dZxy);
00255         }
00256       double GetdPhiyx() const
00257         {
00258           return CalcdPhase(Zyx, dZyx);
00259         }
00260       double GetdPhiyy() const
00261         {
00262           return CalcdPhase(Zyy, dZyy);
00263         }
00264       //! Return Schmucker's rho* for tensor elements
00265       double GetRhoxxStar() const
00266         {
00267           return CalcRhoStar(Zxx);
00268         }
00269       double GetRhoxyStar() const
00270         {
00271           return CalcRhoStar(Zxy);
00272         }
00273       double GetRhoyxStar() const
00274         {
00275           return CalcRhoStar(Zyx);
00276         }
00277       double GetRhoyyStar() const
00278         {
00279           return CalcRhoStar(Zyy);
00280         }
00281       //! Return Schmucker's z* for tensor elements
00282       double GetZxxStar() const
00283         {
00284           return CalcZStar(Zxx);
00285         }
00286       double GetZxyStar() const
00287         {
00288           return CalcZStar(Zxy);
00289         }
00290       double GetZyxStar() const
00291         {
00292           return CalcZStar(Zyx);
00293         }
00294       double GetZyyStar() const
00295         {
00296           return CalcZStar(Zyy);
00297         }
00298       //! Some invariants and intermediate quantities for strike and skew calculation
00299       std::complex<double> GetS1() const
00300         {
00301           return Zxx + Zyy;
00302         }
00303       std::complex<double> GetS2() const
00304         {
00305           return Zxy + Zyx;
00306         }
00307       std::complex<double> GetD1() const
00308         {
00309           return Zxx - Zyy;
00310         }
00311       std::complex<double> GetD2() const
00312         {
00313           return Zxy - Zyx;
00314         }
00315       //! The Berdichevskyi invariant
00316       std::complex<double> GetBerd() const
00317         {
00318           return 0.5 * GetD2();
00319         }
00320       //! The error of the Berdichevskyi invariant
00321       double GetdBerd() const
00322         {
00323           return 0.5 * sqrt(dZxy * dZxy + dZyx * dZyx);
00324         }
00325       double GetRhoBerd() const
00326         {
00327           return CalcRho(GetBerd());
00328         }
00329       double GetPhi90Berd() const
00330         {
00331           return CalcPhase90(GetBerd());
00332         }
00333       double GetdRhoBerd() const
00334         {
00335           return CalcdRho(GetBerd(), GetdBerd());
00336         }
00337       double GetdPhi90Berd() const
00338         {
00339           return CalcdPhase(GetBerd(), GetdBerd());
00340         }
00341       //! The determinant
00342       std::complex<double> GetDet() const
00343         {
00344           return Zxx * Zyy - Zxy * Zyx;
00345         }
00346       //! The error of the determinant
00347       double GetdDet() const
00348         {
00349           return sqrt(abs(Zyy * Zyy) * dZxx * dZxx + abs(Zxx * Zxx) * dZyy
00350               * dZyy + abs(Zxy * Zxy) * dZyx * dZyx + abs(Zyx * Zyx) * dZxy
00351               * dZxy);
00352         }
00353       //! The determinant of the real parts of Z
00354       double GetDetreal() const
00355         {
00356           return Zxx.real() * Zyy.real() - Zxy.real() * Zyx.real();
00357         }
00358       //! Get the error of the determinant of the real part
00359       double GetdDetreal() const
00360         {
00361           return sqrt(pow2(Zyy.real()) * pow2(dZxx) + pow2(
00362               Zxx.real()) * pow2(dZyy) + pow2(Zxy.real())
00363               * pow2(dZyx) + pow2(Zyx.real()) * pow2(dZxy));
00364         }
00365       //! Rotationally invariant phase difference
00366       double GetMu() const
00367         {
00368           return sqrt(std::abs(Commute(GetD1(), GetS2())
00369               + Commute(GetS1(), GetD2()))) / abs(GetD2());
00370         }
00371       //! Swift's skew
00372       double GetKappa() const
00373         {
00374           return abs(GetS1()) / abs(GetD2());
00375         }
00376       double GetSigma() const
00377         {
00378           return (pow2(abs(GetD1())) + pow2(abs(GetS2())))
00379               / pow2(abs(GetD2()));
00380         }
00381       //! Bahr's skew
00382       double GetEta() const
00383         {
00384           return sqrt(std::abs(Commute(GetD1(), GetS2())
00385               - Commute(GetS1(), GetD2()))) / abs(GetD2());
00386         }
00387       //Bahr's phase sensitive strike angle
00388       double GetAlpha() const
00389         {
00390           double denominator = Commute(GetS1(), GetD1()) - Commute(GetS2(),
00391               GetD2());
00392           if (fcmp(denominator, 0.0, std::numeric_limits<double>::epsilon()) != 0)
00393             return 0.5 * atan((Commute(GetS1(), GetS2()) - Commute(GetD1(),
00394                 GetD2())) / (denominator));
00395           else
00396             return 0.0;
00397         }
00398       //! Calculate strike angle, so it points to high conductivity direction (Phixy > Phiyx)
00399       double GetAlphaHigh() const
00400         {
00401           double regalpha = GetAlpha(); // calculate strike angle
00402           MTTensor tmp(*this); //create local copy
00403           tmp.Rotate(regalpha); //rotate to new coordinate system
00404           if (tmp.GetPhiyx() > tmp.GetPhixy())
00405             regalpha += PI / 2.0;
00406           return regalpha;
00407         }
00408       //! Maximum phase difference
00409       double GetMaxPhiDiff() const
00410         {
00411           MTTensor tmp(*this); //create local copy
00412           tmp.Rotate(GetAlpha());
00413           return std::abs(tmp.GetPhixy() - tmp.GetPhiyx());
00414         }
00415       //! All the following quantities are defined in Caldwell GJI 158, 457-469, the phase tensor elements
00416       double GetPhi11() const
00417         {
00418           const double dr = GetDetreal();
00419           if (fcmp(dr, 0.0, std::numeric_limits<double>::epsilon()) != 0)
00420             return ((Zyy.real() * Zxx.imag() - Zxy.real() * Zyx.imag()) / dr);
00421           else
00422             return 0.0;
00423         }
00424       double GetPhi12() const
00425         {
00426           const double dr = GetDetreal();
00427           if (fcmp(dr, 0.0, std::numeric_limits<double>::epsilon()) != 0)
00428             return ((Zyy.real() * Zxy.imag() - Zxy.real() * Zyy.imag()) / dr);
00429           else
00430             return 0.0;
00431         }
00432       double GetPhi21() const
00433         {
00434           const double dr = GetDetreal();
00435           if (fcmp(dr, 0.0, std::numeric_limits<double>::epsilon()) != 0)
00436             return ((Zxx.real() * Zyx.imag() - Zyx.real() * Zxx.imag()) / dr);
00437           else
00438             return 0.0;
00439         }
00440       double GetPhi22() const
00441         {
00442           const double dr = GetDetreal();
00443           if (fcmp(dr,0.0,std::numeric_limits<double>::epsilon()) != 0)
00444             return ((Zxx.real() * Zyy.imag() - Zyx.real() * Zxy.imag()) / dr);
00445           else
00446             return 0.0;
00447         }
00448       double GetAlpha_phi() const
00449         {
00450           const double diff = GetPhi11() - GetPhi22();
00451           if (fcmp(diff, 0.0, std::numeric_limits<double>::epsilon()) != 0)
00452             return 0.5 * atan2((GetPhi12() + GetPhi21()), diff);
00453           else
00454             return 0.0;
00455         }
00456       double GetBeta_phi() const
00457         {
00458           return 0.5 * atan2((GetPhi12() - GetPhi21()), (GetPhi11()
00459               + GetPhi22()));
00460         }
00461       double GetPi1() const
00462         {
00463           return 0.5 * sqrt(std::pow(GetPhi11() - GetPhi22(), 2) + std::pow(GetPhi12()
00464               + GetPhi21(), 2));
00465         }
00466       double GetPi2() const
00467         {
00468           return 0.5 * sqrt(std::pow(GetPhi11() + GetPhi22(), 2) + std::pow(GetPhi12()
00469               - GetPhi21(), 2));
00470         }
00471       double GetPhiStrike() const
00472         {
00473           return GetAlpha_phi() - GetBeta_phi();
00474         }
00475       double GetPhiMax() const
00476         {
00477           return GetPi2() + GetPi1();
00478         }
00479       double GetPhiMin() const
00480         {
00481           return GetPi2() - GetPi1();
00482         }
00483       double GettrPhi() const
00484         {
00485           return GetPhi11() + GetPhi22();
00486         }
00487       double GetskPhi() const
00488         {
00489           return GetPhi12() - GetPhi21();
00490         }
00491       double GetdetPhi() const
00492         {
00493           return GetPhi11() * GetPhi22() - GetPhi12() * GetPhi21();
00494         }
00495       double GetPhi1() const
00496         {
00497           return GettrPhi() / 2.;
00498         }
00499       double GetPhi2() const
00500         {
00501           return sqrt(std::abs(GetdetPhi()));
00502         }
00503       double GetPhi2Sq() const
00504         {
00505           return GetdetPhi();
00506         }
00507       double GetPhi3() const
00508         {
00509           return GetskPhi() / 2.;
00510         }
00511       double GetPhiEllip() const
00512         {
00513           return (GetPhiMax() - GetPhiMin()) / (GetPhiMax() + GetPhiMin());
00514         }
00515       //Here start the Weaver et al. invariants, equations taken from Marti, GJI 2004
00516       double GetXi1() const
00517         {
00518           return 0.5 * (Zxx.real() + Zyy.real());
00519         }
00520       double GetXi2() const
00521         {
00522           return 0.5 * (Zxy.real() + Zyx.real());
00523         }
00524       double GetXi3() const
00525         {
00526           return 0.5 * (Zxx.real() - Zyy.real());
00527         }
00528       double GetXi4() const
00529         {
00530           return 0.5 * (Zxy.real() - Zyx.real());
00531         }
00532       double GetEta1() const
00533         {
00534           return 0.5 * (Zxx.imag() + Zyy.imag());
00535         }
00536       double GetEta2() const
00537         {
00538           return 0.5 * (Zxy.imag() + Zyx.imag());
00539         }
00540       double GetEta3() const
00541         {
00542           return 0.5 * (Zxx.imag() - Zyy.imag());
00543         }
00544       double GetEta4() const
00545         {
00546           return 0.5 * (Zxy.imag() - Zyx.imag());
00547         }
00548       double GetI1() const
00549         {
00550           return sqrt(pow2(GetXi1()) + pow2(GetXi4()));
00551         }
00552       double GetI2() const
00553         {
00554           return sqrt(pow2(GetEta1()) + pow2(GetEta4()));
00555         }
00556       double GetI3() const
00557         {
00558           return sqrt(pow2(GetXi2()) + pow2(GetXi3())) / GetI1();
00559         }
00560       double GetI4() const
00561         {
00562           return sqrt(pow2(GetEta2()) + pow2(GetEta3())) / GetI2();
00563         }
00564       double GetI5() const
00565         {
00566           return (GetXi4() * GetEta1() + GetXi1() * GetEta4()) / (GetI1()
00567               * GetI2());
00568         }
00569       double GetI6() const
00570         {
00571           return (GetXi4() * GetEta1() - GetXi1() * GetEta4()) / (GetI1()
00572               * GetI2());
00573         }
00574       double Getd13() const
00575         {
00576           return (GetXi1() * GetEta3() - GetXi3() * GetEta1()) / (GetI1()
00577               * GetI2());
00578         }
00579       double Getd12() const
00580         {
00581           return (GetXi1() * GetEta2() - GetXi2() * GetEta1()) / (GetI1()
00582               * GetI2());
00583         }
00584       double Getd24() const
00585         {
00586           return (GetXi2() * GetEta4() - GetXi4() * GetEta2()) / (GetI1()
00587               * GetI2());
00588         }
00589       double Getd34() const
00590         {
00591           return (GetXi3() * GetEta4() - GetXi4() * GetEta3()) / (GetI1()
00592               * GetI2());
00593         }
00594       double Getd41() const
00595         {
00596           return (GetXi4() * GetEta1() - GetXi1() * GetEta4()) / (GetI1()
00597               * GetI2());
00598         }
00599       double Getd23() const
00600         {
00601           return (GetXi2() * GetEta3() - GetXi3() * GetEta2()) / (GetI1()
00602               * GetI2());
00603         }
00604       double GetQ() const
00605         {
00606           return sqrt(pow2(Getd12() - Getd34()) + pow2(Getd13()
00607               + Getd24()));
00608         }
00609       double GetI7() const
00610         {
00611           return (Getd41() + Getd23()) / GetQ();
00612         }
00613       double Geta() const
00614         {
00615           return pow2(GetI5() - GetI6());
00616         }
00617       double Getb() const
00618         {
00619           return 1.0 - GetI5() * GetI6() + sqrt(1.0 + pow2(GetI5())
00620               * pow2(GetI6()) - pow2(GetI5()) - pow2(GetI6()));
00621         }
00622       double Getr() const
00623         {
00624           return GetI2() / GetI1();
00625         }
00626       //const std::complex<double> GetThetaBar(){}
00627       //const std::complex<double> GetdTheta(){}
00628       //const std::complex<double> GetZs(){}
00629       //const std::complex<double> GetZp(){}
00630       //! Return coherency for the x-direction
00631       double GetRx() const
00632         {
00633           return Rx;
00634         }
00635       //! Return coherency for the y-direction
00636       double GetRy() const
00637         {
00638           return Ry;
00639         }
00640       //! The degrees of freedom used for transfer function estimation
00641       double GetNu() const
00642         {
00643           return Nu;
00644         }
00645       friend class MTStation;
00646       friend class C1DMTSynthData;
00647       friend class JParser;
00648       friend class EDIParser;
00649       MTTensor();
00650       MTTensor(const std::complex<double> &xx, const std::complex<double> &xy,
00651           const std::complex<double> &yx, const std::complex<double> &yy,
00652           const double freq = 1., const double angle = 0.0);
00653       MTTensor& operator=(const MTTensor& source);
00654       virtual ~MTTensor();
00655       };
00656 
00657     std::complex<double> inline RhoPhiToZ(const double freq, const double rho,
00658         const double phi)
00659       {
00660         return sqrt(2 * PI * freq / mu) * 0.001 * sqrt(rho) * (cos(phi / 180
00661             * PI) + I * sin(phi / 180 * PI));
00662       }
00663 
00664   // This is some remnant code for parallel and serial transforms of the impedance tensor
00665   // only kept so I remember how they are calculated
00666   /*    temp1 = gsl_complex_rect(D1.at(i).real(),D1.at(i).imag());
00667    temp2 = gsl_complex_rect(S2.at(i).real(),S2.at(i).imag());
00668    temp3= gsl_complex_mul_real(gsl_complex_arctan(gsl_complex_div(gsl_complex_mul_real(temp1,-1.),temp2)),0.5);
00669    thetabar.at(i) = GSL_REAL(temp3);
00670    thetabar.at(i) += I*GSL_IMAG(temp3);
00671    temp1 = gsl_complex_rect(S1.at(i).real(),S1.at(i).imag());
00672    temp2 = gsl_complex_rect(D2.at(i).real(),D2.at(i).imag());
00673    temp3 = gsl_complex_arctan(gsl_complex_div(temp1,temp2));
00674    dtheta.at(i) = GSL_REAL(temp3);
00675    dtheta.at(i) += I*GSL_IMAG(temp3);
00676    //dtheta.at(i) = atan(S1.at(i)/D2.at(i))
00677    Zs.at(i) = sqrt((pow(DataXX.Z.at(i),2) + pow(DataXY.Z.at(i),2) + pow(DataYX.Z.at(i),2) + pow(DataYY.Z.at(i),2))/2.);
00678    Zp.at(i) = (DataXY.Z.at(i)*DataYX.Z.at(i)-DataXX.Z.at(i)*DataYY.Z.at(i))/Zs.at(i);
00679    rhos.at(i) = 1/(2 * PI * frequency.at(i)) * mu * (pow(Zs.at(i).real(),2)+pow(Zs.at(i).imag(),2)) * pow(1000.0,2);
00680    rhop.at(i) = 1/(2 * PI * frequency.at(i)) * mu * (pow(Zp.at(i).real(),2)+pow(Zp.at(i).imag(),2)) * pow(1000.0,2);
00681    phis.at(i) = atan(Zs.at(i).imag()/Zs.at(i).real());
00682    phip.at(i) = atan(Zp.at(i).imag()/Zp.at(i).real());
00683    dPhips.at(i) = phip.at(i) - phis.at(i);*/
00684   }
00685 
00686 #endif /*CMTTENSOR_H_*/

Generated on Tue May 4 16:52:14 2010 for GPLIB++ by  doxygen 1.5.8