1 /****************************************************************************/
2 /* This file is part of FreeFEM.                                            */
3 /*                                                                          */
4 /* FreeFEM is free software: you can redistribute it and/or modify          */
5 /* it under the terms of the GNU Lesser General Public License as           */
6 /* published by the Free Software Foundation, either version 3 of           */
7 /* the License, or (at your option) any later version.                      */
8 /*                                                                          */
9 /* FreeFEM is distributed in the hope that it will be useful,               */
10 /* but WITHOUT ANY WARRANTY; without even the implied warranty of           */
11 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
12 /* GNU Lesser General Public License for more details.                      */
13 /*                                                                          */
14 /* You should have received a copy of the GNU Lesser General Public License */
15 /* along with FreeFEM. If not, see <http://www.gnu.org/licenses/>.          */
16 /****************************************************************************/
17 // SUMMARY : ...
18 // LICENSE : LGPLv3
19 // ORG     : LJLL Universite Pierre et Marie Curie, Paris, FRANCE
20 // AUTHORS : Sala Lorenzo
21 // E-MAIL  : salallo80@gmail.com
22 
23 /* clang-format off */
24 //ff-c++-LIBRARY-dep:
25 //ff-c++-cpp-dep:
26 /* clang-format on */
27 
28 #include "mode_open.hpp"
29 #include <iostream>
30 #include <cfloat>
31 #include <cmath>
32 #include <iterator>
33 using namespace std;
34 #include "ff++.hpp"
35 using namespace Fem2D;
36 
37 /*!The class DxWriter permits to save in opendx format a "field"
38  * (in the dx-language a "field" means the values of a function f(x,y,z) on a grid),
39  * a time series (an ordered collection of "fields", so we have field0=f(x,y,z,t0),
40  * field1=f(x,y,z,t1) and so on). DxWriter creates two files: one with extension .data where it puts
41  * the position of the grid, the connessions, the values; and one with extension.dx where it puts
42  * the time series. Now you can save only scalar fields. An example <code> load "DxWriter" mesh
43  * Th=square(5,5); DxWriter ff("pippo"); Dxaddmesh(ff, Th); Dxaddtimeseries(ff, "Vx",Th); fespace
44  * Vh(Th, P1); Vh vx=x*y; Dxaddsol2ts(ff,"Vx",1.0, vx); vx=0.5*x*y^2+0.2; Dxaddsol2ts(ff,"Vx",2.0,
45  * vx); cout<<"Ciao";
46  * </code>
47  */
48 
49 class DxWriter {
50   struct tsinfo {
51     int imesh;    //!< index of the mesh
52     std::string name;
53     std::vector< double > vecistant;
54   };
55 
56  private:
57   std::vector< const Fem2D::Mesh * > _vecmesh;
58   std::vector< tsinfo > _vecofts;
59   std::string _nameoffile;
60   /*! This string contains the name of data file with \\ where there's a \ in the path*/
61   std::string _nameofdatafile;
62   //! files containing the data and the timeseries
63   std::ofstream _ofdata, _ofts;
64 
65   /*!This function is called frequently, so if the main program crashes the files are good
66    * and you need only write "end" at the end of data file: echo end>>nameoffile.data and then the
67    * files are good
68    */
save_header()69   void save_header( ) {
70     std::string s = _nameoffile;
71 
72     s.append(".dx");
73     _ofts.open(s.c_str( ), std::ios_base::out);
74 
75     for (int i = 0; i < _vecofts.size( ); ++i) {
76       _ofts << "object \"" << _vecofts[i].name << "\" class series" << std::endl;
77 
78       for (int j = 0; j < _vecofts[i].vecistant.size( ); ++j) {
79         _ofts << "member " << j << " value file \"" << _nameofdatafile << "\",\""
80               << _vecofts[i].name << "_" << j << "\" position " << _vecofts[i].vecistant[j]
81               << std::endl;
82       }
83 
84       _ofts << std::endl;
85     }
86 
87     _ofts << "end" << std::endl;
88     _ofts.close( );
89   }
90 
91  public:
DxWriter()92   DxWriter( ) { std::cout << "Constructor of DxWriter" << endl; }
93 
openfiles(const std::string & s)94   void openfiles(const std::string &s) {
95     _nameoffile = s;
96     std::string tmp = s + ".data";
97     std::cout << tmp << " ";
98     _ofdata.open(tmp.c_str( ), std::ios_base::out);
99     _nameofdatafile = "";
100 
101     for (int i = 0; i < tmp.length( ); ++i) {
102       if (tmp.at(i) == '\\') {
103         _nameofdatafile.append(1, '\\');
104       }
105 
106       _nameofdatafile.append(1, tmp.at(i));
107     }
108   }
109 
addmesh(const Fem2D::Mesh * mesh)110   void addmesh(const Fem2D::Mesh *mesh) {
111     const Fem2D::Mesh &Th(*mesh);
112 
113     _vecmesh.push_back(mesh);
114     _ofdata.flags(std::ios_base::scientific);
115     _ofdata.precision(15);
116     _ofdata << "object \"pos_" << _vecmesh.size( ) - 1
117             << "\" class array type float rank 1 shape 2 items " << Th.nv << " data follows"
118             << std::endl;
119 
120     for (int k = 0; k < Th.nv; ++k) {    // Scorre tutti i vertici
121       _ofdata << Th(k).x << " " << Th(k).y << endl;
122     }
123 
124     _ofdata << std::endl;
125     _ofdata.flags(std::ios_base::fixed);
126     _ofdata << "object \"conn_" << _vecmesh.size( ) - 1
127             << "\" class array type int rank 1 shape 3 items " << Th.nt << " data follows " << endl;
128 
129     for (int i = 0; i < Th.nt; i++) {
130       for (int j = 0; j < 3; j++) {
131         _ofdata << Th(i, j) << " ";
132       }
133 
134       _ofdata << endl;
135     }
136 
137     _ofdata << "attribute \"element type\" string \"triangles\" " << std::endl;
138     _ofdata << "attribute \"ref\" string \"positions\" " << std::endl << std::endl;
139   }
140 
141   /*!Add a new time series, defined on the mesh*/
addtimeseries(const string & nameofts,const Fem2D::Mesh * mesh)142   void addtimeseries(const string &nameofts, const Fem2D::Mesh *mesh) {
143     tsinfo ts;
144 
145     ts.name = nameofts;
146     std::vector< const Fem2D::Mesh * >::const_iterator first = _vecmesh.begin( ),
147                                                        last = _vecmesh.end( );
148 
149     if (std::find(first, last, mesh) == last) {
150       addmesh(mesh);
151       ts.imesh = _vecmesh.size( ) - 1;
152     } else {
153       ts.imesh = std::distance(first, std::find(first, last, mesh));
154     }
155 
156     _vecofts.push_back(ts);
157   }
158 
159   /*!Add an instant to a time series name*/
addistant2ts(const string & nameofts,const double t,const KN<double> & val)160   void addistant2ts(const string &nameofts, const double t, const KN< double > &val) {
161     int jj = -1;
162 
163     for (int i = 0; i < _vecofts.size( ); ++i) {
164       if (_vecofts[i].name == nameofts) {
165         jj = i;
166       }
167     }
168 
169     _vecofts[jj].vecistant.push_back(t);
170     _ofdata.flags(std::ios_base::scientific);
171     _ofdata.precision(15);
172     _ofdata << "object \"" << nameofts << "_data_" << _vecofts[jj].vecistant.size( ) - 1
173             << "\" class array type float rank 0 items " << val.size( ) << " data follows"
174             << std::endl;
175 
176     for (int i = 0; i < val.size( ); ++i) {
177       _ofdata << val[i] << std::endl;
178     }
179 
180     _ofdata << "attribute \"dep\" string \"positions\"" << std::endl << std::endl;
181     _ofdata << "object \"" << nameofts << "_" << _vecofts[jj].vecistant.size( ) - 1
182             << "\" class field" << std::endl;
183     _ofdata << "component \"positions\" value \"pos_" << _vecofts[jj].imesh << "\"" << std::endl;
184     _ofdata << "component \"connections\" value \"conn_" << _vecofts[jj].imesh << "\"" << std::endl;
185     _ofdata << "component \"data\" value \"" << nameofts << "_data_"
186             << _vecofts[jj].vecistant.size( ) - 1 << "\"" << std::endl
187             << std::endl;
188     _ofdata.flush( );
189     save_header( );
190   }
191 
192   /*!Add a field*/
addfield(const string & nameoffield,const Fem2D::Mesh * mesh,const KN<double> & val)193   void addfield(const string &nameoffield, const Fem2D::Mesh *mesh, const KN< double > &val) {
194     std::vector< const Fem2D::Mesh * >::const_iterator first = _vecmesh.begin( ),
195                                                        last = _vecmesh.end( );
196     int im;
197 
198     if (std::find(first, last, mesh) == last) {
199       addmesh(mesh);
200       im = _vecmesh.size( ) - 1;
201     } else {
202       im = std::distance(first, std::find(first, last, mesh));
203     }
204 
205     _ofdata.flags(std::ios_base::scientific);
206     _ofdata.precision(15);
207     _ofdata << "object \"" << nameoffield << "_data\" class array type float rank 0 items "
208             << val.size( ) << " data follows" << std::endl;
209 
210     for (int i = 0; i < val.size( ); ++i) {
211       _ofdata << val[i] << std::endl;
212     }
213 
214     _ofdata << "attribute \"dep\" string \"positions\"" << std::endl << std::endl;
215     _ofdata << "object \"" << nameoffield << "\" class field" << std::endl;
216     _ofdata << "component \"positions\" value \"pos_" << im << "\"" << std::endl;
217     _ofdata << "component \"connections\" value \"conn_" << im << "\"" << std::endl;
218     _ofdata << "component \"data\" value \"" << nameoffield << "_data\"" << std::endl << std::endl;
219     _ofdata.flush( );
220   }
221 
222   /*!Get the mesh associated with the series nameofts*/
getmeshts(const string & nameofts)223   const Fem2D::Mesh *getmeshts(const string &nameofts) {
224     for (int i = 0; i < _vecofts.size( ); ++i) {
225       if (_vecofts[i].name == nameofts) {
226         return _vecmesh[_vecofts[i].imesh];
227       }
228     }
229 
230     return NULL;
231   }
232 
init()233   void init( ) { new (this) DxWriter( ); }
234 
destroy()235   void destroy( ) {
236     if (_ofdata.is_open( )) {
237       _ofdata << std::endl << "end" << std::endl;
238       _ofdata.close( );
239     }
240   }
241 };
242 
243 class Dxwritesol_Op : public E_F0mps {
244  public:
245   typedef long Result;
246   Expression edx;
247   Expression ename;    //!< name of time series or field
248   Expression et;       //!< time
249   long what;           // 1 scalar, 2 vector, 3 symtensor
250   long nbfloat;        // 1 scalar, n vector (3D), n symtensor(3D)
251   Expression evct;
252 
253  public:
Dxwritesol_Op(const basicAC_F0 & args)254   Dxwritesol_Op(const basicAC_F0 &args) : what(0), nbfloat(0) {
255     evct = 0;
256     // There's no named parameter
257     args.SetNameParam( );
258     if (args.size( ) != 4) {
259       CompileError("Dxwritesol accepts only 4 parameters");
260     }
261 
262     if (BCastTo< DxWriter * >(args[0])) {
263       edx = CastTo< DxWriter * >(args[0]);
264     }
265 
266     if (BCastTo< string * >(args[1])) {
267       ename = CastTo< string * >(args[1]);
268     }
269 
270     if (BCastTo< double >(args[2])) {
271       et = CastTo< double >(args[2]);
272     }
273 
274     if (args[3].left( ) == atype< double >( )) {
275       what = 1;
276       nbfloat = 1;
277       evct = to< double >(args[3]);
278     } else if (args[3].left( ) == atype< double * >( )) {
279       what = 1;
280       nbfloat = 1;
281       evct = to< double >(args[3]);
282     } else if (BCastTo< pfer >(args[3])) {
283       what = 1;
284       nbfloat = 1;
285       evct = to< double >(args[3]);
286     } else if (args[3].left( ) == atype< E_Array >( )) {
287       CompileError("Until now only scalar solution");
288     } else {
289       CompileError("savesol in 2D: Sorry no way to save this kind of data");
290     }
291   }
292 
typeargs()293   static ArrayOfaType typeargs( ) {
294     return ArrayOfaType(atype< DxWriter * >( ), atype< string * >( ), atype< double >( ), true);
295   }    // all type
296 
f(const basicAC_F0 & args)297   static E_F0 *f(const basicAC_F0 &args) { return new Dxwritesol_Op(args); }
298 
299   AnyType operator( )(Stack stack) const;
300 };
301 
operator ( )(Stack stack) const302 AnyType Dxwritesol_Op::operator( )(Stack stack) const {
303   MeshPoint *mp(MeshPointStack(stack));
304   DxWriter &dx = *(GetAny< DxWriter * >((*edx)(stack)));
305   string &name = *(GetAny< string * >((*ename)(stack)));
306   double t = GetAny< double >((*et)(stack));
307   const Mesh &Th = *(dx.getmeshts(name));
308   int nt = Th.nt;
309   int nv = Th.nv;
310   int nbsol = nv;
311   long longdefault = 0;
312 
313   KN< double > valsol(nbsol);
314   valsol = 0.;
315   KN< int > takemesh(nbsol);
316   takemesh = 0;
317   MeshPoint *mp3(MeshPointStack(stack));
318 
319   for (int it = 0; it < nt; it++) {
320     for (int iv = 0; iv < 3; iv++) {
321       int i = Th(it, iv);
322       mp3->setP(&Th, it, iv);
323       valsol[i] = valsol[i] + GetAny< double >((*evct)(stack));
324       ++takemesh[i];
325     }
326   }
327 
328   for (int i = 0; i < nbsol; i++) {
329     valsol[i] /= takemesh[i];
330   }
331 
332   // Writes valsol on the file file
333   dx.addistant2ts(name, t, valsol);
334 
335   return longdefault;
336 }
337 
338 // le vrai constructeur est la
init_DxWriter(DxWriter * const & a,string * const & s)339 DxWriter *init_DxWriter(DxWriter *const &a, string *const &s) {
340   std::cout << "start init_DxWriter" << std::endl;
341 
342   a->init( );
343   a->openfiles(*s);
344   std::cout << "end init_DxWriter" << std::endl;
345   return a;
346 }
347 
call_addmesh(DxWriter * const & mt,const Fem2D::Mesh * const & pTh)348 void *call_addmesh(DxWriter *const &mt, const Fem2D::Mesh *const &pTh) {
349   mt->addmesh(pTh);
350   return NULL;
351 }
352 
call_addtimeseries(DxWriter * const & mt,string * const & name,const Fem2D::Mesh * const & pTh)353 void *call_addtimeseries(DxWriter *const &mt, string *const &name, const Fem2D::Mesh *const &pTh) {
354   mt->addtimeseries(*name, pTh);
355   return NULL;
356 }
357 
358 // Add the function name to the freefem++ table
Load_Init()359 static void Load_Init( ) {
360   Dcl_Type< DxWriter * >(
361     InitP< DxWriter >,
362     Destroy< DxWriter >);    // declare deux nouveau type pour freefem++  un pointeur et
363 
364   zzzfff->Add("DxWriter", atype< DxWriter * >( ));    // ajoute le type myType a freefem++
365   // constructeur  d'un type myType  dans freefem
366   TheOperators->Add("<-", new OneOperator2_< DxWriter *, DxWriter *, string * >(&init_DxWriter));
367 
368   Global.Add("Dxaddmesh", "(",
369              new OneOperator2_< void *, DxWriter *, const Fem2D::Mesh * >(call_addmesh));
370   Global.Add("Dxaddtimeseries", "(",
371              new OneOperator3_< void *, DxWriter *, std::string *, const Fem2D::Mesh * >(
372                call_addtimeseries));
373 
374   Global.Add("Dxaddsol2ts", "(", new OneOperatorCode< Dxwritesol_Op >);
375 
376   // atype< myType * >()->Add("(","",new OneOperator3_<myType_uv,myType *,double,double
377   // >(set_myType_uv));
378 }
379 
380 LOADFUNC(Load_Init)
381