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

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