1 /*!
2  * \file   mfront/src/CyranoInterface.cxx
3  * \brief
4  * \author Thomas Helfer
5  * \date   17 Jan 2007
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 <fstream>
15 #include <sstream>
16 #include <cstdlib>
17 #include <iterator>
18 #include <stdexcept>
19 #include <algorithm>
20 
21 #include "TFEL/Raise.hxx"
22 #include "TFEL/Config/GetInstallPath.hxx"
23 #include "TFEL/Glossary/Glossary.hxx"
24 #include "TFEL/Glossary/GlossaryEntry.hxx"
25 #include "TFEL/Utilities/StringAlgorithms.hxx"
26 #include "TFEL/System/System.hxx"
27 
28 #include "MFront/DSLUtilities.hxx"
29 #include "MFront/MFrontUtilities.hxx"
30 #include "MFront/MFrontLogStream.hxx"
31 #include "MFront/FileDescription.hxx"
32 #include "MFront/TargetsDescription.hxx"
33 #include "MFront/MaterialPropertyDescription.hxx"
34 #include "MFront/CyranoMaterialPropertyInterface.hxx"
35 #include "MFront/CyranoSymbolsGenerator.hxx"
36 #include "MFront/CyranoInterface.hxx"
37 
38 #ifndef _MSC_VER
39 static const char* const constexpr_c = "constexpr";
40 #else
41 static const char* const constexpr_c = "const";
42 #endif
43 
44 namespace mfront {
45 
getName()46   std::string CyranoInterface::getName() { return "cyrano"; }
47 
getModellingHypothesisIdentifier(const Hypothesis h)48   int CyranoInterface::getModellingHypothesisIdentifier(const Hypothesis h) {
49     switch (h) {
50       case ModellingHypothesis::AXISYMMETRICALGENERALISEDPLANESTRAIN:
51         return 1;
52       case ModellingHypothesis::AXISYMMETRICALGENERALISEDPLANESTRESS:
53         return 2;
54       default:
55         break;
56     }
57     std::ostringstream msg;
58     msg << "CyranoInterface::getModellingHypothesisIdentifier : "
59         << "unsupported hypothesis";
60     if (h == ModellingHypothesis::UNDEFINEDHYPOTHESIS) {
61       msg << " (default)";
62     } else {
63       msg << " (" << ModellingHypothesis::toString(h) << "')";
64     }
65     tfel::raise(msg.str());
66   }
67 
getLibraryName(const BehaviourDescription & mb) const68   std::string CyranoInterface::getLibraryName(const BehaviourDescription& mb) const {
69     auto lib = std::string{};
70     if (mb.getLibrary().empty()) {
71       if (!mb.getMaterialName().empty()) {
72         lib = "Cyrano" + mb.getMaterialName();
73       } else {
74         lib = "CyranoBehaviour";
75       }
76     } else {
77       lib = "Cyrano" + mb.getLibrary();
78     }
79     return lib;
80   }  // end of CyranoInterface::getLibraryName
81 
getInterfaceName() const82   std::string CyranoInterface::getInterfaceName() const {
83     return "Cyrano";
84   }  // end of CyranoInterface::getInterfaceName
85 
getFunctionNameBasis(const std::string & n) const86   std::string CyranoInterface::getFunctionNameBasis(
87       const std::string& n) const {
88     return "cyrano" + makeLowerCase(n);
89   }  // end of CyranoInterface::getLibraryName
90 
getBehaviourName(const std::string & library,const std::string & className) const91   std::string CyranoInterface::getBehaviourName(const std::string& library,
92                                                 const std::string& className) const {
93     return library + className;
94   }  // end of CyranoInterface::getBehaviourName
95 
getUmatFunctionName(const std::string & library,const std::string & className) const96   std::string CyranoInterface::getUmatFunctionName(const std::string& library,
97                                                    const std::string& className) const {
98     return "cyrano" + makeLowerCase(this->getBehaviourName(library, className));
99   }  // end of CyranoInterface::getUmatFunctionName
100 
CyranoInterface()101   CyranoInterface::CyranoInterface()
102       : useTimeSubStepping(false),
103         doSubSteppingOnInvalidResults(false),
104         maximumSubStepping(0u) {}  // end of CyranoInterface::CyranoInterface
105 
treatKeyword(BehaviourDescription & bd,const std::string & key,const std::vector<std::string> & i,tokens_iterator current,const tokens_iterator end)106   std::pair<bool, CyranoInterface::tokens_iterator> CyranoInterface::treatKeyword(
107       BehaviourDescription& bd,
108       const std::string& key,
109       const std::vector<std::string>& i,
110       tokens_iterator current,
111       const tokens_iterator end) {
112     auto throw_if = [](const bool b, const std::string& m) {
113       tfel::raise_if(b, "Cyrano::treatKeyword: " + m);
114     };
115     if (!i.empty()) {
116       if (std::find(i.begin(), i.end(), this->getName()) == i.end()) {
117         return {false, current};
118       }
119     }
120     if ((key == "@CyranoGenerateMTestFileOnFailure") ||
121         (key == "@UMATGenerateMTestFileOnFailure") ||
122         (key == "@GenerateMTestFileOnFailure")) {
123       this->setGenerateMTestFileOnFailureAttribute(
124           bd, this->readBooleanValue(key, current, end));
125       return {true, current};
126     } else if ((key == "@CyranoUseTimeSubStepping") || (key == "@UMATUseTimeSubStepping")) {
127       this->useTimeSubStepping = this->readBooleanValue(key, current, end);
128       return {true, current};
129     } else if ((key == "@CyranoMaximumSubStepping") || (key == "@UMATMaximumSubStepping")) {
130       throw_if(!this->useTimeSubStepping,
131                "time sub stepping is not enabled at this stage.\n"
132                "Use the @CyranoUseTimeSubStepping directive before "
133                "@CyranoMaximumSubStepping");
134       throw_if(current == end, "unexpected end of file");
135       std::istringstream flux(current->value);
136       flux >> this->maximumSubStepping;
137       throw_if(flux.fail(), "failed to read maximum substepping value.");
138       ++(current);
139       throw_if(current == end, "unexpected end of file");
140       throw_if(current->value != ";", "expected ';',read '" + current->value + "'");
141       ++(current);
142       return {true, current};
143     } else if ((key == "@CyranoDoSubSteppingOnInvalidResults") ||
144                (key == "@UMATDoSubSteppingOnInvalidResults")) {
145       throw_if(!this->useTimeSubStepping,
146                "time sub stepping is not "
147                "enabled at this stage.\n"
148                "Use the @CyranoUseTimeSubStepping directive before "
149                "@CyranoMaximumSubStepping");
150       this->doSubSteppingOnInvalidResults = this->readBooleanValue(key, current, end);
151       return {true, current};
152     }
153     return {false, current};
154   }  // end of treatKeyword
155 
getModellingHypothesesToBeTreated(const BehaviourDescription & mb) const156   std::set<CyranoInterface::Hypothesis> CyranoInterface::getModellingHypothesesToBeTreated(
157       const BehaviourDescription& mb) const {
158     // treatment
159     std::set<Hypothesis> h;
160     // modelling hypotheses handled by the behaviour
161     const auto& bh = mb.getModellingHypotheses();
162     // cyrano only supports the AxisymmetricalGeneralisedPlaneStrain
163     // and the AxisymmetricalGeneralisedPlaneStress hypotheses
164     if (bh.find(ModellingHypothesis::AXISYMMETRICALGENERALISEDPLANESTRAIN) != bh.end()) {
165       h.insert(ModellingHypothesis::AXISYMMETRICALGENERALISEDPLANESTRAIN);
166     }
167     if (bh.find(ModellingHypothesis::AXISYMMETRICALGENERALISEDPLANESTRESS) != bh.end()) {
168       h.insert(ModellingHypothesis::AXISYMMETRICALGENERALISEDPLANESTRESS);
169     }
170     tfel::raise_if(h.empty(),
171                    "CyranoInterface::getModellingHypothesesToBeTreated : "
172                    "no hypotheses selected. This means that the given beahviour "
173                    "can't be used neither in 'AxisymmetricalGeneralisedPlaneStrain' "
174                    "nor in 'AxisymmetricalGeneralisedPlaneStress', so it does not "
175                    "make sense to use the Cyrano interface");
176     return h;
177   }  // end of CyranoInterface::getModellingHypothesesToBeTreated
178 
writeGetOutOfBoundsPolicyFunctionImplementation(std::ostream & out,const std::string & name) const179   void CyranoInterface::writeGetOutOfBoundsPolicyFunctionImplementation(
180       std::ostream& out, const std::string& name) const {
181     out << "static tfel::material::OutOfBoundsPolicy&\n"
182         << this->getFunctionNameBasis(name) << "_getOutOfBoundsPolicy(){\n"
183         << "using namespace cyrano;\n"
184         << "using namespace tfel::material;\n"
185         << "static OutOfBoundsPolicy policy = "
186            "CyranoOutOfBoundsPolicy::getCyranoOutOfBoundsPolicy()."
187            "getOutOfBoundsPolicy();\n"
188         << "return policy;\n"
189         << "}\n\n";
190   }  // end of MFrontCyranoInterface::writeGetOutOfBoundsPolicyFunctionImplementation
191 
endTreatment(const BehaviourDescription & mb,const FileDescription & fd) const192   void CyranoInterface::endTreatment(const BehaviourDescription& mb,
193                                      const FileDescription& fd) const {
194     using namespace std;
195     using namespace tfel::system;
196     using namespace tfel::utilities;
197     auto throw_if = [](const bool b, const std::string& m) {
198       tfel::raise_if(b, "Cyrano::endTreatment: " + m);
199     };
200     // get the modelling hypotheses to be treated
201     const auto& mhs = this->getModellingHypothesesToBeTreated(mb);
202     const auto name = mb.getLibrary() + mb.getClassName();
203     // some checks
204     throw_if(mb.getBehaviourType() != BehaviourDescription::STANDARDSTRAINBASEDBEHAVIOUR,
205              "the cyrano interface only supports "
206              "small strain behaviours");
207     if (mb.isStrainMeasureDefined()) {
208       throw_if((mb.getStrainMeasure() != BehaviourDescription::LINEARISED) &&
209                    (mb.getStrainMeasure() != BehaviourDescription::HENCKY),
210                "the cyrano interface only supports:\n"
211                "- small strain behaviours: the only strain measure "
212                "supported is the HPP one (linearised)\n"
213                "- finite strain behaviours based on the Hencky strain measure");
214     }
215     if (mb.getAttribute(BehaviourDescription::requiresStiffnessTensor, false)) {
216       throw_if(mb.getSymmetryType() != mb.getElasticSymmetryType(),
217                "the type of the behaviour (isotropic or orthotropic) does not "
218                "match the the type of its elastic behaviour.\n"
219                "This is not allowed here :\n"
220                "- an isotropic behaviour must have an isotropic elastic behaviour\n"
221                "- an orthotropic behaviour must have an orthotropic elastic behaviour");
222     }
223     if (this->useTimeSubStepping) {
224       throw_if(this->maximumSubStepping == 0u,
225                "use of time sub stepping requested but MaximumSubStepping is zero.\n"
226                "Please use the @CyranoMaximumSubStepping directive");
227     }
228     // create the output directories
229     systemCall::mkdir("include/MFront");
230     systemCall::mkdir("include/MFront/Cyrano");
231 
232     // write the material properties
233     if (mb.areElasticMaterialPropertiesDefined()) {
234       for (const auto& emp : mb.getElasticMaterialPropertiesDescriptions()) {
235         CyranoMaterialPropertyInterface i;
236         i.writeOutputFiles(emp, fd);
237       }
238     }
239 
240     // opening header file
241     auto fileName = "cyrano"+name+".hxx";
242     std::ofstream out("include/MFront/Cyrano/" + fileName);
243     throw_if(!out, "could not open file '" + fileName + "'");
244 
245     out << "/*!\n";
246     out << "* \\file   " << fileName << endl;
247     out << "* \\brief  This file declares the cyrano interface for the " << mb.getClassName()
248         << " behaviour law\n";
249     out << "* \\author " << fd.authorName << endl;
250     out << "* \\date   " << fd.date << endl;
251     out << "*/\n\n";
252 
253     const auto header = this->getHeaderGuard(mb);
254     out << "#ifndef " << header << "\n";
255     out << "#define " << header << "\n\n";
256 
257     out << "#include\"TFEL/Config/TFELConfig.hxx\"\n\n";
258     out << "#include\"MFront/Cyrano/Cyrano.hxx\"\n\n";
259 
260     out << "#ifdef __cplusplus\n";
261     out << "#include\"MFront/Cyrano/CyranoTraits.hxx\"\n";
262     out << "#include\"TFEL/Material/" << mb.getClassName() << ".hxx\"\n";
263     out << "#endif /* __cplusplus */\n\n";
264 
265     this->writeVisibilityDefines(out);
266 
267     out << "#ifdef __cplusplus\n\n";
268 
269     out << "namespace cyrano{\n\n";
270 
271     if (!mb.areAllMechanicalDataSpecialised(mhs)) {
272       this->writeCyranoBehaviourTraits(out, mb, ModellingHypothesis::UNDEFINEDHYPOTHESIS);
273     }
274     for (const auto& h : mhs) {
275       if (mb.hasSpecialisedMechanicalData(h)) {
276         this->writeCyranoBehaviourTraits(out, mb, h);
277       }
278     }
279 
280     out << "} // end of namespace cyrano\n\n";
281 
282     out << "#endif /* __cplusplus */\n\n";
283 
284     out << "#ifdef __cplusplus\n";
285     out << "extern \"C\"{\n";
286     out << "#endif /* __cplusplus */\n\n";
287 
288     this->writeSetParametersFunctionsDeclarations(out, mb, name);
289     this->writeSetOutOfBoundsPolicyFunctionDeclaration(out, name);
290 
291     this->writeCyranoFunctionDeclaration(out, name);
292 
293     out << "#ifdef __cplusplus\n";
294     out << "}\n";
295     out << "#endif /* __cplusplus */\n\n";
296 
297     out << "#endif /* " << header << " */\n";
298 
299     out.close();
300 
301     fileName = "cyrano"+name+".cxx";
302     out.open("src/" + fileName);
303     tfel::raise_if(!out,
304                    "CyranoInterface::endTreatment: "
305                    "could not open file '" +
306                        fileName + "'");
307 
308     out << "/*!\n";
309     out << "* \\file   " << fileName << endl;
310     out << "* \\brief  This file implements the cyrano interface for the " << mb.getClassName()
311         << " behaviour law\n";
312     out << "* \\author " << fd.authorName << endl;
313     out << "* \\date   " << fd.date << endl;
314     out << "*/\n\n";
315 
316     this->getExtraSrcIncludes(out, mb);
317     if (mb.getAttribute(BehaviourData::profiling, false)) {
318       out << "#include\"MFront/BehaviourProfiler.hxx\"\n\n";
319     }
320     if (this->shallGenerateMTestFileOnFailure(mb)) {
321       out << "#include\"MFront/Cyrano/CyranoGetModellingHypothesis.hxx\"\n";
322     }
323     out << "#include\"MFront/Cyrano/CyranoInterface.hxx\"\n\n";
324     out << "#include\"TFEL/Material/" << mb.getClassName() << ".hxx\"\n";
325     out << "#include\"MFront/Cyrano/CyranoOutOfBoundsPolicy.hxx\"\n";
326     out << "#include\"MFront/Cyrano/CyranoStressFreeExpansionHandler.hxx\"\n";
327     out << "#include\"MFront/Cyrano/cyrano" << name << ".hxx\"\n\n";
328 
329     this->writeGetOutOfBoundsPolicyFunctionImplementation(out, name);
330 
331     out << "extern \"C\"{\n\n";
332 
333     CyranoSymbolsGenerator sg;
334     sg.generateGeneralSymbols(out, *this, mb, fd, mhs, name);
335     if (!mb.areAllMechanicalDataSpecialised(mhs)) {
336       const Hypothesis uh = ModellingHypothesis::UNDEFINEDHYPOTHESIS;
337       sg.generateSymbols(out, *this, mb, fd, name, uh);
338     }
339     for (const auto& h : mhs) {
340       if (mb.hasSpecialisedMechanicalData(h)) {
341         sg.generateSymbols(out, *this, mb, fd, name, h);
342       }
343     }
344 
345     out << "MFRONT_SHAREDOBJ unsigned short cyrano" << makeLowerCase(name)
346         << "_Interface = 1u;\n\n";
347 
348     this->writeSetParametersFunctionsImplementations(out, mb, name);
349     this->writeSetOutOfBoundsPolicyFunctionImplementation(out, name);
350 
351     if (mb.isStrainMeasureDefined()) {
352       if (mb.getStrainMeasure() == BehaviourDescription::HENCKY) {
353         this->writeLogarithmicStrainCyranoFunction(out, name, mb);
354       } else {
355         this->writeStandardCyranoFunction(out, name, mb);
356       }
357     } else {
358       this->writeStandardCyranoFunction(out, name, mb);
359     }
360     out << "} // end of extern \"C\"\n";
361     out.close();
362   }  // end of CyranoInterface::endTreatment
363 
writeMTestFileGeneratorSetModellingHypothesis(std::ostream & out) const364   void CyranoInterface::writeMTestFileGeneratorSetModellingHypothesis(std::ostream& out) const {
365     out << "mg.setModellingHypothesis(cyrano::getModellingHypothesis(*NDI));\n";
366   }
367 
368   CyranoInterface::~CyranoInterface() = default;
369 
getTargetsDescription(TargetsDescription & d,const BehaviourDescription & bd)370   void CyranoInterface::getTargetsDescription(TargetsDescription& d,
371                                               const BehaviourDescription& bd) {
372     const auto lib = CyranoInterface::getLibraryName(bd);
373     const auto name = ((!bd.getLibrary().empty()) ? bd.getLibrary() : "") + bd.getClassName();
374     const auto tfel_config = tfel::getTFELConfigExecutableName();
375     auto& l = d.getLibrary(lib);
376     insert_if(l.cppflags,
377               "$(shell " + tfel_config + " --cppflags --compiler-flags)");
378 #if CYRANO_ARCH == 64
379     insert_if(l.cppflags, "-DCYRANO_ARCH=64");
380 #elif CYRANO_ARCH == 32
381     insert_if(l.cppflags, "-DCYRANO_ARCH=32");
382 #else
383 #error "CyranoInterface::getGlobalIncludes : unsuported architecture"
384 #endif
385     insert_if(l.include_directories,
386               "$(shell " + tfel_config + " --include-path)");
387     insert_if(l.sources, "cyrano" + name + ".cxx");
388     insert_if(l.epts, name);
389     insert_if(l.epts, this->getFunctionNameBasis(name));
390     insert_if(d.headers, "MFront/Cyrano/cyrano" + name + ".hxx");
391     insert_if(l.link_directories,
392               "$(shell " + tfel_config + " --library-path)");
393     insert_if(l.link_libraries, tfel::getLibraryInstallName("CyranoInterface"));
394     if (this->shallGenerateMTestFileOnFailure(bd)) {
395       insert_if(l.link_libraries,
396                 tfel::getLibraryInstallName("MTestFileGenerator"));
397     }
398 #if __cplusplus >= 201703L
399     insert_if(l.link_libraries, "$(shell " + tfel_config +
400                                     " --library-dependency "
401                                     "--material --mfront-profiling)");
402 #else  /* __cplusplus < 201703L */
403     insert_if(l.link_libraries,
404               "$(shell " + tfel_config +
405                   " --library-dependency "
406                   "--material --mfront-profiling --physical-constants)");
407 #endif /* __cplusplus < 201703L */
408     if (bd.areElasticMaterialPropertiesDefined()) {
409       for (const auto& emp : bd.getElasticMaterialPropertiesDescriptions()) {
410         CyranoMaterialPropertyInterface i;
411         i.getLibraryDescription(d, l, emp);
412       }
413     }
414   }    // end of CyranoInterface::getTargetsDescription(TargetsDescription&)
415 
writeInterfaceSpecificIncludes(std::ostream & out,const BehaviourDescription &) const416   void CyranoInterface::writeInterfaceSpecificIncludes(std::ostream& out,
417                                                        const BehaviourDescription&) const {
418     out << "#include\"MFront/Cyrano/Cyrano.hxx\"\n\n";
419   }
420 
writeCyranoFunctionDeclaration(std::ostream & out,const std::string & name) const421   void CyranoInterface::writeCyranoFunctionDeclaration(std::ostream& out,
422                                                        const std::string& name) const {
423     using namespace std;
424     out << "MFRONT_SHAREDOBJ void\n"
425         << name << "(const cyrano::CyranoInt *const,const cyrano::CyranoReal *const,\n"
426         << "const cyrano::CyranoReal *const,      cyrano::CyranoReal *const,\n"
427         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
428         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
429         << "const cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
430         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
431         << "      cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
432         << "      cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
433         << "      cyrano::CyranoInt *const);\n\n";
434     out << "MFRONT_SHAREDOBJ void\n"
435         << makeUpperCase(name) << "_F77"
436         << "(const cyrano::CyranoInt *const,const cyrano::CyranoReal *const,\n"
437         << "const cyrano::CyranoReal *const,      cyrano::CyranoReal *const,\n"
438         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
439         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
440         << "const cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
441         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
442         << "      cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
443         << "      cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
444         << "      cyrano::CyranoInt *const);\n\n";
445     out << "MFRONT_SHAREDOBJ void\ncyrano" << makeLowerCase(name)
446         << "(const cyrano::CyranoInt *const,const cyrano::CyranoReal *const,\n"
447         << "const cyrano::CyranoReal *const,      cyrano::CyranoReal *const,\n"
448         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
449         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
450         << "const cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
451         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
452         << "      cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
453         << "      cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
454         << "      cyrano::CyranoInt *const);\n\n";
455     out << "MFRONT_SHAREDOBJ void\n"
456         << "cyrano" << makeUpperCase(name) << "_F77"
457         << "(const cyrano::CyranoInt *const,const cyrano::CyranoReal *const,\n"
458         << "const cyrano::CyranoReal *const,      cyrano::CyranoReal *const,\n"
459         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
460         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
461         << "const cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
462         << "const cyrano::CyranoReal *const,const cyrano::CyranoReal *const,\n"
463         << "      cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
464         << "      cyrano::CyranoReal *const,const cyrano::CyranoInt  *const,\n"
465         << "      cyrano::CyranoInt *const);\n\n";
466   }  // end of CyranoInterface::writeCyranoFunctionDeclaration
467 
writeSecondaryCyranoFunction(std::ostream & out,const std::string & fname,const std::string & n)468   static void writeSecondaryCyranoFunction(std::ostream& out,
469                                            const std::string& fname,
470                                            const std::string& n) {
471     out << "MFRONT_SHAREDOBJ void\n"
472         << fname
473         << "(const cyrano::CyranoInt *const NTENS, const cyrano::CyranoReal *const DTIME,\n"
474         << "const cyrano::CyranoReal *const DROT,  cyrano::CyranoReal *const DDSDDE,\n"
475         << "const cyrano::CyranoReal *const STRAN, const cyrano::CyranoReal *const DSTRAN,\n"
476         << "const cyrano::CyranoReal *const TEMP,  const cyrano::CyranoReal *const DTEMP,\n"
477         << "const cyrano::CyranoReal *const PROPS, const cyrano::CyranoInt    *const NPROPS,\n"
478         << "const cyrano::CyranoReal *const PREDEF,const cyrano::CyranoReal *const DPRED,\n"
479         << "cyrano::CyranoReal *const STATEV,const cyrano::CyranoInt    *const NSTATV,\n"
480         << "cyrano::CyranoReal *const STRESS,const cyrano::CyranoInt    *const NDI,\n"
481         << "cyrano::CyranoInt    *const KINC)\n"
482         << "{\n"
483         << n << "(NTENS, DTIME,DROT,DDSDDE,STRAN,DSTRAN,TEMP,DTEMP,\n"
484         << "PROPS,NPROPS,PREDEF,DPRED,STATEV,NSTATV,\n"
485         << "STRESS,NDI,KINC);\n";
486     out << "}\n\n";
487   }  // end of writeSecondaryCyranoFunction
488 
writeStandardCyranoFunction(std::ostream & out,const std::string & n,const BehaviourDescription & mb) const489   void CyranoInterface::writeStandardCyranoFunction(std::ostream& out,
490                                                     const std::string& n,
491                                                     const BehaviourDescription& mb) const {
492     out << "MFRONT_SHAREDOBJ void\n"
493         << n << "(const cyrano::CyranoInt *const NTENS, const cyrano::CyranoReal *const DTIME,\n"
494         << "const cyrano::CyranoReal *const DROT,  cyrano::CyranoReal *const DDSDDE,\n"
495         << "const cyrano::CyranoReal *const STRAN, const cyrano::CyranoReal *const DSTRAN,\n"
496         << "const cyrano::CyranoReal *const TEMP,  const cyrano::CyranoReal *const DTEMP,\n"
497         << "const cyrano::CyranoReal *const PROPS, const cyrano::CyranoInt    *const NPROPS,\n"
498         << "const cyrano::CyranoReal *const PREDEF,const cyrano::CyranoReal *const DPRED,\n"
499         << "cyrano::CyranoReal *const STATEV,const cyrano::CyranoInt    *const NSTATV,\n"
500         << "cyrano::CyranoReal *const STRESS,const cyrano::CyranoInt    *const NDI,\n"
501         << "cyrano::CyranoInt    *const KINC)\n";
502     out << "{\n";
503     out << "const auto op = " << this->getFunctionNameBasis(n)
504         << "_getOutOfBoundsPolicy();\n";
505     if (mb.getAttribute(BehaviourData::profiling, false)) {
506       out << "using mfront::BehaviourProfiler;\n";
507       out << "using tfel::material::" << mb.getClassName() << "Profiler;\n";
508       out << "auto total_timer(" << mb.getClassName() << "Profiler::getProfiler(),\n"
509           << "BehaviourProfiler::TOTALTIME);\n";
510     }
511     this->generateMTestFile1(out, mb);
512     out << "cyrano::CyranoInterface<tfel::material::" << mb.getClassName()
513         << ">::exe(NTENS,DTIME,DROT,DDSDDE,STRAN,DSTRAN,TEMP,DTEMP,PROPS,NPROPS,"
514         << "PREDEF,DPRED,STATEV,NSTATV,STRESS,NDI,KINC,"
515         << "cyrano::CyranoStandardSmallStrainStressFreeExpansionHandler,op);\n";
516     if (this->shallGenerateMTestFileOnFailure(mb)) {
517       out << "if(*KINC<0){\n";
518       this->generateMTestFile2(out, mb, BehaviourDescription::STANDARDSTRAINBASEDBEHAVIOUR, n, "");
519       out << "}\n";
520     }
521     out << "}\n\n";
522     writeSecondaryCyranoFunction(out, this->getFunctionNameBasis(n), n);
523   }  // end of CyranoInterface::writeStandardCyranoFunction
524 
writeLogarithmicStrainCyranoFunction(std::ostream & out,const std::string & n,const BehaviourDescription & mb) const525   void CyranoInterface::writeLogarithmicStrainCyranoFunction(std::ostream& out,
526                                                              const std::string& n,
527                                                              const BehaviourDescription& mb) const {
528     auto throw_if = [](const bool b, const std::string& m) {
529       if (b) {
530         tfel::raise("CyranoInterface::writeLogarithmicStrainCyranoFunction: " + m);
531       }
532     };
533     out << "MFRONT_SHAREDOBJ void\n"
534         << n << "(const cyrano::CyranoInt *const NTENS, const cyrano::CyranoReal *const DTIME,\n"
535         << "const cyrano::CyranoReal *const DROT,  cyrano::CyranoReal *const DDSDDE,\n"
536         << "const cyrano::CyranoReal *const STRAN, const cyrano::CyranoReal *const DSTRAN,\n"
537         << "const cyrano::CyranoReal *const TEMP,  const cyrano::CyranoReal *const DTEMP,\n"
538         << "const cyrano::CyranoReal *const PROPS, const cyrano::CyranoInt    *const NPROPS,\n"
539         << "const cyrano::CyranoReal *const PREDEF,const cyrano::CyranoReal *const DPRED,\n"
540         << "cyrano::CyranoReal *const STATEV,const cyrano::CyranoInt    *const NSTATV,\n"
541         << "cyrano::CyranoReal *const STRESS,const cyrano::CyranoInt    *const NDI,\n"
542         << "cyrano::CyranoInt    *const KINC)\n"
543         << "{\n";
544     out << "const auto op = " << this->getFunctionNameBasis(n)
545         << "_getOutOfBoundsPolicy();\n";
546     if (mb.getAttribute(BehaviourData::profiling, false)) {
547       out << "using mfront::BehaviourProfiler;\n"
548           << "using tfel::material::" << mb.getClassName() << "Profiler;\n"
549           << "auto total_timer(" << mb.getClassName() << "Profiler::getProfiler(),\n"
550           << "BehaviourProfiler::TOTALTIME);\n";
551     }
552     this->generateMTestFile1(out, mb);
553     out << "const auto k = std::abs(*DDSDDE)>0.5;\n"
554         << "// computing the logarithmic strain\n"
555         << "cyrano::CyranoReal eto[3];\n"
556         << "cyrano::CyranoReal deto[3];\n"
557         << "cyrano::CyranoReal s[3];\n"
558         << "cyrano::CyranoReal K[9];\n";
559     // axisymmetrical generalised plane stress
560     out << "if(*NDI!=1){\n"
561         << "// axisymmetrical generalised plane stress\n";
562     if (mb.isModellingHypothesisSupported(
563             ModellingHypothesis::AXISYMMETRICALGENERALISEDPLANESTRESS)) {
564       const auto& d =
565           mb.getBehaviourData(ModellingHypothesis::AXISYMMETRICALGENERALISEDPLANESTRESS);
566       // this must be added for gcc 4.7.2
567       const auto astress = [this,&d, throw_if]() -> std::pair<bool, SupportedTypes::TypeSize> {
568         SupportedTypes::TypeSize o;
569         // skipping the temperature
570         auto pev = std::next(d.getExternalStateVariables().begin());
571         while (pev != d.getExternalStateVariables().end()) {
572           if (d.getExternalName(pev->name) == tfel::glossary::Glossary::AxialStress) {
573             throw_if(
574                 SupportedTypes::getTypeFlag(pev->type) !=
575                     SupportedTypes::SCALAR,
576                 "invalid type for the `AxialStress` external state variable");
577             return {true, o};
578           }
579           o += SupportedTypes::getTypeSize(pev->type, pev->arraySize);
580           ++pev;
581         }
582         return {false, o};
583       }();
584       const auto astrain = this->checkIfAxialStrainIsDefinedAndGetItsOffset(
585           mb, ModellingHypothesis::AXISYMMETRICALGENERALISEDPLANESTRESS);
586       tfel::raise_if(!astress.first, "no external state state variable standing for the axial stress");
587       tfel::raise_if(!astrain.first, "no state variable standing for the axial strain");
588       if (mb.getAttribute(BehaviourData::profiling, false)) {
589         out << "{\n"
590             << "auto pre_timer(" << mb.getClassName() << "Profiler::getProfiler(),\n"
591             << "BehaviourProfiler::FINITESTRAINPREPROCESSING);\n";
592       }
593       out << "const auto Pzz0 = PREDEF[" << astress.second.getValueForDimension(1) << "];\n"
594           << "const auto Pzz1 = Pzz0+DPRED[" << astress.second.getValueForDimension(1) << "];\n"
595 	  << "const auto Tzz0 = "
596           << "Pzz0*std::exp(STATEV[" << astrain.second.getValueForDimension(1) << "]);\n"
597           << "eto[0]=std::log1p(*STRAN);\n"
598           << "eto[1]=std::log1p(*(STRAN+1));\n"
599           << "eto[2]=0;\n"
600           << "deto[0]=std::log1p(*STRAN+*DSTRAN)-eto[0];\n"
601           << "deto[1]=std::log1p(*(STRAN+1)+*(DSTRAN+1))-eto[1];\n"
602           << "deto[2]=0;\n"
603           << "s[0]=(*STRESS)*(1+*STRAN);\n"
604           << "s[1]=(*(STRESS+1))*(1+*(STRAN+1));\n"
605           << "s[2]=Tzz0;\n"
606           << "K[0]=*DDSDDE;\n";
607       if (mb.getAttribute(BehaviourData::profiling, false)) {
608         out << "}\n";
609       }
610       out << "cyrano::CyranoInterface<tfel::material::" << mb.getClassName()
611           << ">::exe(NTENS,DTIME,DROT,K,eto,deto,TEMP,DTEMP,PROPS,NPROPS,"
612           << "PREDEF,DPRED,STATEV,NSTATV,s,NDI,KINC,"
613           << "cyrano::CyranoLogarithmicStrainStressFreeExpansionHandler,op);\n";
614       out << "if(*KINC>=0){\n";
615       if (mb.getAttribute(BehaviourData::profiling, false)) {
616         out << "{\n"
617             << "auto post_timer(" << mb.getClassName() << "Profiler::getProfiler(),\n"
618             << "BehaviourProfiler::FINITESTRAINPOSTPROCESSING);\n";
619       }
620       // First Piola-Kirchhoff stress
621       out << "STRESS[0]=s[0]/(1+*STRAN+*DSTRAN);\n"
622           << "STRESS[1]=s[1]/(1+*(STRAN+1)+*(DSTRAN+1));\n"
623           << "STRESS[2]=Pzz1;\n";
624       // computation of the stiffness matrix
625       out << "if(k){\n"
626           << "*DDSDDE     = (-STRESS[0]+K[0]/(1+STRAN[0]+DSTRAN[0]))/(1+STRAN[0]+DSTRAN[0]);\n"
627           << "*(DDSDDE+3) = K[3]/((1+STRAN[1]+DSTRAN[1])*(1+STRAN[0]+DSTRAN[0]));\n"
628           << "*(DDSDDE+6) = 0;\n"
629           << "*(DDSDDE+1) = K[1]/((1+STRAN[0]+DSTRAN[0])*(1+STRAN[1]+DSTRAN[1]));\n"
630           << "*(DDSDDE+4) = (-STRESS[1]+K[4]/(1+STRAN[1]+DSTRAN[1]))/(1+STRAN[1]+DSTRAN[1]);\n"
631           << "*(DDSDDE+7) = 0;\n"
632           << "*(DDSDDE+2) = 0;\n"
633           << "*(DDSDDE+5) = 0;\n"
634           << "*(DDSDDE+8) = 0;\n"
635           << "}\n";
636       if (mb.getAttribute(BehaviourData::profiling, false)) {
637         out << "}\n";
638       }
639       out << "}\n";
640     } else {
641       out << "*KINC=-7;\n"
642           << "return;\n";
643     }
644     // axisymmetrical generalised plane strain
645     out << "} else {\n"
646         << "// axisymmetrical generalised plane strain\n";
647     if (mb.getAttribute(BehaviourData::profiling, false)) {
648       out << "{\n"
649           << "auto pre_timer(" << mb.getClassName() << "Profiler::getProfiler(),\n"
650           << "BehaviourProfiler::FINITESTRAINPREPROCESSING);\n";
651     }
652     out << "eto[0]=std::log1p(*STRAN);\n"
653         << "eto[1]=std::log1p(*(STRAN+1));\n"
654         << "eto[2]=std::log1p(*(STRAN+2));\n"
655         << "deto[0]=std::log1p(*STRAN+*DSTRAN)-eto[0];\n"
656         << "deto[1]=std::log1p(*(STRAN+1)+*(DSTRAN+1))-eto[1];\n"
657         << "deto[2]=std::log1p(*(STRAN+2)+*(DSTRAN+2))-eto[2];\n"
658         << "s[0]=(*STRESS)*(1+*STRAN);\n"
659         << "s[1]=(*(STRESS+1))*(1+*(STRAN+1));\n"
660         << "s[2]=(*(STRESS+2))*(1+*(STRAN+2));\n";
661     if (mb.getAttribute(BehaviourData::profiling, false)) {
662       out << "}\n";
663     }
664     out << "K[0]=*DDSDDE;\n"
665         << "cyrano::CyranoInterface<tfel::material::" << mb.getClassName()
666         << ">::exe(NTENS,DTIME,DROT,K,eto,deto,TEMP,DTEMP,PROPS,NPROPS,"
667         << "PREDEF,DPRED,STATEV,NSTATV,s,NDI,KINC,"
668         << "cyrano::CyranoLogarithmicStrainStressFreeExpansionHandler,op);\n";
669     out << "if(*KINC>=0){\n";
670     if (mb.getAttribute(BehaviourData::profiling, false)) {
671       out << "{\n"
672           << "auto post_timer(" << mb.getClassName() << "Profiler::getProfiler(),\n"
673           << "BehaviourProfiler::FINITESTRAINPOSTPROCESSING);\n";
674     }
675     // First Piola-Kirchhoff stress
676     out << "STRESS[0]=s[0]/(1+*STRAN+*DSTRAN);\n"
677         << "STRESS[1]=s[1]/(1+*(STRAN+1)+*(DSTRAN+1));\n"
678         << "STRESS[2]=s[2]/(1+*(STRAN+2)+*(DSTRAN+2));\n";
679     // computation of the stiffness matrix
680     out << "if(k){\n"
681         << "*DDSDDE     = (-STRESS[0]+K[0]/(1+STRAN[0]+DSTRAN[0]))/(1+STRAN[0]+DSTRAN[0]);\n"
682         << "*(DDSDDE+3) = K[3]/((1+STRAN[1]+DSTRAN[1])*(1+STRAN[0]+DSTRAN[0]));\n"
683         << "*(DDSDDE+6) = K[6]/((1+STRAN[2]+DSTRAN[2])*(1+STRAN[0]+DSTRAN[0]));\n"
684         << "*(DDSDDE+1) = K[1]/((1+STRAN[0]+DSTRAN[0])*(1+STRAN[1]+DSTRAN[1]));\n"
685         << "*(DDSDDE+4) = (-STRESS[1]+K[4]/(1+STRAN[1]+DSTRAN[1]))/(1+STRAN[1]+DSTRAN[1]);\n"
686         << "*(DDSDDE+7) = K[7]/((1+STRAN[2]+DSTRAN[2])*(1+STRAN[1]+DSTRAN[1]));\n"
687         << "*(DDSDDE+2) = K[2]/((1+STRAN[0]+DSTRAN[0])*(1+STRAN[2]+DSTRAN[2]));\n"
688         << "*(DDSDDE+5) = K[5]/((1+STRAN[1]+DSTRAN[1])*(1+STRAN[2]+DSTRAN[2]));\n"
689         << "*(DDSDDE+8) = (-STRESS[2]+K[8]/(1+STRAN[2]+DSTRAN[2]))/(1+STRAN[2]+DSTRAN[2]);\n"
690         << "}\n";
691     if (mb.getAttribute(BehaviourData::profiling, false)) {
692       out << "}\n";
693     }
694     out << "} // end of if(*KINC>=0)\n";
695     out << "} // end of if(*NDI!=1)\n";
696     if (this->shallGenerateMTestFileOnFailure(mb)) {
697       out << "if(*KINC<0){\n";
698       this->generateMTestFile2(out, mb,BehaviourDescription::STANDARDSTRAINBASEDBEHAVIOUR, n, "");
699       out << "}\n";
700     }
701     out << "}\n\n";
702     writeSecondaryCyranoFunction(out, this->getFunctionNameBasis(n), n);
703   }  // end of CyranoInterface::writeLogarithmicStrainCyranoFunction
704 
writeCyranoBehaviourTraits(std::ostream & out,const BehaviourDescription & mb,const Hypothesis h) const705   void CyranoInterface::writeCyranoBehaviourTraits(std::ostream& out,
706                                                    const BehaviourDescription& mb,
707                                                    const Hypothesis h) const {
708     using namespace tfel::material;
709     const auto mvs = mb.getMainVariablesSize();
710     const auto mprops = this->buildMaterialPropertiesList(mb, h);
711     if (h == ModellingHypothesis::UNDEFINEDHYPOTHESIS) {
712       out << "template<tfel::material::ModellingHypothesis::Hypothesis H,typename Type";
713       if (mb.useQt()) {
714         out << ",bool use_qt";
715       }
716     } else {
717       out << "template<typename Type";
718       if (mb.useQt()) {
719         out << ",bool use_qt";
720       }
721     }
722     out << ">\n";
723     if (h == ModellingHypothesis::UNDEFINEDHYPOTHESIS) {
724       out << "struct CyranoTraits<tfel::material::" << mb.getClassName() << "<H,Type,";
725     } else {
726       out << "struct CyranoTraits<tfel::material::" << mb.getClassName()
727           << "<tfel::material::ModellingHypothesis::" << ModellingHypothesis::toUpperCaseString(h)
728           << ",Type,";
729     }
730     if (mb.useQt()) {
731       out << "use_qt";
732     } else {
733       out << "false";
734     }
735     out << "> >{\n";
736     if (h != ModellingHypothesis::UNDEFINEDHYPOTHESIS) {
737       out << "// modelling hypothesis\n";
738       out << "static " << constexpr_c << " tfel::material::ModellingHypothesis::Hypothesis H = "
739           << "tfel::material::ModellingHypothesis::" << ModellingHypothesis::toUpperCaseString(h)
740           << ";\n";
741     }
742     out << "// space dimension\n";
743     out << "static " << constexpr_c << " unsigned short N           = "
744                                        "tfel::material::ModellingHypothesisToSpaceDimension<H>::"
745                                        "value;\n";
746     out << "// tiny vector size\n";
747     out << "static " << constexpr_c << " unsigned short TVectorSize = N;\n";
748     out << "// symmetric tensor size\n";
749     out << "static " << constexpr_c
750         << " unsigned short StensorSize = tfel::math::StensorDimeToSize<N>::value;\n";
751     out << "// tensor size\n";
752     out << "static " << constexpr_c
753         << " unsigned short TensorSize  = tfel::math::TensorDimeToSize<N>::value;\n";
754     out << "// size of the driving variable array (STRAN)\n";
755     out << "static " << constexpr_c << " unsigned short GradientSize  = " << mvs.first
756         << ";\n";
757     out << "// size of the thermodynamic force variable array (STRAN)\n";
758     out << "static " << constexpr_c
759         << " unsigned short ThermodynamicForceVariableSize  = " << mvs.second << ";\n";
760     out << "static " << constexpr_c << " bool useTimeSubStepping = ";
761     if (this->useTimeSubStepping) {
762       out << "true;\n";
763     } else {
764       out << "false;\n";
765     }
766     out << "static " << constexpr_c << " bool doSubSteppingOnInvalidResults = ";
767     if (this->doSubSteppingOnInvalidResults) {
768       out << "true;\n";
769     } else {
770       out << "false;\n";
771     }
772     out << "static " << constexpr_c << " unsigned short maximumSubStepping = ";
773     if (this->useTimeSubStepping) {
774       out << this->maximumSubStepping << ";\n";
775     } else {
776       out << "0u;\n";
777     }
778     if (mb.getAttribute(BehaviourDescription::requiresStiffnessTensor, false)) {
779       out << "static " << constexpr_c << " bool requiresStiffnessTensor = true;\n";
780       if (mb.getAttribute(BehaviourDescription::requiresUnAlteredStiffnessTensor, false)) {
781         out << "static " << constexpr_c << " bool requiresUnAlteredStiffnessTensor = true;\n";
782       } else {
783         out << "static " << constexpr_c << " bool requiresUnAlteredStiffnessTensor = false;\n";
784       }
785     } else {
786       out << "static " << constexpr_c << " bool requiresStiffnessTensor = false;\n";
787     }
788     if (mb.getAttribute(BehaviourDescription::requiresThermalExpansionCoefficientTensor, false)) {
789       out << "static " << constexpr_c
790           << " bool requiresThermalExpansionCoefficientTensor = true;\n";
791     } else {
792       out << "static " << constexpr_c
793           << " bool requiresThermalExpansionCoefficientTensor = false;\n";
794     }
795     // computing material properties size
796     SupportedTypes::TypeSize msize;
797     if (!mprops.first.empty()) {
798       const auto& m = mprops.first.back();
799       msize = m.offset;
800       msize += SupportedTypes::getTypeSize(m.type, m.arraySize);
801       msize -= mprops.second;
802     }
803     out << "static " << constexpr_c << " unsigned short material_properties_nb = " << msize
804         << ";\n";
805     if (mb.getSymmetryType() == mfront::ISOTROPIC) {
806       if (mb.getAttribute(BehaviourDescription::requiresStiffnessTensor, false)) {
807         if (mb.getAttribute(BehaviourDescription::requiresThermalExpansionCoefficientTensor,
808                             false)) {
809           out << "static " << constexpr_c << " unsigned short propertiesOffset        = 3u;\n";
810           out << "static " << constexpr_c << " unsigned short elasticPropertiesOffset = 2u;\n";
811         } else {
812           out << "static " << constexpr_c << " unsigned short propertiesOffset        = 2u;\n";
813           out << "static " << constexpr_c << " unsigned short elasticPropertiesOffset = 2u;\n";
814         }
815       } else {
816         if (mb.getAttribute(BehaviourDescription::requiresThermalExpansionCoefficientTensor,
817                             false)) {
818           out << "static " << constexpr_c << " unsigned short propertiesOffset        = 1u;\n";
819           out << "static " << constexpr_c << " unsigned short elasticPropertiesOffset = 0u;\n";
820         } else {
821           out << "static " << constexpr_c << " unsigned short propertiesOffset        = 0u;\n";
822           out << "static " << constexpr_c << " unsigned short elasticPropertiesOffset = 0u;\n";
823         }
824       }
825     } else if (mb.getSymmetryType() == mfront::ORTHOTROPIC) {
826       if (mb.getAttribute(BehaviourDescription::requiresStiffnessTensor, false)) {
827         if (mb.getAttribute(BehaviourDescription::requiresThermalExpansionCoefficientTensor,
828                             false)) {
829           out << "static " << constexpr_c << " unsigned short propertiesOffset        = 9u;\n";
830           out << "static " << constexpr_c << " unsigned short elasticPropertiesOffset = 6u;\n";
831         } else {
832           out << "static " << constexpr_c << " unsigned short propertiesOffset        = 6u;\n";
833           out << "static " << constexpr_c << " unsigned short elasticPropertiesOffset = 6u;\n";
834         }
835       } else {
836         if (mb.getAttribute(BehaviourDescription::requiresThermalExpansionCoefficientTensor,
837                             false)) {
838           out << "static " << constexpr_c << " unsigned short propertiesOffset        = 3u;\n";
839           out << "static " << constexpr_c << " unsigned short elasticPropertiesOffset = 0u;\n";
840         } else {
841           out << "static " << constexpr_c << " unsigned short propertiesOffset = 0u;\n";
842           out << "static " << constexpr_c << " unsigned short elasticPropertiesOffset = 0u;\n";
843         }
844       }
845     } else {
846       tfel::raise(
847           "CyranoInterface::writeCyranoBehaviourTraits: "
848           "unsupported behaviour symmetry type.\n"
849           "The cyrano interface only support isotropic or "
850           "orthotropic behaviour at this time.");
851     }
852     if (mb.getSymmetryType() == mfront::ISOTROPIC) {
853       out << "static " << constexpr_c << " CyranoSymmetryType stype = cyrano::ISOTROPIC;\n";
854     } else if (mb.getSymmetryType() == mfront::ORTHOTROPIC) {
855       out << "static " << constexpr_c << " CyranoSymmetryType stype = cyrano::ORTHOTROPIC;\n";
856     } else {
857       tfel::raise(
858           "CyranoInterface::writeCyranoBehaviourTraits: "
859           "unsupported behaviour symmetry type.\n"
860           "The cyrano interface only support isotropic or "
861           "orthotropic behaviour at this time.");
862     }
863     out << "}; // end of class CyranoTraits\n\n";
864   }  // end of CyranoInterface::writeCyranoBehaviourTraits
865 
getModellingHypothesisTest(const Hypothesis h) const866   std::string CyranoInterface::getModellingHypothesisTest(const Hypothesis h) const {
867     std::ostringstream test;
868     test << "*NDI==" << this->getModellingHypothesisIdentifier(h);
869     return test.str();
870   }
871 
872 }  // end of namespace mfront
873