Lemma is an Electromagnetics API
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

EMEarth1D.cpp 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. /* This file is part of Lemma, a geophysical modelling and inversion API */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /**
  6. @file
  7. @author Trevor Irons
  8. @date 12/02/2009
  9. **/
  10. #include "EMEarth1D.h"
  11. #include "FieldPoints.h"
  12. #include "WireAntenna.h"
  13. #include "PolygonalWireAntenna.h"
  14. #ifdef LEMMAUSEOMP
  15. #include "omp.h"
  16. #endif
  17. namespace Lemma {
  18. std::ostream &operator << (std::ostream &stream, const EMEarth1D &ob) {
  19. stream << ob.Serialize() << "\n";
  20. return stream;
  21. }
  22. #ifdef KIHALEE_EM1D
  23. // Wrapper function for Fortran subroutine Em1D bi kihand
  24. // Returns E or H fields (SLOW)
  25. extern "C" { void em1dcall_(int &itype, // source
  26. int &ipol, // source
  27. int &nlay, // Earth
  28. int &nfreq, // source
  29. int &nfield, // Calculator
  30. int &nres, // Receivers
  31. int &jtype, // N/A
  32. int &jgamma, // Controller
  33. double &acc, // Controller
  34. double *dep, // Earth
  35. std::complex<double> *sig, // Earth
  36. double *susl, // Earth
  37. double *sush, // Earth
  38. double *sustau, // Earth
  39. double *susalp, // Earth
  40. double *eprl, // Earth
  41. double *eprh, // Earth
  42. double *eprtau, // Earth
  43. double *epralp, // Earth
  44. double &finit, // N/A
  45. double &flimit, // N/A
  46. double &dlimit, // N/A
  47. double &lfinc, // N/A
  48. double &tx, // Source
  49. double &ty, // Source
  50. double &tz, // Source
  51. double *rxx, // Receivers
  52. double *rxy, // Receivers
  53. double *rxz, // Receivers
  54. std::complex<double> *ex, // Receivers
  55. std::complex<double> *ey, // |
  56. std::complex<double> *ez, // |
  57. std::complex<double> *hx, // |
  58. std::complex<double> *hy, // V
  59. std::complex<double> *hz ); // ___
  60. }
  61. #endif
  62. // ==================== LIFECYCLE ===================================
  63. // TODO init large arrays here.
  64. EMEarth1D::EMEarth1D( const ctor_key& key ) : LemmaObject( key ),
  65. Dipole(nullptr), Earth(nullptr), Receivers(nullptr), Antenna(nullptr),
  66. FieldsToCalculate(BOTH), HankelType(ANDERSON801), icalcinner(0), icalc(0) {
  67. }
  68. EMEarth1D::~EMEarth1D() {
  69. }
  70. std::shared_ptr<EMEarth1D> EMEarth1D::NewSP() {
  71. return std::make_shared<EMEarth1D>(ctor_key());
  72. }
  73. YAML::Node EMEarth1D::Serialize() const {
  74. YAML::Node node = LemmaObject::Serialize();
  75. node["FieldsToCalculate"] = enum2String(FieldsToCalculate);
  76. node["HankelType"] = enum2String(HankelType);
  77. //if (Dipole != nullptr) node["Dipole"] = Dipole->Serialize();
  78. if (Earth != nullptr) node["Earth"] = Earth->Serialize();
  79. //if (Receivers != nullptr) node["Receivers"] = Receivers->Serialize(); Can be huge?
  80. if (Antenna != nullptr) node["Antenna"] = Antenna->Serialize();
  81. node.SetTag( this->GetName() );
  82. return node;
  83. }
  84. //--------------------------------------------------------------------------------------
  85. // Class: EMEarth1D
  86. // Method: GetName
  87. // Description: Class identifier
  88. //--------------------------------------------------------------------------------------
  89. inline std::string EMEarth1D::GetName ( ) const {
  90. return CName;
  91. } // ----- end of method EMEarth1D::GetName -----
  92. // ==================== ACCESS ===================================
  93. void EMEarth1D::AttachDipoleSource( std::shared_ptr<DipoleSource> dipoleptr) {
  94. Dipole = dipoleptr;
  95. if (Receivers != nullptr) {
  96. // Check to make sure Receivers are set up for all calculations
  97. switch(FieldsToCalculate) {
  98. case E:
  99. if (Receivers->NumberOfBinsE != Dipole->GetNumberOfFrequencies())
  100. Receivers->SetNumberOfBinsE(Dipole->GetNumberOfFrequencies());
  101. break;
  102. case H:
  103. if (Receivers->NumberOfBinsH != Dipole->GetNumberOfFrequencies())
  104. Receivers->SetNumberOfBinsH(Dipole->GetNumberOfFrequencies());
  105. break;
  106. case BOTH:
  107. if (Receivers->NumberOfBinsH != Dipole->GetNumberOfFrequencies())
  108. Receivers->SetNumberOfBinsH(Dipole->GetNumberOfFrequencies());
  109. if (Receivers->NumberOfBinsE != Dipole->GetNumberOfFrequencies())
  110. Receivers->SetNumberOfBinsE(Dipole->GetNumberOfFrequencies());
  111. break;
  112. }
  113. }
  114. }
  115. void EMEarth1D::AttachFieldPoints( std::shared_ptr<FieldPoints> recptr) {
  116. Receivers = recptr;
  117. if (Receivers == nullptr) {
  118. std::cerr << "nullptr Receivers in emearth1d.cpp " << std::endl;
  119. return;
  120. }
  121. // This has an implicit need to first set a source before receivers, users
  122. // will not expect this. Fix
  123. if (Dipole != nullptr) {
  124. switch (FieldsToCalculate) {
  125. case E:
  126. Receivers->SetNumberOfBinsE(Dipole->GetNumberOfFrequencies());
  127. break;
  128. case H:
  129. Receivers->SetNumberOfBinsH(Dipole->GetNumberOfFrequencies());
  130. break;
  131. case BOTH:
  132. Receivers->SetNumberOfBinsE(Dipole->GetNumberOfFrequencies());
  133. Receivers->SetNumberOfBinsH(Dipole->GetNumberOfFrequencies());
  134. break;
  135. }
  136. } else if (Antenna != nullptr) {
  137. switch (FieldsToCalculate) {
  138. case E:
  139. Receivers->SetNumberOfBinsE(Antenna->GetNumberOfFrequencies());
  140. break;
  141. case H:
  142. Receivers->SetNumberOfBinsH(Antenna->GetNumberOfFrequencies());
  143. break;
  144. case BOTH:
  145. Receivers->SetNumberOfBinsE(Antenna->GetNumberOfFrequencies());
  146. Receivers->SetNumberOfBinsH(Antenna->GetNumberOfFrequencies());
  147. break;
  148. }
  149. }
  150. }
  151. void EMEarth1D::AttachLayeredEarthEM( std::shared_ptr<LayeredEarthEM> earthptr) {
  152. Earth = earthptr;
  153. }
  154. void EMEarth1D::AttachWireAntenna(std::shared_ptr<WireAntenna> antennae) {
  155. this->Antenna = antennae;
  156. }
  157. void EMEarth1D::SetFieldsToCalculate(const FIELDCALCULATIONS &calc) {
  158. FieldsToCalculate = calc;
  159. }
  160. void EMEarth1D::SetHankelTransformMethod( const HANKELTRANSFORMTYPE &type) {
  161. HankelType = type;
  162. }
  163. /*
  164. void EMEarth1D::Query() {
  165. std::cout << "EmEarth1D::Query()" << std::endl;
  166. std::cout << "Dipole " << Dipole;
  167. if (Dipole) std::cout << *Dipole << std::endl;
  168. std::cout << "Earth " << Earth;
  169. if (Earth) std::cout << *Earth << std::endl;
  170. std::cout << "Receivers " << Earth;
  171. if (Earth) std::cout << *Receivers << std::endl;
  172. std::cout << "Antenna " << Earth;
  173. if (Antenna) std::cout << *Antenna << std::endl;
  174. std::cout << "icalc " << icalc << std::endl;
  175. std::cout << "icalcinner " << icalcinner << std::endl;
  176. }
  177. */
  178. // ==================== OPERATIONS ===================================
  179. void EMEarth1D::CalculateWireAntennaFields(bool progressbar) {
  180. if (Earth == nullptr) {
  181. throw NullEarth();
  182. }
  183. if (Receivers == nullptr) {
  184. throw NullReceivers();
  185. }
  186. if (Antenna == nullptr) {
  187. throw NullAntenna();
  188. }
  189. if (Dipole != nullptr) {
  190. throw DipoleSourceSpecifiedForWireAntennaCalc();
  191. }
  192. Receivers->ClearFields();
  193. // Check to make sure Receivers are set up for all calculations
  194. switch(FieldsToCalculate) {
  195. case E:
  196. if (Receivers->NumberOfBinsE != Antenna->GetNumberOfFrequencies())
  197. Receivers->SetNumberOfBinsE(Antenna->GetNumberOfFrequencies());
  198. break;
  199. case H:
  200. if (Receivers->NumberOfBinsH != Antenna->GetNumberOfFrequencies())
  201. Receivers->SetNumberOfBinsH(Antenna->GetNumberOfFrequencies());
  202. break;
  203. case BOTH:
  204. if (Receivers->NumberOfBinsH != Antenna->GetNumberOfFrequencies())
  205. Receivers->SetNumberOfBinsH(Antenna->GetNumberOfFrequencies());
  206. if (Receivers->NumberOfBinsE != Antenna->GetNumberOfFrequencies())
  207. Receivers->SetNumberOfBinsE(Antenna->GetNumberOfFrequencies());
  208. break;
  209. }
  210. if (Antenna->GetName() == std::string("PolygonalWireAntenna") || Antenna->GetName() == std::string("TEMTransmitter") ) {
  211. icalc += 1;
  212. // Check to see if they are all on a plane? If so we can do this fast
  213. if ( Antenna->IsHorizontallyPlanar() && ( HankelType == ANDERSON801 || HankelType == FHTKEY201 || HankelType==FHTKEY101 ||
  214. HankelType == FHTKEY51 || HankelType == FHTKONG61 || HankelType == FHTKONG121 ||
  215. HankelType == FHTKONG241 || HankelType == IRONS )) {
  216. std::unique_ptr<ProgressBar> mdisp;
  217. if (progressbar) {
  218. mdisp = std::make_unique< ProgressBar >( Receivers->GetNumberOfPoints()*Antenna->GetNumberOfFrequencies() );
  219. }
  220. for (int ifreq=0; ifreq<Antenna->GetNumberOfFrequencies();++ifreq) {
  221. Real wavef = 2.*PI* Antenna->GetFrequency(ifreq);
  222. #ifdef LEMMAUSEOMP
  223. #pragma omp parallel
  224. {
  225. #endif
  226. auto Hankel = HankelTransformFactory::NewSP( HankelType );
  227. auto AntCopy = static_cast<PolygonalWireAntenna*>(Antenna.get())->ClonePA();
  228. #ifdef LEMMAUSEOMP
  229. #pragma omp for schedule(static, 1)
  230. #endif
  231. for (int irec=0; irec<Receivers->GetNumberOfPoints(); ++irec) {
  232. SolveLaggedTxRxPair(irec, Hankel.get(), wavef, ifreq, AntCopy.get());
  233. if (progressbar) {
  234. ++ *mdisp;
  235. }
  236. }
  237. #ifdef LEMMAUSEOMP
  238. #pragma omp barrier
  239. }
  240. #endif
  241. }
  242. } else if (Receivers->GetNumberOfPoints() > Antenna->GetNumberOfFrequencies()) {
  243. /* Progress display bar for long calculations */
  244. std::unique_ptr<ProgressBar> mdisp;
  245. if (progressbar) {
  246. mdisp = std::make_unique< ProgressBar > ( Receivers->GetNumberOfPoints()*Antenna->GetNumberOfFrequencies() );
  247. }
  248. // parallelise across receivers
  249. #ifdef LEMMAUSEOMP
  250. #pragma omp parallel
  251. #endif
  252. { // OpenMP Parallel Block
  253. // Since these antennas change we need a local copy for each
  254. // thread.
  255. auto AntCopy = static_cast<PolygonalWireAntenna*>(Antenna.get())->ClonePA();
  256. auto Hankel = HankelTransformFactory::NewSP( HankelType );
  257. #ifdef LEMMAUSEOMP
  258. #pragma omp for schedule(static, 1) //nowait
  259. #endif
  260. for (int irec=0; irec<Receivers->GetNumberOfPoints(); ++irec) {
  261. if (!Receivers->GetMask(irec)) {
  262. AntCopy->ApproximateWithElectricDipoles(Receivers->GetLocation(irec));
  263. for (int idip=0; idip < static_cast<int>(AntCopy->GetNumberOfDipoles()); ++idip) {
  264. auto tDipole = AntCopy->GetDipoleSource(idip);
  265. //#ifdef LEMMAUSEOMP
  266. //#pragma omp for schedule(static, 1)
  267. //#endif
  268. for (int ifreq=0; ifreq<tDipole->GetNumberOfFrequencies();
  269. ++ifreq) {
  270. // Propogation constant in free space
  271. Real wavef = tDipole->GetAngularFrequency(ifreq) *
  272. std::sqrt(MU0*EPSILON0);
  273. SolveSingleTxRxPair(irec, Hankel.get(), wavef, ifreq, tDipole.get());
  274. } // freq loop
  275. } // dipole loop
  276. } // mask
  277. //std::cout << "Normal Path\n";
  278. //std::cout << Receivers->GetHfield(0, irec) << std::endl;
  279. //if (irec == 1) exit(0);
  280. if (progressbar) {
  281. ++ *mdisp;
  282. }
  283. } // receiver loop
  284. } // OMP_PARALLEL BLOCK
  285. } else if (Antenna->GetNumberOfFrequencies() > 8) {
  286. // parallel across frequencies
  287. //std::cout << "freq parallel #2" << std::endl;
  288. for (int irec=0; irec<Receivers->GetNumberOfPoints(); ++irec) {
  289. if (!Receivers->GetMask(irec)) {
  290. static_cast<PolygonalWireAntenna*>(Antenna.get())->ApproximateWithElectricDipoles(Receivers->GetLocation(irec));
  291. #ifdef LEMMAUSEOMP
  292. #pragma omp parallel
  293. #endif
  294. { // OpenMP Parallel Block
  295. auto Hankel = HankelTransformFactory::NewSP( HankelType );
  296. #ifdef LEMMAUSEOMP
  297. #pragma omp for schedule(static, 1)
  298. #endif
  299. for (int ifreq=0; ifreq<Antenna->GetNumberOfFrequencies(); ++ifreq) {
  300. for (int idip=0; idip< static_cast<int>(Antenna->GetNumberOfDipoles()); ++idip) {
  301. auto tDipole = Antenna->GetDipoleSource(idip);
  302. // Propogation constant in free space
  303. Real wavef = tDipole->GetAngularFrequency(ifreq) *
  304. std::sqrt(MU0*EPSILON0);
  305. SolveSingleTxRxPair(irec, Hankel.get(), wavef, ifreq, tDipole.get());
  306. } // dipole loop
  307. } // frequency loop
  308. } // OMP_PARALLEL BLOCK
  309. } // mask loop
  310. //if (Receivers->GetNumberOfPoints() > 100) {
  311. // ++ mdisp;
  312. //}
  313. } // receiver loop
  314. } // Frequency Parallel
  315. else {
  316. //std::cout << "parallel across transmitter dipoles " << std::endl;
  317. for (int irec=0; irec<Receivers->GetNumberOfPoints(); ++irec) {
  318. if (!Receivers->GetMask(irec)) {
  319. static_cast<PolygonalWireAntenna*>(Antenna.get())->ApproximateWithElectricDipoles(Receivers->GetLocation(irec));
  320. // std::cout << "Not Masked " << std::endl;
  321. // std::cout << "n Freqs " << Antenna->GetNumberOfFrequencies() << std::endl;
  322. // std::cout << "n Dipoles " << Antenna->GetNumberOfDipoles() << std::endl;
  323. // if ( !Antenna->GetNumberOfDipoles() ) {
  324. // std::cout << "NO DIPOLES!!!!!!!!!!!!!!!!!!!!!!!!!!\n";
  325. // // std::cout << "rec location " << Receivers->GetLocation(irec) << std::endl;
  326. // // }
  327. #ifdef LEMMAUSEOMP
  328. #pragma omp parallel
  329. #endif
  330. { // OpenMP Parallel Block
  331. auto Hankel = HankelTransformFactory::NewSP( HankelType );
  332. for (int ifreq=0; ifreq<Antenna->GetNumberOfFrequencies(); ++ifreq) {
  333. #ifdef LEMMAUSEOMP
  334. #pragma omp for schedule(static, 1)
  335. #endif
  336. for (int idip=0; idip<static_cast<int>(Antenna->GetNumberOfDipoles()); ++idip) {
  337. //#pragma omp critical
  338. //{
  339. //cout << "idip=" << idip << "\tthread num=" << omp_get_thread_num() << '\n';
  340. //}
  341. auto tDipole = Antenna->GetDipoleSource(idip);
  342. // Propogation constant in free space
  343. Real wavef = tDipole->GetAngularFrequency(ifreq) * std::sqrt(MU0*EPSILON0);
  344. SolveSingleTxRxPair(irec, Hankel.get(), wavef, ifreq, tDipole.get());
  345. } // dipole loop
  346. } // frequency loop
  347. } // OMP_PARALLEL BLOCK
  348. } // mask loop
  349. //if (Receivers->GetNumberOfPoints() > 100) {
  350. // ++ disp;
  351. //}
  352. } // receiver loop
  353. } // Polygonal parallel logic
  354. } else {
  355. std::cerr << "Lemma with WireAntenna class is currently broken"
  356. << " fix or use PolygonalWireAntenna\n" << std::endl;
  357. exit(EXIT_FAILURE);
  358. // TODO, getting wrong answer, curiously worKernel->GetKs() with MakeCalc, maybe
  359. // a threading issue, use SolveSingleTxRxPair maype instead of call
  360. // to MakeCalc3? !!!
  361. for (unsigned int idip=0; idip<Antenna->GetNumberOfDipoles(); ++idip) {
  362. this->Dipole = Antenna->GetDipoleSource(idip);
  363. MakeCalc3();
  364. //++disp;
  365. }
  366. this->Dipole = nullptr;
  367. }
  368. }
  369. #ifdef KIHALEE_EM1D
  370. void EMEarth1D::MakeCalc() {
  371. int itype; // 1 = elec, 2 = mag
  372. switch (this->Dipole->GetDipoleSourceType()) {
  373. case (GROUNDEDELECTRICDIPOLE) :
  374. itype = 1;
  375. break;
  376. case (MAGNETICDIPOLE) :
  377. itype = 2;
  378. break;
  379. case (UNGROUNDEDELECTRICDIPOLE) :
  380. std::cerr << "Fortran routine cannot calculate ungrounded"
  381. "electric dipole\n";
  382. default:
  383. throw NonValidDipoleType();
  384. }
  385. int ipol ;
  386. Vector3r Pol = this->Dipole->GetPolarisation();
  387. if (std::abs(Pol[0]-1) < 1e-5) {
  388. ipol = 1;
  389. } else if (std::abs(Pol[1]-1) < 1e-5) {
  390. ipol = 2;
  391. } else if (std::abs(Pol[2]-1) < 1e-5) {
  392. ipol = 3;
  393. } else {
  394. std::cerr << "Fortran routine cannot calculate arbitrary "
  395. "dipole polarisation, set to x, y, or z\n";
  396. }
  397. int nlay = Earth->GetNumberOfNonAirLayers();
  398. if (nlay > MAXLAYERS) {
  399. std::cerr << "FORTRAN CODE CAN ONLY HANDLE " << MAXLAYERS
  400. << " LAYERS\n";
  401. throw EarthModelWithMoreThanMaxLayers();
  402. }
  403. int nfreq = 1; // number of freqs
  404. int nfield; // field output 1 = elec, 2 = mag, 3 = both
  405. switch (FieldsToCalculate) {
  406. case E:
  407. nfield = 1;
  408. break;
  409. case H:
  410. nfield = 2;
  411. break;
  412. case BOTH:
  413. nfield = 3;
  414. break;
  415. default:
  416. throw 7;
  417. }
  418. int nres = Receivers->GetNumberOfPoints();
  419. int jtype = 3; // form ouf output,
  420. // 1 = horizontal,
  421. // 2 = down hole,
  422. // 3 = freq sounding
  423. // 4 = down hole logging
  424. int jgamma = 0; // Units 0 = MKS (H->A/m and E->V/m)
  425. // 1 = h->Gammas E->V/m
  426. double acc = 0.; // Tolerance
  427. // TODO, fix FORTRAN calls so these arrays can be nlay long, not
  428. // MAXLAYERS.
  429. // Model Parameters
  430. double *dep = new double[MAXLAYERS];
  431. dep[0] = 0.; // We always say air starts at 0
  432. for (int ilay=1; ilay<Earth->GetNumberOfLayers(); ++ilay) {
  433. dep[ilay] = dep[ilay-1] + Earth->GetLayerThickness(ilay);
  434. //std::cout << "Depth " << dep[ilay] << std::endl;
  435. }
  436. std::complex<double> *sig = new std::complex<double> [MAXLAYERS];
  437. for (int ilay=1; ilay<=nlay; ++ilay) {
  438. sig[ilay-1] = (std::complex<double>)(Earth->GetLayerConductivity(ilay));
  439. }
  440. // TODO, pass these into Fortran call, and return Cole-Cole model
  441. // parameters. Right now this does nothing
  442. //std::complex<double> *sus = new std::complex<double>[MAXLAYERS];
  443. //std::complex<double> *epr = new std::complex<double>[MAXLAYERS];
  444. // Cole-Cole model stuff
  445. double *susl = new double[MAXLAYERS];
  446. for (int ilay=1; ilay<=nlay; ++ilay) {
  447. susl[ilay-1] = Earth->GetLayerLowFreqSusceptibility(ilay);
  448. }
  449. double *sush = new double[MAXLAYERS];
  450. for (int ilay=1; ilay<=nlay; ++ilay) {
  451. sush[ilay-1] = Earth->GetLayerHighFreqSusceptibility(ilay);
  452. }
  453. double *sustau = new double[MAXLAYERS];
  454. for (int ilay=1; ilay<=nlay; ++ilay) {
  455. sustau[ilay-1] = Earth->GetLayerTauSusceptibility(ilay);
  456. }
  457. double *susalp = new double[MAXLAYERS];
  458. for (int ilay=1; ilay<=nlay; ++ilay) {
  459. susalp[ilay-1] = Earth->GetLayerBreathSusceptibility(ilay);
  460. }
  461. double *eprl = new double[MAXLAYERS];
  462. for (int ilay=1; ilay<=nlay; ++ilay) {
  463. eprl[ilay-1] = Earth->GetLayerLowFreqPermitivity(ilay);
  464. }
  465. double *eprh = new double[MAXLAYERS];
  466. for (int ilay=1; ilay<=nlay; ++ilay) {
  467. eprh[ilay-1] = Earth->GetLayerHighFreqPermitivity(ilay);
  468. }
  469. double *eprtau = new double[MAXLAYERS];
  470. for (int ilay=1; ilay<=nlay; ++ilay) {
  471. eprtau[ilay-1] = Earth->GetLayerTauPermitivity(ilay);
  472. }
  473. double *epralp = new double[MAXLAYERS];
  474. for (int ilay=1; ilay<=nlay; ++ilay) {
  475. epralp[ilay-1] = Earth->GetLayerBreathPermitivity(ilay);
  476. }
  477. // Freq stuff
  478. double finit = Dipole->GetFrequency(0); //(1000); // Starting freq
  479. double flimit = Dipole->GetFrequency(0); //(1000); // max freq
  480. double dlimit = Dipole->GetFrequency(0); //(1000); // difusion limit
  481. double lfinc(1); // no. freq per decade
  482. // tx location jtype != 4
  483. double txx = Dipole->GetLocation(0); // (0.);
  484. double txy = Dipole->GetLocation(1); // (0.);
  485. double txz = Dipole->GetLocation(2); // (0.);
  486. // rx position
  487. // TODO, fix Fortran program to not waste this memory
  488. // maybe
  489. const int MAXREC = 15;
  490. double *rxx = new double [MAXREC];
  491. double *rxy = new double [MAXREC];
  492. double *rxz = new double [MAXREC];
  493. std::complex<double> *ex = new std::complex<double>[MAXREC];
  494. std::complex<double> *ey = new std::complex<double>[MAXREC];
  495. std::complex<double> *ez = new std::complex<double>[MAXREC];
  496. std::complex<double> *hx = new std::complex<double>[MAXREC];
  497. std::complex<double> *hy = new std::complex<double>[MAXREC];
  498. std::complex<double> *hz = new std::complex<double>[MAXREC];
  499. int nres2 = MAXREC;
  500. int ii=0;
  501. for (ii=0; ii<nres-MAXREC; ii+=MAXREC) {
  502. for (int ir=0; ir<MAXREC; ++ir) {
  503. //Vector3r pos = Receivers->GetLocation(ii+ir);
  504. rxx[ir] = Receivers->GetLocation(ii+ir)[0];
  505. rxy[ir] = Receivers->GetLocation(ii+ir)[1];
  506. rxz[ir] = Receivers->GetLocation(ii+ir)[2];
  507. }
  508. em1dcall_(itype, ipol, nlay, nfreq, nfield, nres2, jtype,
  509. jgamma, acc, dep, sig, susl, sush, sustau, susalp,
  510. eprl, eprh, eprtau, epralp, finit, flimit, dlimit,
  511. lfinc, txx, txy, txz, rxx, rxy, rxz, ex, ey, ez,
  512. hx, hy, hz);
  513. // Scale By Moment
  514. for (int ir=0; ir<MAXREC; ++ir) {
  515. ex[ir] *= Dipole->GetMoment();
  516. ey[ir] *= Dipole->GetMoment();
  517. ez[ir] *= Dipole->GetMoment();
  518. hx[ir] *= Dipole->GetMoment();
  519. hy[ir] *= Dipole->GetMoment();
  520. hz[ir] *= Dipole->GetMoment();
  521. // Append values instead of setting them
  522. this->Receivers->AppendEfield(0, ii+ir, (Complex)(ex[ir]),
  523. (Complex)(ey[ir]),
  524. (Complex)(ez[ir]) );
  525. this->Receivers->AppendHfield(0, ii+ir, (Complex)(hx[ir]),
  526. (Complex)(hy[ir]),
  527. (Complex)(hz[ir]) );
  528. }
  529. }
  530. //ii += MAXREC;
  531. nres2 = 0;
  532. // Perform last positions
  533. for (int ir=0; ir<nres-ii; ++ir) {
  534. rxx[ir] = Receivers->GetLocation(ii+ir)[0];
  535. rxy[ir] = Receivers->GetLocation(ii+ir)[1];
  536. rxz[ir] = Receivers->GetLocation(ii+ir)[2];
  537. ++nres2;
  538. }
  539. em1dcall_(itype, ipol, nlay, nfreq, nfield, nres2, jtype,
  540. jgamma, acc, dep, sig, susl, sush, sustau, susalp,
  541. eprl, eprh, eprtau, epralp, finit, flimit, dlimit,
  542. lfinc, txx, txy, txz, rxx, rxy, rxz, ex, ey, ez,
  543. hx, hy, hz);
  544. // Scale By Moment
  545. for (int ir=0; ir<nres-ii; ++ir) {
  546. ex[ir] *= Dipole->GetMoment();
  547. ey[ir] *= Dipole->GetMoment();
  548. ez[ir] *= Dipole->GetMoment();
  549. hx[ir] *= Dipole->GetMoment();
  550. hy[ir] *= Dipole->GetMoment();
  551. hz[ir] *= Dipole->GetMoment();
  552. // Append values instead of setting them
  553. this->Receivers->AppendEfield(0, ii+ir, (Complex)(ex[ir]),
  554. (Complex)(ey[ir]),
  555. (Complex)(ez[ir]) );
  556. this->Receivers->AppendHfield(0, ii+ir, (Complex)(hx[ir]),
  557. (Complex)(hy[ir]),
  558. (Complex)(hz[ir]) );
  559. }
  560. delete [] sig;
  561. delete [] dep;
  562. //delete [] sus;
  563. //delete [] epr;
  564. delete [] susl;
  565. delete [] sush;
  566. delete [] susalp;
  567. delete [] sustau;
  568. delete [] eprl;
  569. delete [] eprh;
  570. delete [] epralp;
  571. delete [] eprtau;
  572. delete [] rxx;
  573. delete [] rxy;
  574. delete [] rxz;
  575. delete [] ex;
  576. delete [] ey;
  577. delete [] ez;
  578. delete [] hx;
  579. delete [] hy;
  580. delete [] hz;
  581. }
  582. #endif
  583. void EMEarth1D::SolveSingleTxRxPair (const int &irec, HankelTransform *Hankel, const Real &wavef, const int &ifreq,
  584. DipoleSource *tDipole) {
  585. ++icalcinner;
  586. // The PGI compilers fail on the below line, and others like it.
  587. Real rho = (Receivers->GetLocation(irec).head<2>() - tDipole->GetLocation().head<2>()).norm();
  588. //Real rho = ( ((Receivers->GetLocation(irec) - tDipole->GetLocation()).head(2)).eval() ).norm();
  589. tDipole->SetKernels( ifreq, FieldsToCalculate, Receivers, irec, Earth );
  590. Hankel->ComputeRelated( rho, tDipole->GetKernelManager() );
  591. tDipole->UpdateFields( ifreq, Hankel, wavef );
  592. }
  593. // void EMEarth1D::SolveSingleTxRxPair (const int &irec, std::shared_ptr<HankelTransform> Hankel, const Real &wavef, const int &ifreq,
  594. // std::shared_ptr<DipoleSource> tDipole) {
  595. // ++icalcinner;
  596. // Real rho = (Receivers->GetLocation(irec).head<2>() - tDipole->GetLocation().head<2>()).norm();
  597. // tDipole->SetKernels(ifreq, FieldsToCalculate, Receivers, irec, Earth);
  598. // //Hankel->ComputeRelated( rho, tDipole->GetKernelManager() );
  599. // //tDipole->UpdateFields( ifreq, Hankel, wavef );
  600. // }
  601. void EMEarth1D::SolveLaggedTxRxPair(const int &irec, HankelTransform* Hankel,
  602. const Real &wavef, const int &ifreq, PolygonalWireAntenna* antenna) {
  603. antenna->ApproximateWithElectricDipoles(Receivers->GetLocation(irec));
  604. // Determine the min and max arguments
  605. Real rhomin = 1e9;
  606. Real rhomax = 1e-9;
  607. for (unsigned int idip=0; idip<antenna->GetNumberOfDipoles(); ++idip) {
  608. auto tDipole = antenna->GetDipoleSource(idip);
  609. Real rho = (Receivers->GetLocation(irec).head<2>() - tDipole->GetLocation().head<2>()).norm();
  610. rhomin = std::min(rhomin, rho);
  611. rhomax = std::max(rhomax, rho);
  612. }
  613. // Determine number of lagged convolutions to do
  614. int nlag = 1; // (Key==0) We need an extra for some reason for stability? Maybe in Spline?
  615. Real lrho ( 1.0 * rhomax );
  616. while ( lrho > rhomin ) {
  617. nlag += 1;
  618. lrho *= Hankel->GetABSER();
  619. }
  620. auto tDipole = antenna->GetDipoleSource(0);
  621. tDipole->SetKernels(ifreq, FieldsToCalculate, Receivers, irec, Earth);
  622. // Instead we should pass the antenna into this so that Hankel hass all the rho arguments...
  623. Hankel->ComputeLaggedRelated( 1.0*rhomax, nlag, tDipole->GetKernelManager() );
  624. // Sort the dipoles by rho
  625. for (unsigned int idip=0; idip<antenna->GetNumberOfDipoles(); ++idip) {
  626. // Can we avoid these two lines, and instead vary the moment of the previous tDipole?
  627. // SetKernels is somewhat heavy
  628. auto rDipole = antenna->GetDipoleSource(idip);
  629. //tDipole->SetKernels(ifreq, FieldsToCalculate, Receivers, irec, Earth); // expensive, and not used
  630. tDipole->SetLocation( rDipole->GetLocation() );
  631. tDipole->SetMoment( rDipole->GetMoment() );
  632. tDipole->SetPolarisation( rDipole->GetPolarisation() );
  633. tDipole->SetupLight( ifreq, FieldsToCalculate, irec );
  634. // Pass Hankel2 a message here so it knows which one to return in Zgauss!
  635. Real rho = (Receivers->GetLocation(irec).head<2>() - tDipole->GetLocation().head<2>()).norm();
  636. Hankel->SetLaggedArg( rho );
  637. tDipole->UpdateFields( ifreq, Hankel, wavef );
  638. }
  639. }
  640. //////////////////////////////////////////////////////////
  641. // Thread safe OO Reimplimentation of KiHand's
  642. // EM1DNEW.for programme
  643. void EMEarth1D::MakeCalc3() {
  644. if ( Dipole == nullptr ) throw NullDipoleSource();
  645. if (Earth == nullptr) throw NullEarth();
  646. if (Receivers == nullptr) throw NullReceivers();
  647. #ifdef LEMMAUSEOMP
  648. #pragma omp parallel
  649. #endif
  650. { // OpenMP Parallel Block
  651. #ifdef LEMMAUSEOMP
  652. int tid = omp_get_thread_num();
  653. int nthreads = omp_get_num_threads();
  654. #else
  655. int tid=0;
  656. int nthreads=1;
  657. #endif
  658. auto tDipole = Dipole->Clone();
  659. std::shared_ptr<HankelTransform> Hankel;
  660. switch (HankelType) {
  661. case ANDERSON801:
  662. Hankel = FHTAnderson801::NewSP();
  663. break;
  664. case CHAVE:
  665. Hankel = GQChave::NewSP();
  666. break;
  667. case FHTKEY201:
  668. Hankel = FHTKey201::NewSP();
  669. break;
  670. case FHTKEY101:
  671. Hankel = FHTKey101::NewSP();
  672. break;
  673. case FHTKEY51:
  674. Hankel = FHTKey51::NewSP();
  675. break;
  676. case QWEKEY:
  677. Hankel = QWEKey::NewSP();
  678. break;
  679. default:
  680. std::cerr << "Hankel transform cannot be created\n";
  681. exit(EXIT_FAILURE);
  682. }
  683. if ( tDipole->GetNumberOfFrequencies() < Receivers->GetNumberOfPoints() ) {
  684. for (int ifreq=0; ifreq<tDipole->GetNumberOfFrequencies(); ++ifreq) {
  685. // Propogation constant in free space being input to Hankel
  686. Real wavef = tDipole->GetAngularFrequency(ifreq) * std::sqrt(MU0*EPSILON0);
  687. for (int irec=tid; irec<Receivers->GetNumberOfPoints(); irec+=nthreads) {
  688. SolveSingleTxRxPair(irec, Hankel.get(), wavef, ifreq, tDipole.get());
  689. }
  690. }
  691. } else {
  692. for (int irec=0; irec<Receivers->GetNumberOfPoints(); ++irec) {
  693. for (int ifreq=tid; ifreq<tDipole->GetNumberOfFrequencies(); ifreq+=nthreads) {
  694. // Propogation constant in free space being input to Hankel
  695. Real wavef = tDipole->GetAngularFrequency(ifreq) * std::sqrt(MU0*EPSILON0);
  696. SolveSingleTxRxPair(irec, Hankel.get(), wavef, ifreq, tDipole.get());
  697. }
  698. }
  699. }
  700. } // OpenMP Parallel Block
  701. }
  702. NullReceivers::NullReceivers() :
  703. runtime_error("nullptr RECEIVERS") {}
  704. NullAntenna::NullAntenna() :
  705. runtime_error("nullptr ANTENNA") {}
  706. NullInstrument::NullInstrument(LemmaObject* ptr) :
  707. runtime_error("nullptr INSTRUMENT") {
  708. std::cout << "Thrown by instance of "
  709. << ptr->GetName() << std::endl;
  710. }
  711. DipoleSourceSpecifiedForWireAntennaCalc::
  712. DipoleSourceSpecifiedForWireAntennaCalc() :
  713. runtime_error("DIPOLE SOURCE SPECIFIED FOR WIRE ANTENNA CALC"){}
  714. } // end of Lemma Namespace