Browse Source

Addition of Matlab.mex support. Currently only hantenna is wrapped as a

.mex. Additionally, the latest Eigen release is now used.
master
Trevor 1 year ago
parent
commit
4fecc41995

+ 1
- 1
CMake/SuperBuild.cmake View File

@@ -8,7 +8,7 @@ else()
8 8
         ExternalProject_Add(EIGEN
9 9
 	    GIT_REPOSITORY "https://gitlab.com/libeigen/eigen.git"
10 10
         UPDATE_COMMAND "" 
11
-	    GIT_TAG "3.3.7" 
11
+	    GIT_TAG "3.4.0" 
12 12
    	    PREFIX ${CMAKE_CURRENT_BINARY_DIR}/external/eigen
13 13
    	    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}
14 14
 		#CONFIGURE_COMMAND ""

+ 8
- 2
CMakeLists.txt View File

@@ -14,7 +14,7 @@ SET_PROPERTY(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE)
14 14
 # Lemma versioning, set Major, minor, and patch here                                               #
15 15
 set(LEMMA_VERSION_MAJOR "0")                                                                       #
16 16
 set(LEMMA_VERSION_MINOR "4")                                                                       #
17
-set(LEMMA_VERSION_PATCH "0")                                                                       #
17
+set(LEMMA_VERSION_PATCH "1")                                                                       #
18 18
 set(LEMMA_VERSION "\"${LEMMA_VERSION_MAJOR}.${LEMMA_VERSION_MINOR}.${LEMMA_VERSION_PATCH}\"")      #
19 19
 set(LEMMA_VERSION_NOQUOTES "${LEMMA_VERSION_MAJOR}.${LEMMA_VERSION_MINOR}.${LEMMA_VERSION_PATCH}") #
20 20
 ####################################################################################################
@@ -49,6 +49,7 @@ option ( LEMMA_BUILD_DOCUMENTATION  "Build Doxygen man pages" OFF )
49 49
 option ( LEMMA_VTK8_SUPPORT         "VTK 8.x library for visualisation and grids" OFF )
50 50
 option ( LEMMA_VTK9_SUPPORT         "VTK 9.x library for visualisation and grids" OFF )
51 51
 option ( LEMMA_PYTHON3_BINDINGS     "Compile Python 3 bindings" OFF )
52
+option ( LEMMA_MATLAB_MEX           "Build Matlab mex files for selected examples" OFF )
52 53
 
53 54
 # We end up using this for all builds, TODO remove this variable but follow same path
54 55
 #option (CMAKE_CROSSCOMPILING "Target different arch than you are on" OFF)
@@ -72,7 +73,7 @@ if (CMAKE_CROSSCOMPILING)
72 73
 #    endif()
73 74
 
74 75
     find_package (yaml-cpp 0.6    PATHS ${CMAKE_INSTALL_PREFIX}  NO_DEFAULT_PATH) # Serialisation of classes 
75
-    find_package (Eigen3   3.3    PATHS ${CMAKE_INSTALL_PREFIX}  NO_DEFAULT_PATH) # Matrix/Vector & Math
76
+    find_package (Eigen3   3.4    PATHS ${CMAKE_INSTALL_PREFIX}  NO_DEFAULT_PATH) # Matrix/Vector & Math
76 77
 
77 78
     if (LEMMA_PYTHON3_BINDINGS)
78 79
         find_package(pybind11 PATHS ${CMAKE_INSTALL_PREFIX} NO_DEFAULT_PATH)           # Python 3 bindings
@@ -290,6 +291,11 @@ if (LEMMA_PYTHON3_BINDINGS)
290 291
     install ( DIRECTORY python/pyLemma DESTINATION ${CMAKE_INSTALL_PREFIX}/pyLemma/ ) 
291 292
 endif()
292 293
 
294
+if (LEMMA_MATLAB_MEX)
295
+    find_package(Matlab REQUIRED)
296
+    #matlab_add_mex(NAME mex_file_name SRC source_file.cpp)   
297
+endif()
298
+
293 299
 ####################
294 300
 # MSVC error fix 
295 301
 ####################

+ 4
- 0
Modules/FDEM1D/CMakeLists.txt View File

@@ -40,6 +40,10 @@ if (LEMMA_PYTHON3_BINDINGS)
40 40
 	add_subdirectory(python)
41 41
 endif()
42 42
 
43
+if (LEMMA_MATLAB_MEX)
44
+	add_subdirectory(matlab)
45
+endif()
46
+
43 47
 # Install
44 48
 install ( TARGETS fdem1d DESTINATION ${CMAKE_INSTALL_PREFIX}/lib )
