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

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