diff --git a/Plugin/NearToFarField.cpp b/Plugin/NearToFarField.cpp new file mode 100644 index 0000000000000000000000000000000000000000..241806aa0b07c0827171951bb45901754328cfd1 --- /dev/null +++ b/Plugin/NearToFarField.cpp @@ -0,0 +1,410 @@ +// Gmsh - Copyright (C) 1997-2011 C. Geuzaine, J.-F. Remacle +// +// See the LICENSE.txt file for license information. Please report all +// bugs and problems to <gmsh@geuz.org>. + +#include "NearToFarField.h" +#include "Numeric.h" +#include "PViewOptions.h" +#include "MElement.h" +#include "GModel.h" + +StringXNumber NearToFarFieldOptions_Number[] = { + {GMSH_FULLRC, "Wavenumber", NULL, 1.}, + {GMSH_FULLRC, "FarDistance", NULL, 1.}, + {GMSH_FULLRC, "NumPointsPhi", NULL, 120}, + {GMSH_FULLRC, "NumPointsTheta", NULL, 60}, + {GMSH_FULLRC, "EView", NULL, 0}, + {GMSH_FULLRC, "HView", NULL, 1}, + {GMSH_FULLRC, "Normalize", NULL, 1}, + {GMSH_FULLRC, "dB", NULL, 1}, +}; + +extern "C" +{ + GMSH_Plugin *GMSH_RegisterNearToFarFieldPlugin() + { + return new GMSH_NearToFarFieldPlugin(); + } +} + +std::string GMSH_NearToFarFieldPlugin::getHelp() const +{ + return "Plugin(NearToFarField) computes the far field pattern " + "from the near electric and magnetic fields on a surface (regular grid) " + "enclosing the radiating device (antenna).\n\n" + "Parameters: the wavenumber, the far field distance (radious) and " + "angular discretisation, i.e. the number of divisions for " + "phi in [0, 2*Pi] and theta in [0, Pi].\n\n" + "If `View' < 0, the plugin is run on the current view.\n\n" + "Plugin(NearToFarField) creates one new view."; +} + +int GMSH_NearToFarFieldPlugin::getNbOptions() const +{ + return sizeof(NearToFarFieldOptions_Number) / sizeof(StringXNumber); +} + +StringXNumber *GMSH_NearToFarFieldPlugin::getOption(int iopt) +{ + return &NearToFarFieldOptions_Number[iopt]; +} + +void GMSH_NearToFarFieldPlugin::CartesianToSpherical(int numSteps, double theta, double phi, double **Fc, double **Fsp) +{ + double sTheta = sin(theta) ; + double cTheta = cos(theta) ; + double sPhi = sin(phi) ; + double cPhi = cos(phi) ; + + for(int step = 0; step < numSteps; step++){ + Fsp[step][0] = Fc[step][0] * sTheta * cPhi + Fc[step][1] * sTheta * sPhi + Fc[step][2] *cTheta ; + Fsp[step][1] = Fc[step][0] * cTheta * cPhi + Fc[step][1] * cTheta * sPhi - Fc[step][2]* sTheta ; + Fsp[step][2] =-Fc[step][0] * sPhi + Fc[step][1] * cPhi ; + } +} + +double GMSH_NearToFarFieldPlugin::getFarField(PViewData *eData, PViewData *hData, double k0, double r_far, double theta, double phi) +{ + + // theta in [0, pi] (elevation/polar angle) + // phi in [0, 2*pi] (azimuthal angle) + + double r[3] = { sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta) }; // Unit vector position + double Z0 = 120 * M_PI ; // free-space impedance + + int numSteps = eData->getNumTimeSteps() ; + int numEntities = eData->getNumEntities(0) ; + + double **N = new double * [numSteps] ; + double **Ns = new double * [numSteps] ; + double **L = new double * [numSteps] ; + double **Ls = new double * [numSteps] ; + for(int step = 0; step < numSteps; step++){ + N [step] = new double[3] ; + Ns[step] = new double[3] ; + L [step] = new double[3] ; + Ls[step] = new double[3] ; + } + + for (int step = 0; step < numSteps; step++) + for(int comp = 0; comp < 3; comp++){ + N[step][comp]= Ns[step][comp]= 0. ; + L[step][comp]= Ls[step][comp]= 0. ; + } + + // tag all the nodes with "0" (the default tag) + for(int step = 0; step < numSteps; step++){ + for(int ent = 0; ent < numEntities; ent++){ + for(int ele = 0; ele < eData->getNumElements(step, ent); ele++){ + if(eData->skipElement(step, ent, ele)) continue; + for(int nod = 0; nod < eData->getNumNodes(step, ent, ele); nod++) + eData->tagNode(step, ent, ele, nod, 0); + } + } + } + + for(int ent = 0; ent < eData->getNumEntities(0); ent++){ + for(int ele = 0; ele < eData->getNumElements(0, ent); ele++){ + if(eData->skipElement(0, ent, ele)) continue; + int numComp = eData->getNumComponents(0, ent, ele); + if(numComp != 3) continue ; + int numNodes = eData->getNumNodes(0, ent, ele); + double x[numNodes], y[numNodes], z[numNodes] ; + int tag[numNodes]; + + for(int nod = 0; nod < numNodes; nod++) + tag[nod] = eData->getNode(0, ent, ele, nod, x[nod], y[nod], z[nod]); + double n[3] = {0.,0.,0.}; + normal3points(x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2], n); + double Js[numSteps][numNodes*numComp], Ms[numSteps][numNodes*numComp] ; + + for(int step = 0; step < numSteps; step++){ + for(int nod = 0; nod < numNodes; nod++){ + if(tag[nod]) continue ; // already condisered in integration + for(int comp = 0; comp < numComp; comp++){ + eData->getValue(step, ent, ele, nod, comp, Ms[numSteps][numComp*nod + comp ]); + hData->getValue(step, ent, ele, nod, comp, Js[numSteps][numComp * nod + comp]); + } + } + } + + // Integration + double P0[3] = {x[0], y[0], z[0]} ; + double P1[3] = {x[1], y[1], z[1]} ; + double P2[3] = {x[2], y[2], z[2]} ; + double quad_area = triangle_area(P0,P1,P2); + + for(int nod = 0; nod < numNodes; nod++){ + double rr, r_nod[3] = {x[nod], y[nod], z[nod]}; + prosca(r_nod, r, &rr) ; + double cos_k0rr = quad_area*cos(k0*rr) ; + double sin_k0rr = quad_area*sin(k0*rr) ; + + N[0][0] += Js[0][numComp*nod + 0] * cos_k0rr - Js[1][numComp*nod + 0] * sin_k0rr ; + N[0][1] += Js[0][numComp*nod + 1] * cos_k0rr - Js[1][numComp*nod + 1] * sin_k0rr ; + N[0][2] += Js[0][numComp*nod + 2] * cos_k0rr - Js[1][numComp*nod + 2] * sin_k0rr ; + + N[1][0] += Js[0][numComp*nod + 0] * sin_k0rr + Js[1][numComp*nod + 0] * cos_k0rr ; + N[1][1] += Js[0][numComp*nod + 1] * sin_k0rr + Js[1][numComp*nod + 1] * cos_k0rr ; + N[1][2] += Js[0][numComp*nod + 2] * sin_k0rr + Js[1][numComp*nod + 2] * cos_k0rr ; + + L[0][0] += Ms[0][numComp*nod + 0] * cos_k0rr - Ms[1][numComp*nod + 0] * sin_k0rr ; + L[0][1] += Ms[0][numComp*nod + 1] * cos_k0rr - Ms[1][numComp*nod + 1] * sin_k0rr ; + L[0][2] += Ms[0][numComp*nod + 2] * cos_k0rr - Ms[1][numComp*nod + 2] * sin_k0rr ; + + L[1][0] += Ms[0][numComp*nod + 0] * sin_k0rr + Ms[1][numComp*nod + 0] * cos_k0rr ; + L[1][1] += Ms[0][numComp*nod + 1] * sin_k0rr + Ms[1][numComp*nod + 1] * cos_k0rr ; + L[1][2] += Ms[0][numComp*nod + 2] * sin_k0rr + Ms[1][numComp*nod + 2] * cos_k0rr ; + + eData->tagNode(0, ent, ele, nod, 1); + } + } + } + + + CartesianToSpherical(numSteps, theta, phi, N, Ns) ; + CartesianToSpherical(numSteps, theta, phi, L, Ls) ; + + // E_r radial component is negligible in far field + double E_theta[2] ; + double E_phi[2] ; + double k0_over_4pir = k0/(4*M_PI*r_far) ; + double cos_k0r = cos(k0*r_far) ; + double sin_k0r = sin(k0*r_far) ; + + // Elevation component + E_theta[0] = -k0_over_4pir * ( (Ls[0][2] + Z0 * Ns[0][1]) * sin_k0r - + (Ls[1][2] + Z0 * Ns[1][1]) * cos_k0r ) ; + E_theta[1] = -k0_over_4pir * ( (Ls[0][2] + Z0 * Ns[0][1]) * cos_k0r + + (Ls[1][2] + Z0 * Ns[1][1]) * sin_k0r ) ; + // Azimuthal component + E_phi[0] = k0_over_4pir * ( (Ls[0][1] - Z0 * Ns[0][2]) * sin_k0r - + (Ls[1][1] - Z0 * Ns[1][2]) * cos_k0r ) ; + E_phi[1] = k0_over_4pir * ( (Ls[0][1] - Z0 * Ns[0][2]) * cos_k0r + + (Ls[1][1] - Z0 * Ns[1][2]) * sin_k0r ) ; + + + //printf("Ephi %g %g \n ", E_phi[0], E_phi[1]) ; + //printf("Etheta %g %g\n ", E_theta[0], E_theta[1]) ; + + double farF = 1./2./Z0 * ( (E_theta[0]*E_theta[0] + E_theta[1]*E_theta[1]) + (E_phi[0]*E_phi[0]+E_phi[1] *E_phi[1]) ) ; + + for (int step = 0; step < numSteps; step++){ + delete [] N [step] ; + delete [] Ns[step] ; + delete [] L [step] ; + delete [] Ls[step] ; + } + delete [] N ; + delete [] Ns ; + delete [] L ; + delete [] Ls ; + + return farF ; + +} + + +PView *GMSH_NearToFarFieldPlugin::execute(PView * v) +{ + + double _k0 = (double)NearToFarFieldOptions_Number[0].def; + double _r_far = (double)NearToFarFieldOptions_Number[1].def; + int _NbPhi = (int)NearToFarFieldOptions_Number[2].def; + int _NbThe = (int)NearToFarFieldOptions_Number[3].def; + int _eView = (int)NearToFarFieldOptions_Number[4].def; + int _hView = (int)NearToFarFieldOptions_Number[5].def; + bool _normalize = (bool)NearToFarFieldOptions_Number[6].def; + bool _dB = (bool)NearToFarFieldOptions_Number[7].def; + + PView *ve = getView(_eView, v); + if(!ve){ + Msg::Error("NearToFarField plugin could not find EView %i", _eView); + return v; + } + PView *vh = getView(_hView, v); + if(!vh){ + Msg::Error("NearToFarField plugin could not find HView %i", _hView); + return v; + } + PViewData *eData = ve->getData() ; + PViewData *hData = vh->getData() ; + + if(eData->getNumEntities() != hData->getNumEntities() || + eData->getNumElements() != hData->getNumElements() || + eData->getNumTimeSteps()!= hData->getNumTimeSteps()){ + Msg::Error("Incompatible views for e-field and h-field"); + return v; + } + + if(eData->getNumTimeSteps()!= 2){ + Msg::Error("NearToFarField Plugin only implemented for frequency domain, fields must be complex"); + return v; + } + + // Center of the Far Field sphere + double x0 = eData->getBoundingBox().center().x(); + double y0 = eData->getBoundingBox().center().y(); + double z0 = eData->getBoundingBox().center().z(); + if(x0 != hData->getBoundingBox().center().x() || + y0 != hData->getBoundingBox().center().y() || + z0 != hData->getBoundingBox().center().z()){ + Msg::Error("EView %i and HView %i must be given on the same grid", _eView, _hView); + return v; + } + + + // View for far field: represented on a sphere of radious determined by the size of the BoundingBox + PView *vf = new PView(); + PViewDataList *dataFar = getDataList(vf); + + double phi, dPhi = 2*M_PI/_NbPhi ; + double theta, dTheta = M_PI/_NbThe ; + double ffmax = 0.0 ; + + double **allPhi = new double *[_NbPhi+1] ; + double **allThe = new double *[_NbPhi+1] ; + double **farF = new double *[_NbPhi+1] ; + for (int i = 0; i <= _NbPhi; i++){ + allPhi[i] = new double [_NbThe+1] ; + allThe[i] = new double [_NbThe+1] ; + farF[i] = new double [_NbThe+1] ; + } + + ve->setChanged(true); + vh->setChanged(true); + + for(int step = 0; step < eData->getNumTimeSteps(); step++){ + // tag all the nodes with "0" (the default tag) + for(int ent = 0; ent < eData->getNumEntities(step); ent++){ + for(int ele = 0; ele < eData->getNumElements(step, ent); ele++){ + if(eData->skipElement(step, ent, ele)) continue; + if(hData->skipElement(step, ent, ele)) continue; + for(int nod = 0; nod < eData->getNumNodes(step, ent, ele); nod++){ + eData->tagNode(step, ent, ele, nod, 0); + hData->tagNode(step, ent, ele, nod, 0); + } + } + } + + for(int ent = 0; ent < eData->getNumEntities(step); ent++){ + for(int ele = 0; ele < eData->getNumElements(step, ent); ele++){ + if(eData->skipElement(0, ent, ele)) continue; + if(hData->skipElement(0, ent, ele)) continue; + int numComp = eData->getNumComponents(0, ent, ele); + if(numComp != 3) continue ; + int numNodes = eData->getNumNodes(0, ent, ele); + double x[numNodes], y[numNodes], z[numNodes] ; + int tag[numNodes]; + + for(int nod = 0; nod < numNodes; nod++) + tag[nod] = eData->getNode(step, ent, ele, nod, x[nod], y[nod], z[nod]); + double n[3] = {0.,0.,0.}; + normal3points(x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2], n); + double valE[numNodes*numComp], valH[numNodes*numComp] ; + + for(int nod = 0; nod < numNodes; nod++){ + if(tag[nod]) continue ; // already considered + for(int comp = 0; comp < numComp; comp++){ + eData->getValue(step, ent, ele, nod, comp, valE[numComp * nod + comp]); + hData->getValue(step, ent, ele, nod, comp, valH[numComp * nod + comp]); + } + double H[3] = { valH[numComp * nod + 0], valH[numComp * nod + 1], valH[numComp * nod + 2] } ; + double E[3] = { valE[numComp * nod + 0], valE[numComp * nod + 1], valE[numComp * nod + 2] } ; + double J[3], M[3] ; + prodve(n, H, J) ; // Js = n x H ; Surface electric current + prodve(E, n, M) ; // Ms = - n x E ; Surface magnetic current + + for(int comp = 0; comp < numComp; comp++){ + eData->setValue(step, ent, ele, nod, comp, M[comp]); + hData->setValue(step, ent, ele, nod, comp, J[comp]); + eData->tagNode(step, ent, ele, nod, 1); + hData->tagNode(step, ent, ele, nod, 1); + } + } + } + } + } + + eData->finalize(); + hData->finalize(); + +//**************************************** + for (int i = 0; i <= _NbPhi; i++){ + phi = i*dPhi ; + for (int j = 0; j <= _NbThe; j++){ + theta = j * dTheta ; + allPhi[i][j] = phi ; + allThe[i][j] = theta ; + farF[i][j] = getFarField(eData, hData, _k0, _r_far, theta, phi) ; + ffmax = (ffmax < farF[i][j]) ? farF[i][j] : ffmax ; + } + } + + if(_normalize){ + for (int i = 0; i <= _NbPhi; i++) + for (int j = 0; j <= _NbThe; j++) + if(ffmax!=0.0) + farF[i][j] /= ffmax ; + else + Msg::Warning("Far field pattern not normalized, max value = %g", ffmax); + } + + // Constructing sphere for visualization + // centered at center of bb and with radious relative to the bb size + double r_bb[3] = { eData->getBoundingBox().max().x()-eData->getBoundingBox().min().x(), + eData->getBoundingBox().max().y()-eData->getBoundingBox().min().y(), + eData->getBoundingBox().max().z()-eData->getBoundingBox().min().z() }; + double r_sph = norm3(r_bb) ; + r_sph = (r_sph) ? r_sph/2 : 1./2. ; // radious of sphere for visu + + for (int i = 0; i < _NbPhi; i++){ + for (int j = 0; j < _NbThe; j++){ + double P1[3] = { x0 + r_sph * farF[i ][j ] * sin(allThe[i ][j ]) * cos(allPhi[i ][j ]), + y0 + r_sph * farF[i ][j ] * sin(allThe[i ][j ]) * sin(allPhi[i ][j ]), + z0 + r_sph * farF[i ][j ] * cos(allThe[i ][j ]) } ; + double P2[3] = { x0 + r_sph * farF[i+1][j ] * sin(allThe[i+1][j ]) * cos(allPhi[i+1][j ]), + y0 + r_sph * farF[i+1][j ] * sin(allThe[i+1][j ]) * sin(allPhi[i+1][j ]), + z0 + r_sph * farF[i+1][j ] * cos(allThe[i+1][j ]) } ; + double P3[3] = { x0 + r_sph * farF[i+1][j+1] * sin(allThe[i+1][j+1]) * cos(allPhi[i+1][j+1]), + y0 + r_sph * farF[i+1][j+1] * sin(allThe[i+1][j+1]) * sin(allPhi[i+1][j+1]), + z0 + r_sph * farF[i+1][j+1] * cos(allThe[i+1][j+1]) } ; + double P4[3] = { x0 + r_sph * farF[i ][j+1] * sin(allThe[i ][j+1]) * cos(allPhi[i ][j+1]), + y0 + r_sph * farF[i ][j+1] * sin(allThe[i ][j+1]) * sin(allPhi[i ][j+1]), + z0 + r_sph * farF[i ][j+1] * cos(allThe[i ][j+1]) } ; + + dataFar->SQ.push_back(P1[0]); dataFar->SQ.push_back(P2[0]); dataFar->SQ.push_back(P3[0]); dataFar->SQ.push_back(P4[0]); + dataFar->SQ.push_back(P1[1]); dataFar->SQ.push_back(P2[1]); dataFar->SQ.push_back(P3[1]); dataFar->SQ.push_back(P4[1]); + dataFar->SQ.push_back(P1[2]); dataFar->SQ.push_back(P2[2]); dataFar->SQ.push_back(P3[2]); dataFar->SQ.push_back(P4[2]); + (dataFar->NbSQ)++; + if(!_dB){ + dataFar->SQ.push_back(farF[i ][j ]); + dataFar->SQ.push_back(farF[i+1][j ]); + dataFar->SQ.push_back(farF[i+1][j+1]); + dataFar->SQ.push_back(farF[i ][j+1]); + } + else{ + dataFar->SQ.push_back(log10(farF[i ][j ])); + dataFar->SQ.push_back(log10(farF[i+1][j ])); + dataFar->SQ.push_back(log10(farF[i+1][j+1])); + dataFar->SQ.push_back(log10(farF[i ][j+1])); + } + } + } + + for (int i = 0; i <= _NbPhi; i++){ + delete[] allPhi[i] ; + delete[] allThe[i] ; + delete[] farF[i]; + } + delete [] allPhi ; + delete [] allThe ; + delete [] farF ; + + dataFar->setName("_NearToFarField"); + dataFar->setFileName("_NearToFarField.pos"); + dataFar->finalize(); + + return vf; +} diff --git a/Plugin/NearToFarField.h b/Plugin/NearToFarField.h new file mode 100644 index 0000000000000000000000000000000000000000..51d0ecf12c8e0e3a5bb0bface920e26bfc10fbfb --- /dev/null +++ b/Plugin/NearToFarField.h @@ -0,0 +1,35 @@ +// Gmsh - Copyright (C) 1997-2011 C. Geuzaine, J.-F. Remacle +// +// See the LICENSE.txt file for license information. Please report all +// bugs and problems to <gmsh@geuz.org>. + +#ifndef _NEARTOFARFIELD_H_ +#define _NEARTOFARFIELD_H_ + +#include "Plugin.h" + +extern "C" +{ + GMSH_Plugin *GMSH_RegisterNearToFarFieldPlugin(); +} + +class GMSH_NearToFarFieldPlugin : public GMSH_PostPlugin +{ + public: + GMSH_NearToFarFieldPlugin(){} + std::string getName() const { return "NearToFarField"; } + std::string getShortHelp() const + { + return "Compute Far Field pattern from Near Field on a surface"; + } + std::string getHelp() const; + int getNbOptions() const; + StringXNumber* getOption(int iopt); + PView *execute(PView *); + + static double getFarField(PViewData *eData, PViewData *hData, double k0, double r_far, double theta, double phi) ; + static void CartesianToSpherical(int numSteps, double theta, double phi, double **Fc, double **Fsp) ; + +}; + +#endif