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

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