45 49
 install ( FILES include/FDEM1D  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/Lemma ) 

+ 3
- 3
Modules/FDEM1D/examples/Hantenna.cpp View File

@@ -204,9 +204,9 @@ const char *buildString = __DATE__ ", " __TIME__;
204 204
 	for (int iy=0; iy<ny; ++iy) {
205 205
 	for (int ix=0; ix<nx; ++ix) {
206 206
         hreal << receivers->GetLocation(i).transpose() << "\t";
207
- 		//hreal << receivers->GetHfield(0, i).transpose() << "\n"; // ( complex, notation )
208
- 		hreal << receivers->GetHfield(0, i).transpose().real() << "\t";
209
- 		hreal << receivers->GetHfield(0, i).transpose().imag() << "\n";
207
+ 		hreal << receivers->GetHfield(0, i).transpose() << "\n"; // ( complex, notation )
208
+ 		//hreal << receivers->GetHfield(0, i).transpose().real() << "\t";
209
+ 		//hreal << receivers->GetHfield(0, i).transpose().imag() << "\n";
210 210
         ++i;
211 211
     }
212 212
     }

+ 9
- 0
Modules/FDEM1D/matlab/CMakeLists.txt View File

@@ -0,0 +1,9 @@
1
+# Builds Matlab executable (mex) files
2
+matlab_add_mex(NAME hantenna SRC hantenna_mex.cpp LINK_TO lemmacore fdem1d)
3
+
4
+install(TARGETS hantenna
5
+	COMPONENT python
6
+	RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/matlab/"
7
+	LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/matlab/"
8
+	ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/matlab/"
9
+)

+ 337
- 0
Modules/FDEM1D/matlab/hantenna_mex.cpp View File

