Lemma is an Electromagnetics API
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

EMEarth1D.cpp 37KB

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