util.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. /* Implementation of util.h */
  2. #include "util.h"
  3. #include "result.h"
  4. #include "dsblock.h"
  5. #ifndef ONLY_INCLUDE_INLINE_INTEGRATION
  6. #include "integration.h"
  7. #endif
  8. #include "fmiFunctions_fwd.h"
  9. #include "adymosim.h"
  10. #ifndef ONLY_INCLUDE_INLINE_INTEGRATION
  11. #include <cvode/cvode.h>
  12. /* Sundials */
  13. #endif
  14. #include <stdarg.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <assert.h>
  18. /* ----------------- local variables ----------------- */
  19. /* when compiling as a single complilation unit, this is already defined */
  20. #ifndef FMU_SOURCE_SINGLE_UNIT
  21. extern Component* globalComponent2;
  22. #endif
  23. /* ----------------- function definitions ----------------- */
  24. extern void ModelicaMessage(const char *string);
  25. extern void ModelicaError(const char *string);
  26. /* -------------------------------------------------------- */
  27. DYMOLA_STATIC void util_logger(Component* comp, FMIString instanceName, FMIStatus status,
  28. FMIString category, FMIString message, ...)
  29. {
  30. char buf[4096];
  31. va_list ap;
  32. int capacity;
  33. int n = 0;
  34. if (comp->loggingOn == FMIFalse && (status < FMIWarning)) {
  35. return;
  36. }
  37. va_start(ap, message);
  38. capacity = sizeof(buf) - 1;
  39. if (comp->logbufp > comp->logbuf) {
  40. /* add buffered messages */
  41. #if defined(_MSC_VER) && _MSC_VER>=1400
  42. n = vsnprintf_s(buf, capacity, _TRUNCATE, comp->logbuf, ap);
  43. #else
  44. buf[capacity]=0;
  45. n = vsnprintf(buf, capacity, comp->logbuf, ap);
  46. #endif
  47. if (n >= capacity || n <= 0) {
  48. goto done;
  49. }
  50. }
  51. #if defined(_MSC_VER) && _MSC_VER>=1400
  52. vsnprintf_s(buf + n, capacity - n, _TRUNCATE, message, ap);
  53. #else
  54. buf[capacity]=0;
  55. vsnprintf(buf + n, capacity - n, message, ap);
  56. #endif
  57. va_end(ap);
  58. #ifdef FMI_2
  59. comp->functions->logger(comp->functions->componentEnvironment, instanceName, status, category, buf);
  60. #else
  61. comp->functions.logger(comp, instanceName, status, category, buf);
  62. #endif
  63. done:
  64. comp->logbufp = comp->logbuf;
  65. }
  66. /* -------------------------------------------------------- */
  67. DYMOLA_STATIC void util_buflogger(Component* comp, FMIString instanceName, FMIStatus status,
  68. FMIString category, FMIString message, ...)
  69. {
  70. va_list ap;
  71. int capacity;
  72. int n;
  73. if (comp->loggingOn == FMIFalse && (status < FMIWarning)) {
  74. return;
  75. }
  76. capacity = (int) (sizeof(comp->logbuf) - (comp->logbufp - comp->logbuf) - 1);
  77. if (capacity <= 0) {
  78. goto fail;
  79. }
  80. va_start(ap, message);
  81. n = vsnprintf(comp->logbufp, capacity, message, ap);
  82. if (n >= capacity || n <= 0) {
  83. goto fail;
  84. }
  85. va_end(ap);
  86. comp->logbufp += n;
  87. return;
  88. fail:
  89. comp->logbufp = comp->logbuf;
  90. }
  91. /* -------------------------------------------------------- */
  92. DYMOLA_STATIC FMIString util_strdup(const FMICallbackFunctions *functions, FMIString s)
  93. {
  94. static const FMIString nullString = "<NULL>";
  95. static const int maxLen = 1024;
  96. FMIString pos = s;
  97. int len;
  98. char* newS;
  99. if (s == NULL) {
  100. s = nullString;
  101. }
  102. len = (int) strlen(s);
  103. if (len > maxLen) {
  104. len = maxLen;
  105. }
  106. newS = (FMIChar*) functions->allocateMemory(len + 1, sizeof(FMIChar));
  107. if (newS == NULL) {
  108. return NULL;
  109. }
  110. strncpy(newS, s, len);
  111. return newS;
  112. }
  113. #ifndef ONLY_INCLUDE_INLINE_INTEGRATION
  114. /* ------------------------------------------------------ */
  115. DYMOLA_STATIC int util_check_flag(void *flagvalue, char *funcname, int opt, Component* comp)
  116. {
  117. int *errflag;
  118. /* Check if SUNDIALS function returned NULL pointer - no memory allocated */
  119. if (opt == 0 && flagvalue == NULL) {
  120. util_logger(comp, comp->instanceName, FMIError, "",
  121. "SUNDIALS_ERROR: %s() failed - returned NULL pointer", funcname);
  122. return 1;
  123. }
  124. /* Check if flag < 0 */
  125. else if (opt == 1) {
  126. errflag = (int *) flagvalue;
  127. if (*errflag < 0) {
  128. char * CVodeFlagName = CVodeGetReturnFlagName(*errflag);
  129. util_logger(comp, comp->instanceName, FMIError, "",
  130. "SUNDIALS_ERROR: %s() failed with flag = %s",
  131. funcname, CVodeFlagName);
  132. free(CVodeFlagName);
  133. return 1;
  134. }
  135. }
  136. return 0;
  137. }
  138. #endif /* ONLY_INCLUDE_INLINE_INTEGRATION */
  139. /* ------------------------------------------------------ */
  140. DYMOLA_STATIC int GetDymolaOneIteration(struct DYNInstanceData*);
  141. DYMOLA_STATIC void SetDymolaOneIteration(struct DYNInstanceData*, int val);
  142. DYMOLA_STATIC void SetDymolaJacobianPointers(struct DYNInstanceData*did_, double * QJacobian_,double * QBJacobian_,double * QCJacobian_,double * QDJacobian_,int QJacobianN_,
  143. int QJacobianNU_,int QJacobianNY_,double * QJacobianSparse_,int * QJacobianSparseR_,int * QJacobianSparseC_,int QJacobianNZ_);
  144. DYMOLA_STATIC int util_refresh_cache(Component* comp, int idemand, const char* label, FMIBoolean* iterationConverged)
  145. {
  146. #ifdef FMI_2
  147. switch (comp->mStatus) {
  148. case modelInstantiated:
  149. if (GetDymolaOneIteration(comp->did) == 0) {
  150. SetDymolaOneIteration(comp->did, 5);
  151. }
  152. break;
  153. case modelInitializationMode:
  154. if (GetDymolaOneIteration(comp->did) == 0) {
  155. SetDymolaOneIteration(comp->did, 5);
  156. }
  157. idemand=iDemandStart;
  158. break;
  159. case modelEventMode:
  160. case modelEventMode2:
  161. if(comp->firstEventCall){
  162. SetDymolaOneIteration(comp->did, 2);
  163. comp->firstEventCall=FMIFalse;
  164. }else if (GetDymolaOneIteration(comp->did) == 0) {
  165. SetDymolaOneIteration(comp->did, 5);
  166. }
  167. idemand=iDemandEventHandling;
  168. break;
  169. default:
  170. //assert(GetDymolaOneIteration(comp->did) == 0);
  171. break;
  172. }
  173. #endif
  174. comp->QiErr=0;
  175. if (comp->did) {
  176. globalComponent2=comp;
  177. dsblock_tid(&idemand, &comp->icall, &comp->time, comp->states, 0,
  178. comp->inputs, comp->parameters, 0, 0, comp->derivatives,
  179. comp->outputs, comp->auxiliary,
  180. comp->crossingFunctions, comp->duser, comp->iuser, (void**) comp->sParameters, comp->did, &comp->QiErr, 0);
  181. globalComponent2=0;
  182. } else {
  183. dsblock_(&idemand, &comp->icall, &comp->time, comp->states, 0,
  184. comp->inputs, comp->parameters, 0, 0, comp->derivatives,
  185. comp->outputs, comp->auxiliary,
  186. comp->crossingFunctions, comp->duser, comp->iuser, (void**) comp->sParameters, &comp->QiErr);
  187. }
  188. /*Only check convergence criteria for functions that need it i.e. event handling*/
  189. if(iterationConverged){
  190. *iterationConverged = (GetDymolaOneIteration(comp->did) == 0) ? FMITrue : FMIFalse;
  191. }
  192. /*Always reset*/
  193. SetDymolaOneIteration(comp->did, 0);
  194. comp->valWasSet = 0;
  195. if (comp->QiErr>=-995 && comp->QiErr<=-990) comp->QiErr=0; /* Ignore special cases for now */
  196. if (comp->QiErr == 0) {
  197. return 0;
  198. }
  199. if (label != NULL) {
  200. util_logger(comp, comp->instanceName, FMIError, "",
  201. "%s: dsblock_ failed, QiErr = %d", label, comp->QiErr);
  202. }
  203. return comp->QiErr;
  204. }
  205. /* ------------------------------------------------------ */
  206. DYMOLA_STATIC FMIStatus util_error(Component* comp)
  207. {
  208. /*Termination should be done exteranly when error is thrown*/
  209. switch(comp->mStatus) {
  210. case modelInstantiated:
  211. /* internally state "error" and "terminated" are equivalent */
  212. comp->mStatus = modelTerminated;
  213. break;
  214. case modelTerminated:
  215. break;
  216. default:
  217. if (comp->isCoSim == FMITrue) {
  218. #ifndef FMU_SKIP_CO_SIM
  219. fmiTerminateSlave_(comp);
  220. #endif
  221. } else {
  222. #ifdef FMI_2
  223. fmiTerminateModel_(comp);
  224. #elif !defined(FMU_SKIP_MODEL_EXCHANGE)
  225. fmiTerminate_(comp);
  226. #endif
  227. }
  228. break;
  229. }
  230. return FMIError;
  231. }
  232. /* ------------------------------------------------------ */
  233. extern void InitializeDymosimStruct(struct BasicDDymosimStruct* basicD, struct BasicIDymosimStruct* basicI);
  234. extern void SetDymolaEventOptional(struct DYNInstanceData*did_, int val);
  235. extern int GetDymolaHaveEventIterated(struct DYNInstanceData*did_);
  236. DYMOLA_STATIC FMIStatus util_initialize_model(FMIComponent c, FMIBoolean toleranceControlled, FMIReal relativeTolerance, FMIBoolean complete)
  237. {
  238. Component* comp = (Component*) c;
  239. FMIStatus status = FMIOK;
  240. int QiErr = 0;
  241. /* Start of integration */
  242. int idemand = iDemandStart;
  243. comp->terminationByModel = FMIFalse;
  244. /* #ifdef _DEBUG fails on dSPACE platforms where _DEBUG is always defined, 0 or 1. */
  245. #if _DEBUG
  246. /* catch numerical exceptions */
  247. /*_EM_INVALID_ is not compatible with the underlying handling of NaN */
  248. _controlfp(0, _EM_ZERODIVIDE | _EM_OVERFLOW);
  249. #endif
  250. if (comp->mStatus != modelInstantiated) {
  251. util_logger(comp, comp->instanceName, FMIWarning, "", "model cannot be initialized in current state(%d)", comp->mStatus);
  252. return FMIWarning;
  253. }
  254. /* Ignore toleranceControlled for external integration according to recommendation from Hans Olsson:
  255. The tolerance for the equations to be solved at events are based on the crossing function
  256. tolerance (to avoid chattering), which is normally stricter than relativeTolerance.
  257. Hence, relativeTolerance is normally not significant. */
  258. if (toleranceControlled == FMITrue) {
  259. util_logger(comp, comp->instanceName, FMIWarning, "", "fmiInitialize: tolerance control not supported");
  260. }
  261. InitializeDymosimStruct(comp->dstruct, comp->istruct);
  262. #ifdef OVERIDE_DYMOLA_EVENT_OPTIONAL
  263. SetDymolaEventOptional(comp->did, 1);
  264. #else
  265. #ifdef ONLY_INCLUDE_INLINE_INTEGRATION
  266. if(comp->isCoSim){
  267. SetDymolaEventOptional(comp->did, 1);
  268. }else{
  269. SetDymolaEventOptional(comp->did, 0);
  270. }
  271. #else
  272. if(comp->isCoSim && comp->istruct->mInlineIntegration){
  273. SetDymolaEventOptional(comp->did, 1);
  274. }else{
  275. SetDymolaEventOptional(comp->did, 0);
  276. }
  277. #endif
  278. #endif
  279. comp->icall = iDemandStart - 1;
  280. if (complete ==FMIFalse) {
  281. SetDymolaOneIteration(comp->did, 2);
  282. } else {
  283. SetDymolaOneIteration(comp->did, 0);
  284. }
  285. QiErr = util_refresh_cache(comp, idemand, NULL, NULL);
  286. if (QiErr != 0) {
  287. status = FMIError;
  288. util_logger(comp, comp->instanceName, status, "",
  289. "fmiInitialize: dsblock_ failed, QiErr = %d", QiErr);
  290. util_logger(comp, comp->instanceName, status, "",
  291. "Unless otherwise indicated by error messages, possible errors are (non-exhaustive):\n"
  292. "The model references external data that is not present on the target machine, at least not with the same location.\n",
  293. QiErr);
  294. return util_error(comp);
  295. }
  296. if (comp->storeResult == FMITrue) {
  297. result_setup(comp);
  298. }
  299. return status;
  300. }
  301. /* ------------------------------------------------------ */
  302. DYMOLA_STATIC FMIStatus util_event_update(FMIComponent c, FMIBoolean intermediateResults,
  303. #ifdef FMI_2
  304. /* needs another argument since not in eventInfo for FMI 2*/
  305. FMIBoolean* terminateSolution
  306. #else
  307. fmiEventInfo* eventInfo
  308. #endif
  309. )
  310. {
  311. Component* comp = (Component*) c;
  312. FMIStatus status = FMIOK;
  313. int QiErr = 0;
  314. FMIBoolean converged = 0;
  315. /* make sure idemand up to auxilliary variables are computed prior to starting event iteration */
  316. /* (necessary for e.g. co-simulating Modelica.Electrical.Machines.Examples.SynchronousInductionMachines.SMEE_Generator) */
  317. {
  318. static IDemandLevel idemand[] = {iDemandVariable, iDemandEventHandling};
  319. int i;
  320. for (i = 0; i < 2; i++) {
  321. /* TODO: figure out why intermediate results are not retrieved if dsblock_ is first called with idemand == iDemandVariable */
  322. if (i == 0) {
  323. if (intermediateResults == FMITrue) {
  324. continue;
  325. }
  326. } else {
  327. /* configure actual event iteration */
  328. if (intermediateResults == FMITrue)
  329. {
  330. if (comp->eventIterationOnGoing) {
  331. SetDymolaOneIteration(comp->did, 3);
  332. } else {
  333. SetDymolaOneIteration(comp->did, 2);
  334. comp->eventIterationOnGoing = 1;
  335. }
  336. } else {
  337. if (comp->eventIterationOnGoing) {
  338. SetDymolaOneIteration(comp->did, 4);
  339. } else {
  340. SetDymolaOneIteration(comp->did, 0);
  341. }
  342. }
  343. comp->icall = 0;
  344. }
  345. QiErr = util_refresh_cache(comp, idemand[i], NULL, &converged);
  346. #ifdef FMI_2
  347. *terminateSolution = FMIFalse;
  348. #else
  349. eventInfo->terminateSimulation = FMIFalse;
  350. #endif
  351. if (QiErr>=-995 && QiErr<=-990) QiErr=0; /* Ignore special cases for now */
  352. if (QiErr != 0) {
  353. if (QiErr == -999) {
  354. util_logger(comp, comp->instanceName, FMIOK, "",
  355. "event updating: simulation terminated by model");
  356. #ifdef FMI_2
  357. *terminateSolution = FMITrue;
  358. #else
  359. eventInfo->terminateSimulation = FMITrue;
  360. #endif
  361. return FMIOK;
  362. } else {
  363. util_logger(comp, comp->instanceName, FMIError, "",
  364. "event updating: dsblock_ failed, QiErr = %d", QiErr);
  365. return util_error(comp);
  366. }
  367. }
  368. }
  369. #ifdef FMI_2
  370. /* for FMI 2 we only use this function internally for co-simulation and then always expect converged */
  371. assert(intermediateResults == FMIFalse && converged == FMITrue);
  372. #else
  373. if (converged == FMIFalse)
  374. {
  375. /* more iterations needed */
  376. assert(intermediateResults == FMITrue);
  377. eventInfo->iterationConverged = FMIFalse;
  378. } else {
  379. eventInfo->iterationConverged = FMITrue;
  380. comp->eventIterationOnGoing = 0;
  381. }
  382. #endif
  383. }
  384. /* for FMI 2 we only use this internally for co-simulation and do not use these values */
  385. #ifndef FMI_2
  386. /* always fmiFalse since we hide states that might be exchanged */
  387. eventInfo->stateValueReferencesChanged = FMIFalse;
  388. /* TODO: introduce a flag in dymosim that can be checked for this purpose (for now it is faster to
  389. to fetch states each time than to conclude the truth value from copy + compare) */
  390. eventInfo->stateValuesChanged = (comp->nStates > 0) ? FMITrue : FMIFalse;
  391. eventInfo->upcomingTimeEvent = (comp->dstruct->mNextTimeEvent < (1.0E37 - 1)) ? FMITrue : FMIFalse;
  392. if (eventInfo->upcomingTimeEvent == FMITrue) {
  393. eventInfo->nextEventTime = comp->dstruct->mNextTimeEvent;
  394. }
  395. #endif
  396. comp->recalJacobian = 1;
  397. return status;
  398. }
  399. DYMOLA_STATIC FMIStatus util_initialize_slave(FMIComponent c,
  400. FMIReal relativeTolerance,
  401. FMIReal tStart,
  402. FMIBoolean StopTimeDefined,
  403. FMIReal tStop)
  404. {
  405. FMIStatus status;
  406. FMIReal relTol = relativeTolerance;
  407. #ifndef FMI_2
  408. FMIEventInfo eventInfo;
  409. #endif
  410. Component* comp = (Component*) c;
  411. fmiSetTime_(c, tStart);
  412. #ifdef FMI_2
  413. status = util_initialize_model(c, FMIFalse, 0, FMIFalse);
  414. #else
  415. status = fmiInitialize_(c, FMIFalse, 0, &eventInfo);
  416. #endif /* FMI_2 */
  417. if (status != FMIOK) {
  418. return status;
  419. }
  420. comp->StopTimeDefined = StopTimeDefined;
  421. comp->tStop = tStop;
  422. comp->valWasSet = 0;
  423. #ifndef ONLY_INCLUDE_INLINE_INTEGRATION
  424. if(!comp->istruct->mInlineIntegration){
  425. if (integration_setup(c, relTol) != integrationOK) {
  426. return FMIFatal;
  427. }
  428. }
  429. #endif
  430. return FMIOK;
  431. }
  432. #ifdef FMI_2
  433. /* ------------------------------------------------------ */
  434. DYMOLA_STATIC FMIStatus util_exit_model_initialization_mode(FMIComponent c, const char* label, ModelStatus nextStatus)
  435. {
  436. Component* comp = (Component*) c;
  437. int QiErr;
  438. if (comp->mStatus != modelInitializationMode) {
  439. util_logger(comp, comp->instanceName, FMIWarning, "", "%s: may only called in initialization mode", label);
  440. return FMIWarning;
  441. }
  442. SetDymolaOneIteration(comp->did, 4);
  443. QiErr = util_refresh_cache(comp, iDemandStart, NULL, NULL);
  444. if (QiErr != 0) {
  445. return util_error(comp);
  446. }
  447. /* reset */
  448. SetDymolaOneIteration(comp->did, 0);
  449. comp->mStatus = nextStatus;
  450. return FMIOK;
  451. }
  452. #endif /* FMI_2 */
  453. extern struct DymolaTimes* GetDymolaTimers(struct DYNInstanceData*, int*);
  454. DYMOLA_STATIC void util_print_dymola_timers(FMIComponent c){
  455. Component* comp = (Component*) c;
  456. int DymolaTimerStructsLen_=0;
  457. struct DymolaTimes* DymolaTimerStructs_=0;
  458. DymolaTimerStructs_=GetDymolaTimers(comp->did, &DymolaTimerStructsLen_);
  459. if (DymolaTimerStructsLen_>0 && DymolaTimerStructs_) {
  460. double overhead=0,overheadSum=0;
  461. int i=0;
  462. int nrFound=0;
  463. for(i=0;i<DymolaTimerStructsLen_;++i){
  464. if (DymolaTimerStructs_[i].numAccum>0) {
  465. if (nrFound==0 || DymolaTimerStructs_[i].minimAccum<overhead){
  466. overhead=DymolaTimerStructs_[i].minimAccum;
  467. }
  468. nrFound+=DymolaTimerStructs_[i].numAccum;
  469. }
  470. }
  471. if (nrFound>0) {
  472. util_logger(comp, comp->instanceName, FMIOK, "","\nProfiling information for the blocks.\n"
  473. "Estimated overhead per call %11.2f[us] total %12.3f[s]\n"
  474. "the estimated overhead has been subtracted below.\n"
  475. "Name of block , Block, Total CPU[s], Mean[us] ( Min[us] to Max[us] ), Called\n",overhead*1e6,overhead*nrFound);
  476. for(i=0;i<DymolaTimerStructsLen_;++i) if (DymolaTimerStructs_[i].numAccum>0) {
  477. double overheadTotal=overhead*DymolaTimerStructs_[i].numAccum;
  478. util_logger(comp, "", FMIOK, "","%-16.16s: %5d, %12.3f, %11.2f (%11.2f to %11.2f), %8d\n",
  479. DymolaTimerStructs_[i].name,
  480. i,DymolaTimerStructs_[i].totalAccum-overheadTotal,
  481. ((DymolaTimerStructs_[i].totalAccum-overheadTotal)/DymolaTimerStructs_[i].numAccum)*1e6,
  482. (DymolaTimerStructs_[i].minimAccum-overhead)*1e6,(DymolaTimerStructs_[i].maximAccum-overhead)*1e6,
  483. DymolaTimerStructs_[i].numAccum);
  484. }
  485. }
  486. }
  487. return;
  488. }