@@ -0,0 +1,337 @@
1
+/* MyMEXFunction
2
+ * Adds second input to each  
3
+ * element of first input
4
+ * a = MyMEXFunction(a,b);
5
+*/
6
+
7
+#include "mex.hpp"
8
+#include "mexAdapter.hpp"
9
+#include "MatlabDataArray.hpp"
10
+#include "LemmaCore"
11
+#include "FDEM1D"
12
+#include "timer.h"
13
+#include <Eigen/Core>
14
+
15
+#if defined(__clang__)
16
+	/* Clang/LLVM. ---------------------------------------------- */
17
+    const char* compiler = "clang";
18
+    const char* ver = __VERSION__;
19
+#elif defined(__ICC) || defined(__INTEL_COMPILER)
20
+	/* Intel ICC/ICPC. ------------------------------------------ */
21
+    const char* compiler = "icpc";
22
+    const char* ver = __VERSION__;
23
+#elif defined(__GNUC__) || defined(__GNUG__)
24
+	/* GNU GCC/G++. --------------------------------------------- */
25
+    const char* compiler = "gcc (GCC) ";//  __VERSION__;
26
+    const char* ver = __VERSION__;
27
+#elif defined(_MSC_VER)
28
+	/* Microsoft Visual Studio. --------------------------------- */
29
+    const char* compiler = "msvc ";
30
+    const int ver = _MSC_FULL_VER;
31
+#elif defined(__PGI)
32
+	/* Portland Group PGCC/PGCPP. ------------------------------- */
33
+    const char* compiler = "pgc";
34
+#endif
35
+
36
+using namespace Lemma;
37
+using namespace matlab::data;
38
+using matlab::mex::ArgumentList;
39
+
40
+std::vector<Real>  readinpfile(const std::string& fname);
41
+std::vector<std::string>  readinpfile2(const std::string& fname);
42
+
43
+
44
+class MexFunction : public matlab::mex::Function {
45
+
46
+public:
47
+
48
+    void operator()(ArgumentList outputs, ArgumentList inputs) {
49
+	
50
+	const char *buildString = __DATE__ ", " __TIME__;
51
+    	std::cout
52
+    << "===========================================================================\n"
53
+    << "Lemma " << LEMMA_VERSION << "\n"
54
+    << "[" << compiler << " " << ver << " " <<  buildString << "]\n"
55
+    << "This program is part of Lemma, a geophysical modelling and inversion API. \n"
56
+    << "     This Source Code Form is subject to the terms of the Mozilla Public\n"
57
+    << "     License, v. 2.0. If a copy of the MPL was not distributed with this\n"
58
+    << "     file, You can obtain one at http://mozilla.org/MPL/2.0/. \n"
59
+    << "Copyright (C) 2018 Lemma Software \n"
60
+    << "More information may be found at: https://lemmasoftware.org\n"
61
+    << "                                     info@lemmasoftware.org\n"
62
+    << "===========================================================================\n\n"
63
+    << "Hantenna calculates the harmonic H field from polygonal wire loop sources\n";
64
+
65
+	// TODO add this
66
+        //checkArguments(outputs, inputs);
67
+       
68
+        // TODO, add this to check arguments and formalize 	
69
+	if (inputs.size() < 4) {
70
+            std::cout << "usage: hantenna(trans.inp cond.inp points.inp config.inp) \n";
71
+	    ArrayFactory ef;
72
+	    
73
+            outputs[0] = ef.createCharArray("Insufficient inputs");
74
+            return;
75
+	    //exit(EXIT_SUCCESS); 
76
+	}
77
+
78
+	#ifdef LEMMAUSEOMP
79
+        std::cout << "OpenMP is using " << omp_get_max_threads() << " threads" << std::endl;
80
+        #endif
81
+
82
+	/*
83
+	std::vector<Real> Trans   = readinpfile(std::string(inputs[1]));
84
+        std::vector<Real> CondMod = readinpfile(std::string(inputs[2]));
85
+        std::vector<Real> Points  = readinpfile(std::string(inputs[3]));
86
+        std::vector<std::string> config  = readinpfile2(std::string(inputs[4]));
87
+	*/
88
+
89
+	// Let's fix this inputs thing
90
+	matlab::data::CharArray charVector2 = inputs[0];
91
+	std::string TransFile = charVector2.toAscii();
92
+
93
+	std::vector<Real> Trans   = readinpfile( "trans.inp" );
94
+        std::vector<std::string> config  = readinpfile2("config.inp");
95
+        std::vector<Real> CondMod = readinpfile("cond.inp");
96
+        std::vector<Real> Points  = readinpfile("points.inp");
97
+
98
+	//////////////////////////////////////
99
+        // Define transmitter
100
+        auto trans = PolygonalWireAntenna::NewSP();
101
+            trans->SetNumberOfPoints((int)(Trans[0]));
102
+            int ip=1;
103
+            for ( ; ip<=(int)(Trans[0])*2; ip+=2) {
104
+                trans->SetPoint(ip/2, Vector3r (Trans[ip], Trans[ip+1], -1e-3));
105
+                //trans->SetPoint(ip/2, Vector3r (Trans[ip], Trans[ip+1], 50.));
106
+            }
107
+ 	    trans->SetNumberOfFrequencies(1);
108
+ 	    trans->SetFrequency(0, Trans[ip]);
109
+            trans->SetCurrent(Trans[ip+1]);
110
+            trans->SetMinDipoleRatio(atof(config[1].c_str()));
111
+            trans->SetMinDipoleMoment(atof(config[2].c_str()));
112
+            trans->SetMaxDipoleMoment(atof(config[3].c_str()));
113
+
114
+        /////////////////////////////////////
115
+        // Field calculations
116
+        auto receivers = FieldPoints::NewSP();
117
+            int nx = (int)Points[0];
118
+            int ny = (int)Points[1];
119
+            int nz = (int)Points[2];
120
+            Real ox = Points[3];
121
+            Real oy = Points[4];
122
+            Real oz = Points[5];
123
+     	    Vector3r loc;
124
+            VectorXr dx(nx-1);
125
+            VectorXr dy(ny-1);
126
+            VectorXr dz(nz-1);
127
+            ip = 6;
128
+            int ir = 0;
129
+            for ( ; ip <6+nx-1; ++ip) {
130
+                dx[ir] = Points[ip];
131
+                ++ir;
132
+            }
133
+            ir = 0;
134
+            for ( ; ip <6+ny-1+nx-1; ++ip) {
135
+                dy[ir] = Points[ip];
136
+                ++ir;
137
+            }
138
+            ir = 0;
139
+            for ( ; ip <6+nz-1+ny-1+nx-1; ++ip) {
140
+                dz[ir] = Points[ip];
141
+                ++ir;
142
+            }
143
+            receivers->SetNumberOfPoints(nx*ny*nz);
144
+            ir = 0;
145
+            Real pz  = oz;
146
+            for (int iz=0; iz<nz; ++iz) {
147
+                Real py    =  oy;
148
+                for (int iy=0; iy<ny; ++iy) {
149
+                    Real px    =  ox;
150
+                    for (int ix=0; ix<nx; ++ix) {
151
+                        loc << px, py, pz;
152
+                        receivers->SetLocation(ir, loc);
153
+                        if (ix < nx-1) px += dx[ix];
154
+                        ++ ir;
155
+                    }
156
+                   if (iy<ny-1) py += dy[iy];
157
+                }
158
+                if (iz<nz-1) pz += dz[iz];
159
+            }
160
+
161
+	////////////////////////////////////
162
+        // Define model
163
+        auto earth = LayeredEarthEM::NewSP();
164
+            VectorXcr sigma;
165
+            VectorXr  thick;
166
+ 	    earth->SetNumberOfLayers(static_cast<int>(CondMod[0])+1);
167
+ 	    sigma.resize(static_cast<int>(CondMod[0])+1); sigma(0) = 0; // airlayer
168
+            thick.resize(static_cast<int>(CondMod[0])-1);
169
+            int ilay=1;
170
+            for ( ; ilay/2<CondMod[0]-1; ilay+=2) {
171
+                sigma(ilay/2+1) =  1./CondMod[ilay];
172
+                thick(ilay/2) =  CondMod[ilay+1];
173
+            }
174
+            sigma(ilay/2+1) = 1./ CondMod[ilay];
175
+	    earth->SetLayerConductivity(sigma);
176
+            if (thick.size() > 0) earth->SetLayerThickness(thick);
177
+
178
+	auto EmEarth = EMEarth1D::NewSP();
179
+            EmEarth->AttachWireAntenna(trans);
180
+            EmEarth->AttachLayeredEarthEM(earth);
181
+            EmEarth->AttachFieldPoints(receivers);
182
+            EmEarth->SetFieldsToCalculate(H);
183
+            EmEarth->SetHankelTransformMethod(string2Enum<HANKELTRANSFORMTYPE>(config[0]));
184
+
185
+        ///////////////////////////////////////////////
186
+        // Keep track of time
187
+	jsw_timer timer;
188
+  	timer.begin();
189
+        clock_t launch = clock();
190
+        EmEarth->CalculateWireAntennaFields(false);    // false=status bar, doesn't work in Matlab well 
191
+        Real paTime = timer.end();
192
+
193
+        std::cout << "\n\n===========================================\ncalc. real time: " << paTime/60. << "\t[m]\n";
194
+        std::cout << "calc. user time: " <<  (clock()-launch)/CLOCKS_PER_SEC/60.   << "\t[CPU m]"
195
+                  << std::endl;
196
+
197
+
198
+	////////////////////////////////////////////////////////////////////
199
+	// This is kind of ugly, but the Matlab conversion is really picky
200
+	// Convert C++ arrays to Matlab 
201
+        
202
+	ArrayFactory f;
203
+	StructArray S = f.createStructArray({ 3,1 }, { "Component", "Data", "Units" });
204
+	
205
+	S[0]["Component"] = f.createCharArray("H_x");
206
+	S[0]["Units"] = f.createCharArray("A/m");
207
+	Eigen::Array<Complex, Eigen::Dynamic, 1> Hx = receivers->GetHfield(0).row(0).array(); 
208
+	std::vector<Complex> Hxv = std::vector<Complex>(Hx.begin(), Hx.end());
209
+	matlab::data::TypedArray<Complex> Hxm = f.createArray<Complex>({1, Hxv.size()}, Hxv.data(), Hxv.data()+Hxv.size());
210
+	S[0]["Data"] = std::move(Hxm);
211
+
212
+        S[1]["Component"] = f.createCharArray("H_y");
213
+	S[1]["Units"] = f.createCharArray("A/m");
214
+	Eigen::Array<Complex, Eigen::Dynamic, 1> Hy = receivers->GetHfield(0).row(1).array(); 
215
+	std::vector<Complex> Hyv = std::vector<Complex>(Hy.begin(), Hy.end());
216
+	matlab::data::TypedArray<Complex> Hym = f.createArray<Complex>({1, Hyv.size()}, Hyv.data(), Hyv.data()+Hyv.size());
217
+	S[1]["Data"] = std::move(Hym);
218
+        
219
+	S[2]["Component"] = f.createCharArray("H_z");
220
+	S[2]["Units"] = f.createCharArray("A/m");
221
+	Eigen::Array<Complex, Eigen::Dynamic, 1> Hz = receivers->GetHfield(0).row(2).array(); 
222
+	std::vector<Complex> Hzv = std::vector<Complex>(Hz.begin(), Hz.end());
223
+	matlab::data::TypedArray<Complex> Hzm = f.createArray<Complex>({1, Hzv.size()}, Hzv.data(), Hzv.data()+Hzv.size());
224
+	S[2]["Data"] = std::move(Hzm);
225
+	
226
+
227
+	outputs[0] = std::move(S);
228
+
229
+	////////////////////////////////////
230
+        // Report, file write is s l o w 
231
+	/*
232
+        std::fstream hrep("hfield.yaml", std::ios::out);
233
+        std::fstream hreal("hfield.dat", std::ios::out);
234
+
235
+        hrep << *EmEarth << std::endl;
236
+        hrep.close();
237
+
238
+        hreal << "// Right hand coordinate system, z is positive down\n";
239
+        hreal << "// x[m]\ty[m]\tz[m]\tRe(Hx[A/m])\tRe(Hy[A/m])\tRe(Hz[A/m])\tIm(Hx)\tIm(Hy)\tIm(Hz)\n";
240
+        hreal.precision(8);
241
+        int i=0;
242
+	for (int iz=0; iz<nz; ++iz) {
243
+	for (int iy=0; iy<ny; ++iy) {
244
+	for (int ix=0; ix<nx; ++ix) {
245
+            hreal << receivers->GetLocation(i).transpose() << "\t";
246
+ 		hreal << receivers->GetHfield(0, i).transpose() << "\n"; // ( complex, notation )
247
+ 		//hreal << receivers->GetHfield(0, i).transpose().real() << "\t";
248
+ 		//hreal << receivers->GetHfield(0, i).transpose().imag() << "\n";
249
+            ++i;
250
+        }
251
+        }
252
+        }
253
+        hreal.close();
254
+        */ 
255
+
256
+        // Demo stuff
257
+	/*
258
+	const double offSet = inputs[0][0];
259
+	TypedArray<double> doubleArray = std::move(inputs[1]);
260
+        for (auto& elem : doubleArray) {
261
+            elem += offSet;
262
+        }
263
+	outputs[0] = doubleArray;
264
+	*/
265
+    }
266
+
267
+    void checkArguments(ArgumentList outputs, ArgumentList inputs) {
268
+
269
+        // Get pointer to engine
270
+        std::shared_ptr<matlab::engine::MATLABEngine> matlabPtr = getEngine();
271
+
272
+        // Get array factory
273
+        ArrayFactory factory;
274
+
275
+        // Check first input argument
276
+        if (inputs[0].getType() != ArrayType::CHAR ||
277
+            inputs[0].getNumberOfElements() != 1)
278
+        {
279
+            matlabPtr->feval(u"error",
280
+                0,
281
+                std::vector<Array>({ factory.createScalar("First input must be the name of a trans.inp file") }));
282
+        }
283
+
284
+        // Check second input argument
285
+        if (inputs[1].getType() != ArrayType::DOUBLE ||
286
+            inputs[1].getType() == ArrayType::COMPLEX_DOUBLE)
287
+        {
288
+            matlabPtr->feval(u"error",
289
+                0,
290
+                std::vector<Array>({ factory.createScalar("Input must be double array") }));
291
+        }
292
+        // Check number of outputs
293
+        if (outputs.size() > 1) {
294
+            matlabPtr->feval(u"error",
295
+                0,
296
+                std::vector<Array>({ factory.createScalar("Only one output is returned") }));
297
+        }
298
+    }
299
+};
300
+
301
+std::vector<Real>  readinpfile(const std::string& fname) {
302
+    std::string buf;
303
+    char dump[255];
304
+    std::vector<Real> vals;
305
+    std::fstream input(fname.c_str(), std::ios::in);
306
+    if (input.fail()) {
307
+        std::cerr << "Input file " << fname << " failed to open\n";
308
+        exit(EXIT_FAILURE);
309
+    }
310
+    while (input >> buf) {
311
+        if (buf.substr(0,2) == "//") {
312
+            input.getline(dump, 255);
313
+        } else {
314
+            vals.push_back( atof(buf.c_str() ));
315
+        }
316
+    }
317
+    return vals;
318
+}
319
+
320
+std::vector<std::string>  readinpfile2(const std::string& fname) {
321
+    std::string buf;
322
+    char dump[255];
323
+    std::vector<std::string> vals;
324
+    std::fstream input(fname.c_str(), std::ios::in);
325
+    if (input.fail()) {
326
+        std::cerr << "Input file " << fname << " failed to open\n";
327
+        exit(EXIT_FAILURE);
328
+    }
329
+    while (input >> buf) {
330
+        if (buf.substr(0,2) == "//") {
331
+            input.getline(dump, 255);
332
+        } else {
333
+            vals.push_back( std::string(buf.c_str() ));
334
+        }
335
+    }
336
+    return vals;
337
+}

+ 4
- 0
WindowsBuild.bat View File

@@ -0,0 +1,4 @@
1
+REM If you want to avoid using Visual Studio, you can build using the .proj 
2
+REM files within CMAKE as follows. 
3
+cmake --build . --config=Release
4
+cmake --install . --config=Release

Loading…
Cancel
Save