1 /*!
2  * \file   mfront/src/CppMaterialPropertyInterface.cxx
3  * \brief
4  * \author Thomas Helfer
5  * \date   06 mai 2008
6  * \copyright Copyright (C) 2006-2018 CEA/DEN, EDF R&D. All rights
7  * reserved.
8  * This project is publicly released under either the GNU GPL Licence
9  * or the CECILL-A licence. A copy of thoses licences are delivered
10  * with the sources of TFEL. CEA or EDF may also distribute this
11  * project under specific licensing conditions.
12  */
13 
14 #include<sstream>
15 #include<fstream>
16 #include<algorithm>
17 #include<stdexcept>
18 
19 #include"TFEL/Raise.hxx"
20 #include"TFEL/Config/GetInstallPath.hxx"
21 #include"TFEL/System/System.hxx"
22 
23 #include"MFront/DSLUtilities.hxx"
24 #include"MFront/MFrontUtilities.hxx"
25 #include"MFront/MFrontHeader.hxx"
26 #include"MFront/FileDescription.hxx"
27 #include"MFront/TargetsDescription.hxx"
28 #include"MFront/MaterialPropertyDescription.hxx"
29 #include"MFront/CppMaterialPropertyInterface.hxx"
30 
31 namespace mfront
32 {
33 
getHeaderFileName(const std::string & n)34   static std::string getHeaderFileName(const std::string& n){
35     return "include/"+n+"-cxx.hxx";
36   }
37 
getName()38   std::string CppMaterialPropertyInterface::getName()
39   {
40     return "c++";
41   }
42 
43   CppMaterialPropertyInterface::CppMaterialPropertyInterface() = default;
44 
45   std::pair<bool,tfel::utilities::CxxTokenizer::TokensContainer::const_iterator>
treatKeyword(const std::string & k,const std::vector<std::string> & i,tokens_iterator current,const tokens_iterator)46   CppMaterialPropertyInterface::treatKeyword(const std::string& k,
47 					     const std::vector<std::string>& i,
48 					     tokens_iterator current,
49 					     const tokens_iterator)
50   {
51     tfel::raise_if((std::find(i.begin(),i.end(),"c++")!=i.end())||
52 		   (std::find(i.begin(),i.end(),"C++")!=i.end())||
53 		   (std::find(i.begin(),i.end(),"cxx")!=i.end())||
54 		   (std::find(i.begin(),i.end(),"Cxx")!=i.end())||
55 		   (std::find(i.begin(),i.end(),"cpp")!=i.end())||
56 		   (std::find(i.begin(),i.end(),"Cpp")!=i.end()),
57 		   "CppMaterialPropertyInterface::treatKeyword: "
58 		   "unsupported keyword '"+k+"'");
59     return {false,current};
60   } // end of treatKeyword
61 
62     void
getTargetsDescription(TargetsDescription & d,const MaterialPropertyDescription & mpd) const63     CppMaterialPropertyInterface::getTargetsDescription(TargetsDescription& d,
64 							const MaterialPropertyDescription& mpd) const
65     {
66       const auto lib  = "Cpp"+getMaterialLawLibraryNameBase(mpd);
67       const auto name = mpd.material.empty() ? mpd.className : mpd.material+"_"+mpd.className;
68       const auto tfel_config = tfel::getTFELConfigExecutableName();
69       insert_if(d[lib].cppflags,
70 		"$(shell "+tfel_config+" --cppflags --compiler-flags)");
71       insert_if(d[lib].include_directories,
72 	      "$(shell "+tfel_config+" --include-path)");
73       insert_if(d[lib].sources,name+"-cxx.cxx");
74       insert_if(d.headers,getHeaderFileName(name));
75 #if  !((defined _WIN32) && (defined _MSC_VER))
76       insert_if(d[lib].link_libraries,"m");
77 #endif /* !((defined _WIN32) && (defined _MSC_VER)) */
78       auto cn = std::string{};
79 #pragma message("handle namespace")
80       // for(const auto& ns : this->namespaces){
81       //   cc += ns + "::"
82       // }
83       cn += name;
84       insert_if(d[lib].epts,cn);
85     } // end of CMaterialPropertyInterface::getTargetsDescription
86 
writeOutputFiles(const MaterialPropertyDescription & mpd,const FileDescription & fd) const87   void CppMaterialPropertyInterface::writeOutputFiles(const MaterialPropertyDescription& mpd,
88 						      const FileDescription& fd) const
89   {
90     this->writeHeaderFile(mpd,fd);
91     this->writeSrcFile(mpd,fd);
92   } // end of CppMaterialPropertyInterface::writeOutputFiles
93 
writeHeaderFile(const MaterialPropertyDescription & mpd,const FileDescription & fd) const94   void CppMaterialPropertyInterface::writeHeaderFile(const MaterialPropertyDescription& mpd,
95 						     const FileDescription& fd) const
96   {
97     const auto name = mpd.material.empty() ? mpd.className : mpd.material+"_"+mpd.className;
98     std::ofstream header(getHeaderFileName(name));
99     tfel::raise_if(!header,"CppMaterialPropertyInterface::writeHeaderFile: "
100 		   "unable to open '"+getHeaderFileName(name)+"' "
101 		   "for writing output file.");
102     header.exceptions(std::ios::badbit|std::ios::failbit);
103     header << "/*!\n"
104 	   << "* \\file   " << getHeaderFileName(name)  << '\n'
105 	   << "* \\brief  " << "this file declares the "
106 	   << name << " MaterialLaw.\n"
107 	   << "*         File generated by "
108 	   << MFrontHeader::getVersionName() << " "
109 	   << "version " << MFrontHeader::getVersionNumber() << '\n';
110     if(!fd.authorName.empty()){
111       header << "* \\author " << fd.authorName << '\n';
112     }
113     if(!fd.date.empty()){
114       header << "* \\date   " << fd.date << '\n';
115     }
116     if(!fd.description.empty()){
117       header << fd.description << '\n';
118     }
119     header << " */\n\n";
120 
121     header << "#ifndef LIB_MFRONT_" << makeUpperCase(name) << "_HXX\n"
122 	   << "#define LIB_MFRONT_" << makeUpperCase(name) << "_HXX\n\n"
123 	   << "#include<ostream>\n"
124 	   << "#include<cmath>\n"
125 	   << "#include<algorithm>\n"
126 	   << "#include<stdexcept>\n\n"
127 	   << "#include<functional>\n\n";
128     if(!mpd.includes.empty()){
129       header << mpd.includes << '\n';
130     }
131     writeExportDirectives(header);
132     header << "namespace mfront\n{\n\n"
133 	   << "struct MFRONT_SHAREDOBJ " << name << '\n';
134     if(!mpd.inputs.empty()){
135       if(mpd.inputs.size()==1){
136 	header << ": std::unary_function<double,double>\n";
137       } else if(mpd.inputs.size()==2){
138 	header << ": std::binary_function<double,double,double>\n";
139       }
140     }
141     header << "{\n\n";
142     if(mpd.inputs.empty()){
143       header << "//! nested typedef to make " << name
144 	     << " model an adaptable generator (STL compliance)\n\n";
145       header << "typedef double result_type;\n\n";
146     }
147     header << "//! default constructor\n"
148 	   << name << "() noexcept;\n\n"
149 	   << "//! move constructor\n"
150 	   << name << "(" << name << "&&) noexcept = default;\n"
151 	   << "//! copy constructor\n"
152 	   << name << "(const " << name << "&) noexcept = default;\n"
153 	   << "//! move assignement operator\n"
154 	   << name << "& operator=(" << name << "&&) noexcept = default;\n"
155 	   << "//! assignement operator\n"
156 	   << name << "& operator=(const " << name << "&) noexcept = default;\n\n"
157 	   << "double\noperator()(";
158     for(auto p4=mpd.inputs.begin();p4!=mpd.inputs.end();){
159       header << "const double";
160       if((++p4)!=mpd.inputs.end()){
161 	header << ",";
162       }
163     }
164     header << ") const;\n\n";
165     if((hasBounds(mpd.inputs))||(hasPhysicalBounds(mpd.inputs))){
166       header << "static void\ncheckBounds(";
167       for(auto p4=mpd.inputs.begin();p4!=mpd.inputs.end();){
168 	header << "const double";
169 	if((++p4)!=mpd.inputs.end()){
170 	  header << ",";
171 	}
172       }
173       header << ");\n\n";
174     }
175     for(const auto& p : mpd.parameters){
176       header << "const double& get" << p.name << "() const;\n";
177     }
178     for(const auto& p : mpd.parameters){
179       header << "double& get" << p.name << "();\n";
180     }
181     for(const auto& p : mpd.parameters){
182       header << "void set" << p.name << "(const double);\n";
183     }
184     if(!mpd.parameters.empty()){
185       header << "private:\n";
186       for(const auto& p : mpd.parameters){
187 	header << "double " << p.name << ";\n";
188       }
189     }
190     header << "}; // end of class " << name << "\n\n"
191 	   << "std::ostream& operator<<(std::ostream&,const " << name << "&);\n\n"
192 	   << "} // end of namespace mfront\n\n"
193 	   << "#endif /* LIB_MFRONT_" << makeUpperCase(name) << "_HXX */\n";
194     header.close();
195   } // end of CppMaterialPropertyInterface::writeHeaderFile()
196 
writeSrcFile(const MaterialPropertyDescription & mpd,const FileDescription & fd) const197   void CppMaterialPropertyInterface::writeSrcFile(const MaterialPropertyDescription& mpd,
198 						  const FileDescription& fd) const
199   {
200     auto throw_if = [](const bool b,const std::string& m){
201       tfel::raise_if(b,"CppMaterialPropertyInterface::writeSrcFile: "+m);
202     };
203     const auto name = mpd.material.empty() ?
204       mpd.className : mpd.material+"_"+mpd.className;
205     const auto src_name  = "src/" +name+"-cxx.cxx";
206     std::ofstream src(src_name);
207     throw_if(!src,"unable to open '"+src_name+"'");
208     src.exceptions(std::ios::badbit|std::ios::failbit);
209     src << "/*!\n"
210 	<< "* \\file   " << src_name  << '\n'
211 	<< "* \\brief  " << "this file implements the "
212 	<< name << " MaterialLaw.\n"
213 	<< "*         File generated by "
214 	<< MFrontHeader::getVersionName() << " "
215 	<< "version " << MFrontHeader::getVersionNumber()
216 	<< '\n';
217     if(!fd.authorName.empty()){
218       src << "* \\author " << fd.authorName << '\n';
219     }
220     if(!fd.date.empty()){
221       src << "* \\date   " << fd.date << '\n';
222     }
223     src << " */\n\n"
224 	<< "#include<stdexcept>\n"
225 	<< "#include<iostream>\n"
226 	<< "#include<cstdlib>\n"
227 	<< "#include<sstream>\n"
228 	<< "#include<cstring>\n"
229       	<< "#include<vector>\n"
230       	<< "#include<string>\n"
231 	<< "#include\"TFEL/Raise.hxx\"\n"
232 	<< "#include\"TFEL/Math/General/IEEE754.hxx\"\n"
233 	<< "#include\"" << name << "-cxx.hxx\"\n\n";
234     writeExportDirectives(src);
235     src << "#ifdef __cplusplus\n"
236 	    << "extern \"C\"{\n"
237 	    << "#endif /* __cplusplus */\n\n";
238     // mfront metadata
239     writeEntryPointSymbol(src,name);
240     writeTFELVersionSymbol(src,name);
241     writeInterfaceSymbol(src,name,"C++");
242     writeMaterialSymbol(src,name,mpd.material);
243     writeMaterialKnowledgeTypeSymbol(src,name,MATERIALPROPERTY);
244     src << "#ifdef __cplusplus\n"
245 	<< "} // end of extern \"C\"\n"
246 	<< "#endif /* __cplusplus */\n\n";
247     src	<< "namespace mfront\n{\n\n"
248 	<< name << "::" << name << "() noexcept\n";
249     if(!mpd.parameters.empty()){
250       src << ": ";
251     }
252     for(auto p=mpd.parameters.begin();p!=mpd.parameters.end();){
253       throw_if(!p->hasAttribute(VariableDescription::defaultValue),
254 	       "internal error (can't find value of "
255 	       "parameter '" + p->name + "')");
256       src << p->name << "("
257 	  << p->getAttribute<double>(VariableDescription::defaultValue) << ")";
258       if(++p!=mpd.parameters.end()){
259 	src << ",";
260       }
261       src << "\n";
262     }
263     src << "{} // end of " << name << "::" << name << "\n\n";
264     for(const auto& p : mpd.parameters){
265       src << "const double& ";
266       src << name;
267       src << "::get"
268 	  << p.name << "() const{\n"
269 	  << "return this->" << p.name << ";\n"
270 	  << "} // end of " << name << "::get\n\n";
271       src << "double& " << name << "::get" << p.name << "(){\n"
272 	  << "return " << p.name << ";\n"
273 	  << "} // end of " << name << "::get\n\n";
274       src << "void " << name;
275       src  << "::set" << p.name;
276       src << "(const double " << name << "_value_)";
277       src << "{\n"
278 	  << "this->" << p.name << " = " << name << "_value_;\n"
279 	  << "} // end of " << name << "::set\n\n";
280     }
281     src << "double " << name << "::operator()(";
282     for(auto pi=mpd.inputs.begin();pi!=mpd.inputs.end();){
283       src << "const double " << pi->name;
284       if(++pi!=mpd.inputs.end()){
285 	src << ",";
286       }
287     }
288     src << ") const\n{\n"
289 	<< "using namespace std;\n"
290 	<< "using real = double;\n";
291     // material laws
292     writeMaterialLaws(src,mpd.materialLaws);
293     // static variables
294     writeStaticVariables(src,mpd.staticVars,fd.fileName);
295     // body
296     src << "real " << mpd.output.name << ";\n";
297     if((hasBounds(mpd.inputs))||(hasPhysicalBounds(mpd.inputs))){
298       src << "#ifndef MFRONT_NO_BOUNDS_CHECK\n";
299       src << name << "::checkBounds(";
300       for(auto pi=mpd.inputs.begin();pi!=mpd.inputs.end();){
301 	src << pi->name;
302 	if((++pi)!=mpd.inputs.end()){
303 	  src << ",";
304 	}
305       }
306       src << ");\n";
307       src << "#endif /* MFRONT_NO_BOUNDS_CHECK */\n";
308     }
309     if(!mpd.inputs.empty()){
310       src << "#ifndef MFRONT_NOERRNO_HANDLING\n"
311 	  << "const auto mfront_errno_old = errno;\n"
312 	  << "errno=0;\n"
313 	  << "#endif /* MFRONT_NOERRNO_HANDLING */\n";
314     }
315     src << mpd.f.body;
316     if(!mpd.inputs.empty()){
317       src << "#ifndef MFRONT_NOERRNO_HANDLING\n"
318 	// can't use std::swap here as errno might be a macro
319 	  << "const auto mfront_errno = errno;\n"
320 	  << "errno = mfront_errno_old;\n"
321 	  << "tfel::raise_if((mfront_errno!=0)||"
322 	  << "(!tfel::math::ieee754::isfinite(" << mpd.output.name << ")),\n"
323 	  << "\""<< name << ": errno has been set \"\n"
324 	  << "\"(\"+std::string(::strerror(errno))+\")\");\n"
325 	  << "#endif /* MFRONT_NOERRNO_HANDLING */\n";
326     }
327     src << "return " << mpd.output.name << ";\n"
328 	<< "} // end of " << name << "::operator()\n\n";
329     if((hasBounds(mpd.inputs))||(hasPhysicalBounds(mpd.inputs))){
330       src << "void\n";
331       src << name;
332       src << "::checkBounds(";
333       if(!mpd.inputs.empty()){
334 	for(auto pi=mpd.inputs.begin();pi!=mpd.inputs.end();){
335 	  src << "const double " << pi->name;
336 	  if((++pi)!=mpd.inputs.end()){
337 	    src << ",";
338 	  }
339 	}
340       } else {
341 	src << "void";
342       }
343       src << ")\n{\n";
344       if(hasPhysicalBounds(mpd.inputs)){
345 	src << "using namespace std;\n"
346 	    << "// treating physical bounds\n";
347 	for(const auto& i : mpd.inputs){
348 	  if(!i.hasPhysicalBounds()){
349 	    continue;
350 	  }
351 	  const auto& b = i.getPhysicalBounds();
352 	  if(b.boundsType==VariableBoundsDescription::LOWER){
353 	    src << "if(" << i.name<< " < "<< b.lowerBound << "){\n"
354 		<< "ostringstream msg;\n"
355 		<< "msg << \"" << name << " : "  << i.name << " is below its physical lower bound \";\n"
356 		<< "msg << \"(\" << " << i.name
357 		<< " << \" < " << b.lowerBound << ")\";\n"
358 		<< "tfel::raise<range_error>(msg.str());\n"
359 		<< "}\n";
360 	  } else if(b.boundsType==VariableBoundsDescription::UPPER){
361 	    src << "if(" << i.name<< " > "<< b.upperBound << "){\n"
362 		<< "ostringstream msg;\n"
363 		<< "msg << \"" << name << " : "  << i.name << " is beyond its physical upper bound \";\n"
364 		<< "msg << \"(\" << " << i.name
365 		<< " << \" > " << b.upperBound << ")\";\n"
366 		<< "tfel::raise<range_error>(msg.str());\n"
367 		<< "}\n";
368 	  } else {
369 	    src << "if(" << i.name<< " < "<< b.lowerBound << "){\n"
370 		<< "ostringstream msg;\n"
371 		<< "msg << \"" << name << " : "  << i.name << " is below its physical lower bound \";\n"
372 		<< "msg << \"(\" << " << i.name
373 		<< " << \" < " << b.lowerBound << ")\";\n"
374 		<< "tfel::raise<range_error>(msg.str());\n"
375 		<< "}\n"
376 		<< "if(" << i.name<< " > "<< b.upperBound << "){\n"
377 		<< "ostringstream msg;\n"
378 		<< "msg << \"" << name << " : "  << i.name << " is beyond its physical upper bound \";\n"
379 		<< "msg << \"(\" << " << i.name
380 		<< " << \" > " << b.upperBound << ")\";\n"
381 		<< "tfel::raise<range_error>(msg.str());\n"
382 		<< "}\n";
383 	  }
384 	}
385       }
386       if(hasBounds(mpd.inputs)){
387 	src << "// treating standard bounds\n";
388 	for(const auto& i : mpd.inputs){
389 	  if(!i.hasBounds()){
390 	    continue;
391 	  }
392 	  const auto& b = i.getBounds();
393 	  if(b.boundsType==VariableBoundsDescription::LOWER){
394 	    src << "if(" << i.name<< " < "<< b.lowerBound << "){\n"
395 		<< "const char * const policy = "
396 		<< "::getenv(\"OUT_OF_BOUNDS_POLICY\");\n"
397 		<< "if(policy!=nullptr){\n"
398 		<< "if(strcmp(policy,\"STRICT\")==0){\n"
399 		<< "ostringstream msg;\n"
400 		<< "msg << \"" << name << " : "  << i.name << " is below its lower bound \";\n"
401 		<< "msg << \"(\" << " << i.name
402 		<< " << \" < " << b.lowerBound << ")\";\n"
403 		<< "tfel::raise<range_error>(msg.str());\n"
404 		<< "} else if(strcmp(policy,\"WARNING\")==0){\n"
405 		<< "cerr << \"" << i.name << " is below its lower bound \";\n"
406 		<< "cerr << \"(\" << " << i.name
407 		<< " << \" < " << b.lowerBound << ")\\n\";\n"
408 		<< "}\n"
409 		<< "}\n"
410 		<< "}\n";
411 	  } else if(b.boundsType==VariableBoundsDescription::UPPER){
412 	    src << "if(" << i.name<< " > "<< b.upperBound << "){\n"
413 		<< "const char * const policy = "
414 		<< "::getenv(\"OUT_OF_BOUNDS_POLICY\");\n"
415 		<< "if(policy!=nullptr){\n"
416 		<< "if(strcmp(policy,\"STRICT\")==0){\n"
417 		<< "ostringstream msg;\n"
418 		<< "msg << \"" << name << " : "  << i.name << " is beyond its upper bound \";\n"
419 		<< "msg << \"(\" << " << i.name
420 		<< " << \" > " << b.upperBound << ")\";\n"
421 		<< "tfel::raise<range_error>(msg.str());\n"
422 		<< "} else if(strcmp(policy,\"WARNING\")==0){\n"
423 		<< "cerr << \"" << i.name << " is beyond its upper bound \";\n"
424 		<< "cerr << \"(\" << " << i.name
425 		<< " << \" > " << b.upperBound << ")\\n\";\n"
426 		<< "}\n"
427 		<< "}\n"
428 		<< "}\n";
429 	  } else {
430 	    src << "if(" << i.name<< " < "<< b.lowerBound << "){\n"
431 		<< "const char * const policy = "
432 		<< "::getenv(\"OUT_OF_BOUNDS_POLICY\");\n"
433 		<< "if(policy!=nullptr){\n"
434 		<< "if(strcmp(policy,\"STRICT\")==0){\n"
435 		<< "ostringstream msg;\n"
436 		<< "msg << \"" << name << " : "  << i.name << " is below its lower bound \";\n"
437 		<< "msg << \"(\" << " << i.name
438 		<< " << \" < " << b.lowerBound << ")\";\n"
439 		<< "tfel::raise<range_error>(msg.str());\n"
440 		<< "} else if(strcmp(policy,\"WARNING\")==0){\n"
441 		<< "cerr << \"" << i.name << " is below its lower bound \";\n"
442 		<< "cerr << \"(\" << " << i.name
443 		<< " << \" < " << b.lowerBound << ")\\n\";\n"
444 		<< "}\n"
445 		<< "}\n"
446 		<< "}\n"
447 		<< "if(" << i.name<< " > "<< b.upperBound << "){\n"
448 		<< "const char * const policy = "
449 		<< "::getenv(\"OUT_OF_BOUNDS_POLICY\");\n"
450 		<< "if(policy!=nullptr){\n"
451 		<< "if(strcmp(policy,\"STRICT\")==0){\n"
452 		<< "ostringstream msg;\n"
453 		<< "msg << \"" << name << " : "  << i.name << " is beyond its upper bound \";\n"
454 		<< "msg << \"(\" << " << i.name
455 		<< " << \" > " << b.upperBound << ")\";\n"
456 		<< "tfel::raise<range_error>(msg.str());\n"
457 		<< "} else if(strcmp(policy,\"WARNING\")==0){\n"
458 		<< "cerr << \"" << i.name << " is beyond its upper bound \";\n"
459 		<< "cerr << \"(\" << " << i.name
460 		<< " << \" > " << b.upperBound << ")\\n\";\n"
461 		<< "}\n"
462 		<< "}\n"
463 		<< "}\n";
464 	  }
465 	}
466       }
467       src << "} // end of " << name << "::checkBounds\n\n";
468     }
469     src << "std::ostream& operator<<(std::ostream& os,const " << name;
470     if(!mpd.parameters.empty()){
471       src << "& src){\n";
472     } else {
473       src << "&){\n";
474     }
475     for(const auto& p : mpd.parameters){
476       src << "os << \"" << p.name
477 	  << " : \" << src.get" << p.name << "() << std::endl;\n";
478     }
479     src << "return os;\n}// end of operator(std::ostream& os,"
480 	<< name << "\n\n"
481 	<< "} // end of namespace mfront\n\n";
482     src.close();
483   } // end of CppMaterialPropertyInterface::writeSrcFile()
484 
485   CppMaterialPropertyInterface::~CppMaterialPropertyInterface() = default;
486 
487 } // end of namespace mfront
488