diff --git a/src/OMSimulatorLib/SystemSC.cpp b/src/OMSimulatorLib/SystemSC.cpp index 2f55cb6ee..26a793fb4 100644 --- a/src/OMSimulatorLib/SystemSC.cpp +++ b/src/OMSimulatorLib/SystemSC.cpp @@ -64,7 +64,9 @@ int oms::cvode_rhs(realtype t, N_Vector y, N_Vector ydot, void* user_data) if (oms_status_ok != status) return status; } - system->updateInputs(system->eventGraph); + system->restoreInputs(system->simulationGraph, system->initial_guess); + + system->updateInputs(system->simulationGraph); // get state derivatives for (size_t j=0, k=0; j < system->fmus.size(); ++j) @@ -106,7 +108,9 @@ int oms::cvode_roots(realtype t, N_Vector y, realtype *gout, void *user_data) if (oms_status_ok != status) return status; } - system->updateInputs(system->eventGraph); + system->restoreInputs(system->simulationGraph, system->initial_guess); + + system->updateInputs(system->simulationGraph); for (size_t i = 0, j=0; i < system->fmus.size(); ++i) { @@ -534,23 +538,23 @@ oms_status_enu_t oms::SystemSC::doStep() switch(solverMethod) { case oms_solver_sc_explicit_euler: - return doStepEuler(); + return doStepEuler(time + maximumStepSize); case oms_solver_sc_cvode: - return doStepCVODE(); + return doStepCVODE(time + maximumStepSize); default: return logError_InternalError; } } -oms_status_enu_t oms::SystemSC::doStepEuler() +oms_status_enu_t oms::SystemSC::doStepEuler(double stopTime) { fmi2Status fmistatus; oms_status_enu_t status; // Step 1: Initialize state variables and time - const fmi2Real end_time = std::min(time + maximumStepSize, getModel().getStopTime()); + const fmi2Real end_time = std::min(time + maximumStepSize, stopTime); const fmi2Real event_time_tolerance = 1e-4; logDebug("doStepEuler: " + std::to_string(time) + " -> " + std::to_string(end_time)); @@ -604,6 +608,8 @@ oms_status_enu_t oms::SystemSC::doStepEuler() } } + saveInputs(simulationGraph, initial_guess); + // Step 3: Main integration loop while (time < end_time && !terminated) { @@ -612,7 +618,11 @@ oms_status_enu_t oms::SystemSC::doStepEuler() step_size_adjustment *= 0.5; // reduce the step size in each iteration - // a. Evaluate derivatives for each FMU + // a. Evaluate states at time 'event_time' for each FMU + // set time + for (const auto& component : getComponents()) + component.second->setTime(event_time); + const fmi2Real step_size = event_time - time; // Substep size, do one step from current time to the event logDebug("step_size: " + std::to_string(step_size) + " | " + std::to_string(time) + " -> " + std::to_string(event_time)); for (size_t i = 0; i < fmus.size(); ++i) @@ -627,6 +637,8 @@ oms_status_enu_t oms::SystemSC::doStepEuler() if (oms_status_ok != status) return status; } + updateInputs(simulationGraph); + // b. Event Detection event_detected = event_time == tnext; logDebug("Event detected: " + std::to_string(event_detected)); @@ -657,20 +669,25 @@ oms_status_enu_t oms::SystemSC::doStepEuler() time = event_time; step_size_adjustment = maximumStepSize; - // set time - for (const auto& component : getComponents()) - component.second->setTime(time); + // FMUs are already at time 'event_time' + // for (const auto& component : getComponents()) + // component.second->setTime(time); + // emit the left limit of the event (if it hasn't already been emitted) + // We already did an input update earlier + // restoreInputs(simulationGraph, savedInputs); + // updateInputs(simulationGraph); //pass the continuousTimeMode dependency graph which involves only connections of type Real + if (isTopLevelSystem()) + getModel().emit(time, false); + for (size_t i = 0; i < fmus.size(); ++i) { fmistatus = fmi2_completedIntegratorStep(fmus[i]->getFMU(), fmi2True, &callEventUpdate[i], &terminateSimulation[i]); if (fmi2OK != fmistatus) return logError_FMUCall("fmi2_completedIntegratorStep", fmus[i]); } - // emit the left limit of the event (if it hasn't already been emitted) - updateInputs(simulationGraph); //pass the continuousTimeMode dependency graph which involves only connections of type Real - if (isTopLevelSystem()) - getModel().emit(time, false); + // Save new initial guesses for algebraic loops + saveInputs(simulationGraph, initial_guess); logDebug("integrate normally to the end time if no events are ahead"); } @@ -692,14 +709,14 @@ oms_status_enu_t oms::SystemSC::doStepEuler() step_size_adjustment = maximumStepSize; event_time = end_time; + // FMUs are already at time 'event_time' + // for (const auto& component : getComponents()) + // component.second->setTime(time); + // emit the left limit of the event (if it hasn't already been emitted) if (isTopLevelSystem()) getModel().emit(time, false); - // set time - for (const auto& component : getComponents()) - component.second->setTime(time); - // Enter event mode and handle discrete state updates for each FMU for (size_t i = 0; i < fmus.size(); ++i) { @@ -710,10 +727,27 @@ oms_status_enu_t oms::SystemSC::doStepEuler() if (fmi2OK != fmistatus) logError_FMUCall("fmi2_enterEventMode", fmus[i]); fmus[i]->doEventIteration(); - + } + + // Update all inputs, including non-continuous ones + updateInputs(eventGraph); + + // emit the right limit of the event + if (isTopLevelSystem()) + getModel().emit(time, true); + + // Go back to continuous time mode + for (size_t i = 0; i < fmus.size(); ++i) + { fmistatus = fmi2_enterContinuousTimeMode(fmus[i]->getFMU()); if (fmi2OK != fmistatus) logError_FMUCall("fmi2_enterContinuousTimeMode", fmus[i]); - + } + + // Save new initial guesses for algebraic loops + saveInputs(simulationGraph, initial_guess); + + for (size_t i = 0; i < fmus.size(); ++i) + { if (nStates[i] > 0) { status = fmus[i]->getContinuousStates(states_backup[i]); @@ -722,8 +756,11 @@ oms_status_enu_t oms::SystemSC::doStepEuler() if (oms_status_ok != status) return status; } - status = fmus[i]->getEventindicators(event_indicators_prev[i]); - if (oms_status_ok != status) return status; + if (nEventIndicators[i] > 0) + { + status = fmus[i]->getEventindicators(event_indicators_prev[i]); + if (oms_status_ok != status) return status; + } } // find next time event @@ -740,16 +777,14 @@ oms_status_enu_t oms::SystemSC::doStepEuler() terminated = true; } } - - // emit the right limit of the event - updateInputs(eventGraph); - if (isTopLevelSystem()) - getModel().emit(time, true); } else { // Ok, event must be little earlier event_time -= step_size_adjustment; + + // Put algebraic loop inputs back to the values at the last finished time step + restoreInputs(simulationGraph, initial_guess); } } } @@ -766,13 +801,14 @@ oms_status_enu_t oms::SystemSC::doStepEuler() return oms_status_ok; } -oms_status_enu_t oms::SystemSC::doStepCVODE() +oms_status_enu_t oms::SystemSC::doStepCVODE(double stopTime) { fmi2Status fmistatus; oms_status_enu_t status; - int flag; + int flag = 0; + bool tnext_is_event = false; - const fmi2Real end_time = std::min(time + maximumStepSize, getModel().getStopTime()); + const fmi2Real end_time = stopTime; // find next time event fmi2Real tnext = end_time+1.0; @@ -788,19 +824,72 @@ oms_status_enu_t oms::SystemSC::doStepCVODE() time = end_time; } } + + tnext_is_event = tnext <= end_time; + tnext = std::min(tnext, end_time); + logDebug("tnext: " + std::to_string(tnext)); - while (time < end_time) - { + //while (time < end_time) + //{ logDebug("CVode: " + std::to_string(time) + " -> " + std::to_string(end_time)); + + saveInputs(simulationGraph, initial_guess); + for (size_t j=0, k=0; j < fmus.size(); ++j) for (size_t i=0; i < nStates[j]; ++i, ++k) NV_Ith_S(solverData.cvode.y, k) = states[j][i]; - flag = CVode(solverData.cvode.mem, std::min(tnext, end_time), solverData.cvode.y, &time, CV_NORMAL); + // This will force the solver to take a step only to tnext + // The 'tout' argument for CVode will integrate beyond it and return an interpolant + CVodeSetStopTime(solverData.cvode.mem, tnext); + + // Advance integrator (to end of step or next root) + double cvode_time = time; + flag = CVode(solverData.cvode.mem, tnext, solverData.cvode.y, &cvode_time, CV_ONE_STEP); + if (flag < 0) + return logError("SUNDIALS_ERROR: CVode() failed with flag = " + std::to_string(flag)); + + // CVode will return an interpolant for roots. In that case it should always be reset at the root. + // Otherwise it will make use of rhs calculations made before the event. + // This will cause it to ignore any changes in the rhs from current time to cv_mem->cv_tn. + bool is_interpolated = flag == CV_ROOT_RETURN; + + // Sanity check, should not be triggered. + // To avoid resorting to this, CV_NORMAL is used above when tnext is too close. + if (cvode_time > tnext) + { + logWarning("SystemSC::doStepCVODE: Stopping time overstepped by CVODE"); + + // This fails to do the necessary interpolation when the previous call has stopped at a root. + // flag = CVode(solverData.cvode.mem, tnext, solverData.cvode.y, &cvode_time, CV_NORMAL); + // if (flag < 0) + // return logError("SUNDIALS_ERROR: CVode() failed with flag = " + std::to_string(flag)); + + // Issue with the approach below: + // - Internal time of CVODE stays at previously returned value. + // - This may cause it to skip a root. + + // Interpolate states to value at tnext + int retval = CVodeGetDky(solverData.cvode.mem, tnext, 0, solverData.cvode.y); + if (retval != CV_SUCCESS) + return logError("SUNDIALS_ERROR: CVodeGetDky() failed with flag = " + std::to_string(retval)); + + flag = CV_TSTOP_RETURN; + cvode_time = tnext; + } + + time = cvode_time; + + // set time + for (const auto& component : getComponents()) + component.second->setTime(time); for (size_t i = 0, j=0; i < fmus.size(); ++i) { + if (nStates[i] == 0) + continue; + for (size_t k = 0; k < nStates[i]; k++, j++) states[i][k] = NV_Ith_S(solverData.cvode.y, j); @@ -809,23 +898,20 @@ oms_status_enu_t oms::SystemSC::doStepCVODE() if (oms_status_ok != status) return status; } - if (flag == CV_ROOT_RETURN || time == tnext) + restoreInputs(simulationGraph, initial_guess); + updateInputs(simulationGraph); + if (isTopLevelSystem()) + getModel().emit(time, false); + + for (size_t i = 0; i < fmus.size(); ++i) { - logDebug("event found!!! " + std::to_string(time)); - - // set time - for (const auto& component : getComponents()) - component.second->setTime(time); - - for (size_t i = 0; i < fmus.size(); ++i) - { - fmistatus = fmi2_completedIntegratorStep(fmus[i]->getFMU(), fmi2True, &callEventUpdate[i], &terminateSimulation[i]); - if (fmi2OK != fmistatus) return logError_FMUCall("fmi2_completedIntegratorStep", fmus[i]); - } + fmistatus = fmi2_completedIntegratorStep(fmus[i]->getFMU(), fmi2True, &callEventUpdate[i], &terminateSimulation[i]); + if (fmi2OK != fmistatus) return logError_FMUCall("fmi2_completedIntegratorStep", fmus[i]); + } - updateInputs(eventGraph); - if (isTopLevelSystem()) - getModel().emit(time, false); + if (flag == CV_ROOT_RETURN || tnext_is_event && time == tnext) + { + logDebug("event found!!! " + std::to_string(time)); // Enter event mode and handle discrete state updates for each FMU for (size_t i = 0; i < fmus.size(); ++i) @@ -834,18 +920,14 @@ oms_status_enu_t oms::SystemSC::doStepCVODE() if (fmi2OK != fmistatus) logError_FMUCall("fmi2_enterEventMode", fmus[i]); fmus[i]->doEventIteration(); - - fmistatus = fmi2_enterContinuousTimeMode(fmus[i]->getFMU()); - if (fmi2OK != fmistatus) logError_FMUCall("fmi2_enterContinuousTimeMode", fmus[i]); } + updateInputs(eventGraph); + for (size_t i = 0; i < fmus.size(); ++i) { - if (0 == nStates[i]) - continue; - - status = fmus[i]->getContinuousStates(states[i]); - if (oms_status_ok != status) return status; + fmistatus = fmi2_enterContinuousTimeMode(fmus[i]->getFMU()); + if (fmi2OK != fmistatus) logError_FMUCall("fmi2_enterContinuousTimeMode", fmus[i]); } // find next time event @@ -862,10 +944,13 @@ oms_status_enu_t oms::SystemSC::doStepCVODE() time = end_time; } } + + tnext_is_event = tnext <= end_time; + tnext = std::min(tnext, end_time); + logDebug("tnext: " + std::to_string(tnext)); // emit the right limit of the event - updateInputs(eventGraph); if (isTopLevelSystem()) getModel().emit(time, true); @@ -885,36 +970,14 @@ oms_status_enu_t oms::SystemSC::doStepCVODE() flag = CVodeReInit(solverData.cvode.mem, time, solverData.cvode.y); if (flag < 0) return logError("SUNDIALS_ERROR: CVodeReInit() failed with flag = " + std::to_string(flag)); - continue; + return oms_status_ok; } - if (flag == CV_SUCCESS) - { + if (flag >= 0) // Some kind of success logDebug("CVode completed successfully at t = " + std::to_string(time)); - - // set time - for (const auto& component : getComponents()) - component.second->setTime(time); - - for (size_t i = 0; i < fmus.size(); ++i) - { - fmistatus = fmi2_completedIntegratorStep(fmus[i]->getFMU(), fmi2True, &callEventUpdate[i], &terminateSimulation[i]); - if (fmi2OK != fmistatus) return logError_FMUCall("fmi2_completedIntegratorStep", fmus[i]); - - if (0 == nStates[i]) - continue; - - status = fmus[i]->getContinuousStates(states[i]); - if (oms_status_ok != status) return status; - } - - updateInputs(eventGraph); - if (isTopLevelSystem()) - getModel().emit(time, false); - } else return logError("CVode failed with flag = " + std::to_string(flag)); - } + //} return oms_status_ok; @@ -930,14 +993,29 @@ oms_status_enu_t oms::SystemSC::stepUntil(double stopTime) // main simulation loop oms_status_enu_t status = oms_status_ok; - while (time < std::min(stopTime, getModel().getStopTime()) && oms_status_ok == status) + double endTime = std::min(stopTime, getModel().getStopTime()); + double lastTime = time; + while (time < endTime && oms_status_ok == status) { - status = doStep(); + if (solverMethod == oms_solver_sc_explicit_euler) + status = doStepEuler(endTime); + else if (solverMethod == oms_solver_sc_cvode) + status = doStepCVODE(endTime); + else + return logError_InternalError; + if (status != oms_status_ok) logWarning("Bad return code at time " + std::to_string(time)); - if (isTopLevelSystem() && Flags::ProgressBar()) + // Check whether stopping time has changed due to a request from an FMU + if (getModel().getStopTime() < endTime) + endTime = getModel().getStopTime(); + + if (isTopLevelSystem() && Flags::ProgressBar() && time > lastTime + maximumStepSize) + { Log::ProgressBar(startTime, stopTime, time); + lastTime = time; + } } if (isTopLevelSystem() && Flags::ProgressBar()) @@ -1005,3 +1083,65 @@ oms_status_enu_t oms::SystemSC::updateInputs(DirectedGraph& graph) } return oms_status_ok; } + +oms_status_enu_t oms::SystemSC::saveInputs(DirectedGraph& graph, std::vector& saved_inputs) +{ + CallClock callClock(clock); + oms_status_enu_t status; + int loopNum = 0; + + // input := output + const std::vector< scc_t >& sortedConnections = graph.getSortedConnections(); + updateAlgebraicLoops(sortedConnections, graph); + + saved_inputs.clear(); + + for (int i = 0; i < sortedConnections.size(); i++) + { + if (!sortedConnections[i].thisIsALoop) + continue; + + for (std::pair conn : sortedConnections[i].connections) { + int input = conn.second; + + if (graph.getNodes()[input].getType() == oms_signal_type_real) + { + double value = 0.0; + if (oms_status_ok != getReal(graph.getNodes()[input].getName(), value)) return oms_status_error; + + saved_inputs.push_back(value); + } + } + } + + return oms_status_ok; +} + +oms_status_enu_t oms::SystemSC::restoreInputs(DirectedGraph& graph, const std::vector& saved_inputs) +{ + CallClock callClock(clock); + oms_status_enu_t status; + + // input := output + const std::vector< scc_t >& sortedConnections = graph.getSortedConnections(); + + auto it = saved_inputs.cbegin(); + + for (int i = 0; i < sortedConnections.size(); i++) + { + if (!sortedConnections[i].thisIsALoop) + continue; + + for (std::pair conn : sortedConnections[i].connections) { + int input = conn.second; + + if (graph.getNodes()[input].getType() == oms_signal_type_real) + { + double value = *it++; + if (oms_status_ok != setReal(graph.getNodes()[input].getName(), value)) return oms_status_error; + } + } + } + + return oms_status_ok; +} diff --git a/src/OMSimulatorLib/SystemSC.h b/src/OMSimulatorLib/SystemSC.h index f9b07d830..6bd118c0c 100644 --- a/src/OMSimulatorLib/SystemSC.h +++ b/src/OMSimulatorLib/SystemSC.h @@ -65,14 +65,18 @@ namespace oms oms_status_enu_t updateInputs(DirectedGraph& graph); + oms_status_enu_t saveInputs(DirectedGraph& graph, std::vector &saved_inputs); + + oms_status_enu_t restoreInputs(DirectedGraph& graph, const std::vector& saved_outputs); + std::string getSolverName() const; oms_status_enu_t setSolverMethod(std::string); oms_status_enu_t setSolver(oms_solver_enu_t solver) {if (solver > oms_solver_sc_min && solver < oms_solver_sc_max) {solverMethod=solver; return oms_status_ok;} return oms_status_error;} private: - oms_status_enu_t doStepEuler(); - oms_status_enu_t doStepCVODE(); + oms_status_enu_t doStepEuler(double stopTime); + oms_status_enu_t doStepCVODE(double stopTime); protected: SystemSC(const ComRef& cref, Model* parentModel, System* parentSystem); @@ -95,6 +99,8 @@ namespace oms std::vector event_indicators; std::vector event_indicators_prev; + std::vector initial_guess; // Initial guess for all algebraic loops + struct SolverDataEuler_t { }; diff --git a/testsuite/reference-fmus/Dahlquist.lua b/testsuite/reference-fmus/Dahlquist.lua index 0d14840d8..9910e1905 100644 --- a/testsuite/reference-fmus/Dahlquist.lua +++ b/testsuite/reference-fmus/Dahlquist.lua @@ -58,7 +58,7 @@ end -- info: maximum step size for 'model.root': 0.001000 -- info: Result file: Dahlquist-me.mat (bufferSize=1) -- info: Final Statistics for 'model.root': --- NumSteps = 10001 NumRhsEvals = 10002 NumLinSolvSetups = 501 +-- NumSteps = 10001 NumRhsEvals = 10002 NumLinSolvSetups = 502 -- NumNonlinSolvIters = 10001 NumNonlinSolvConvFails = 0 NumErrTestFails = 0 -- signal x is equal -- endResult diff --git a/testsuite/references/BouncingBall-me.mat b/testsuite/references/BouncingBall-me.mat index 1fa62874d..247828336 100644 Binary files a/testsuite/references/BouncingBall-me.mat and b/testsuite/references/BouncingBall-me.mat differ diff --git a/testsuite/simulation/DualMassOscillator.lua b/testsuite/simulation/DualMassOscillator.lua index 962a072c4..a0c20fdaa 100644 --- a/testsuite/simulation/DualMassOscillator.lua +++ b/testsuite/simulation/DualMassOscillator.lua @@ -50,9 +50,9 @@ oms_delete("DualMassOscillator") -- info: system1.x1: 0.0 -- info: system2.x2: 0.5 -- info: Simulation --- info: system1.x1: 0.051037644866066 --- info: system2.x2: 0.031639500249976 +-- info: system1.x1: 0.051037644963887 +-- info: system2.x2: 0.031639500306724 -- info: Final Statistics for 'DualMassOscillator.root': --- NumSteps = 10007 NumRhsEvals = 10008 NumLinSolvSetups = 507 +-- NumSteps = 10007 NumRhsEvals = 10008 NumLinSolvSetups = 508 -- NumNonlinSolvIters = 10007 NumNonlinSolvConvFails = 0 NumErrTestFails = 0 -- endResult diff --git a/testsuite/simulation/EventTest.lua b/testsuite/simulation/EventTest.lua index d620e2775..107f0c0f6 100644 --- a/testsuite/simulation/EventTest.lua +++ b/testsuite/simulation/EventTest.lua @@ -79,16 +79,15 @@ readFile("EventTest_lua.csv") -- 0, 0.3, -1 -- 0.30000001, -1.00000002723e-08, -1 -- 0.30000001, 0.3, -1 --- 0.60000002, -1.00000154823e-08, -1 +-- 0.60000002, -1.00000004943e-08, -1 -- 0.60000002, 0.3, -1 --- 0.90000003, -1.00000214776e-08, -1 +-- 0.90000003, -1.00000004943e-08, -1 -- 0.90000003, 0.3, -1 --- 1, 0.20000003, -1 --- 1.20000004, -1.0000003936e-08, -1 +-- 1.20000004, -1.00000006054e-08, -1 -- 1.20000004, 0.3, -1 --- 1.50000005, -1.00000143721e-08, -1 +-- 1.50000005, -1.00000290271e-08, -1 -- 1.50000005, 0.3, -1 --- 1.80000006, -1.00000390191e-08, -1 +-- 1.80000006, -1.0000053674e-08, -1 -- 1.80000006, 0.3, -1 -- 2, 0.10000006, -1 -- 2, 0.10000006, -1 diff --git a/testsuite/simulation/Makefile b/testsuite/simulation/Makefile index 1a0a65f9f..cbdcce975 100644 --- a/testsuite/simulation/Makefile +++ b/testsuite/simulation/Makefile @@ -50,6 +50,7 @@ importPartialSnapshot.lua \ importStartValues.lua \ importStartValues1.lua \ importSuppressUnitConversion.lua \ +integratorTest.lua \ listVariants1.lua \ multipleConnections.lua \ multipleInstance.lua \ diff --git a/testsuite/simulation/integratorTest.lua b/testsuite/simulation/integratorTest.lua new file mode 100644 index 000000000..abe471046 --- /dev/null +++ b/testsuite/simulation/integratorTest.lua @@ -0,0 +1,79 @@ +-- status: correct +-- teardown_command: rm -rf integratorTest_lua/ +-- linux: yes +-- ucrt64: yes +-- win: yes +-- mac: no + +function readFile(filename) + local f = assert(io.open(filename, "r")) + local content = f:read("*all") + print(content) + f:close() +end + +oms_setCommandLineOption("--suppressPath=true") +oms_setTempDirectory("./integratorTest_lua/") +oms_setWorkingDirectory("./integratorTest_lua/") + +oms_newModel("IntegratorTest") +oms_addSystem("IntegratorTest.root", oms_system_sc) +oms_addSubModel("IntegratorTest.root.integrator", "../../resources/Modelica.Blocks.Continuous.Integrator.fmu") + +-- simulation settings +oms_setSolver("IntegratorTest.root", oms_solver_sc_cvode) +oms_setResultFile("IntegratorTest", "integratorTest_lua.csv") +oms_setStopTime("IntegratorTest", 10.0) +oms_setVariableStepSize("IntegratorTest.root", 1.0, 1e-8, 1.0) + +oms_instantiate("IntegratorTest") +oms_initialize("IntegratorTest") + +oms_setReal("IntegratorTest.root.integrator.u", 0.0) +print(string.format("t = 0.0: y = %g", oms_getReal("IntegratorTest.root.integrator.y"))) +oms_stepUntil("IntegratorTest", 2.5) +oms_setReal("IntegratorTest.root.integrator.u", 10.0) + +print(string.format("t = 2.5: y = %g", oms_getReal("IntegratorTest.root.integrator.y"))) +oms_stepUntil("IntegratorTest", 5.0) +print(string.format("t = 5.0: y = %g", oms_getReal("IntegratorTest.root.integrator.y"))) +oms_stepUntil("IntegratorTest", 10.0) +print(string.format("t = 10.0: y = %g", oms_getReal("IntegratorTest.root.integrator.y"))) + +oms_delete("IntegratorTest") + +readFile("integratorTest_lua.csv") + +-- Result: +-- info: maximum step size for 'IntegratorTest.root': 1.000000 +-- info: Result file: integratorTest_lua.csv (bufferSize=1) +-- t = 0.0: y = 0 +-- t = 2.5: y = 0 +-- t = 5.0: y = 25 +-- t = 10.0: y = 75 +-- info: Final Statistics for 'IntegratorTest.root': +-- NumSteps = 16 NumRhsEvals = 26 NumLinSolvSetups = 13 +-- NumNonlinSolvIters = 24 NumNonlinSolvConvFails = 0 NumErrTestFails = 4 +-- time,IntegratorTest.root.integrator._D_outputAlias_y,IntegratorTest.root.integrator.der(_D_outputAlias_y),IntegratorTest.root.integrator.local_set,IntegratorTest.root.integrator.u,IntegratorTest.root.integrator.y,IntegratorTest.root.integrator.initType,IntegratorTest.root.integrator.local_reset,IntegratorTest.root.integrator.use_reset,IntegratorTest.root.integrator.use_set +-- 0, 0, 0, 0, 0, 0, 3, 0, 0, 0 +-- 1, 0, 0, 0, 0, 0, 3, 0, 0, 0 +-- 2, 0, 0, 0, 0, 0, 3, 0, 0, 0 +-- 2.5, 0, 0, 0, 0, 0, 3, 0, 0, 0 +-- 2.5, 0, 0, 0, 0, 0, 3, 0, 0, 0 +-- 2.5001, 0.001, 10, 0, 10, 0.001, 3, 0, 0, 0 +-- 2.5002, 0.002, 10, 0, 10, 0.002, 3, 0, 0, 0 +-- 2.5012, 0.012, 10, 0, 10, 0.012, 3, 0, 0, 0 +-- 2.5112, 0.112, 10, 0, 10, 0.112, 3, 0, 0, 0 +-- 2.6112, 1.112, 10, 0, 10, 1.112, 3, 0, 0, 0 +-- 3.6112, 11.112, 10, 0, 10, 11.112, 3, 0, 0, 0 +-- 4.6112, 21.112, 10, 0, 10, 21.112, 3, 0, 0, 0 +-- 5, 25, 10, 0, 10, 25, 3, 0, 0, 0 +-- 5, 25, 10, 0, 10, 25, 3, 0, 0, 0 +-- 6, 35, 10, 0, 10, 35, 3, 0, 0, 0 +-- 7, 45, 10, 0, 10, 45, 3, 0, 0, 0 +-- 8, 55, 10, 0, 10, 55, 3, 0, 0, 0 +-- 9, 65, 10, 0, 10, 65, 3, 0, 0, 0 +-- 10, 75, 10, 0, 10, 75, 3, 0, 0, 0 +-- 10, 75, 10, 0, 10, 75, 3, 0, 0, 0 +-- +-- endResult