1 /*!
2  * \file   umat.cpp
3  * \brief
4  * \author Thomas Helfer
5  * \date   21/03/2016
6  */
7 
8 #include<iostream>
9 #include<cstdlib>
10 
11 #include<map>
12 #include<string>
13 #include<cctype>
14 #include<vector>
15 #include<utility>
16 #include<cstring>
17 #include<stdexcept>
18 #include<algorithm>
19 
20 #if __cplusplus >= 201103L
21 #include<mutex>
22 #define HAVE_STD_MUTEX
23 #define NULLPTR(X) nullptr
24 #else  /* __cplusplus >= 201103L */
25 #define NULLPTR(X) static_cast<X>(0)
26 #endif /* __cplusplus >= 201103L */
27 
28 #if (defined _WIN32 || defined _WIN64) && (!defined __CYGWIN__)
29 #ifndef NOMINMAX
30 #define NOMINMAX
31 #endif
32 #include<windows.h>
33 #ifdef min
34 #undef min
35 #endif
36 
37 #ifdef max
38 #undef max
39 #endif
40 typedef HINSTANCE__*  libptr;
41 
42 #ifndef HAVE_STD_MUTEX
43 
44 struct Mutex
45 {
getMutexMutex46   static Mutex& getMutex(){
47     static Mutex m;
48     return m;
49   }
50   HANDLE m;
51 private:
MutexMutex52   Mutex(){
53     this->m = CreateMutex(NULL,  // default security attributes
54 			FALSE, // initially not owned
55 			NULL); // unnamed mutex
56   }
57 };
58 
59 struct lock
60 {
locklock61   lock(){
62     DWORD r = WaitForSingleObject(Mutex::getMutex().m,
63 				  INFINITE);
64     if(r==WAIT_ABANDONED){
65       std::cerr << "umat: abandonned mutex" << std::endl;
66       std::exit(EXIT_FAILURE);
67     }
68   }
~locklock69   ~lock(){
70     CloseHandle(Mutex::getMutex().m);
71   }
72 };
73 #endif /* HAVE_STD_MUTEX */
74 
75 // code retrieved from
76 // http://www.codeproject.com/Tips/479880/GetLastError-as-std-string
getLastWin32Error()77 static std::string getLastWin32Error()
78 {
79   const DWORD error = GetLastError();
80   if (error){
81     LPVOID lpMsgBuf;
82     DWORD bufLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
83 				 FORMAT_MESSAGE_FROM_SYSTEM |
84 				 FORMAT_MESSAGE_IGNORE_INSERTS,
85 				 NULL,error,
86 				 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
87 				 (LPTSTR) &lpMsgBuf,0,NULL );
88     if (bufLen){
89       LPCSTR lpMsgStr = (LPTSTR) lpMsgBuf;
90       std::string result(lpMsgStr, lpMsgStr+bufLen);
91       LocalFree(lpMsgBuf);
92       return result;
93     }
94   }
95   return std::string();
96 }
97 
98 #else
99 #include<dlfcn.h>
100 typedef void * libptr;
101 #ifndef HAVE_STD_MUTEX
102 #error "Unsupported platform"
103 #endif /* HAVE_STD_MUTEX */
104 #endif
105 
106 #if (defined _WIN32 || defined _WIN64) && (!defined __CYGWIN__)
107 #define UMATFCT  umat
108 #define UMATFCT2 umat2
109 #else
110 #define UMATFCT  umat_
111 #define UMATFCT2 umat2_
112 #endif
113 
114 #if (defined _WIN32) && (!defined _WIN64)
115 typedef int    fortran_string_size;
116 #elif defined _WIN64
117 typedef size_t fortran_string_size;
118 #else
119 typedef int    fortran_string_size;
120 #endif
121 
122 typedef double abaqus_real;
123 typedef int    abaqus_int;
124 
125 typedef  void (*umatptr)(abaqus_real *const,
126 			 abaqus_real *const,
127 			 abaqus_real *const,
128 			 abaqus_real *const,
129 			 abaqus_real *const,
130 			 abaqus_real *const,
131 			 abaqus_real *const,
132 			 abaqus_real *const,
133 			 abaqus_real *const,
134 			 abaqus_real *const,
135 			 const abaqus_real *const,
136 			 const abaqus_real *const,
137 			 const abaqus_real *const,
138 			 const abaqus_real *const,
139 			 const abaqus_real *const,
140 			 const abaqus_real *const,
141 			 const abaqus_real *const,
142 			 const abaqus_real *const,
143 			 const char        *const,
144 			 const abaqus_int  *const,
145 			 const abaqus_int  *const,
146 			 const abaqus_int  *const,
147 			 const abaqus_int  *const,
148 			 const abaqus_real *const,
149 			 const abaqus_int  *const,
150 			 const abaqus_real *const,
151 			 const abaqus_real *const,
152 			 abaqus_real *const,
153 			 const abaqus_real *const,
154 			 const abaqus_real *const,
155 			 const abaqus_real *const,
156 			 const abaqus_int  *const,
157 			 const abaqus_int  *const,
158 			 const abaqus_int  *const,
159 			 const abaqus_int  *const,
160 			 const abaqus_int  *const,
161 			 abaqus_int  *const,
162 			 const int);
163 
164 extern "C" {
165   void UMATFCT(abaqus_real *const,
166 	       abaqus_real *const,
167 	       abaqus_real *const,
168 	       abaqus_real *const,
169 	       abaqus_real *const,
170 	       abaqus_real *const,
171 	       abaqus_real *const,
172 	       abaqus_real *const,
173 	       abaqus_real *const,
174 	       abaqus_real *const,
175 	       const abaqus_real *const,
176 	       const abaqus_real *const,
177 	       const abaqus_real *const,
178 	       const abaqus_real *const,
179 	       const abaqus_real *const,
180 	       const abaqus_real *const,
181 	       const abaqus_real *const,
182 	       const abaqus_real *const,
183 	       const char        *const,
184 	       const abaqus_int  *const,
185 	       const abaqus_int  *const,
186 	       const abaqus_int  *const,
187 	       const abaqus_int  *const,
188 	       const abaqus_real *const,
189 	       const abaqus_int  *const,
190 	       const abaqus_real *const,
191 	       const abaqus_real *const,
192 	       abaqus_real *const,
193 	       const abaqus_real *const,
194 	       const abaqus_real *const,
195 	       const abaqus_real *const,
196 	       const abaqus_int  *const,
197 	       const abaqus_int  *const,
198 	       const abaqus_int  *const,
199 	       const abaqus_int  *const,
200 	       const abaqus_int  *const,
201 	       abaqus_int  *const,
202 	       const fortran_string_size);
203 
204   void UMATFCT2(abaqus_real *const,
205 		abaqus_real *const,
206 		abaqus_real *const,
207 		abaqus_real *const,
208 		abaqus_real *const,
209 		abaqus_real *const,
210 		abaqus_real *const,
211 		abaqus_real *const,
212 		abaqus_real *const,
213 		abaqus_real *const,
214 		const abaqus_real *const,
215 		const abaqus_real *const,
216 		const abaqus_real *const,
217 		const abaqus_real *const,
218 		const abaqus_real *const,
219 		const abaqus_real *const,
220 		const abaqus_real *const,
221 		const abaqus_real *const,
222 		const char        *const,
223 		const abaqus_int  *const,
224 		const abaqus_int  *const,
225 		const abaqus_int  *const,
226 		const abaqus_int  *const,
227 		const abaqus_real *const,
228 		const abaqus_int  *const,
229 		const abaqus_real *const,
230 		const abaqus_real *const,
231 		abaqus_real *const,
232 		const abaqus_real *const,
233 		const abaqus_real *const,
234 		const abaqus_real *const,
235 		const abaqus_int  *const,
236 		const abaqus_int  *const,
237 		const abaqus_int  *const,
238 		const abaqus_int  *const,
239 		const abaqus_int  *const,
240 		abaqus_int  *const,
241 		const fortran_string_size);
242 }
243 
244 struct LibrariesHandler
245   : public std::map<std::string,libptr>
246 {
~LibrariesHandlerLibrariesHandler247   ~LibrariesHandler(){
248     for(iterator pl=this->begin();pl!=this->end();++pl){
249 #if (defined _WIN32 || defined _WIN64) && (!defined __CYGWIN__)
250       ::FreeLibrary(pl->second);
251 #else
252       ::dlclose(pl->second);
253 #endif
254     }
255   } // end of ~LibrariesHandler
256 }; // end of LibrariesHandler
257 
258 struct UmatPtrHandler
259 {
260   std::string name;
261   umatptr ptr;
262 }; // end of UmatPtrHandler
263 
report(const std::string & ln,const std::string & fn,const std::string & en,const char * const n)264 static void report(const std::string& ln,
265 		   const std::string& fn,
266 		   const std::string& en,
267 		   const char * const n){
268   std::cerr << "umat::load : could not load behaviour '"
269   << std::string(n,n+80) << '\'';
270   if(!ln.empty()){
271     std::cerr << "\nTried library '" << ln << '\'';
272   }
273   if(!fn.empty()){
274     std::cerr << "\nTried function '" << fn << '\'';
275   }
276   if(!en.empty()){
277     std::cerr << '\n' << en;
278   }
279   std::cerr << std::endl;
280 }
281 
282 struct UMATNameCompare
283 {
UMATNameCompareUMATNameCompare284   UMATNameCompare(const char* const s)
285     : n(s)
286   {} // end of UMATNameCompare
UMATNameCompareUMATNameCompare287   UMATNameCompare(const UMATNameCompare &p)
288     : n(p.n)
289   {}
operator ()UMATNameCompare290   bool operator()(const UmatPtrHandler& h) const{
291     return ::strncmp(h.name.data(),n,std::min(h.name.size(),
292 					      std::string::size_type(79)))==0;
293   }
294 private:
295   const char* const n;
296 };
error_msg(const char * const msg)297 static void error_msg(const char* const msg){
298   std::cerr << "umat: " << msg << std::endl;
299 }
300 
extract(const char * & p,const char * const pe,const char * const msg)301 static std::string extract(const char*& p,
302 			   const char* const pe,
303 			   const char* const msg)
304 {
305   if(p==pe){
306     error_msg(msg);
307     return "";
308   }
309   const char* const n = std::find(p,pe,'_');
310   if(n==p){
311     error_msg(msg);
312     return "";
313   }
314   std::string r = std::string(p,n);
315   p=n;
316   return r;
317 }
318 
decompose(const char * const n)319 static std::pair<std::string,std::string> decompose(const char * const n)
320 {
321   const char* pn = n;
322   const char* pne = n+80;
323   // removing spaces at the end
324   while((pne!=pn)&&((std::isspace(*(pne-1)))||(*(pne-1)=='\0'))){
325     --pne;
326   }
327   if(pn==pne){
328     error_msg("empty string");
329     return std::make_pair(std::string(),std::string());
330   };
331   const std::string ln = extract(pn,pne,"can't extract library name");
332 #ifdef _WIN32
333   std::string lib = "lib" + ln + ".dll";
334 #else
335   std::string lib = "lib" + ln + ".so";
336 #endif
337   if(pn==pne){
338     error_msg("extract function name");
339     return std::make_pair(std::string(),std::string());
340   }
341   ++pn;
342   std::string fct = extract(pn,pne,"can't extract function name");
343   if(fct.empty()){
344     return std::make_pair(std::string(),std::string());
345   }
346   if(pn!=pne){
347     ++pn;
348     fct += '_'+extract(pn,pne,"can't extract hypothesis");
349   }
350 #ifdef MFRONT_UMAT_DEBUG
351   std::cout << "library: "   << lib << std::endl;
352   std::cout << "behaviour: " << fct << std::endl;
353   if(pn!=pne){
354     ++pn;
355     if(pn!=pne){
356       std::cout << "suffix: '" << std::string(pn,pne) << "'" << std::endl;
357     }
358   }
359 #endif /* MFRONT_UMAT_DEBUG */
360   return std::make_pair(lib,fct);
361 }
362 
load(const char * n)363 static umatptr load(const char* n){
364   typedef std::vector<UmatPtrHandler> UmatPtrContainer;
365   static LibrariesHandler libraries;
366   static UmatPtrContainer fcts;
367 #ifdef HAVE_STD_MUTEX
368    static std::mutex m;
369    std::lock_guard<std::mutex> lock(m);
370 #else /* HAVE_STD_MUTEX */
371    lock l;
372 #endif  /* HAVE_STD_MUTEX */
373   try{
374     UmatPtrContainer::const_iterator p;
375     p = std::find_if(fcts.begin(),fcts.end(),UMATNameCompare(n));
376     if(p==fcts.end()){
377       const std::pair<std::string,std::string> lf = decompose(n);
378       const std::string& lib = lf.first;
379       const std::string& fct = lf.second;
380       if(lib.empty()){
381 	report("","","",n);
382 	return NULLPTR(umatptr);
383       }
384 #if (defined _WIN32 || defined _WIN64) && (!defined __CYGWIN__)
385       libptr l = ::LoadLibrary(TEXT (lib.c_str()));
386 #else
387       libptr l = ::dlopen(lib.c_str(),RTLD_NOW);
388 #endif
389       if(l==NULLPTR(libptr)){
390 #if (defined _WIN32 || defined _WIN64) && (!defined __CYGWIN__)
391 	report(lib,"",getLastWin32Error(),n);
392 #else
393 	report(lib,"",::dlerror(),n);
394 #endif
395 	return NULLPTR(umatptr);
396       }
397       libraries.insert(std::make_pair(lib,l));
398       union {
399 	void *ptr;
400 	umatptr f;
401       } r;
402 #if (defined _WIN32 || defined _WIN64) && (!defined __CYGWIN__)
403       r.f = reinterpret_cast<umatptr>(::GetProcAddress(l,fct.c_str()));
404 #else
405       r.ptr = ::dlsym(l,fct.c_str());
406 #endif
407       if(r.ptr==NULLPTR(void *)){
408 #if (defined _WIN32 || defined _WIN64) && (!defined __CYGWIN__)
409 	report(lib,fct,getLastWin32Error(),n);
410 #else
411 	report(lib,fct,::dlerror(),n);
412 #endif
413 	return NULLPTR(umatptr);
414       }
415       UmatPtrHandler h;
416       h.name = std::string(n,n+80);
417       h.ptr  = r.f;
418       fcts.push_back(h);
419       return r.f;
420     }
421     return p->ptr;
422   }
423   catch(const std::exception& e){
424     std::cerr << "umat::load : " << e.what() << std::endl;
425   }
426   catch(...){
427     std::cerr << "umat::load : unknown exception" << std::endl;
428   }
429   return NULLPTR(umatptr);
430 }
431 
432 extern "C" {
433 
UMATFCT(abaqus_real * const STRESS,abaqus_real * const STATEV,abaqus_real * const DDSDDE,abaqus_real * const SSE,abaqus_real * const SPD,abaqus_real * const SCD,abaqus_real * const RPL,abaqus_real * const DDSDDT,abaqus_real * const DRPLDE,abaqus_real * const DRPLDT,const abaqus_real * const STRAN,const abaqus_real * const DSTRAN,const abaqus_real * const TIME,const abaqus_real * const DTIME,const abaqus_real * const TEMP,const abaqus_real * const DTEMP,const abaqus_real * const PREDEF,const abaqus_real * const DPRED,const char * const CMNAME,const abaqus_int * const NDI,const abaqus_int * const NSHR,const abaqus_int * const NTENS,const abaqus_int * const NSTATV,const abaqus_real * const PROPS,const abaqus_int * const NPROPS,const abaqus_real * const COORDS,const abaqus_real * const DROT,abaqus_real * const PNEWDT,const abaqus_real * const CELENT,const abaqus_real * const DFGRD0,const abaqus_real * const DFGRD1,const abaqus_int * const NOEL,const abaqus_int * const NPT,const abaqus_int * const LAYER,const abaqus_int * const KSPT,const abaqus_int * const KSTEP,abaqus_int * const KINC,const fortran_string_size size)434   void UMATFCT(abaqus_real *const STRESS,
435 	       abaqus_real *const STATEV,
436 	       abaqus_real *const DDSDDE,
437 	       abaqus_real *const SSE,
438 	       abaqus_real *const SPD,
439 	       abaqus_real *const SCD,
440 	       abaqus_real *const RPL,
441 	       abaqus_real *const DDSDDT,
442 	       abaqus_real *const DRPLDE,
443 	       abaqus_real *const DRPLDT,
444 	       const abaqus_real *const STRAN,
445 	       const abaqus_real *const DSTRAN,
446 	       const abaqus_real *const TIME,
447 	       const abaqus_real *const DTIME,
448 	       const abaqus_real *const TEMP,
449 	       const abaqus_real *const DTEMP,
450 	       const abaqus_real *const PREDEF,
451 	       const abaqus_real *const DPRED,
452 	       const char           *const CMNAME,
453 	       const abaqus_int  *const NDI,
454 	       const abaqus_int  *const NSHR,
455 	       const abaqus_int  *const NTENS,
456 	       const abaqus_int  *const NSTATV,
457 	       const abaqus_real *const PROPS,
458 	       const abaqus_int  *const NPROPS,
459 	       const abaqus_real *const COORDS,
460 	       const abaqus_real *const DROT,
461 	       abaqus_real *const PNEWDT,
462 	       const abaqus_real *const CELENT,
463 	       const abaqus_real *const DFGRD0,
464 	       const abaqus_real *const DFGRD1,
465 	       const abaqus_int  *const NOEL,
466 	       const abaqus_int  *const NPT,
467 	       const abaqus_int  *const LAYER,
468 	       const abaqus_int  *const KSPT,
469 	       const abaqus_int  *const KSTEP,
470 	       abaqus_int  *const KINC,
471 	       const fortran_string_size size){
472     umatptr f = load(CMNAME);
473     if(f!=NULLPTR(umatptr)){
474       f(STRESS,STATEV,DDSDDE,SSE,SPD,SCD,RPL,
475 	DDSDDT,DRPLDE,DRPLDT,STRAN,DSTRAN,TIME,
476 	DTIME,TEMP,DTEMP,PREDEF,DPRED,CMNAME,
477 	NDI,NSHR,NTENS,NSTATV,PROPS,NPROPS,
478 	COORDS,DROT,PNEWDT,CELENT,DFGRD0,DFGRD1,
479 	NOEL,NPT,LAYER,KSPT,KSTEP,KINC,size);
480       return;
481     }
482 
483     /*
484      * by default, we fail if we could not load an external function
485      */
486 
487     std::cout << "umat : unsupported material" << std::endl;
488     ::exit(-1);
489 
490     /*
491      * However, if we want to combine mfront laws and standard umat,
492      * it is possible:
493      * - just rename your standard umat subroutine in umat2
494      * - comment the two previous lines
495      * - uncomment the newt lines
496      */
497 
498     //     UMATFCT2(STRESS,STATEV,DDSDDE,SSE,SPD,SCD,RPL,
499     // 	   DDSDDT,DRPLDE,DRPLDT,STRAN,DSTRAN,TIME,
500     // 	   DTIME,TEMP,DTEMP,PREDEF,DPRED,CMNAME,
501     // 	   NDI,NSHR,NTENS,NSTATV,PROPS,NPROPS,
502     // 	   COORDS,DROT,PNEWDT,CELENT,DFGRD0,DFGRD1,
503     // 	   NOEL,NPT,LAYER,KSPT,KSTEP,KINC,size);
504 
505   } // end of umat_
506 
507 } // end of extern "C"
508 
509 // int main(void){
510 //   char n[80] = {'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
511 // 		'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
512 // 		'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
513 // 		'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
514 // 		'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
515 // 		'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
516 // 		'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
517 // 		'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'};
518 //   const char * b = "UMATBEHAVIOUR_CHABOCHE_3D_1";
519 //   std::copy(b,b+std::strlen(b),n);
520 //   std::pair<std::string,std::string> r = decompose(n);
521 //   std::cout << r.first << std::endl;
522 //   std::cout << r.second << std::endl;
523 //   std::fill(n,n+80,'\0');
524 //   const char * b2 = "UMATBEHAVIOUR_CHABOCHE_PSTRAIN";
525 //   std::copy(b2,b2+std::strlen(b2),n);
526 //   r = decompose(n);
527 //   std::cout << r.first << std::endl;
528 //   std::cout << r.second << std::endl;
529 //   std::fill(n,n+80,'\0');
530 //   r = decompose(n);
531 //   std::cout << r.first << std::endl;
532 //   std::cout << r.second << std::endl;
533 //   return 0;
534 // }
535