@@ -50,9 +50,10 @@ inline bool checkFlag(int flag, std::string functionName)
5050{
5151 if (flag < 0 )
5252 {
53- logError (" SUNDIALS_ERROR: " + functionName + " () failed with flag = " + std::to_string (flag));
53+ logError (" SUNDIALS_ERROR: " + functionName + " failed with flag = " + std::to_string (flag));
5454 return false ;
5555 }
56+ logDebug (" SUNDIALS_INFO: " + functionName + " failed with flag = " + std::to_string (flag));
5657 return true ;
5758}
5859
@@ -63,25 +64,25 @@ inline bool checkFlag(int flag, std::string functionName)
6364 * @param module Name of the module reporting the error.
6465 * @param function Name of the function in which the error occurred.
6566 * @param msg Error Message.
66- * @param userData Pointer to user data. Unused.
67+ * @param user_data Pointer to user data. Unused.
6768 */
6869void oms::KinsolSolver::sundialsErrorHandlerFunction (int errorCode, const char *module ,
6970 const char *function, char *msg,
70- void *userData )
71+ void *user_data )
7172{
7273 KINSOL_USER_DATA* kinsolUserData;
7374 std::string systNum = " unknown" ;
7475 std::string mod = module ;
7576 std::string func = function;
7677
77- if (userData != NULL ) {
78- kinsolUserData = (KINSOL_USER_DATA *)userData;
78+ if (user_data != NULL )
79+ {
80+ kinsolUserData = (KINSOL_USER_DATA *)user_data;
7981 systNum = std::to_string (kinsolUserData->algLoopNumber );
8082 }
8183
8284 logError (" SUNDIALS_ERROR: [system] " + systNum + " [module] " + mod + " | [function] " + func
83- +" | [error_code] " + std::to_string (errorCode));
84- logError (msg);
85+ + " | [error_code] " + std::to_string (errorCode) + " \n " + std::string (msg));
8586}
8687
8788/* *
@@ -92,39 +93,106 @@ void oms::KinsolSolver::sundialsErrorHandlerFunction(int errorCode, const char *
9293 * @param module Name of the module reporting the information.
9394 * @param function Name of the function reporting the information.
9495 * @param msg Message.
95- * @param userData Pointer to user data. Unused.
96+ * @param user_data Pointer to user data. Unused.
9697 */
9798void oms::KinsolSolver::sundialsInfoHandlerFunction (const char *module , const char *function,
98- char *msg, void *userData )
99+ char *msg, void *user_data )
99100{
100101 KINSOL_USER_DATA* kinsolUserData;
101102 std::string systNum = " unknown" ;
102103 std::string mod = module ;
103104 std::string func = function;
104105
105- if (userData != NULL ) {
106- kinsolUserData = (KINSOL_USER_DATA *)userData ;
106+ if (user_data != NULL ) {
107+ kinsolUserData = (KINSOL_USER_DATA *)user_data ;
107108 systNum = std::to_string (kinsolUserData->algLoopNumber );
108109 }
109110
110- logDebug (" SUNDIALS_INFO: [system] " + systNum + " [module] " + mod + " | [function] " + func);
111- logDebug (msg);
111+ logDebug (" SUNDIALS_INFO: [system] " + systNum + " [module] " + mod + " | [function] " + func + " \n " + std::string (msg));
112+ }
113+
114+ /* *
115+ * @brief Jacobian function for KINSOL
116+ *
117+ * @param u is the current (unscaled) iterate
118+ * @param fu is the current value of the vector F(u)
119+ * @param J is the output approximate Jacobian matrix, J = ∂F/∂u,
120+ * of type SUNMatrix
121+ * @param data is a pointer to user data, the same as the user data
122+ * parameter passed to KINSetUserData
123+ * @param tmp1 pointer to memory allocated for variables of type
124+ * N_Vector which can be used by the KINJacFn function as
125+ * temporary storage or work space
126+ * @param tmp2 pointer to memory allocated for variables of type
127+ * N_Vector which can be used by the KINJacFn function as
128+ * temporary storage or work space
129+ * @return int A function of type KINLsJacFn should return 0 if
130+ * successful or a non-zero value otherwise
131+ */
132+ int oms::KinsolSolver::nlsKinsolJac (N_Vector u, N_Vector fu, SUNMatrix J, void *user_data, N_Vector tmp1, N_Vector tmp2)
133+ {
134+ KINSOL_USER_DATA* kinsoluserData = (KINSOL_USER_DATA*) user_data;
135+ System* syst = kinsoluserData->syst ;
136+ AlgLoop* algLoop = syst->getAlgLoop (kinsoluserData->algLoopNumber );
137+ DirectedGraph* graph = kinsoluserData->graph ;
138+ const oms_ssc_t SCC = algLoop->getSCC ();
139+ const int size = SCC.size ();
140+
141+ oms_status_enu_t status;
142+ double *u_data = NV_DATA_S (u);
143+
144+ for (int j=0 ; j<size; j++)
145+ {
146+ const int output = SCC[j].first ;
147+ const oms::ComRef& crefOutput = graph->getNodes ()[output].getName ();
148+ const int input = SCC[j].second ;
149+ const oms::ComRef& crefInput = graph->getNodes ()[input].getName ();
150+ // res[j] = output - input
151+ // std::cout << "res[" << j << "] = " << std::string(crefOutput) << " - " << std::string(crefInput) << std::endl;
152+
153+ for (int i=0 ; i<size; i++)
154+ {
155+ double der = 0.0 ;
156+ const int input_col = SCC[i].second ;
157+ oms::ComRef crefInput_col = graph->getNodes ()[input_col].getName ();
158+ oms::ComRef front = crefInput_col.pop_front ();
159+
160+ if (front == crefOutput.front ())
161+ if (oms_status_ok != syst->getDirectionalDerivative (crefOutput, crefInput_col, der))
162+ return logError (" not recoverable error" );
163+ if (input_col == input)
164+ der -= 1.0 ;
165+
166+ SM_ELEMENT_D (J, j, i) = der;
167+ }
168+ }
169+
170+ return 0 ;
112171}
113172
114173/* *
115174 * @brief Residual function for KINSOL
116175 *
117- * @param uu Input value
118- * @param fval Contains residual at output
119- * @param userData Pointer to user data to access System, AlgLoop, Directed Graph and CSS as well as getReal and setReal
120- * @return int Return 0 on success, 1 if an recoverable error occured, -1 if a fatal error occured.
176+ * This function computes F(u) (or G(u) for fixed-point iteration and
177+ * Anderson acceleration) for a given value of the vector u.
178+ *
179+ * @param u is the current value of the variable vector, u
180+ * @param fval is the output vector F(u)
181+ * @param user_data a is a pointer to user data, the pointer user
182+ * data passed to KINSetUserData
183+ * @return int A KINSysFn function should return 0 if
184+ * successful, a positive value if a recoverable
185+ * error occurred (in which case kinsol will attempt
186+ * to correct), or a negative value if it failed
187+ * unrecoverably (in which case the solution process
188+ * is halted and KIN SYSFUNC FAIL is returned).
121189 */
122- int oms::KinsolSolver::nlsKinsolResiduals (N_Vector uu , N_Vector fval, void *userData )
190+ int oms::KinsolSolver::nlsKinsolResiduals (N_Vector u , N_Vector fval, void *user_data )
123191{
124- double *uu_data = NV_DATA_S (uu );
192+ double *u_data = NV_DATA_S (u );
125193 double *fval_data = NV_DATA_S (fval);
126194
127- KINSOL_USER_DATA* kinsoluserData = (KINSOL_USER_DATA*) userData ;
195+ KINSOL_USER_DATA* kinsoluserData = (KINSOL_USER_DATA*) user_data ;
128196 System* syst = kinsoluserData->syst ;
129197 AlgLoop* algLoop = syst->getAlgLoop (kinsoluserData->algLoopNumber );
130198 DirectedGraph* graph = kinsoluserData->graph ;
@@ -142,13 +210,13 @@ int oms::KinsolSolver::nlsKinsolResiduals(N_Vector uu, N_Vector fval, void *user
142210 ss << " inputs:" << std::endl;
143211 }
144212
145- // Set values from uu
213+ // Set values from u
146214 for (int i=0 ; i<size; ++i)
147215 {
148- int output = SCC[i].second ;
149- status = syst->setReal (graph->getNodes ()[output ].getName (), uu_data [i]);
216+ int input = SCC[i].second ;
217+ status = syst->setReal (graph->getNodes ()[input ].getName (), u_data [i]);
150218 if (Flags::DumpAlgLoops ())
151- ss << " " << graph->getNodes ()[output ].getName ().c_str () << " : " << uu_data [i] << std::endl;
219+ ss << " " << graph->getNodes ()[input ].getName ().c_str () << " : " << u_data [i] << std::endl;
152220 if (status == oms_status_discard || status == oms_status_error || status == oms_status_warning)
153221 {
154222 logInfo (" iteration " + std::to_string (kinsoluserData->iteration ) + " : recoverable error (1)" );
@@ -181,7 +249,7 @@ int oms::KinsolSolver::nlsKinsolResiduals(N_Vector uu, N_Vector fval, void *user
181249 logInfo (" iteration " + std::to_string (kinsoluserData->iteration ) + " : not recoverable error (2)" );
182250 return -1 /* not recoverable error */ ;
183251 }
184- fval_data[i] = fval_data[i] - uu_data [i];
252+ fval_data[i] = fval_data[i] - u_data [i];
185253 }
186254
187255 if (Flags::DumpAlgLoops ())
@@ -212,7 +280,7 @@ oms::KinsolSolver::~KinsolSolver()
212280 SUNMatDestroy (this ->J );
213281 N_VDestroy_Serial (this ->y );
214282
215- delete ((KINSOL_USER_DATA*)(this ->userData ));
283+ delete ((KINSOL_USER_DATA*)(this ->user_data ));
216284}
217285
218286/* *
@@ -223,7 +291,7 @@ oms::KinsolSolver::~KinsolSolver()
223291 * @param absoluteTolerance Tolerance used for solving the loop
224292 * @return oms::KinsolSolver* Retruns pointer to KinsolSolver object
225293 */
226- oms::KinsolSolver* oms::KinsolSolver::NewKinsolSolver (const int algLoopNum, const unsigned int size, double absoluteTolerance)
294+ oms::KinsolSolver* oms::KinsolSolver::NewKinsolSolver (const int algLoopNum, const unsigned int size, double absoluteTolerance, const bool useDirectionalDerivative )
227295{
228296 int flag;
229297 int printLevel;
@@ -250,24 +318,27 @@ oms::KinsolSolver* oms::KinsolSolver::NewKinsolSolver(const int algLoopNum, cons
250318 }
251319
252320 /* Set user data given to KINSOL */
253- kinsolSolver->userData = new KINSOL_USER_DATA{/* .syst=*/ NULL , /* .graph=*/ NULL , /* .algLoopNumber=*/ algLoopNum, /* .iteration=*/ 0 };
254- flag = KINSetUserData (kinsolSolver->kinsolMemory , kinsolSolver->userData );
321+ kinsolSolver->user_data = new KINSOL_USER_DATA{/* .syst=*/ NULL , /* .graph=*/ NULL , /* .algLoopNumber=*/ algLoopNum, /* .iteration=*/ 0 };
322+ flag = KINSetUserData (kinsolSolver->kinsolMemory , kinsolSolver->user_data );
255323 if (!checkFlag (flag, " KINSetUserData" )) return NULL ;
256324
257325 /* Set error handler and print level */
258- if (Log::DebugEnabled ()) {
326+ if (Log::DebugEnabled ())
327+ {
259328 logDebug (" SUNDIALS KINSOL: Set print level to maximum." );
260329 printLevel = 3 ;
261- } else {
330+ }
331+ else
332+ {
262333 printLevel = 0 ;
263334 }
264335 flag = KINSetPrintLevel (kinsolSolver->kinsolMemory , printLevel);
265336 if (!checkFlag (flag, " KINSetPrintLevel" )) return NULL ;
266337
267- flag = KINSetErrHandlerFn (kinsolSolver->kinsolMemory , sundialsErrorHandlerFunction, kinsolSolver->userData );
338+ flag = KINSetErrHandlerFn (kinsolSolver->kinsolMemory , sundialsErrorHandlerFunction, kinsolSolver->user_data );
268339 if (!checkFlag (flag, " KINSetErrHandlerFn" )) return NULL ;
269340
270- flag = KINSetInfoHandlerFn (kinsolSolver->kinsolMemory , sundialsInfoHandlerFunction, kinsolSolver->userData );
341+ flag = KINSetInfoHandlerFn (kinsolSolver->kinsolMemory , sundialsInfoHandlerFunction, kinsolSolver->user_data );
271342 if (!checkFlag (flag, " KINSetInfoHandlerFn" )) return NULL ;
272343
273344 /* Initialize KINSOL object */
@@ -285,7 +356,10 @@ oms::KinsolSolver* oms::KinsolSolver::NewKinsolSolver(const int algLoopNum, cons
285356 if (!checkFlag (flag, " KINSetLinearSolver" )) return NULL ;
286357
287358 /* Set Jacobian for linear solver */
288- flag = KINSetJacFn (kinsolSolver->kinsolMemory , NULL ); /* Use KINSOLs internal difference quotient function for Jacobian approximation */
359+ if (useDirectionalDerivative && Flags::DirectionalDerivatives ())
360+ flag = KINSetJacFn (kinsolSolver->kinsolMemory , nlsKinsolJac); /* Use symbolic Jacobian */
361+ else
362+ flag = KINSetJacFn (kinsolSolver->kinsolMemory , NULL ); /* Use KINSOLs internal difference quotient function for Jacobian approximation */
289363 if (!checkFlag (flag, " KINSetJacFn" )) return NULL ;
290364
291365 /* Set function-norm stopping tolerance */
@@ -328,7 +402,7 @@ oms::KinsolSolver* oms::KinsolSolver::NewKinsolSolver(const int algLoopNum, cons
328402oms_status_enu_t oms::KinsolSolver::kinsolSolve (System& syst, DirectedGraph& graph)
329403{
330404 /* Update user data */
331- KINSOL_USER_DATA* kinsolUserData = (KINSOL_USER_DATA*) userData ;
405+ KINSOL_USER_DATA* kinsolUserData = (KINSOL_USER_DATA*) user_data ;
332406 kinsolUserData->syst = &syst;
333407 kinsolUserData->graph = &graph;
334408 kinsolUserData->iteration = 0 ;
@@ -364,24 +438,16 @@ oms_status_enu_t oms::KinsolSolver::kinsolSolve(System& syst, DirectedGraph& gra
364438 flag = KINSol (kinsolMemory, /* KINSol memory block */
365439 initialGuess, /* initial guess on input; solution vector */
366440 KIN_NONE, /* global strategy choice: Basic newton iteration */
367- uScale, /* scaling vector, for the variable uu */
441+ uScale, /* scaling vector, for the variable u */
368442 fScale ); /* scaling vector for function values fval */
369- if (flag < 0 )
370- {
371- logError (" SUNDIALS_ERROR: KINSol() failed with flag = " + std::to_string (flag));
372- return oms_status_error;
373- }
374- else
375- {
376- logDebug (" SUNDIALS_INFO: KINSol() succeded with flag = " + std::to_string (flag));
377- }
443+ if (!checkFlag (flag, " KINSol" )) return oms_status_error;
378444
379445 /* Check solution */
380- flag = nlsKinsolResiduals (initialGuess, fTmp , userData );
446+ flag = nlsKinsolResiduals (initialGuess, fTmp , user_data );
381447 fNormValue = N_VWL2Norm (fTmp , fTmp );
382448 if ( fNormValue > fnormtol )
383449 {
384- logWarning (" Solution of algebraic loop " + std::to_string (((KINSOL_USER_DATA *)userData )->algLoopNumber ) + " not within precission given by fnormtol: " + std::to_string (fnormtol));
450+ logWarning (" Solution of algebraic loop " + std::to_string (((KINSOL_USER_DATA *)user_data )->algLoopNumber ) + " not within precission given by fnormtol: " + std::to_string (fnormtol));
385451 logDebug (" 2-norm of residual of solution: " + std::to_string (fNormValue ));
386452 return oms_status_warning;
387453 }
@@ -400,7 +466,7 @@ oms_status_enu_t oms::KinsolSolver::kinsolSolve(System& syst, DirectedGraph& gra
400466 * @param scc Strong Connected Componten, a vector of connected
401467 * @param number
402468 */
403- oms::AlgLoop::AlgLoop (oms_alg_solver_enu_t method, double absTol, oms_ssc_t scc, const int number): absoluteTolerance(absTol), SCC(scc), systNumber(number)
469+ oms::AlgLoop::AlgLoop (oms_alg_solver_enu_t method, double absTol, oms_ssc_t scc, const int number, const bool useDirectionalDerivative ): absoluteTolerance(absTol), SCC(scc), systNumber(number)
404470{
405471 switch (method)
406472 {
@@ -415,7 +481,7 @@ oms::AlgLoop::AlgLoop(oms_alg_solver_enu_t method, double absTol, oms_ssc_t scc,
415481
416482 if (method == oms_alg_solver_kinsol)
417483 {
418- kinsolData = KinsolSolver::NewKinsolSolver (systNumber, SCC.size (), absoluteTolerance);
484+ kinsolData = KinsolSolver::NewKinsolSolver (systNumber, SCC.size (), absoluteTolerance, useDirectionalDerivative );
419485 if (kinsolData==NULL )
420486 {
421487 logError (" NewKinsolSolver() failed. Aborting!" );
@@ -574,18 +640,20 @@ std::string oms::AlgLoop::getAlgSolverName(void)
574640std::string oms::AlgLoop::dumpLoopVars (DirectedGraph& graph)
575641{
576642 const int size = SCC.size ();
577- std::string varNames = " \t " ;
643+ std::string varNames = " " ;
578644 int output;
579645
580646 for (int i=0 ; i<size-1 ; ++i)
581647 {
648+ varNames.append (" " );
582649 output = SCC[i].first ;
583650 varNames.append (graph.getNodes ()[output].getName ().c_str ());
584651 varNames.append (" -> " );
585652 output = SCC[i].second ;
586653 varNames.append (graph.getNodes ()[output].getName ().c_str ());
587- varNames.append (" \n\t " );
654+ varNames.append (" \n " );
588655 }
656+ varNames.append (" " );
589657 output = SCC[size-1 ].first ;
590658 varNames.append (graph.getNodes ()[output].getName ().c_str ());
591659 varNames.append (" -> " );
0 commit comments