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 36KB

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