diff --git a/CHANGELOG b/CHANGELOG index a156bca6..34c00e23 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ --- CHANGELOG --- +--- Assimulo-3.2.8 --- + * Sundials 5.x port + --- Assimulo-3.2.7 --- * Resolved deprecation warnings visible when using numpy 1.20. * Resolved deprecation warnings visible when creating an ndarray from ragged nested sequences. diff --git a/doc/sphinx/source/markup.py b/doc/sphinx/source/markup.py index 1d58454a..16599faf 100644 --- a/doc/sphinx/source/markup.py +++ b/doc/sphinx/source/markup.py @@ -1,174 +1,174 @@ -from assimulo.solvers import * -from assimulo.problem import * -import os - -def mark_examples(): - import assimulo.examples as examples - - for ex in examples.__all__: - file = open("EXAMPLE_"+ex+".rst",'w') - - file.write(ex + '.py\n') - file.write('=============================================\n\n') - file.write('.. autofunction:: assimulo.examples.'+ex+'.run_example\n\n') - file.write('=============================================\n\n') - file.write('.. program-output:: python '+os.path.join(os.getcwd(),'execute_example.py')+' '+os.path.join(os.getcwd(), examples.__path__[0]+os.sep+ex+'.py')+' \n\n') - if os.path.isfile(os.path.join(os.getcwd(),ex+'.png')): - file.write('.. image:: '+os.sep+os.path.join(os.getcwd(),ex+'.png')+'\n\n') - file.write('.. note::\n\n') - file.write(' Press [source] (to the top right) to view the example.\n\n') - file.close() - -def mark_solvers(): - - solvers = [(sundials.CVode, "ODE"), (sundials.IDA, "DAE"), (radau5.Radau5ODE, "ODE"), (radau5.Radau5DAE, "DAE"), - (euler.ExplicitEuler, "ODE"), (runge_kutta.RungeKutta4, "ODE"), (runge_kutta.RungeKutta34, "ODE"), - (runge_kutta.Dopri5, "ODE"), (rosenbrock.RodasODE, "ODE"), (odepack.LSODAR, "ODE"),(glimda.GLIMDA, "DAE"), - (euler.ImplicitEuler, "ODE"), (dasp3.DASP3ODE, "ODE_SING"), (odassl.ODASSL,"DAE_OVER")] - - - rhs = lambda t,y: [1.0] - res = lambda t,y,yd: [1.0] - dydt = lambda t,y,z: [1.0] - dzdt = lambda t,y,z: [1.0] - - exp_mod = Explicit_Problem(rhs, 0.0) - imp_mod = Implicit_Problem(res, 0.0, 0.0) - sing_mod = SingPerturbed_Problem(dydt, dzdt, 0.0, 0.0) - over_mod = Overdetermined_Problem(res,0.0,0.0) - - for solver in solvers: - if solver[1] == "ODE": - method = solver[0](exp_mod) - str_ret = "t, y = " - elif solver[1] == "DAE": - str_ret = "t, y, yd = " - method = solver[0](imp_mod) - elif solver[1] == "ODE_SING": - str_ret = "t, y = " - method = solver[0](sing_mod) - elif solver[1] == "DAE_OVER": - str_ret = "t, y, yd = " - method = solver[0](over_mod) - - options = method.get_options() - supports = method.supports - - module_name = method.__class__.__module__.split(".")[-1] - solver_name = method.__class__.__name__ - problem_name = method.problem.__class__.__name__ - - file = open(solver[1]+"_"+solver_name+".rst",'w') - - file.write('\n') - file.write(solver_name + '\n') - file.write('=================================\n\n') - file.write(solver[0].__doc__.replace("\n ", "\n")) #REMOVES EXTRA INDENTATION - file.write('\nSupport\n----------------\n\n') - file.write('- State events (root funtions) : '+str(supports["state_events"])+'\n') - file.write('- Step events (completed step) : '+str(supports["report_continuously"])+'\n') - file.write('- Time events : '+'True\n') - file.write('\nUsage\n--------------\n\n') - file.write('Import the solver together with the correct problem:: \n\n') - file.write(' from assimulo.solvers import '+ solver_name+'\n') - file.write(' from assimulo.problem import '+problem_name+'\n\n') - file.write('Define the problem, such as:: \n\n') - - if solver[1] == "ODE": - file.write(' def rhs('+str_ret[:-3]+'): #Note that y are a 1-D numpy array.\n') - file.write(' yd = -1.0\n') - file.write(' return N.array([yd]) #Note that the return must be numpy array, NOT a scalar.\n\n') - file.write(' y0 = [1.0]\n') - file.write(' t0 = 1.0\n\n') - elif solver[1] == "DAE": - file.write(' def res('+str_ret[:-3]+'): #Note that y and yd are 1-D numpy arrays.\n') - file.write(' res = yd[0]-1.0\n') - file.write(' return N.array([res]) #Note that the return must be numpy array, NOT a scalar.\n\n') - file.write(' y0 = [1.0]\n') - file.write(' yd0 = [1.0]\n') - file.write(' t0 = 1.0\n\n') - elif solver[1] == "ODE_SING": - file.write(' def rhs_slow(t,y,z): #Note that y and z are 1-D numpy arrays.\n') - file.write(' return N.array([1.0]) #Note that the return must be numpy array, NOT a scalar.\n\n') - file.write(' def rhs_fast(t,y,z): #Note that y and z are 1-D numpy arrays.\n') - file.write(' return N.array([1.0]) #Note that the return must be numpy array, NOT a scalar.\n\n') - file.write(' yy0 = [1.0]\n') - file.write(' zz0 = [1.0]\n') - file.write(' t0 = 1.0\n\n') - elif solver[1] == "DAE_OVER": - file.write(' def res('+str_ret[:-3]+'): #Note that y and yd are 1-D numpy arrays.\n') - file.write(' res = [yd[0]-1.0, y[0]-1.0] \n') - file.write(' return N.array([res]) #Note that the return must be numpy array, NOT a scalar.\n\n') - file.write(' y0 = [1.0]\n') - file.write(' yd0 = [1.0]\n') - file.write(' t0 = 1.0\n\n') - - file.write('Create a problem instance::\n\n') - if solver[1] == "ODE": - file.write(' mod = '+problem_name+'(rhs, y0, t0)\n\n') - elif solver[1] == "DAE": - file.write(' mod = '+problem_name+'(res, y0, yd0, t0)\n\n') - elif solver[1] == "ODE_SING": - file.write(' mod = '+problem_name+'(rhs_slow, rhs_fast, yy0, zz0, t0)\n\n') - elif solver[1] == "DAE_OVER": - file.write(' mod = '+problem_name+'(res, y0, yd0, t0)\n\n') - else: - print "Unknown solver type" - file.write('.. note::\n\n') - file.write(' For complex problems, it is recommended to check the available :doc:`examples ` and the documentation in the problem class, :class:`'+problem_name+ ' `. It is also recommended to define your problem as a subclass of :class:`'+problem_name+ ' `.\n\n') - file.write('.. warning::\n\n') - file.write(' When subclassing from a problem class, the function for calculating the right-hand-side (for ODEs) must be named *rhs* and in the case with a residual function (for DAEs) it must be named *res*.\n\n') - file.write('Create a solver instance::\n\n') - file.write(' sim = '+solver_name+'(mod)\n\n') - file.write('Modify (optionally) the solver parameters.\n\n') - file.write(' Parameters:\n\n') - - iter_options = options.keys() - iter_options.sort() - for opt in iter_options: - - str_name = ' - :class:`' + opt + ' `' - - def find_doc(solv, opt): - try: - str_doc = " ".join(solv.__dict__[opt].__doc__[:solv.__dict__[opt].__doc__.find(".")].split()) - except KeyError: - str_doc = " " - if len(solv.mro()) > 1: - str_doc = find_doc(solv.mro()[1], opt) - if str_doc == " " and len(solv.mro()) > 2: - str_doc = find_doc(solv.mro()[2], opt) - return str_doc - str_doc = find_doc(solver[0], opt) - file.write(str_name + ' ' + str_doc + '.\n') - - if supports["interpolated_output"] or supports["interpolated_sensitivity_output"]: - file.write('\nMethods:\n\n') - file.write('- :class:`'+solver_name+'.interpolate `\n') - - if supports["interpolated_sensitivity_output"]: - file.write('- :class:`'+solver_name+'.interpolate_sensitivity `\n') - - #SPECIAL FOR IDA - if solver_name == "IDA": - file.write('- :class:`'+solver_name+'.make_consistent ` Directs IDA to try to calculate consistent initial conditions.\n') - - file.write('\nSimulate the problem:\n\n') - file.write(' :class:`'+str_ret+solver_name+'.simulate(tfinal, ncp, ncp_list) ` \n\n') - - #file.write('Plot the solution.\n\n') - #file.write(' :class:`'+solver_name+'.plot() `\n\n') - - file.write('Information:\n\n') - file.write('- :class:`'+solver_name+'.get_options() ` Returns the current solver options.\n') - file.write('- :class:`'+solver_name+'.get_supports() ` Returns the functionality which the solver supports.\n') - file.write('- :class:`'+solver_name+'.get_statistics() ` Returns the run-time statistics (if any).\n') - file.write('- :class:`'+solver_name+'.get_event_data() ` Returns the event information (if any).\n') - file.write('- :class:`'+solver_name+'.print_event_data() ` Prints the event information (if any).\n') - file.write('- :class:`'+solver_name+'.print_statistics() ` Prints the run-time statistics for the problem.\n') - - file.close() - -mark_examples() -mark_solvers() - +from assimulo.solvers import * +from assimulo.problem import * +import os + +def mark_examples(): + import assimulo.examples as examples + + for ex in examples.__all__: + file = open("EXAMPLE_"+ex+".rst",'w') + + file.write(ex + '.py\n') + file.write('=============================================\n\n') + file.write('.. autofunction:: assimulo.examples.'+ex+'.run_example\n\n') + file.write('=============================================\n\n') + file.write('.. program-output:: python '+os.path.join(os.getcwd(),'execute_example.py')+' '+os.path.join(os.getcwd(), examples.__path__[0]+os.sep+ex+'.py')+' \n\n') + if os.path.isfile(os.path.join(os.getcwd(),ex+'.png')): + file.write('.. image:: '+os.sep+os.path.join(os.getcwd(),ex+'.png')+'\n\n') + file.write('.. note::\n\n') + file.write(' Press [source] (to the top right) to view the example.\n\n') + file.close() + +def mark_solvers(): + + solvers = [(sundials.CVode, "ODE"), (sundials.IDA, "DAE"), (radau5.Radau5ODE, "ODE"), (radau5.Radau5DAE, "DAE"), + (euler.ExplicitEuler, "ODE"), (runge_kutta.RungeKutta4, "ODE"), (runge_kutta.RungeKutta34, "ODE"), + (runge_kutta.Dopri5, "ODE"), (rosenbrock.RodasODE, "ODE"), (odepack.LSODAR, "ODE"),(glimda.GLIMDA, "DAE"), + (euler.ImplicitEuler, "ODE"), (dasp3.DASP3ODE, "ODE_SING"), (odassl.ODASSL,"DAE_OVER")] + + + rhs = lambda t,y: [1.0] + res = lambda t,y,yd: [1.0] + dydt = lambda t,y,z: [1.0] + dzdt = lambda t,y,z: [1.0] + + exp_mod = Explicit_Problem(rhs, 0.0) + imp_mod = Implicit_Problem(res, 0.0, 0.0) + sing_mod = SingPerturbed_Problem(dydt, dzdt, 0.0, 0.0) + over_mod = Overdetermined_Problem(res,0.0,0.0) + + for solver in solvers: + if solver[1] == "ODE": + method = solver[0](exp_mod) + str_ret = "t, y = " + elif solver[1] == "DAE": + str_ret = "t, y, yd = " + method = solver[0](imp_mod) + elif solver[1] == "ODE_SING": + str_ret = "t, y = " + method = solver[0](sing_mod) + elif solver[1] == "DAE_OVER": + str_ret = "t, y, yd = " + method = solver[0](over_mod) + + options = method.get_options() + supports = method.supports + + module_name = method.__class__.__module__.split(".")[-1] + solver_name = method.__class__.__name__ + problem_name = method.problem.__class__.__name__ + + file = open(solver[1]+"_"+solver_name+".rst",'w') + + file.write('\n') + file.write(solver_name + '\n') + file.write('=================================\n\n') + file.write(solver[0].__doc__.replace("\n ", "\n")) #REMOVES EXTRA INDENTATION + file.write('\nSupport\n----------------\n\n') + file.write('- State events (root funtions) : '+str(supports["state_events"])+'\n') + file.write('- Step events (completed step) : '+str(supports["report_continuously"])+'\n') + file.write('- Time events : '+'True\n') + file.write('\nUsage\n--------------\n\n') + file.write('Import the solver together with the correct problem:: \n\n') + file.write(' from assimulo.solvers import '+ solver_name+'\n') + file.write(' from assimulo.problem import '+problem_name+'\n\n') + file.write('Define the problem, such as:: \n\n') + + if solver[1] == "ODE": + file.write(' def rhs('+str_ret[:-3]+'): #Note that y are a 1-D numpy array.\n') + file.write(' yd = -1.0\n') + file.write(' return N.array([yd]) #Note that the return must be numpy array, NOT a scalar.\n\n') + file.write(' y0 = [1.0]\n') + file.write(' t0 = 1.0\n\n') + elif solver[1] == "DAE": + file.write(' def res('+str_ret[:-3]+'): #Note that y and yd are 1-D numpy arrays.\n') + file.write(' res = yd[0]-1.0\n') + file.write(' return N.array([res]) #Note that the return must be numpy array, NOT a scalar.\n\n') + file.write(' y0 = [1.0]\n') + file.write(' yd0 = [1.0]\n') + file.write(' t0 = 1.0\n\n') + elif solver[1] == "ODE_SING": + file.write(' def rhs_slow(t,y,z): #Note that y and z are 1-D numpy arrays.\n') + file.write(' return N.array([1.0]) #Note that the return must be numpy array, NOT a scalar.\n\n') + file.write(' def rhs_fast(t,y,z): #Note that y and z are 1-D numpy arrays.\n') + file.write(' return N.array([1.0]) #Note that the return must be numpy array, NOT a scalar.\n\n') + file.write(' yy0 = [1.0]\n') + file.write(' zz0 = [1.0]\n') + file.write(' t0 = 1.0\n\n') + elif solver[1] == "DAE_OVER": + file.write(' def res('+str_ret[:-3]+'): #Note that y and yd are 1-D numpy arrays.\n') + file.write(' res = [yd[0]-1.0, y[0]-1.0] \n') + file.write(' return N.array([res]) #Note that the return must be numpy array, NOT a scalar.\n\n') + file.write(' y0 = [1.0]\n') + file.write(' yd0 = [1.0]\n') + file.write(' t0 = 1.0\n\n') + + file.write('Create a problem instance::\n\n') + if solver[1] == "ODE": + file.write(' mod = '+problem_name+'(rhs, y0, t0)\n\n') + elif solver[1] == "DAE": + file.write(' mod = '+problem_name+'(res, y0, yd0, t0)\n\n') + elif solver[1] == "ODE_SING": + file.write(' mod = '+problem_name+'(rhs_slow, rhs_fast, yy0, zz0, t0)\n\n') + elif solver[1] == "DAE_OVER": + file.write(' mod = '+problem_name+'(res, y0, yd0, t0)\n\n') + else: + print "Unknown solver type" + file.write('.. note::\n\n') + file.write(' For complex problems, it is recommended to check the available :doc:`examples ` and the documentation in the problem class, :class:`'+problem_name+ ' `. It is also recommended to define your problem as a subclass of :class:`'+problem_name+ ' `.\n\n') + file.write('.. warning::\n\n') + file.write(' When subclassing from a problem class, the function for calculating the right-hand-side (for ODEs) must be named *rhs* and in the case with a residual function (for DAEs) it must be named *res*.\n\n') + file.write('Create a solver instance::\n\n') + file.write(' sim = '+solver_name+'(mod)\n\n') + file.write('Modify (optionally) the solver parameters.\n\n') + file.write(' Parameters:\n\n') + + iter_options = options.keys() + iter_options.sort() + for opt in iter_options: + + str_name = ' - :class:`' + opt + ' `' + + def find_doc(solv, opt): + try: + str_doc = " ".join(solv.__dict__[opt].__doc__[:solv.__dict__[opt].__doc__.find(".")].split()) + except KeyError: + str_doc = " " + if len(solv.mro()) > 1: + str_doc = find_doc(solv.mro()[1], opt) + if str_doc == " " and len(solv.mro()) > 2: + str_doc = find_doc(solv.mro()[2], opt) + return str_doc + str_doc = find_doc(solver[0], opt) + file.write(str_name + ' ' + str_doc + '.\n') + + if supports["interpolated_output"] or supports["interpolated_sensitivity_output"]: + file.write('\nMethods:\n\n') + file.write('- :class:`'+solver_name+'.interpolate `\n') + + if supports["interpolated_sensitivity_output"]: + file.write('- :class:`'+solver_name+'.interpolate_sensitivity `\n') + + #SPECIAL FOR IDA + if solver_name == "IDA": + file.write('- :class:`'+solver_name+'.make_consistent ` Directs IDA to try to calculate consistent initial conditions.\n') + + file.write('\nSimulate the problem:\n\n') + file.write(' :class:`'+str_ret+solver_name+'.simulate(tfinal, ncp, ncp_list) ` \n\n') + + #file.write('Plot the solution.\n\n') + #file.write(' :class:`'+solver_name+'.plot() `\n\n') + + file.write('Information:\n\n') + file.write('- :class:`'+solver_name+'.get_options() ` Returns the current solver options.\n') + file.write('- :class:`'+solver_name+'.get_supports() ` Returns the functionality which the solver supports.\n') + file.write('- :class:`'+solver_name+'.get_statistics() ` Returns the run-time statistics (if any).\n') + file.write('- :class:`'+solver_name+'.get_event_data() ` Returns the event information (if any).\n') + file.write('- :class:`'+solver_name+'.print_event_data() ` Prints the event information (if any).\n') + file.write('- :class:`'+solver_name+'.print_statistics() ` Prints the run-time statistics for the problem.\n') + + file.close() + +mark_examples() +mark_solvers() + diff --git a/doc/sphinx/source/museum.rst b/doc/sphinx/source/museum.rst index 9e06a19d..7102badc 100644 --- a/doc/sphinx/source/museum.rst +++ b/doc/sphinx/source/museum.rst @@ -1,38 +1,38 @@ - - -============= -The Museum -============= - -Assimulo includes an ODE solver museum. Integrators written in FORTRAN and C -were developped at least since the mid-sixties. Many of those are hard to access -others are only available as print-outs. - -``The harsch requirement that a useful numerical method must -permit an efficient, robust, and reliable implementation has -withered the beautiful flowers which bloomed on thousands of journal pages.`` [Stetter93]_ - - -Following this motto, the Assimulo Museum aims at showing these flowers at least in an herbarium. - -At the moment the Assimulo Museum includes - - * DASP3 (scan) - * ODASSL - - wrapping is in preperation for - - * METAN1 - * EULEX - * DIFFSUB - * MEXAX - -.. note:: - - Codes marked by (scan) are reconstructed from a scan and an OCR process. They are tested but - still they might include bugs due to the reconstruction process. - - All other codes are original and untouched. - -.. [Stetter93] Hans Stetter In: Mathematics of Computation 1943-1993: A half-century - of computational mathematics: Mathematics of Computations 50th Aniversary Symposium. August 9-13, 1993. Vancouver, Britisch-Columbia, CA.) + + +============= +The Museum +============= + +Assimulo includes an ODE solver museum. Integrators written in FORTRAN and C +were developped at least since the mid-sixties. Many of those are hard to access +others are only available as print-outs. + +``The harsch requirement that a useful numerical method must +permit an efficient, robust, and reliable implementation has +withered the beautiful flowers which bloomed on thousands of journal pages.`` [Stetter93]_ + + +Following this motto, the Assimulo Museum aims at showing these flowers at least in an herbarium. + +At the moment the Assimulo Museum includes + + * DASP3 (scan) + * ODASSL + + wrapping is in preperation for + + * METAN1 + * EULEX + * DIFFSUB + * MEXAX + +.. note:: + + Codes marked by (scan) are reconstructed from a scan and an OCR process. They are tested but + still they might include bugs due to the reconstruction process. + + All other codes are original and untouched. + +.. [Stetter93] Hans Stetter In: Mathematics of Computation 1943-1993: A half-century + of computational mathematics: Mathematics of Computations 50th Aniversary Symposium. August 9-13, 1993. Vancouver, Britisch-Columbia, CA.) diff --git a/examples/cvode_basic_backward.py b/examples/cvode_basic_backward.py index 23b27878..c033533d 100644 --- a/examples/cvode_basic_backward.py +++ b/examples/cvode_basic_backward.py @@ -1,70 +1,70 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import CVode -from assimulo.problem import Explicit_Problem - -def run_example(with_plots=True): - """ - The same as example :doc:`EXAMPLE_cvode_basic` but now integrated backwards in time. - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - - """ - #Define the rhs - def f(t,y): - ydot = -y[0] - return N.array([ydot]) - - #Define an Assimulo problem - exp_mod = Explicit_Problem(f,t0=5, y0=0.02695, name = r'CVode Test Example (reverse time): $\dot y = - y$ ') - - #Define an explicit solver - exp_sim = CVode(exp_mod) #Create a CVode solver - - #Sets the parameters - exp_sim.iter = 'Newton' #Default 'FixedPoint' - exp_sim.discr = 'BDF' #Default 'Adams' - exp_sim.atol = [1e-8] #Default 1e-6 - exp_sim.rtol = 1e-8 #Default 1e-6 - exp_sim.backward = True - - #Simulate - t, y = exp_sim.simulate(0) #Simulate 5 seconds (t0=5 -> tf=0) - - #Plot - if with_plots: - import pylab as P - P.plot(t, y, color="b") - P.title(exp_mod.name) - P.ylabel('y') - P.xlabel('Time') - P.show() - - #Basic test - nose.tools.assert_almost_equal(float(y[-1]), 4.00000000, 3) - - return exp_mod, exp_sim - -if __name__=='__main__': - mod,sim = run_example() +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import CVode +from assimulo.problem import Explicit_Problem + +def run_example(with_plots=True): + """ + The same as example :doc:`EXAMPLE_cvode_basic` but now integrated backwards in time. + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + + """ + #Define the rhs + def f(t,y): + ydot = -y[0] + return N.array([ydot]) + + #Define an Assimulo problem + exp_mod = Explicit_Problem(f,t0=5, y0=0.02695, name = r'CVode Test Example (reverse time): $\dot y = - y$ ') + + #Define an explicit solver + exp_sim = CVode(exp_mod) #Create a CVode solver + + #Sets the parameters + exp_sim.iter = 'Newton' #Default 'FixedPoint' + exp_sim.discr = 'BDF' #Default 'Adams' + exp_sim.atol = [1e-8] #Default 1e-6 + exp_sim.rtol = 1e-8 #Default 1e-6 + exp_sim.backward = True + + #Simulate + t, y = exp_sim.simulate(0) #Simulate 5 seconds (t0=5 -> tf=0) + + #Plot + if with_plots: + import pylab as P + P.plot(t, y, color="b") + P.title(exp_mod.name) + P.ylabel('y') + P.xlabel('Time') + P.show() + + #Basic test + nose.tools.assert_almost_equal(float(y[-1]), 4.00000000, 3) + + return exp_mod, exp_sim + +if __name__=='__main__': + mod,sim = run_example() diff --git a/examples/cvode_stability.py b/examples/cvode_stability.py index 65ca66d4..314867ba 100644 --- a/examples/cvode_stability.py +++ b/examples/cvode_stability.py @@ -1,98 +1,98 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import CVode -from assimulo.problem import Explicit_Problem - -def run_example(with_plots=True): - r""" - Example for the use of the stability limit detection algorithm - in CVode. - - .. math:: - - \dot y_1 &= y_2 \\ - \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) \\ - \dot y_3 &= sin(ty_2) - - with :math:`\mu=\frac{1}{5} 10^3`. - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - - """ - class Extended_Problem(Explicit_Problem): - order = [] - - def handle_result(self, solver, t, y): - Explicit_Problem.handle_result(self, solver, t, y) - self.order.append(solver.get_last_order()) - - eps = 5.e-3 - my = 1./eps - - #Define the rhs - def f(t,y): - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - yd_2 = N.sin(t*y[1]) - - return N.array([yd_0,yd_1, yd_2]) - - y0 = [2.0,-0.6, 0.1] #Initial conditions - - #Define an Assimulo problem - exp_mod = Extended_Problem(f,y0, - name = "CVode: Stability problem") - - #Define an explicit solver - exp_sim = CVode(exp_mod) #Create a CVode solver - - #Sets the parameters - exp_sim.stablimdet = True - exp_sim.report_continuously = True - - #Simulate - t, y = exp_sim.simulate(2.0) #Simulate 2 seconds - - #Plot - if with_plots: - import pylab as P - P.subplot(211) - P.plot(t,y[:,2]) - P.ylabel("State: $y_1$") - P.subplot(212) - P.plot(t,exp_mod.order) - P.ylabel("Order") - P.suptitle(exp_mod.name) - P.xlabel("Time") - P.show() - - #Basic test - x1 = y[:,0] - assert N.abs(x1[-1]-1.8601438) < 1e-1 #For test purpose - - return exp_mod, exp_sim - -if __name__=='__main__': - mod,sim = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import CVode +from assimulo.problem import Explicit_Problem + +def run_example(with_plots=True): + r""" + Example for the use of the stability limit detection algorithm + in CVode. + + .. math:: + + \dot y_1 &= y_2 \\ + \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) \\ + \dot y_3 &= sin(ty_2) + + with :math:`\mu=\frac{1}{5} 10^3`. + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + + """ + class Extended_Problem(Explicit_Problem): + order = [] + + def handle_result(self, solver, t, y): + Explicit_Problem.handle_result(self, solver, t, y) + self.order.append(solver.get_last_order()) + + eps = 5.e-3 + my = 1./eps + + #Define the rhs + def f(t,y): + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + yd_2 = N.sin(t*y[1]) + + return N.array([yd_0,yd_1, yd_2]) + + y0 = [2.0,-0.6, 0.1] #Initial conditions + + #Define an Assimulo problem + exp_mod = Extended_Problem(f,y0, + name = "CVode: Stability problem") + + #Define an explicit solver + exp_sim = CVode(exp_mod) #Create a CVode solver + + #Sets the parameters + exp_sim.stablimdet = True + exp_sim.report_continuously = True + + #Simulate + t, y = exp_sim.simulate(2.0) #Simulate 2 seconds + + #Plot + if with_plots: + import pylab as P + P.subplot(211) + P.plot(t,y[:,2]) + P.ylabel("State: $y_1$") + P.subplot(212) + P.plot(t,exp_mod.order) + P.ylabel("Order") + P.suptitle(exp_mod.name) + P.xlabel("Time") + P.show() + + #Basic test + x1 = y[:,0] + assert N.abs(x1[-1]-1.8601438) < 1e-1 #For test purpose + + return exp_mod, exp_sim + +if __name__=='__main__': + mod,sim = run_example() + diff --git a/examples/cvode_with_disc.py b/examples/cvode_with_disc.py index a0d29f95..b2487f52 100644 --- a/examples/cvode_with_disc.py +++ b/examples/cvode_with_disc.py @@ -1,169 +1,169 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import CVode -from assimulo.problem import Explicit_Problem - -""" -An example with event iteration and with three switches. - -t=0 , [False, True, True] (Start of simulation) -t=1 (1) , [False, True, False] (Found a root at t=1) -t=1 (2) , [False, False, False] (Second iteration at t=1) -t=1 (3) , [True, False, False] (Third iteration at t=1) -t=10 , [True, False, False] (End of simulation) - -""" - -#Extend Assimulos problem definition -class Extended_Problem(Explicit_Problem): - - #Sets the initial conditons directly into the problem - y0 = [0.0, -1.0, 0.0] - sw0 = [False,True,True] - - #The right-hand-side function (rhs) - def rhs(self,t,y,sw): - """ - This is our function we are trying to simulate. During simulation - the parameter sw should be fixed so that our function is continuous - over the interval. The parameters sw should only be changed when the - integrator has stopped. - """ - yd_0 = (1.0 if sw[0] else -1.0) - yd_1 = 0.0 - yd_2 = 0.0 - - return N.array([yd_0,yd_1,yd_2]) - - #Sets a name to our function - name = 'ODE with discontinuities and a function with consistency problem' - - #The event function - def state_events(self,t,y,sw): - """ - This is our function that keeps track of our events. When the sign - of any of the events has changed, we have an event. - """ - event_0 = y[1] - 1.0 - event_1 = -y[2] + 1.0 - event_2 = -t + 1.0 - - return N.array([event_0,event_1,event_2]) - - - #Responsible for handling the events. - def handle_event(self, solver, event_info): - """ - Event handling. This functions is called when Assimulo finds an event as - specified by the event functions. - """ - event_info = event_info[0] #We only look at the state events information. - while True: #Event Iteration - self.event_switch(solver, event_info) #Turns the switches - - b_mode = self.state_events(solver.t, solver.y, solver.sw) - self.init_mode(solver) #Pass in the solver to the problem specified init_mode - a_mode = self.state_events(solver.t, solver.y, solver.sw) - - event_info = self.check_eIter(b_mode, a_mode) - - if not True in event_info: #Breaks the iteration loop - break - - #Helper function for handle_event - def event_switch(self, solver, event_info): - """ - Turns the switches. - """ - for i in range(len(event_info)): #Loop across all event functions - if event_info[i] != 0: - solver.sw[i] = not solver.sw[i] #Turn the switch - - #Helper function for handle_event - def check_eIter(self, before, after): - """ - Helper function for handle_event to determine if we have event - iteration. - - Input: Values of the event indicator functions (state_events) - before and after we have changed mode of operations. - """ - - eIter = [False]*len(before) - - for i in range(len(before)): - if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): - eIter[i] = True - - return eIter - - def init_mode(self, solver): - """ - Initialize the DAE with the new conditions. - """ - solver.y[1] = (-1.0 if solver.sw[1] else 3.0) - solver.y[2] = (0.0 if solver.sw[2] else 2.0) - - - -def run_example(with_plots=True): - """ - Example of the use of CVode for a differential equation - with a iscontinuity (state event) and the need for an event iteration. - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - """ - #Create an instance of the problem - exp_mod = Extended_Problem() #Create the problem - - exp_sim = CVode(exp_mod) #Create the solver - - exp_sim.verbosity = 0 - exp_sim.report_continuously = True - - #Simulate - t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points - exp_sim.print_event_data() - - #Plot - if with_plots: - import pylab as P - P.plot(t,y) - P.title(exp_mod.name) - P.ylabel('States') - P.xlabel('Time') - P.show() - - #Basic test - nose.tools.assert_almost_equal(y[-1][0],8.0) - nose.tools.assert_almost_equal(y[-1][1],3.0) - nose.tools.assert_almost_equal(y[-1][2],2.0) - - return exp_mod, exp_sim - -if __name__=="__main__": - mod,sim = run_example() - - - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import CVode +from assimulo.problem import Explicit_Problem + +""" +An example with event iteration and with three switches. + +t=0 , [False, True, True] (Start of simulation) +t=1 (1) , [False, True, False] (Found a root at t=1) +t=1 (2) , [False, False, False] (Second iteration at t=1) +t=1 (3) , [True, False, False] (Third iteration at t=1) +t=10 , [True, False, False] (End of simulation) + +""" + +#Extend Assimulos problem definition +class Extended_Problem(Explicit_Problem): + + #Sets the initial conditons directly into the problem + y0 = [0.0, -1.0, 0.0] + sw0 = [False,True,True] + + #The right-hand-side function (rhs) + def rhs(self,t,y,sw): + """ + This is our function we are trying to simulate. During simulation + the parameter sw should be fixed so that our function is continuous + over the interval. The parameters sw should only be changed when the + integrator has stopped. + """ + yd_0 = (1.0 if sw[0] else -1.0) + yd_1 = 0.0 + yd_2 = 0.0 + + return N.array([yd_0,yd_1,yd_2]) + + #Sets a name to our function + name = 'ODE with discontinuities and a function with consistency problem' + + #The event function + def state_events(self,t,y,sw): + """ + This is our function that keeps track of our events. When the sign + of any of the events has changed, we have an event. + """ + event_0 = y[1] - 1.0 + event_1 = -y[2] + 1.0 + event_2 = -t + 1.0 + + return N.array([event_0,event_1,event_2]) + + + #Responsible for handling the events. + def handle_event(self, solver, event_info): + """ + Event handling. This functions is called when Assimulo finds an event as + specified by the event functions. + """ + event_info = event_info[0] #We only look at the state events information. + while True: #Event Iteration + self.event_switch(solver, event_info) #Turns the switches + + b_mode = self.state_events(solver.t, solver.y, solver.sw) + self.init_mode(solver) #Pass in the solver to the problem specified init_mode + a_mode = self.state_events(solver.t, solver.y, solver.sw) + + event_info = self.check_eIter(b_mode, a_mode) + + if not True in event_info: #Breaks the iteration loop + break + + #Helper function for handle_event + def event_switch(self, solver, event_info): + """ + Turns the switches. + """ + for i in range(len(event_info)): #Loop across all event functions + if event_info[i] != 0: + solver.sw[i] = not solver.sw[i] #Turn the switch + + #Helper function for handle_event + def check_eIter(self, before, after): + """ + Helper function for handle_event to determine if we have event + iteration. + + Input: Values of the event indicator functions (state_events) + before and after we have changed mode of operations. + """ + + eIter = [False]*len(before) + + for i in range(len(before)): + if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): + eIter[i] = True + + return eIter + + def init_mode(self, solver): + """ + Initialize the DAE with the new conditions. + """ + solver.y[1] = (-1.0 if solver.sw[1] else 3.0) + solver.y[2] = (0.0 if solver.sw[2] else 2.0) + + + +def run_example(with_plots=True): + """ + Example of the use of CVode for a differential equation + with a iscontinuity (state event) and the need for an event iteration. + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + """ + #Create an instance of the problem + exp_mod = Extended_Problem() #Create the problem + + exp_sim = CVode(exp_mod) #Create the solver + + exp_sim.verbosity = 0 + exp_sim.report_continuously = True + + #Simulate + t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points + exp_sim.print_event_data() + + #Plot + if with_plots: + import pylab as P + P.plot(t,y) + P.title(exp_mod.name) + P.ylabel('States') + P.xlabel('Time') + P.show() + + #Basic test + nose.tools.assert_almost_equal(y[-1][0],8.0) + nose.tools.assert_almost_equal(y[-1][1],3.0) + nose.tools.assert_almost_equal(y[-1][2],2.0) + + return exp_mod, exp_sim + +if __name__=="__main__": + mod,sim = run_example() + + + diff --git a/examples/cvode_with_parameters_modified.py b/examples/cvode_with_parameters_modified.py index 44cea762..ead720fd 100644 --- a/examples/cvode_with_parameters_modified.py +++ b/examples/cvode_with_parameters_modified.py @@ -1,98 +1,98 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import CVode -from assimulo.problem import Explicit_Problem - -def run_example(with_plots=True): - """ - This is the same example from the Sundials package (cvsRoberts_FSA_dns.c) - - This simple example problem for CVode, due to Robertson, - is from chemical kinetics, and consists of the following three - equations: - - .. math:: - - \dot y_1 &= -p_1 y_1 + p_2 y_2 y_3 \\ - \dot y_2 &= p_1 y_1 - p_2 y_2 y_3 - p_3 y_2^2 \\ - \dot y_3 &= p_3 y_2^2 - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - """ - - def f(t, y, p): - p3 = 3.0e7 - - yd_0 = -p[0]*y[0]+p[1]*y[1]*y[2] - yd_1 = p[0]*y[0]-p[1]*y[1]*y[2]-p3*y[1]**2 - yd_2 = p3*y[1]**2 - - return N.array([yd_0,yd_1,yd_2]) - - #The initial conditions - y0 = [1.0,0.0,0.0] #Initial conditions for y - - #Create an Assimulo explicit problem - exp_mod = Explicit_Problem(f,y0, name='Sundials test example: Chemical kinetics') - - #Sets the options to the problem - exp_mod.p0 = [0.040, 1.0e4] #Initial conditions for parameters - exp_mod.pbar = [0.040, 1.0e4] - - #Create an Assimulo explicit solver (CVode) - exp_sim = CVode(exp_mod) - - #Sets the paramters - exp_sim.iter = 'Newton' - exp_sim.discr = 'BDF' - exp_sim.rtol = 1.e-4 - exp_sim.atol = N.array([1.0e-8, 1.0e-14, 1.0e-6]) - exp_sim.sensmethod = 'SIMULTANEOUS' #Defines the sensitvity method used - exp_sim.suppress_sens = False #Dont suppress the sensitivity variables in the error test. - exp_sim.report_continuously = True - - #Simulate - t, y = exp_sim.simulate(4,400) #Simulate 4 seconds with 400 communication points - - #Plot - if with_plots: - import pylab as P - P.plot(t, y) - P.xlabel('Time') - P.ylabel('State') - P.title(exp_mod.name) - P.show() - - #Basic test - nose.tools.assert_almost_equal(y[-1][0], 9.05518032e-01, 4) - nose.tools.assert_almost_equal(y[-1][1], 2.24046805e-05, 4) - nose.tools.assert_almost_equal(y[-1][2], 9.44595637e-02, 4) - nose.tools.assert_almost_equal(exp_sim.p_sol[0][-1][0], -1.8761, 2) #Values taken from the example in Sundials - nose.tools.assert_almost_equal(exp_sim.p_sol[1][-1][0], 2.9614e-06, 8) - - return exp_mod, exp_sim - -if __name__=='__main__': - mod,sim = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import CVode +from assimulo.problem import Explicit_Problem + +def run_example(with_plots=True): + """ + This is the same example from the Sundials package (cvsRoberts_FSA_dns.c) + + This simple example problem for CVode, due to Robertson, + is from chemical kinetics, and consists of the following three + equations: + + .. math:: + + \dot y_1 &= -p_1 y_1 + p_2 y_2 y_3 \\ + \dot y_2 &= p_1 y_1 - p_2 y_2 y_3 - p_3 y_2^2 \\ + \dot y_3 &= p_3 y_2^2 + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + """ + + def f(t, y, p): + p3 = 3.0e7 + + yd_0 = -p[0]*y[0]+p[1]*y[1]*y[2] + yd_1 = p[0]*y[0]-p[1]*y[1]*y[2]-p3*y[1]**2 + yd_2 = p3*y[1]**2 + + return N.array([yd_0,yd_1,yd_2]) + + #The initial conditions + y0 = [1.0,0.0,0.0] #Initial conditions for y + + #Create an Assimulo explicit problem + exp_mod = Explicit_Problem(f,y0, name='Sundials test example: Chemical kinetics') + + #Sets the options to the problem + exp_mod.p0 = [0.040, 1.0e4] #Initial conditions for parameters + exp_mod.pbar = [0.040, 1.0e4] + + #Create an Assimulo explicit solver (CVode) + exp_sim = CVode(exp_mod) + + #Sets the paramters + exp_sim.iter = 'Newton' + exp_sim.discr = 'BDF' + exp_sim.rtol = 1.e-4 + exp_sim.atol = N.array([1.0e-8, 1.0e-14, 1.0e-6]) + exp_sim.sensmethod = 'SIMULTANEOUS' #Defines the sensitvity method used + exp_sim.suppress_sens = False #Dont suppress the sensitivity variables in the error test. + exp_sim.report_continuously = True + + #Simulate + t, y = exp_sim.simulate(4,400) #Simulate 4 seconds with 400 communication points + + #Plot + if with_plots: + import pylab as P + P.plot(t, y) + P.xlabel('Time') + P.ylabel('State') + P.title(exp_mod.name) + P.show() + + #Basic test + nose.tools.assert_almost_equal(y[-1][0], 9.05518032e-01, 4) + nose.tools.assert_almost_equal(y[-1][1], 2.24046805e-05, 4) + nose.tools.assert_almost_equal(y[-1][2], 9.44595637e-02, 4) + nose.tools.assert_almost_equal(exp_sim.p_sol[0][-1][0], -1.8761, 2) #Values taken from the example in Sundials + nose.tools.assert_almost_equal(exp_sim.p_sol[1][-1][0], 2.9614e-06, 8) + + return exp_mod, exp_sim + +if __name__=='__main__': + mod,sim = run_example() + diff --git a/examples/dopri5_basic.py b/examples/dopri5_basic.py index 979fed0c..66ecbe55 100644 --- a/examples/dopri5_basic.py +++ b/examples/dopri5_basic.py @@ -1,64 +1,64 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import Dopri5 -from assimulo.problem import Explicit_Problem - -def run_example(with_plots=True): - r""" - Example to demonstrate the use of the Runge-Kutta solver DOPRI5 - for the linear test equation :math:`\dot y = - y` - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - - """ - #Defines the rhs - def f(t,y): - ydot = -y[0] - return N.array([ydot]) - - #Define an Assimulo problem - exp_mod = Explicit_Problem(f, 4.0, - name = 'DOPRI5 Example: $\dot y = - y$') - - exp_sim = Dopri5(exp_mod) #Create a Dopri5 solver - - #Simulate - t, y = exp_sim.simulate(5) #Simulate 5 seconds - - #Plot - if with_plots: - import pylab as P - P.plot(t,y) - P.title(exp_mod.name) - P.xlabel('Time') - P.ylabel('State') - P.show() - - #Basic test - nose.tools.assert_almost_equal(float(y[-1]),0.02695199,5) - - return exp_mod, exp_sim - -if __name__=='__main__': - mod,sim = run_example() +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import Dopri5 +from assimulo.problem import Explicit_Problem + +def run_example(with_plots=True): + r""" + Example to demonstrate the use of the Runge-Kutta solver DOPRI5 + for the linear test equation :math:`\dot y = - y` + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + + """ + #Defines the rhs + def f(t,y): + ydot = -y[0] + return N.array([ydot]) + + #Define an Assimulo problem + exp_mod = Explicit_Problem(f, 4.0, + name = 'DOPRI5 Example: $\dot y = - y$') + + exp_sim = Dopri5(exp_mod) #Create a Dopri5 solver + + #Simulate + t, y = exp_sim.simulate(5) #Simulate 5 seconds + + #Plot + if with_plots: + import pylab as P + P.plot(t,y) + P.title(exp_mod.name) + P.xlabel('Time') + P.ylabel('State') + P.show() + + #Basic test + nose.tools.assert_almost_equal(float(y[-1]),0.02695199,5) + + return exp_mod, exp_sim + +if __name__=='__main__': + mod,sim = run_example() diff --git a/examples/dopri5_with_disc.py b/examples/dopri5_with_disc.py index 6837a2ef..8ce9eca0 100644 --- a/examples/dopri5_with_disc.py +++ b/examples/dopri5_with_disc.py @@ -1,168 +1,168 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import Dopri5 -from assimulo.problem import Explicit_Problem - -""" -An example with event iteration and with three switches. - -t=0 , [False, True, True] (Start of simulation) -t=1 (1) , [False, True, False] (Found a root at t=1) -t=1 (2) , [False, False, False] (Second iteration at t=1) -t=1 (3) , [True, False, False] (Third iteration at t=1) -t=10 , [True, False, False] (End of simulation) - -""" - -#Extend Assimulos problem definition -class Extended_Problem(Explicit_Problem): - - #Sets the initial conditons directly into the problem - y0 = [0.0, -1.0, 0.0] - sw0 = [False,True,True] - - #The right-hand-side function (rhs) - def rhs(self,t,y,sw): - """ - This is our function we are trying to simulate. During simulation - the parameter sw should be fixed so that our function is continuous - over the interval. The parameters sw should only be changed when the - integrator has stopped. - """ - yd_0 = (1.0 if sw[0] else -1.0) - yd_1 = 0.0 - yd_2 = 0.0 - - return N.array([yd_0,yd_1,yd_2]) - - #Sets a name to our function - name = 'ODE with discontinuities and a function with consistency problem' - - #The event function - def state_events(self,t,y,sw): - """ - This is our function that keeps track of our events. When the sign - of any of the events has changed, we have an event. - """ - event_0 = y[1] - 1.0 - event_1 = -y[2] + 1.0 - event_2 = -t + 1.0 - - return N.array([event_0,event_1,event_2]) - - - #Responsible for handling the events. - def handle_event(self, solver, event_info): - """ - Event handling. This functions is called when Assimulo finds an event as - specified by the event functions. - """ - event_info = event_info[0] #We only look at the state events information. - while True: #Event Iteration - self.event_switch(solver, event_info) #Turns the switches - - b_mode = self.state_events(solver.t, solver.y, solver.sw) - self.init_mode(solver) #Pass in the solver to the problem specified init_mode - a_mode = self.state_events(solver.t, solver.y, solver.sw) - - event_info = self.check_eIter(b_mode, a_mode) - - if not True in event_info: #Breaks the iteration loop - break - - #Helper function for handle_event - def event_switch(self, solver, event_info): - """ - Turns the switches. - """ - for i in range(len(event_info)): #Loop across all event functions - if event_info[i] != 0: - solver.sw[i] = not solver.sw[i] #Turn the switch - - #Helper function for handle_event - def check_eIter(self, before, after): - """ - Helper function for handle_event to determine if we have event - iteration. - - Input: Values of the event indicator functions (state_events) - before and after we have changed mode of operations. - """ - - eIter = [False]*len(before) - - for i in range(len(before)): - if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): - eIter[i] = True - - return eIter - - def init_mode(self, solver): - """ - Initialize the DAE with the new conditions. - """ - solver.y[1] = (-1.0 if solver.sw[1] else 3.0) - solver.y[2] = (0.0 if solver.sw[2] else 2.0) - - - -def run_example(with_plots=True): - """ - Example of the use of DOPRI5 for a differential equation - with a discontinuity (state event) and the need for an event iteration. - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - """ - #Create an instance of the problem - exp_mod = Extended_Problem() #Create the problem - - exp_sim = Dopri5(exp_mod) #Create the solver - - exp_sim.verbosity = 0 - exp_sim.report_continuously = True - - #Simulate - t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points - - #Plot - if with_plots: - import pylab as P - P.plot(t,y) - P.title(exp_mod.name) - P.ylabel('States') - P.xlabel('Time') - P.show() - - #Basic test - nose.tools.assert_almost_equal(y[-1][0],8.0) - nose.tools.assert_almost_equal(y[-1][1],3.0) - nose.tools.assert_almost_equal(y[-1][2],2.0) - - return exp_mod, exp_sim - -if __name__=="__main__": - mod,sim = run_example() - - - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import Dopri5 +from assimulo.problem import Explicit_Problem + +""" +An example with event iteration and with three switches. + +t=0 , [False, True, True] (Start of simulation) +t=1 (1) , [False, True, False] (Found a root at t=1) +t=1 (2) , [False, False, False] (Second iteration at t=1) +t=1 (3) , [True, False, False] (Third iteration at t=1) +t=10 , [True, False, False] (End of simulation) + +""" + +#Extend Assimulos problem definition +class Extended_Problem(Explicit_Problem): + + #Sets the initial conditons directly into the problem + y0 = [0.0, -1.0, 0.0] + sw0 = [False,True,True] + + #The right-hand-side function (rhs) + def rhs(self,t,y,sw): + """ + This is our function we are trying to simulate. During simulation + the parameter sw should be fixed so that our function is continuous + over the interval. The parameters sw should only be changed when the + integrator has stopped. + """ + yd_0 = (1.0 if sw[0] else -1.0) + yd_1 = 0.0 + yd_2 = 0.0 + + return N.array([yd_0,yd_1,yd_2]) + + #Sets a name to our function + name = 'ODE with discontinuities and a function with consistency problem' + + #The event function + def state_events(self,t,y,sw): + """ + This is our function that keeps track of our events. When the sign + of any of the events has changed, we have an event. + """ + event_0 = y[1] - 1.0 + event_1 = -y[2] + 1.0 + event_2 = -t + 1.0 + + return N.array([event_0,event_1,event_2]) + + + #Responsible for handling the events. + def handle_event(self, solver, event_info): + """ + Event handling. This functions is called when Assimulo finds an event as + specified by the event functions. + """ + event_info = event_info[0] #We only look at the state events information. + while True: #Event Iteration + self.event_switch(solver, event_info) #Turns the switches + + b_mode = self.state_events(solver.t, solver.y, solver.sw) + self.init_mode(solver) #Pass in the solver to the problem specified init_mode + a_mode = self.state_events(solver.t, solver.y, solver.sw) + + event_info = self.check_eIter(b_mode, a_mode) + + if not True in event_info: #Breaks the iteration loop + break + + #Helper function for handle_event + def event_switch(self, solver, event_info): + """ + Turns the switches. + """ + for i in range(len(event_info)): #Loop across all event functions + if event_info[i] != 0: + solver.sw[i] = not solver.sw[i] #Turn the switch + + #Helper function for handle_event + def check_eIter(self, before, after): + """ + Helper function for handle_event to determine if we have event + iteration. + + Input: Values of the event indicator functions (state_events) + before and after we have changed mode of operations. + """ + + eIter = [False]*len(before) + + for i in range(len(before)): + if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): + eIter[i] = True + + return eIter + + def init_mode(self, solver): + """ + Initialize the DAE with the new conditions. + """ + solver.y[1] = (-1.0 if solver.sw[1] else 3.0) + solver.y[2] = (0.0 if solver.sw[2] else 2.0) + + + +def run_example(with_plots=True): + """ + Example of the use of DOPRI5 for a differential equation + with a discontinuity (state event) and the need for an event iteration. + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + """ + #Create an instance of the problem + exp_mod = Extended_Problem() #Create the problem + + exp_sim = Dopri5(exp_mod) #Create the solver + + exp_sim.verbosity = 0 + exp_sim.report_continuously = True + + #Simulate + t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points + + #Plot + if with_plots: + import pylab as P + P.plot(t,y) + P.title(exp_mod.name) + P.ylabel('States') + P.xlabel('Time') + P.show() + + #Basic test + nose.tools.assert_almost_equal(y[-1][0],8.0) + nose.tools.assert_almost_equal(y[-1][1],3.0) + nose.tools.assert_almost_equal(y[-1][2],2.0) + + return exp_mod, exp_sim + +if __name__=="__main__": + mod,sim = run_example() + + + diff --git a/examples/euler_basic.py b/examples/euler_basic.py index f221d090..5fece093 100644 --- a/examples/euler_basic.py +++ b/examples/euler_basic.py @@ -1,69 +1,69 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import ExplicitEuler -from assimulo.problem import Explicit_Problem - -def run_example(with_plots=True): - r""" - Demonstration of the use of the use of the explicit euler method by solving the - linear test equation :math:`\dot y = - y` - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - - """ - - #Defines the rhs - def f(t,y): - ydot = -y[0] - return N.array([ydot]) - - #Define an Assimulo problem - exp_mod = Explicit_Problem(f, 4.0, - name = 'ExplicitEuler Example: $\dot y = - y$') - - #Explicit Euler - exp_sim = ExplicitEuler(exp_mod) #Create a explicit Euler solver - exp_sim.options["continuous_output"] = True - - #Simulate - t1, y1 = exp_sim.simulate(3) #Simulate 3 seconds - t2, y2 = exp_sim.simulate(5,100) #Simulate 2 second more - - #Plot - if with_plots: - import pylab as P - P.plot(t1, y1, color="b") - P.plot(t2, y2, color="r") - P.title(exp_mod.name) - P.ylabel('y') - P.xlabel('Time') - P.show() - - #Basic test - nose.tools.assert_almost_equal(float(y2[-1]), 0.02628193) - - return exp_mod, exp_sim - -if __name__=='__main__': - mod,sim = run_example() +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import ExplicitEuler +from assimulo.problem import Explicit_Problem + +def run_example(with_plots=True): + r""" + Demonstration of the use of the use of the explicit euler method by solving the + linear test equation :math:`\dot y = - y` + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + + """ + + #Defines the rhs + def f(t,y): + ydot = -y[0] + return N.array([ydot]) + + #Define an Assimulo problem + exp_mod = Explicit_Problem(f, 4.0, + name = 'ExplicitEuler Example: $\dot y = - y$') + + #Explicit Euler + exp_sim = ExplicitEuler(exp_mod) #Create a explicit Euler solver + exp_sim.options["continuous_output"] = True + + #Simulate + t1, y1 = exp_sim.simulate(3) #Simulate 3 seconds + t2, y2 = exp_sim.simulate(5,100) #Simulate 2 second more + + #Plot + if with_plots: + import pylab as P + P.plot(t1, y1, color="b") + P.plot(t2, y2, color="r") + P.title(exp_mod.name) + P.ylabel('y') + P.xlabel('Time') + P.show() + + #Basic test + nose.tools.assert_almost_equal(float(y2[-1]), 0.02628193) + + return exp_mod, exp_sim + +if __name__=='__main__': + mod,sim = run_example() diff --git a/examples/euler_vanderpol.py b/examples/euler_vanderpol.py index 4ef36742..12e75ba8 100644 --- a/examples/euler_vanderpol.py +++ b/examples/euler_vanderpol.py @@ -1,95 +1,95 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import ImplicitEuler -from assimulo.problem import Explicit_Problem - -def run_example(with_plots=True): - r""" - Example for the use of the implicit Euler method to solve - Van der Pol's equation - - .. math:: - - \dot y_1 &= y_2 \\ - \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) - - with :math:`\mu=\frac{1}{5} 10^3`. - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - - """ - eps = 5.e-3 - my = 1./eps - - #Define the rhs - def f(t,y): - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - return N.array([yd_0,yd_1]) - - #Define the Jacobian - def jac(t,y): - jd_00 = 0.0 - jd_01 = 1.0 - jd_10 = -1.0*my-2*y[0]*y[1]*my - jd_11 = my*(1.-y[0]**2) - - return N.array([[jd_00,jd_01],[jd_10,jd_11]]) - - y0 = [2.0,-0.6] #Initial conditions - - #Define an Assimulo problem - exp_mod = Explicit_Problem(f,y0, - name = "ImplicitEuler: Van der Pol's equation (as explicit problem) ") - exp_mod.jac = jac - - #Define an explicit solver - exp_sim = ImplicitEuler(exp_mod) #Create a ImplicitEuler solver - - #Sets the parameters - exp_sim.h = 1e-4 #Stepsize - exp_sim.usejac = True #If the user defined jacobian should be used or not - - #Simulate - t, y = exp_sim.simulate(2.0) #Simulate 2 seconds - - #Plot - if with_plots: - import pylab as P - P.plot(t,y[:,0], marker='o') - P.title(exp_mod.name) - P.ylabel("State: $y_1$") - P.xlabel("Time") - P.show() - - #Basic test - x1 = y[:,0] - assert N.abs(x1[-1]-1.8601438) < 1e-1 #For test purpose - - return exp_mod, exp_sim - -if __name__=='__main__': - mod,sim = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import ImplicitEuler +from assimulo.problem import Explicit_Problem + +def run_example(with_plots=True): + r""" + Example for the use of the implicit Euler method to solve + Van der Pol's equation + + .. math:: + + \dot y_1 &= y_2 \\ + \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) + + with :math:`\mu=\frac{1}{5} 10^3`. + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + + """ + eps = 5.e-3 + my = 1./eps + + #Define the rhs + def f(t,y): + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + return N.array([yd_0,yd_1]) + + #Define the Jacobian + def jac(t,y): + jd_00 = 0.0 + jd_01 = 1.0 + jd_10 = -1.0*my-2*y[0]*y[1]*my + jd_11 = my*(1.-y[0]**2) + + return N.array([[jd_00,jd_01],[jd_10,jd_11]]) + + y0 = [2.0,-0.6] #Initial conditions + + #Define an Assimulo problem + exp_mod = Explicit_Problem(f,y0, + name = "ImplicitEuler: Van der Pol's equation (as explicit problem) ") + exp_mod.jac = jac + + #Define an explicit solver + exp_sim = ImplicitEuler(exp_mod) #Create a ImplicitEuler solver + + #Sets the parameters + exp_sim.h = 1e-4 #Stepsize + exp_sim.usejac = True #If the user defined jacobian should be used or not + + #Simulate + t, y = exp_sim.simulate(2.0) #Simulate 2 seconds + + #Plot + if with_plots: + import pylab as P + P.plot(t,y[:,0], marker='o') + P.title(exp_mod.name) + P.ylabel("State: $y_1$") + P.xlabel("Time") + P.show() + + #Basic test + x1 = y[:,0] + assert N.abs(x1[-1]-1.8601438) < 1e-1 #For test purpose + + return exp_mod, exp_sim + +if __name__=='__main__': + mod,sim = run_example() + diff --git a/examples/euler_with_disc.py b/examples/euler_with_disc.py index 8dc9adbc..75f19492 100644 --- a/examples/euler_with_disc.py +++ b/examples/euler_with_disc.py @@ -1,168 +1,168 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import ExplicitEuler -from assimulo.problem import Explicit_Problem - -""" -An example with event iteration and with three switches. - -t=0 , [False, True, True] (Start of simulation) -t=1 (1) , [False, True, False] (Found a root at t=1) -t=1 (2) , [False, False, False] (Second iteration at t=1) -t=1 (3) , [True, False, False] (Third iteration at t=1) -t=10 , [True, False, False] (End of simulation) - -""" - -#Extend Assimulos problem definition -class Extended_Problem(Explicit_Problem): - - #Sets the initial conditons directly into the problem - y0 = [0.0, -1.0, 0.0] - sw0 = [False,True,True] - - #The right-hand-side function (rhs) - def rhs(self,t,y,sw): - """ - This is our function we are trying to simulate. During simulation - the parameter sw should be fixed so that our function is continuous - over the interval. The parameters sw should only be changed when the - integrator has stopped. - """ - yd_0 = (1.0 if sw[0] else -1.0) - yd_1 = 0.0 - yd_2 = 0.0 - - return N.array([yd_0,yd_1,yd_2]) - - #Sets a name to our function - name = 'ODE with discontinuities and a function with consistency problem' - - #The event function - def state_events(self,t,y,sw): - """ - This is our function that keeps track of our events. When the sign - of any of the events has changed, we have an event. - """ - event_0 = y[1] - 1.0 - event_1 = -y[2] + 1.0 - event_2 = -t + 1.0 - - return N.array([event_0,event_1,event_2]) - - - #Responsible for handling the events. - def handle_event(self, solver, event_info): - """ - Event handling. This functions is called when Assimulo finds an event as - specified by the event functions. - """ - event_info = event_info[0] #We only look at the state events information. - while True: #Event Iteration - self.event_switch(solver, event_info) #Turns the switches - - b_mode = self.state_events(solver.t, solver.y, solver.sw) - self.init_mode(solver) #Pass in the solver to the problem specified init_mode - a_mode = self.state_events(solver.t, solver.y, solver.sw) - - event_info = self.check_eIter(b_mode, a_mode) - - if not True in event_info: #Breaks the iteration loop - break - - #Helper function for handle_event - def event_switch(self, solver, event_info): - """ - Turns the switches. - """ - for i in range(len(event_info)): #Loop across all event functions - if event_info[i] != 0: - solver.sw[i] = not solver.sw[i] #Turn the switch - - #Helper function for handle_event - def check_eIter(self, before, after): - """ - Helper function for handle_event to determine if we have event - iteration. - - Input: Values of the event indicator functions (state_events) - before and after we have changed mode of operations. - """ - - eIter = [False]*len(before) - - for i in range(len(before)): - if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): - eIter[i] = True - - return eIter - - def init_mode(self, solver): - """ - Initialize the DAE with the new conditions. - """ - solver.y[1] = (-1.0 if solver.sw[1] else 3.0) - solver.y[2] = (0.0 if solver.sw[2] else 2.0) - - - -def run_example(with_plots=True): - r""" - Example of the use of Euler's method for a differential equation - with a discontinuity (state event) and the need for an event iteration. - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - """ - exp_mod = Extended_Problem() #Create the problem - - exp_sim = ExplicitEuler(exp_mod) #Create the solver - - exp_sim.verbosity = 0 - exp_sim.report_continuously = True - - #Simulate - t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points - - #Plot - if with_plots: - import pylab as P - P.plot(t,y) - P.title("Solution of a differential equation with discontinuities") - P.ylabel('States') - P.xlabel('Time') - P.show() - - #Basic test - nose.tools.assert_almost_equal(y[-1][0],8.0) - nose.tools.assert_almost_equal(y[-1][1],3.0) - nose.tools.assert_almost_equal(y[-1][2],2.0) - - return exp_mod, exp_sim - - -if __name__=="__main__": - mod,sim = run_example() - - - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import ExplicitEuler +from assimulo.problem import Explicit_Problem + +""" +An example with event iteration and with three switches. + +t=0 , [False, True, True] (Start of simulation) +t=1 (1) , [False, True, False] (Found a root at t=1) +t=1 (2) , [False, False, False] (Second iteration at t=1) +t=1 (3) , [True, False, False] (Third iteration at t=1) +t=10 , [True, False, False] (End of simulation) + +""" + +#Extend Assimulos problem definition +class Extended_Problem(Explicit_Problem): + + #Sets the initial conditons directly into the problem + y0 = [0.0, -1.0, 0.0] + sw0 = [False,True,True] + + #The right-hand-side function (rhs) + def rhs(self,t,y,sw): + """ + This is our function we are trying to simulate. During simulation + the parameter sw should be fixed so that our function is continuous + over the interval. The parameters sw should only be changed when the + integrator has stopped. + """ + yd_0 = (1.0 if sw[0] else -1.0) + yd_1 = 0.0 + yd_2 = 0.0 + + return N.array([yd_0,yd_1,yd_2]) + + #Sets a name to our function + name = 'ODE with discontinuities and a function with consistency problem' + + #The event function + def state_events(self,t,y,sw): + """ + This is our function that keeps track of our events. When the sign + of any of the events has changed, we have an event. + """ + event_0 = y[1] - 1.0 + event_1 = -y[2] + 1.0 + event_2 = -t + 1.0 + + return N.array([event_0,event_1,event_2]) + + + #Responsible for handling the events. + def handle_event(self, solver, event_info): + """ + Event handling. This functions is called when Assimulo finds an event as + specified by the event functions. + """ + event_info = event_info[0] #We only look at the state events information. + while True: #Event Iteration + self.event_switch(solver, event_info) #Turns the switches + + b_mode = self.state_events(solver.t, solver.y, solver.sw) + self.init_mode(solver) #Pass in the solver to the problem specified init_mode + a_mode = self.state_events(solver.t, solver.y, solver.sw) + + event_info = self.check_eIter(b_mode, a_mode) + + if not True in event_info: #Breaks the iteration loop + break + + #Helper function for handle_event + def event_switch(self, solver, event_info): + """ + Turns the switches. + """ + for i in range(len(event_info)): #Loop across all event functions + if event_info[i] != 0: + solver.sw[i] = not solver.sw[i] #Turn the switch + + #Helper function for handle_event + def check_eIter(self, before, after): + """ + Helper function for handle_event to determine if we have event + iteration. + + Input: Values of the event indicator functions (state_events) + before and after we have changed mode of operations. + """ + + eIter = [False]*len(before) + + for i in range(len(before)): + if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): + eIter[i] = True + + return eIter + + def init_mode(self, solver): + """ + Initialize the DAE with the new conditions. + """ + solver.y[1] = (-1.0 if solver.sw[1] else 3.0) + solver.y[2] = (0.0 if solver.sw[2] else 2.0) + + + +def run_example(with_plots=True): + r""" + Example of the use of Euler's method for a differential equation + with a discontinuity (state event) and the need for an event iteration. + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + """ + exp_mod = Extended_Problem() #Create the problem + + exp_sim = ExplicitEuler(exp_mod) #Create the solver + + exp_sim.verbosity = 0 + exp_sim.report_continuously = True + + #Simulate + t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points + + #Plot + if with_plots: + import pylab as P + P.plot(t,y) + P.title("Solution of a differential equation with discontinuities") + P.ylabel('States') + P.xlabel('Time') + P.show() + + #Basic test + nose.tools.assert_almost_equal(y[-1][0],8.0) + nose.tools.assert_almost_equal(y[-1][1],3.0) + nose.tools.assert_almost_equal(y[-1][2],2.0) + + return exp_mod, exp_sim + + +if __name__=="__main__": + mod,sim = run_example() + + + diff --git a/examples/glimda_vanderpol.py b/examples/glimda_vanderpol.py index 58c6ad4b..8fd819c9 100644 --- a/examples/glimda_vanderpol.py +++ b/examples/glimda_vanderpol.py @@ -1,95 +1,95 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import GLIMDA,IDA -from assimulo.problem import Implicit_Problem - -def run_example(with_plots=True): - r""" - Example for the use of GLIMDA (general linear multistep method) to solve - Van der Pol's equation - - .. math:: - - \dot y_1 &= y_2 \\ - \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) - - with :math:`\mu= 10^6`. - - on return: - - - :dfn:`imp_mod` problem instance - - - :dfn:`imp_sim` solver instance - - """ - - #Define the residual - def f(t,y,yd): - eps = 1.e-6 - my = 1./eps - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - res_0 = yd[0]-yd_0 - res_1 = yd[1]-yd_1 - - return N.array([res_0,res_1]) - - y0 = [2.0,-0.6] #Initial conditions - yd0 = [-.6,-200000.] - - #Define an Assimulo problem - imp_mod = Implicit_Problem(f,y0,yd0, - name = 'Glimbda Example: Van der Pol (implicit)') - - #Define an explicit solver - imp_sim = GLIMDA(imp_mod) #Create a GLIMDA solver - - #Sets the parameters - imp_sim.atol = 1e-4 #Default 1e-6 - imp_sim.rtol = 1e-4 #Default 1e-6 - imp_sim.inith = 1.e-4 #Initial step-size - - #Simulate - t, y, yd = imp_sim.simulate(2.) #Simulate 2 seconds - - #Plot - if with_plots: - import pylab as P - P.subplot(211) - P.plot(t,y[:,0])#, marker='o') - P.xlabel('Time') - P.ylabel('State') - P.subplot(212) - P.plot(t,yd[:,0]*1.e-5)#, marker='o') - P.xlabel('Time') - P.ylabel('State derivatives scaled with $10^{-5}$') - P.suptitle(imp_mod.name) - P.show() - - #Basic test - x1 = y[:,0] - assert N.abs(x1[-1]-1.706168035) < 1e-3 #For test purpose - - return imp_mod, imp_sim - -if __name__=='__main__': - mod,sim = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import GLIMDA,IDA +from assimulo.problem import Implicit_Problem + +def run_example(with_plots=True): + r""" + Example for the use of GLIMDA (general linear multistep method) to solve + Van der Pol's equation + + .. math:: + + \dot y_1 &= y_2 \\ + \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) + + with :math:`\mu= 10^6`. + + on return: + + - :dfn:`imp_mod` problem instance + + - :dfn:`imp_sim` solver instance + + """ + + #Define the residual + def f(t,y,yd): + eps = 1.e-6 + my = 1./eps + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + res_0 = yd[0]-yd_0 + res_1 = yd[1]-yd_1 + + return N.array([res_0,res_1]) + + y0 = [2.0,-0.6] #Initial conditions + yd0 = [-.6,-200000.] + + #Define an Assimulo problem + imp_mod = Implicit_Problem(f,y0,yd0, + name = 'Glimbda Example: Van der Pol (implicit)') + + #Define an explicit solver + imp_sim = GLIMDA(imp_mod) #Create a GLIMDA solver + + #Sets the parameters + imp_sim.atol = 1e-4 #Default 1e-6 + imp_sim.rtol = 1e-4 #Default 1e-6 + imp_sim.inith = 1.e-4 #Initial step-size + + #Simulate + t, y, yd = imp_sim.simulate(2.) #Simulate 2 seconds + + #Plot + if with_plots: + import pylab as P + P.subplot(211) + P.plot(t,y[:,0])#, marker='o') + P.xlabel('Time') + P.ylabel('State') + P.subplot(212) + P.plot(t,yd[:,0]*1.e-5)#, marker='o') + P.xlabel('Time') + P.ylabel('State derivatives scaled with $10^{-5}$') + P.suptitle(imp_mod.name) + P.show() + + #Basic test + x1 = y[:,0] + assert N.abs(x1[-1]-1.706168035) < 1e-3 #For test purpose + + return imp_mod, imp_sim + +if __name__=='__main__': + mod,sim = run_example() + diff --git a/examples/ida_basic_backward.py b/examples/ida_basic_backward.py index 867964a9..62feaad0 100644 --- a/examples/ida_basic_backward.py +++ b/examples/ida_basic_backward.py @@ -1,70 +1,70 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import IDA -from assimulo.problem import Implicit_Problem - -def run_example(with_plots=True): - """ - The same as example :doc:`EXAMPLE_cvode_basic` but now integrated backwards in time. - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - - """ - - #Define the rhs - def f(t,y,yd): - res = yd[0] + y[0] - return N.array([res]) - - #Define an Assimulo problem - imp_mod = Implicit_Problem(f,t0=5, y0=0.02695, yd0=-0.02695, - name = 'IDA Example: $\dot y + y = 0$ (reverse time)') - - #Define an explicit solver - imp_sim = IDA(imp_mod) #Create a IDA solver - - #Sets the parameters - imp_sim.atol = [1e-8] #Default 1e-6 - imp_sim.rtol = 1e-8 #Default 1e-6 - imp_sim.backward = True - - #Simulate - t, y, yd = imp_sim.simulate(0) #Simulate 5 seconds (t0=5 -> tf=0) - - #Plot - if with_plots: - import pylab as P - P.plot(t, y, color="b") - P.xlabel('Time') - P.ylabel('State') - P.title(imp_mod.name) - P.show() - - #Basic test - nose.tools.assert_almost_equal(float(y[-1]), 4.00000000, 3) - - return imp_mod, imp_sim - -if __name__=='__main__': - mod,sim = run_example() +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import IDA +from assimulo.problem import Implicit_Problem + +def run_example(with_plots=True): + """ + The same as example :doc:`EXAMPLE_cvode_basic` but now integrated backwards in time. + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + + """ + + #Define the rhs + def f(t,y,yd): + res = yd[0] + y[0] + return N.array([res]) + + #Define an Assimulo problem + imp_mod = Implicit_Problem(f,t0=5, y0=0.02695, yd0=-0.02695, + name = 'IDA Example: $\dot y + y = 0$ (reverse time)') + + #Define an explicit solver + imp_sim = IDA(imp_mod) #Create a IDA solver + + #Sets the parameters + imp_sim.atol = [1e-8] #Default 1e-6 + imp_sim.rtol = 1e-8 #Default 1e-6 + imp_sim.backward = True + + #Simulate + t, y, yd = imp_sim.simulate(0) #Simulate 5 seconds (t0=5 -> tf=0) + + #Plot + if with_plots: + import pylab as P + P.plot(t, y, color="b") + P.xlabel('Time') + P.ylabel('State') + P.title(imp_mod.name) + P.show() + + #Basic test + nose.tools.assert_almost_equal(float(y[-1]), 4.00000000, 3) + + return imp_mod, imp_sim + +if __name__=='__main__': + mod,sim = run_example() diff --git a/examples/ida_with_jac_spgmr.py b/examples/ida_with_jac_spgmr.py index 07263c40..1db858de 100644 --- a/examples/ida_with_jac_spgmr.py +++ b/examples/ida_with_jac_spgmr.py @@ -1,95 +1,95 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import IDA -from assimulo.problem import Implicit_Problem - - -def run_example(with_plots=True): - r""" - An example for IDA with scaled preconditioned GMRES method - as a special linear solver. - Note, how the operation Jacobian times vector is provided. - - ODE: - - .. math:: - - \dot y_1 - y_2 &= 0\\ - \dot y_2 -9.82 &= 0 - - - on return: - - - :dfn:`imp_mod` problem instance - - - :dfn:`imp_sim` solver instance - - """ - - #Defines the residual - def res(t,y,yd): - res_0 = yd[0] - y[1] - res_1 = yd[1] + 9.82 - - return N.array([res_0,res_1]) - - #Defines the Jacobian*vector product - def jacv(t,y,yd,res,v,c): - jy = N.array([[0,-1.],[0,0]]) - jyd = N.array([[1,0.],[0,1]]) - j = jy+c*jyd - return N.dot(j,v) - - #Initial conditions - y0 = [1.0,0.0] - yd0 = [0.0, -9.82] - - #Defines an Assimulo implicit problem - imp_mod = Implicit_Problem(res,y0,yd0,name = 'Example using the Jacobian Vector product') - - imp_mod.jacv = jacv #Sets the jacobian - - imp_sim = IDA(imp_mod) #Create an IDA solver instance - - #Set the parameters - imp_sim.atol = 1e-5 #Default 1e-6 - imp_sim.rtol = 1e-5 #Default 1e-6 - imp_sim.linear_solver = 'SPGMR' #Change linear solver - #imp_sim.options["usejac"] = False - - #Simulate - t, y, yd = imp_sim.simulate(5, 1000) #Simulate 5 seconds with 1000 communication points - - #Basic tests - nose.tools.assert_almost_equal(y[-1][0],-121.75000000,4) - nose.tools.assert_almost_equal(y[-1][1],-49.100000000) - - #Plot - if with_plots: - import pylab as P - P.plot(t,y) - P.xlabel('Time') - P.ylabel('State') - P.title(imp_mod.name) - P.show() - return imp_mod,imp_sim - -if __name__=='__main__': - mod,sim = run_example() +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import IDA +from assimulo.problem import Implicit_Problem + + +def run_example(with_plots=True): + r""" + An example for IDA with scaled preconditioned GMRES method + as a special linear solver. + Note, how the operation Jacobian times vector is provided. + + ODE: + + .. math:: + + \dot y_1 - y_2 &= 0\\ + \dot y_2 -9.82 &= 0 + + + on return: + + - :dfn:`imp_mod` problem instance + + - :dfn:`imp_sim` solver instance + + """ + + #Defines the residual + def res(t,y,yd): + res_0 = yd[0] - y[1] + res_1 = yd[1] + 9.82 + + return N.array([res_0,res_1]) + + #Defines the Jacobian*vector product + def jacv(t,y,yd,res,v,c): + jy = N.array([[0,-1.],[0,0]]) + jyd = N.array([[1,0.],[0,1]]) + j = jy+c*jyd + return N.dot(j,v) + + #Initial conditions + y0 = [1.0,0.0] + yd0 = [0.0, -9.82] + + #Defines an Assimulo implicit problem + imp_mod = Implicit_Problem(res,y0,yd0,name = 'Example using the Jacobian Vector product') + + imp_mod.jacv = jacv #Sets the jacobian + + imp_sim = IDA(imp_mod) #Create an IDA solver instance + + #Set the parameters + imp_sim.atol = 1e-5 #Default 1e-6 + imp_sim.rtol = 1e-5 #Default 1e-6 + imp_sim.linear_solver = 'SPGMR' #Change linear solver + #imp_sim.options["usejac"] = False + + #Simulate + t, y, yd = imp_sim.simulate(5, 1000) #Simulate 5 seconds with 1000 communication points + + #Basic tests + nose.tools.assert_almost_equal(y[-1][0],-121.75000000,4) + nose.tools.assert_almost_equal(y[-1][1],-49.100000000) + + #Plot + if with_plots: + import pylab as P + P.plot(t,y) + P.xlabel('Time') + P.ylabel('State') + P.title(imp_mod.name) + P.show() + return imp_mod,imp_sim + +if __name__=='__main__': + mod,sim = run_example() diff --git a/examples/kinsol_basic.py b/examples/kinsol_basic.py index 3bf96994..9bbb763b 100644 --- a/examples/kinsol_basic.py +++ b/examples/kinsol_basic.py @@ -1,56 +1,56 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import KINSOL -from assimulo.problem import Algebraic_Problem - -def run_example(with_plots=True): - r""" - Example to demonstrate the use of the Sundials solver Kinsol - for the simple equation :math:`0 = 1 - y` - - on return: - - - :dfn:`alg_mod` problem instance - - - :dfn:`alg_solver` solver instance - - """ - - #Define the res - def res(y): - return 1-y - - #Define an Assimulo problem - alg_mod = Algebraic_Problem(res, y0=0, name = 'Simple KINSOL Example') - - #Define the KINSOL solver - alg_solver = KINSOL(alg_mod) - - #Solve - y = alg_solver.solve() - - #Basic test - nose.tools.assert_almost_equal(y, 1.0, 5) - - return alg_mod, alg_solver - -if __name__=='__main__': - mod, solv = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import KINSOL +from assimulo.problem import Algebraic_Problem + +def run_example(with_plots=True): + r""" + Example to demonstrate the use of the Sundials solver Kinsol + for the simple equation :math:`0 = 1 - y` + + on return: + + - :dfn:`alg_mod` problem instance + + - :dfn:`alg_solver` solver instance + + """ + + #Define the res + def res(y): + return 1-y + + #Define an Assimulo problem + alg_mod = Algebraic_Problem(res, y0=0, name = 'Simple KINSOL Example') + + #Define the KINSOL solver + alg_solver = KINSOL(alg_mod) + + #Solve + y = alg_solver.solve() + + #Basic test + nose.tools.assert_almost_equal(y, 1.0, 5) + + return alg_mod, alg_solver + +if __name__=='__main__': + mod, solv = run_example() + diff --git a/examples/kinsol_ors.py b/examples/kinsol_ors.py index 329abf4a..3cb2b6f7 100644 --- a/examples/kinsol_ors.py +++ b/examples/kinsol_ors.py @@ -1,140 +1,140 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import scipy as S -import scipy.linalg as LIN -import scipy.io as IO -import scipy.sparse as SPARSE -import scipy.sparse.linalg as LINSP -import nose -import os -from assimulo.solvers import KINSOL -from assimulo.problem import Algebraic_Problem -import warnings -import scipy.sparse - - -warnings.simplefilter("ignore", scipy.sparse.SparseEfficiencyWarning) - -file_path = os.path.dirname(os.path.realpath(__file__)) - -def run_example(with_plots=True): - r""" - Example to demonstrate the use of the Sundials solver Kinsol with - a user provided Jacobian and a preconditioner. The example is the - 'Problem 4' taken from the book by Saad: - Iterative Methods for Sparse Linear Systems. - """ - #Read the original matrix - A_original = IO.mmread(os.path.join(file_path,"kinsol_ors_matrix.mtx")) - - #Scale the original matrix - A = SPARSE.spdiags(1.0/A_original.diagonal(), 0, len(A_original.diagonal()), len(A_original.diagonal())) * A_original - - #Preconditioning by Symmetric Gauss Seidel - if True: - D = SPARSE.spdiags(A.diagonal(), 0, len(A_original.diagonal()), len(A_original.diagonal())) - Dinv = SPARSE.spdiags(1.0/A.diagonal(), 0, len(A_original.diagonal()), len(A_original.diagonal())) - E = -SPARSE.tril(A,k=-1) - F = -SPARSE.triu(A,k=1) - L = (D-E).dot(Dinv) - U = D-F - Prec = L.dot(U) - - solvePrec = LINSP.factorized(Prec) - - #Create the RHS - b = A.dot(N.ones((A.shape[0],1))) - - #Define the res - def res(x): - return A.dot(x.reshape(len(x),1))-b - - #The Jacobian - def jac(x): - return A.todense() - - #The Jacobian*Vector - def jacv(x,v): - return A.dot(v.reshape(len(v),1)) - - def prec_setup(u,f, uscale, fscale): - pass - - def prec_solve(r): - return solvePrec(r) - - y0 = S.rand(A.shape[0]) - - #Define an Assimulo problem - alg_mod = Algebraic_Problem(res, y0=y0, jac=jac, jacv=jacv, name = 'ORS Example') - alg_mod_prec = Algebraic_Problem(res, y0=y0, jac=jac, jacv=jacv, prec_solve=prec_solve, prec_setup=prec_setup, name = 'ORS Example (Preconditioned)') - - #Define the KINSOL solver - alg_solver = KINSOL(alg_mod) - alg_solver_prec = KINSOL(alg_mod_prec) - - #Sets the parameters - def setup_param(solver): - solver.linear_solver = "spgmr" - solver.max_dim_krylov_subspace = 10 - solver.ftol = LIN.norm(res(solver.y0))*1e-9 - solver.max_iter = 300 - solver.verbosity = 10 - solver.globalization_strategy = "none" - - setup_param(alg_solver) - setup_param(alg_solver_prec) - - #Solve orignal system - y = alg_solver.solve() - - #Solve Preconditionined system - y_prec = alg_solver_prec.solve() - - print("Error , in y: ", LIN.norm(y-N.ones(len(y)))) - print("Error (preconditioned), in y: ", LIN.norm(y_prec-N.ones(len(y_prec)))) - - if with_plots: - import pylab as P - P.figure(4) - P.semilogy(alg_solver.get_residual_norm_nonlinear_iterations(), label="Original") - P.semilogy(alg_solver_prec.get_residual_norm_nonlinear_iterations(), label='Preconditioned') - P.xlabel("Number of Iterations") - P.ylabel("Residual Norm") - P.title("Solution Progress") - P.legend() - P.grid() - - P.figure(5) - P.plot(y, label="Original") - P.plot(y_prec, label="Preconditioned") - P.legend() - P.grid() - - P.show() - - #Basic test - for j in range(len(y)): - nose.tools.assert_almost_equal(y[j], 1.0, 4) - - return [alg_mod, alg_mod_prec], [alg_solver, alg_solver_prec] - -if __name__=='__main__': - mod, solv = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import scipy as S +import scipy.linalg as LIN +import scipy.io as IO +import scipy.sparse as SPARSE +import scipy.sparse.linalg as LINSP +import nose +import os +from assimulo.solvers import KINSOL +from assimulo.problem import Algebraic_Problem +import warnings +import scipy.sparse + + +warnings.simplefilter("ignore", scipy.sparse.SparseEfficiencyWarning) + +file_path = os.path.dirname(os.path.realpath(__file__)) + +def run_example(with_plots=True): + r""" + Example to demonstrate the use of the Sundials solver Kinsol with + a user provided Jacobian and a preconditioner. The example is the + 'Problem 4' taken from the book by Saad: + Iterative Methods for Sparse Linear Systems. + """ + #Read the original matrix + A_original = IO.mmread(os.path.join(file_path,"kinsol_ors_matrix.mtx")) + + #Scale the original matrix + A = SPARSE.spdiags(1.0/A_original.diagonal(), 0, len(A_original.diagonal()), len(A_original.diagonal())) * A_original + + #Preconditioning by Symmetric Gauss Seidel + if True: + D = SPARSE.spdiags(A.diagonal(), 0, len(A_original.diagonal()), len(A_original.diagonal())) + Dinv = SPARSE.spdiags(1.0/A.diagonal(), 0, len(A_original.diagonal()), len(A_original.diagonal())) + E = -SPARSE.tril(A,k=-1) + F = -SPARSE.triu(A,k=1) + L = (D-E).dot(Dinv) + U = D-F + Prec = L.dot(U) + + solvePrec = LINSP.factorized(Prec) + + #Create the RHS + b = A.dot(N.ones((A.shape[0],1))) + + #Define the res + def res(x): + return A.dot(x.reshape(len(x),1))-b + + #The Jacobian + def jac(x): + return A.todense() + + #The Jacobian*Vector + def jacv(x,v): + return A.dot(v.reshape(len(v),1)) + + def prec_setup(u,f, uscale, fscale): + pass + + def prec_solve(r): + return solvePrec(r) + + y0 = S.rand(A.shape[0]) + + #Define an Assimulo problem + alg_mod = Algebraic_Problem(res, y0=y0, jac=jac, jacv=jacv, name = 'ORS Example') + alg_mod_prec = Algebraic_Problem(res, y0=y0, jac=jac, jacv=jacv, prec_solve=prec_solve, prec_setup=prec_setup, name = 'ORS Example (Preconditioned)') + + #Define the KINSOL solver + alg_solver = KINSOL(alg_mod) + alg_solver_prec = KINSOL(alg_mod_prec) + + #Sets the parameters + def setup_param(solver): + solver.linear_solver = "spgmr" + solver.max_dim_krylov_subspace = 10 + solver.ftol = LIN.norm(res(solver.y0))*1e-9 + solver.max_iter = 300 + solver.verbosity = 10 + solver.globalization_strategy = "none" + + setup_param(alg_solver) + setup_param(alg_solver_prec) + + #Solve orignal system + y = alg_solver.solve() + + #Solve Preconditionined system + y_prec = alg_solver_prec.solve() + + print("Error , in y: ", LIN.norm(y-N.ones(len(y)))) + print("Error (preconditioned), in y: ", LIN.norm(y_prec-N.ones(len(y_prec)))) + + if with_plots: + import pylab as P + P.figure(4) + P.semilogy(alg_solver.get_residual_norm_nonlinear_iterations(), label="Original") + P.semilogy(alg_solver_prec.get_residual_norm_nonlinear_iterations(), label='Preconditioned') + P.xlabel("Number of Iterations") + P.ylabel("Residual Norm") + P.title("Solution Progress") + P.legend() + P.grid() + + P.figure(5) + P.plot(y, label="Original") + P.plot(y_prec, label="Preconditioned") + P.legend() + P.grid() + + P.show() + + #Basic test + for j in range(len(y)): + nose.tools.assert_almost_equal(y[j], 1.0, 4) + + return [alg_mod, alg_mod_prec], [alg_solver, alg_solver_prec] + +if __name__=='__main__': + mod, solv = run_example() + diff --git a/examples/kinsol_with_jac.py b/examples/kinsol_with_jac.py index 31c19286..d61ce76d 100644 --- a/examples/kinsol_with_jac.py +++ b/examples/kinsol_with_jac.py @@ -1,61 +1,61 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import KINSOL -from assimulo.problem import Algebraic_Problem - -def run_example(with_plots=True): - r""" - Example to demonstrate the use of the Sundials solver Kinsol with - a user provided Jacobian. - - on return: - - - :dfn:`alg_mod` problem instance - - - :dfn:`alg_solver` solver instance - - """ - #Define the res - def res(y): - r1 = 2*y[0]+3*y[1]-6 - r2 = 4*y[0]+9*y[1]-15 - return N.array([r1,r2]) - - def jac(y): - return N.array([[2.,3.],[4.,9.]]) - - #Define an Assimulo problem - alg_mod = Algebraic_Problem(res, y0=[0,0], jac=jac, name='KINSOL example with Jac') - - #Define the KINSOL solver - alg_solver = KINSOL(alg_mod) - - #Solve - y = alg_solver.solve() - - #Basic test - nose.tools.assert_almost_equal(y[0], 1.5, 5) - nose.tools.assert_almost_equal(y[1], 1.0, 5) - - return alg_mod, alg_solver - -if __name__=='__main__': - mod, solv = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import KINSOL +from assimulo.problem import Algebraic_Problem + +def run_example(with_plots=True): + r""" + Example to demonstrate the use of the Sundials solver Kinsol with + a user provided Jacobian. + + on return: + + - :dfn:`alg_mod` problem instance + + - :dfn:`alg_solver` solver instance + + """ + #Define the res + def res(y): + r1 = 2*y[0]+3*y[1]-6 + r2 = 4*y[0]+9*y[1]-15 + return N.array([r1,r2]) + + def jac(y): + return N.array([[2.,3.],[4.,9.]]) + + #Define an Assimulo problem + alg_mod = Algebraic_Problem(res, y0=[0,0], jac=jac, name='KINSOL example with Jac') + + #Define the KINSOL solver + alg_solver = KINSOL(alg_mod) + + #Solve + y = alg_solver.solve() + + #Basic test + nose.tools.assert_almost_equal(y[0], 1.5, 5) + nose.tools.assert_almost_equal(y[1], 1.0, 5) + + return alg_mod, alg_solver + +if __name__=='__main__': + mod, solv = run_example() + diff --git a/examples/lsodar_bouncing_ball.py b/examples/lsodar_bouncing_ball.py index 2f794ec5..d1e4ed1e 100644 --- a/examples/lsodar_bouncing_ball.py +++ b/examples/lsodar_bouncing_ball.py @@ -1,160 +1,160 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2014 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import LSODAR -from assimulo.problem import Explicit_Problem -import sys -import os - -""" -The bouncing ball example for LSODAR - -""" - -#Extend Assimulos problem definition -class Extended_Problem(Explicit_Problem): - - #Sets the initial conditons directly into the problem - y0 = [2.0, 0] # position and (downward) velocity - sw0=[True,False] - - g = -9.81 # gravitational constant - - #The right-hand-side function (rhs) - - def rhs(self,t,y,sw): - """ - This is our function we are trying to simulate. - """ - yd_0 = y[1] - yd_1 = self.g - return N.array([yd_0,yd_1]) - - #Sets a name to our function - name = 'Bouncing Ball Problem' - - #The event function - def state_events(self,t,y,sw): - """ - This is our function that keeps track of our events. When the sign - of any of the events has changed, we have an event. - """ - event_0 = y[0] if sw[0] else 5 # hits the ground - event_1 = y[1] if sw[1] else 5 # velocity changes sign at topmost point - - return N.array([event_0,event_1]) - - - #Responsible for handling the events. - def handle_event(self, solver, event_info): - """ - Event handling. This functions is called when Assimulo finds an event as - specified by the event functions. - """ - event_info = event_info[0] #We only look at the state events information. - if event_info[0] !=0: - solver.sw[0] = False - solver.sw[1] = True - solver.y[1] = - 0.88*solver.y[1] - else: - solver.sw[0] = True - solver.sw[1] = False - - def initialize(self, solver): - solver.h_sol=[] - solver.nq_sol=[] - - def handle_result(self, solver, t, y): - Explicit_Problem.handle_result(self, solver, t, y) - # Extra output for algorithm analysis - if solver.report_continuously: - h, nq = solver.get_algorithm_data() - solver.h_sol.extend([h]) - solver.nq_sol.extend([nq]) - -def run_example(with_plots=True): - """ - Bouncing ball example to demonstrate LSODAR's - discontinuity handling. - - Also a way to use :program:`problem.initialize` and :program:`problem.handle_result` - in order to provide extra information is demonstrated. - - The governing differential equation is - - .. math:: - - \\dot y_1 &= y_2\\\\ - \\dot y_2 &= -9.81 - - and the switching functions are - - .. math:: - - \\mathrm{event}_0 &= y_1 \\;\\;\\;\\text{ if } \\;\\;\\;\\mathrm{sw}_0 = 1\\\\ - \\mathrm{event}_1 &= y_2 \\;\\;\\;\\text{ if }\\;\\;\\; \\mathrm{sw}_1 = 1 - - otherwise the events are deactivated by setting the respective value to something different from 0. - - - The event handling sets - - :math:`y_1 = - 0.88 y_1` and :math:`\\mathrm{sw}_1 = 1` if the first event triggers - and :math:`\\mathrm{sw}_1 = 0` if the second event triggers. - - """ - #Create an instance of the problem - exp_mod = Extended_Problem() #Create the problem - - exp_sim = LSODAR(exp_mod) #Create the solver - exp_sim.atol=1.e-8 - exp_sim.report_continuously = True - - exp_sim.verbosity = 30 - - - #Simulate - t, y = exp_sim.simulate(10.0) #Simulate 10 seconds - - #Plot - if with_plots: - import pylab as P - P.subplot(221) - P.plot(t,y) - P.title('LSODAR Bouncing ball (one step mode)') - P.ylabel('States: $y$ and $\dot y$') - P.subplot(223) - P.plot(exp_sim.t_sol,exp_sim.h_sol) - P.title('LSODAR step size plot') - P.xlabel('Time') - P.ylabel('Sepsize') - P.subplot(224) - P.plot(exp_sim.t_sol,exp_sim.nq_sol) - P.title('LSODAR order plot') - P.xlabel('Time') - P.ylabel('Order') - P.suptitle(exp_mod.name) - P.show() - return exp_mod, exp_sim - -if __name__=="__main__": - mod,sim = run_example() - - - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2014 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import LSODAR +from assimulo.problem import Explicit_Problem +import sys +import os + +""" +The bouncing ball example for LSODAR + +""" + +#Extend Assimulos problem definition +class Extended_Problem(Explicit_Problem): + + #Sets the initial conditons directly into the problem + y0 = [2.0, 0] # position and (downward) velocity + sw0=[True,False] + + g = -9.81 # gravitational constant + + #The right-hand-side function (rhs) + + def rhs(self,t,y,sw): + """ + This is our function we are trying to simulate. + """ + yd_0 = y[1] + yd_1 = self.g + return N.array([yd_0,yd_1]) + + #Sets a name to our function + name = 'Bouncing Ball Problem' + + #The event function + def state_events(self,t,y,sw): + """ + This is our function that keeps track of our events. When the sign + of any of the events has changed, we have an event. + """ + event_0 = y[0] if sw[0] else 5 # hits the ground + event_1 = y[1] if sw[1] else 5 # velocity changes sign at topmost point + + return N.array([event_0,event_1]) + + + #Responsible for handling the events. + def handle_event(self, solver, event_info): + """ + Event handling. This functions is called when Assimulo finds an event as + specified by the event functions. + """ + event_info = event_info[0] #We only look at the state events information. + if event_info[0] !=0: + solver.sw[0] = False + solver.sw[1] = True + solver.y[1] = - 0.88*solver.y[1] + else: + solver.sw[0] = True + solver.sw[1] = False + + def initialize(self, solver): + solver.h_sol=[] + solver.nq_sol=[] + + def handle_result(self, solver, t, y): + Explicit_Problem.handle_result(self, solver, t, y) + # Extra output for algorithm analysis + if solver.report_continuously: + h, nq = solver.get_algorithm_data() + solver.h_sol.extend([h]) + solver.nq_sol.extend([nq]) + +def run_example(with_plots=True): + """ + Bouncing ball example to demonstrate LSODAR's + discontinuity handling. + + Also a way to use :program:`problem.initialize` and :program:`problem.handle_result` + in order to provide extra information is demonstrated. + + The governing differential equation is + + .. math:: + + \\dot y_1 &= y_2\\\\ + \\dot y_2 &= -9.81 + + and the switching functions are + + .. math:: + + \\mathrm{event}_0 &= y_1 \\;\\;\\;\\text{ if } \\;\\;\\;\\mathrm{sw}_0 = 1\\\\ + \\mathrm{event}_1 &= y_2 \\;\\;\\;\\text{ if }\\;\\;\\; \\mathrm{sw}_1 = 1 + + otherwise the events are deactivated by setting the respective value to something different from 0. + + + The event handling sets + + :math:`y_1 = - 0.88 y_1` and :math:`\\mathrm{sw}_1 = 1` if the first event triggers + and :math:`\\mathrm{sw}_1 = 0` if the second event triggers. + + """ + #Create an instance of the problem + exp_mod = Extended_Problem() #Create the problem + + exp_sim = LSODAR(exp_mod) #Create the solver + exp_sim.atol=1.e-8 + exp_sim.report_continuously = True + + exp_sim.verbosity = 30 + + + #Simulate + t, y = exp_sim.simulate(10.0) #Simulate 10 seconds + + #Plot + if with_plots: + import pylab as P + P.subplot(221) + P.plot(t,y) + P.title('LSODAR Bouncing ball (one step mode)') + P.ylabel('States: $y$ and $\dot y$') + P.subplot(223) + P.plot(exp_sim.t_sol,exp_sim.h_sol) + P.title('LSODAR step size plot') + P.xlabel('Time') + P.ylabel('Sepsize') + P.subplot(224) + P.plot(exp_sim.t_sol,exp_sim.nq_sol) + P.title('LSODAR order plot') + P.xlabel('Time') + P.ylabel('Order') + P.suptitle(exp_mod.name) + P.show() + return exp_mod, exp_sim + +if __name__=="__main__": + mod,sim = run_example() + + + diff --git a/examples/lsodar_vanderpol.py b/examples/lsodar_vanderpol.py index 8bd02942..22728927 100644 --- a/examples/lsodar_vanderpol.py +++ b/examples/lsodar_vanderpol.py @@ -1,84 +1,84 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import LSODAR -from assimulo.problem import Explicit_Problem - -def run_example(with_plots=True): - r""" - Example for the use of LSODAR method to solve - Van der Pol's equation - - .. math:: - - \dot y_1 &= y_2 \\ - \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) - - with :math:`\mu=\frac{1}{5} 10^3`. - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - - """ - - #Define the rhs - def f(t,y): - eps = 1.e-6 - my = 1./eps - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - return N.array([yd_0,yd_1]) - - y0 = [2.0,-0.6] #Initial conditions - - #Define an Assimulo problem - exp_mod = Explicit_Problem(f,y0, name = "LSODAR: Van der Pol's equation") - - #Define an explicit solver - exp_sim = LSODAR(exp_mod) #Create a Radau5 solver - - #Sets the parameters - exp_sim.atol = 1e-4 #Default 1e-6 - exp_sim.rtol = 1e-4 #Default 1e-6 - - #Simulate - t, y = exp_sim.simulate(2.) #Simulate 2 seconds - - #Plot - if with_plots: - import pylab as P - P.plot(t,y[:,0], marker='o') - P.title(exp_mod.name) - P.ylabel("State: $y_1$") - P.xlabel("Time") - P.show() - - #Basic test - x1 = y[:,0] - assert N.abs(x1[-1]-1.706168035) < 1e-3 #For test purpose - - return exp_mod, exp_sim - -if __name__=='__main__': - mod,sim = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import LSODAR +from assimulo.problem import Explicit_Problem + +def run_example(with_plots=True): + r""" + Example for the use of LSODAR method to solve + Van der Pol's equation + + .. math:: + + \dot y_1 &= y_2 \\ + \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) + + with :math:`\mu=\frac{1}{5} 10^3`. + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + + """ + + #Define the rhs + def f(t,y): + eps = 1.e-6 + my = 1./eps + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + return N.array([yd_0,yd_1]) + + y0 = [2.0,-0.6] #Initial conditions + + #Define an Assimulo problem + exp_mod = Explicit_Problem(f,y0, name = "LSODAR: Van der Pol's equation") + + #Define an explicit solver + exp_sim = LSODAR(exp_mod) #Create a Radau5 solver + + #Sets the parameters + exp_sim.atol = 1e-4 #Default 1e-6 + exp_sim.rtol = 1e-4 #Default 1e-6 + + #Simulate + t, y = exp_sim.simulate(2.) #Simulate 2 seconds + + #Plot + if with_plots: + import pylab as P + P.plot(t,y[:,0], marker='o') + P.title(exp_mod.name) + P.ylabel("State: $y_1$") + P.xlabel("Time") + P.show() + + #Basic test + x1 = y[:,0] + assert N.abs(x1[-1]-1.706168035) < 1e-3 #For test purpose + + return exp_mod, exp_sim + +if __name__=='__main__': + mod,sim = run_example() + diff --git a/examples/lsodar_with_disc.py b/examples/lsodar_with_disc.py index d39b4194..5f87a279 100644 --- a/examples/lsodar_with_disc.py +++ b/examples/lsodar_with_disc.py @@ -1,168 +1,168 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import LSODAR -from assimulo.problem import Explicit_Problem - -""" -An example with event iteration and with three switches. - -t=0 , [False, True, True] (Start of simulation) -t=1 (1) , [False, True, False] (Found a root at t=1) -t=1 (2) , [False, False, False] (Second iteration at t=1) -t=1 (3) , [True, False, False] (Third iteration at t=1) -t=10 , [True, False, False] (End of simulation) - -""" - -#Extend Assimulos problem definition -class Extended_Problem(Explicit_Problem): - - #Sets the initial conditons directly into the problem - y0 = [0.0, -1.0, 0.0] - sw0 = [False,True,True] - - #The right-hand-side function (rhs) - def rhs(self,t,y,sw): - """ - This is our function we are trying to simulate. During simulation - the parameter sw should be fixed so that our function is continuous - over the interval. The parameters sw should only be changed when the - integrator has stopped. - """ - yd_0 = (1.0 if sw[0] else -1.0) - yd_1 = 0.0 - yd_2 = 0.0 - - return N.array([yd_0,yd_1,yd_2]) - - #Sets a name to our function - name = 'ODE with discontinuities and a function with consistency problem' - - #The event function - def state_events(self,t,y,sw): - """ - This is our function that keeps track of our events. When the sign - of any of the events has changed, we have an event. - """ - event_0 = y[1] - 1.0 - event_1 = -y[2] + 1.0 - event_2 = -t + 1.0 - - return N.array([event_0,event_1,event_2]) - - - #Responsible for handling the events. - def handle_event(self, solver, event_info): - """ - Event handling. This functions is called when Assimulo finds an event as - specified by the event functions. - """ - event_info = event_info[0] #We only look at the state events information. - while True: #Event Iteration - self.event_switch(solver, event_info) #Turns the switches - - b_mode = self.state_events(solver.t, solver.y, solver.sw) - self.init_mode(solver) #Pass in the solver to the problem specified init_mode - a_mode = self.state_events(solver.t, solver.y, solver.sw) - - event_info = self.check_eIter(b_mode, a_mode) - - if not True in event_info: #Breaks the iteration loop - break - - #Helper function for handle_event - def event_switch(self, solver, event_info): - """ - Turns the switches. - """ - for i in range(len(event_info)): #Loop across all event functions - if event_info[i] != 0: - solver.sw[i] = not solver.sw[i] #Turn the switch - - #Helper function for handle_event - def check_eIter(self, before, after): - """ - Helper function for handle_event to determine if we have event - iteration. - - Input: Values of the event indicator functions (state_events) - before and after we have changed mode of operations. - """ - - eIter = [False]*len(before) - - for i in range(len(before)): - if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): - eIter[i] = True - - return eIter - - def init_mode(self, solver): - """ - Initialize the ODE with the new conditions. - """ - solver.y[1] = (-1.0 if solver.sw[1] else 3.0) - solver.y[2] = (0.0 if solver.sw[2] else 2.0) - - - -def run_example(with_plots=True): - r""" - Example of the use of Euler's method for a differential equation - with a discontinuity (state event) and the need for an event iteration. - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - """ - #Create an instance of the problem - exp_mod = Extended_Problem() #Create the problem - - exp_sim = LSODAR(exp_mod) #Create the solver - - exp_sim.verbosity = 0 - exp_sim.continuous_output = True - - #Simulate - t, y = exp_sim.simulate(10.0,100) #Simulate 10 seconds with 1000 communications points - - #Plot - if with_plots: - import pylab as P - P.plot(t,y) - P.title("Solution of a differential equation with discontinuities") - P.ylabel('States') - P.xlabel('Time') - P.show() - - return exp_mod, exp_sim - - #Basic test - nose.tools.assert_almost_equal(y[-1][0],8.0) - nose.tools.assert_almost_equal(y[-1][1],3.0) - nose.tools.assert_almost_equal(y[-1][2],2.0) - -if __name__=="__main__": - mod,sim = run_example() - - - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import LSODAR +from assimulo.problem import Explicit_Problem + +""" +An example with event iteration and with three switches. + +t=0 , [False, True, True] (Start of simulation) +t=1 (1) , [False, True, False] (Found a root at t=1) +t=1 (2) , [False, False, False] (Second iteration at t=1) +t=1 (3) , [True, False, False] (Third iteration at t=1) +t=10 , [True, False, False] (End of simulation) + +""" + +#Extend Assimulos problem definition +class Extended_Problem(Explicit_Problem): + + #Sets the initial conditons directly into the problem + y0 = [0.0, -1.0, 0.0] + sw0 = [False,True,True] + + #The right-hand-side function (rhs) + def rhs(self,t,y,sw): + """ + This is our function we are trying to simulate. During simulation + the parameter sw should be fixed so that our function is continuous + over the interval. The parameters sw should only be changed when the + integrator has stopped. + """ + yd_0 = (1.0 if sw[0] else -1.0) + yd_1 = 0.0 + yd_2 = 0.0 + + return N.array([yd_0,yd_1,yd_2]) + + #Sets a name to our function + name = 'ODE with discontinuities and a function with consistency problem' + + #The event function + def state_events(self,t,y,sw): + """ + This is our function that keeps track of our events. When the sign + of any of the events has changed, we have an event. + """ + event_0 = y[1] - 1.0 + event_1 = -y[2] + 1.0 + event_2 = -t + 1.0 + + return N.array([event_0,event_1,event_2]) + + + #Responsible for handling the events. + def handle_event(self, solver, event_info): + """ + Event handling. This functions is called when Assimulo finds an event as + specified by the event functions. + """ + event_info = event_info[0] #We only look at the state events information. + while True: #Event Iteration + self.event_switch(solver, event_info) #Turns the switches + + b_mode = self.state_events(solver.t, solver.y, solver.sw) + self.init_mode(solver) #Pass in the solver to the problem specified init_mode + a_mode = self.state_events(solver.t, solver.y, solver.sw) + + event_info = self.check_eIter(b_mode, a_mode) + + if not True in event_info: #Breaks the iteration loop + break + + #Helper function for handle_event + def event_switch(self, solver, event_info): + """ + Turns the switches. + """ + for i in range(len(event_info)): #Loop across all event functions + if event_info[i] != 0: + solver.sw[i] = not solver.sw[i] #Turn the switch + + #Helper function for handle_event + def check_eIter(self, before, after): + """ + Helper function for handle_event to determine if we have event + iteration. + + Input: Values of the event indicator functions (state_events) + before and after we have changed mode of operations. + """ + + eIter = [False]*len(before) + + for i in range(len(before)): + if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): + eIter[i] = True + + return eIter + + def init_mode(self, solver): + """ + Initialize the ODE with the new conditions. + """ + solver.y[1] = (-1.0 if solver.sw[1] else 3.0) + solver.y[2] = (0.0 if solver.sw[2] else 2.0) + + + +def run_example(with_plots=True): + r""" + Example of the use of Euler's method for a differential equation + with a discontinuity (state event) and the need for an event iteration. + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + """ + #Create an instance of the problem + exp_mod = Extended_Problem() #Create the problem + + exp_sim = LSODAR(exp_mod) #Create the solver + + exp_sim.verbosity = 0 + exp_sim.continuous_output = True + + #Simulate + t, y = exp_sim.simulate(10.0,100) #Simulate 10 seconds with 1000 communications points + + #Plot + if with_plots: + import pylab as P + P.plot(t,y) + P.title("Solution of a differential equation with discontinuities") + P.ylabel('States') + P.xlabel('Time') + P.show() + + return exp_mod, exp_sim + + #Basic test + nose.tools.assert_almost_equal(y[-1][0],8.0) + nose.tools.assert_almost_equal(y[-1][1],3.0) + nose.tools.assert_almost_equal(y[-1][2],2.0) + +if __name__=="__main__": + mod,sim = run_example() + + + diff --git a/examples/radau5dae_time_events.py b/examples/radau5dae_time_events.py index 77a2bfab..13e4a5e9 100644 --- a/examples/radau5dae_time_events.py +++ b/examples/radau5dae_time_events.py @@ -1,81 +1,81 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import Radau5DAE -from assimulo.problem import Implicit_Problem - - -class VanDerPolProblem(Implicit_Problem): - def __init__(self, **kargs): - Implicit_Problem.__init__(self, **kargs) - self.name = 'Van der Pol (implicit) with time events' - self.my = 1.0/1e-6 - - #Define the residual - def res(self, t,y,yd): - yd_0 = y[1] - yd_1 = self.my*((1.-y[0]**2)*y[1]-y[0]) - - res_0 = yd[0]-yd_0 - res_1 = yd[1]-yd_1 - return N.array([res_0,res_1]) - - def time_events(self, t,y,yd,sw): - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - return tnext - - def handle_event(self, solver, event_info): - self.my *= 1e-1 - -def run_example(with_plots=True): - - y0 = [2.0,-0.6] #Initial conditions - yd0 = [-.6,-200000.] - - #Define an extended Assimulo problem - imp_mod = VanDerPolProblem(y0=y0,yd0=yd0) - - #Define an explicit solver - imp_sim = Radau5DAE(imp_mod) #Create a Radau5 solver - - #Simulate - t, y, yd = imp_sim.simulate(8.) #Simulate 8 seconds - - #Plot - if with_plots: - import pylab as P - P.plot(t,y[:,0], marker='o') - P.xlabel('Time') - P.ylabel('State') - P.title(imp_mod.name) - P.show() - - #Basic test - x1 = y[:,0] - assert N.abs(x1[-1]-1.14330840983) < 1e-3 #For test purpose - return imp_mod, imp_sim -if __name__=='__main__': - mod,sim = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import Radau5DAE +from assimulo.problem import Implicit_Problem + + +class VanDerPolProblem(Implicit_Problem): + def __init__(self, **kargs): + Implicit_Problem.__init__(self, **kargs) + self.name = 'Van der Pol (implicit) with time events' + self.my = 1.0/1e-6 + + #Define the residual + def res(self, t,y,yd): + yd_0 = y[1] + yd_1 = self.my*((1.-y[0]**2)*y[1]-y[0]) + + res_0 = yd[0]-yd_0 + res_1 = yd[1]-yd_1 + return N.array([res_0,res_1]) + + def time_events(self, t,y,yd,sw): + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + return tnext + + def handle_event(self, solver, event_info): + self.my *= 1e-1 + +def run_example(with_plots=True): + + y0 = [2.0,-0.6] #Initial conditions + yd0 = [-.6,-200000.] + + #Define an extended Assimulo problem + imp_mod = VanDerPolProblem(y0=y0,yd0=yd0) + + #Define an explicit solver + imp_sim = Radau5DAE(imp_mod) #Create a Radau5 solver + + #Simulate + t, y, yd = imp_sim.simulate(8.) #Simulate 8 seconds + + #Plot + if with_plots: + import pylab as P + P.plot(t,y[:,0], marker='o') + P.xlabel('Time') + P.ylabel('State') + P.title(imp_mod.name) + P.show() + + #Basic test + x1 = y[:,0] + assert N.abs(x1[-1]-1.14330840983) < 1e-3 #For test purpose + return imp_mod, imp_sim +if __name__=='__main__': + mod,sim = run_example() + diff --git a/examples/radau5dae_vanderpol.py b/examples/radau5dae_vanderpol.py index d9573e25..39e14163 100644 --- a/examples/radau5dae_vanderpol.py +++ b/examples/radau5dae_vanderpol.py @@ -1,94 +1,94 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import Radau5DAE -from assimulo.problem import Implicit_Problem - -def run_example(with_plots=True): - r""" - Example for the use of Radau5DAE to solve - Van der Pol's equation - - .. math:: - - \dot y_1 &= y_2 \\ - \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) - - with :math:`\mu= 10^6`. - - on return: - - - :dfn:`imp_mod` problem instance - - - :dfn:`imp_sim` solver instance - - """ - - #Define the residual - def f(t,y,yd): - eps = 1.e-6 - my = 1./eps - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - res_0 = yd[0]-yd_0 - res_1 = yd[1]-yd_1 - - return N.array([res_0,res_1]) - - y0 = [2.0,-0.6] #Initial conditions - yd0 = [-.6,-200000.] - - #Define an Assimulo problem - imp_mod = Implicit_Problem(f,y0,yd0) - imp_mod.name = 'Van der Pol (implicit)' - - #Define an explicit solver - imp_sim = Radau5DAE(imp_mod) #Create a Radau5 solver - - #Sets the parameters - imp_sim.atol = 1e-4 #Default 1e-6 - imp_sim.rtol = 1e-4 #Default 1e-6 - imp_sim.inith = 1.e-4 #Initial step-size - - #Simulate - t, y, yd = imp_sim.simulate(2.) #Simulate 2 seconds - - #Plot - if with_plots: - import pylab as P - P.subplot(211) - P.plot(t,y[:,0])#, marker='o') - P.xlabel('Time') - P.ylabel('State') - P.subplot(212) - P.plot(t,yd[:,0]*1.e-5)#, marker='o') - P.xlabel('Time') - P.ylabel('State derivatives scaled with $10^{-5}$') - P.suptitle(imp_mod.name) - P.show() - - #Basic test - x1 = y[:,0] - assert N.abs(x1[-1]-1.706168035) < 1e-3 #For test purpose - return imp_mod, imp_sim - -if __name__=='__main__': - mod,sim = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import Radau5DAE +from assimulo.problem import Implicit_Problem + +def run_example(with_plots=True): + r""" + Example for the use of Radau5DAE to solve + Van der Pol's equation + + .. math:: + + \dot y_1 &= y_2 \\ + \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) + + with :math:`\mu= 10^6`. + + on return: + + - :dfn:`imp_mod` problem instance + + - :dfn:`imp_sim` solver instance + + """ + + #Define the residual + def f(t,y,yd): + eps = 1.e-6 + my = 1./eps + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + res_0 = yd[0]-yd_0 + res_1 = yd[1]-yd_1 + + return N.array([res_0,res_1]) + + y0 = [2.0,-0.6] #Initial conditions + yd0 = [-.6,-200000.] + + #Define an Assimulo problem + imp_mod = Implicit_Problem(f,y0,yd0) + imp_mod.name = 'Van der Pol (implicit)' + + #Define an explicit solver + imp_sim = Radau5DAE(imp_mod) #Create a Radau5 solver + + #Sets the parameters + imp_sim.atol = 1e-4 #Default 1e-6 + imp_sim.rtol = 1e-4 #Default 1e-6 + imp_sim.inith = 1.e-4 #Initial step-size + + #Simulate + t, y, yd = imp_sim.simulate(2.) #Simulate 2 seconds + + #Plot + if with_plots: + import pylab as P + P.subplot(211) + P.plot(t,y[:,0])#, marker='o') + P.xlabel('Time') + P.ylabel('State') + P.subplot(212) + P.plot(t,yd[:,0]*1.e-5)#, marker='o') + P.xlabel('Time') + P.ylabel('State derivatives scaled with $10^{-5}$') + P.suptitle(imp_mod.name) + P.show() + + #Basic test + x1 = y[:,0] + assert N.abs(x1[-1]-1.706168035) < 1e-3 #For test purpose + return imp_mod, imp_sim + +if __name__=='__main__': + mod,sim = run_example() + diff --git a/examples/radau5ode_vanderpol.py b/examples/radau5ode_vanderpol.py index 84e6960f..26bc87a2 100644 --- a/examples/radau5ode_vanderpol.py +++ b/examples/radau5ode_vanderpol.py @@ -1,85 +1,85 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import Radau5ODE -from assimulo.problem import Explicit_Problem - -def run_example(with_plots=True): - r""" - Example for the use of the implicit Euler method to solve - Van der Pol's equation - - .. math:: - - \dot y_1 &= y_2 \\ - \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) - - with :math:`\mu= 10^6`. - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - - """ - #Define the rhs - def f(t,y): - eps = 1.e-6 - my = 1./eps - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - return N.array([yd_0,yd_1]) - - y0 = [2.0,-0.6] #Initial conditions - - #Define an Assimulo problem - exp_mod = Explicit_Problem(f,y0) - exp_mod.name = 'Van der Pol (explicit)' - - #Define an explicit solver - exp_sim = Radau5ODE(exp_mod) #Create a Radau5 solver - - #Sets the parameters - exp_sim.atol = 1e-4 #Default 1e-6 - exp_sim.rtol = 1e-4 #Default 1e-6 - exp_sim.inith = 1.e-4 #Initial step-size - - #Simulate - t, y = exp_sim.simulate(2.) #Simulate 2 seconds - - #Plot - if with_plots: - import pylab as P - P.plot(t,y[:,0])#, marker='o') - P.xlabel('Time') - P.ylabel('State') - P.title(exp_mod.name) - P.show() - - #Basic test - x1 = y[:,0] - assert N.abs(x1[-1]-1.706168035) < 1e-3 #For test purpose - - return exp_mod, exp_sim - -if __name__=='__main__': - mod,sim = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import Radau5ODE +from assimulo.problem import Explicit_Problem + +def run_example(with_plots=True): + r""" + Example for the use of the implicit Euler method to solve + Van der Pol's equation + + .. math:: + + \dot y_1 &= y_2 \\ + \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) + + with :math:`\mu= 10^6`. + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + + """ + #Define the rhs + def f(t,y): + eps = 1.e-6 + my = 1./eps + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + return N.array([yd_0,yd_1]) + + y0 = [2.0,-0.6] #Initial conditions + + #Define an Assimulo problem + exp_mod = Explicit_Problem(f,y0) + exp_mod.name = 'Van der Pol (explicit)' + + #Define an explicit solver + exp_sim = Radau5ODE(exp_mod) #Create a Radau5 solver + + #Sets the parameters + exp_sim.atol = 1e-4 #Default 1e-6 + exp_sim.rtol = 1e-4 #Default 1e-6 + exp_sim.inith = 1.e-4 #Initial step-size + + #Simulate + t, y = exp_sim.simulate(2.) #Simulate 2 seconds + + #Plot + if with_plots: + import pylab as P + P.plot(t,y[:,0])#, marker='o') + P.xlabel('Time') + P.ylabel('State') + P.title(exp_mod.name) + P.show() + + #Basic test + x1 = y[:,0] + assert N.abs(x1[-1]-1.706168035) < 1e-3 #For test purpose + + return exp_mod, exp_sim + +if __name__=='__main__': + mod,sim = run_example() + diff --git a/examples/radau5ode_with_disc.py b/examples/radau5ode_with_disc.py index fd33ffb9..0e4da63a 100644 --- a/examples/radau5ode_with_disc.py +++ b/examples/radau5ode_with_disc.py @@ -1,158 +1,158 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import Radau5ODE -from assimulo.problem import Explicit_Problem - -""" -An example with event iteration and with three switches. - -t=0 , [False, True, True] (Start of simulation) -t=1 (1) , [False, True, False] (Found a root at t=1) -t=1 (2) , [False, False, False] (Second iteration at t=1) -t=1 (3) , [True, False, False] (Third iteration at t=1) -t=10 , [True, False, False] (End of simulation) - -""" - -#Extend Assimulos problem definition -class Extended_Problem(Explicit_Problem): - - #Sets the initial conditons directly into the problem - y0 = [0.0, -1.0, 0.0] - sw0 = [False,True,True] - - #The right-hand-side function (rhs) - def rhs(self,t,y,sw): - """ - This is our function we are trying to simulate. During simulation - the parameter sw should be fixed so that our function is continuous - over the interval. The parameters sw should only be changed when the - integrator has stopped. - """ - yd_0 = (1.0 if sw[0] else -1.0) - yd_1 = 0.0 - yd_2 = 0.0 - - return N.array([yd_0,yd_1,yd_2]) - - #Sets a name to our function - name = 'ODE with discontinuities and a function with consistency problem' - - #The event function - def state_events(self,t,y,sw): - """ - This is our function that keeps track of our events. When the sign - of any of the events has changed, we have an event. - """ - event_0 = y[1] - 1.0 - event_1 = -y[2] + 1.0 - event_2 = -t + 1.0 - - return N.array([event_0,event_1,event_2]) - - - #Responsible for handling the events. - def handle_event(self, solver, event_info): - """ - Event handling. This functions is called when Assimulo finds an event as - specified by the event functions. - """ - event_info = event_info[0] #We only look at the state events information. - while True: #Event Iteration - self.event_switch(solver, event_info) #Turns the switches - - b_mode = self.state_events(solver.t, solver.y, solver.sw) - self.init_mode(solver) #Pass in the solver to the problem specified init_mode - a_mode = self.state_events(solver.t, solver.y, solver.sw) - - event_info = self.check_eIter(b_mode, a_mode) - - if not True in event_info: #Breaks the iteration loop - break - - #Helper function for handle_event - def event_switch(self, solver, event_info): - """ - Turns the switches. - """ - for i in range(len(event_info)): #Loop across all event functions - if event_info[i] != 0: - solver.sw[i] = not solver.sw[i] #Turn the switch - - #Helper function for handle_event - def check_eIter(self, before, after): - """ - Helper function for handle_event to determine if we have event - iteration. - - Input: Values of the event indicator functions (state_events) - before and after we have changed mode of operations. - """ - - eIter = [False]*len(before) - - for i in range(len(before)): - if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): - eIter[i] = True - - return eIter - - def init_mode(self, solver): - """ - Initialize the DAE with the new conditions. - """ - solver.y[1] = (-1.0 if solver.sw[1] else 3.0) - solver.y[2] = (0.0 if solver.sw[2] else 2.0) - - - -def run_example(with_plots=True): - #Create an instance of the problem - exp_mod = Extended_Problem() #Create the problem - - exp_sim = Radau5ODE(exp_mod) #Create the solver - - exp_sim.verbosity = 0 - exp_sim.report_continuously = True - - #Simulate - t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points - - #Basic test - nose.tools.assert_almost_equal(y[-1][0],8.0) - nose.tools.assert_almost_equal(y[-1][1],3.0) - nose.tools.assert_almost_equal(y[-1][2],2.0) - - #Plot - if with_plots: - import pylab as P - P.plot(t,y) - P.title("Solution of a differential equation with discontinuities") - P.ylabel('States') - P.xlabel('Time') - P.show() - - return exp_mod, exp_sim - -if __name__=="__main__": - mod,sim = run_example() - - - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import Radau5ODE +from assimulo.problem import Explicit_Problem + +""" +An example with event iteration and with three switches. + +t=0 , [False, True, True] (Start of simulation) +t=1 (1) , [False, True, False] (Found a root at t=1) +t=1 (2) , [False, False, False] (Second iteration at t=1) +t=1 (3) , [True, False, False] (Third iteration at t=1) +t=10 , [True, False, False] (End of simulation) + +""" + +#Extend Assimulos problem definition +class Extended_Problem(Explicit_Problem): + + #Sets the initial conditons directly into the problem + y0 = [0.0, -1.0, 0.0] + sw0 = [False,True,True] + + #The right-hand-side function (rhs) + def rhs(self,t,y,sw): + """ + This is our function we are trying to simulate. During simulation + the parameter sw should be fixed so that our function is continuous + over the interval. The parameters sw should only be changed when the + integrator has stopped. + """ + yd_0 = (1.0 if sw[0] else -1.0) + yd_1 = 0.0 + yd_2 = 0.0 + + return N.array([yd_0,yd_1,yd_2]) + + #Sets a name to our function + name = 'ODE with discontinuities and a function with consistency problem' + + #The event function + def state_events(self,t,y,sw): + """ + This is our function that keeps track of our events. When the sign + of any of the events has changed, we have an event. + """ + event_0 = y[1] - 1.0 + event_1 = -y[2] + 1.0 + event_2 = -t + 1.0 + + return N.array([event_0,event_1,event_2]) + + + #Responsible for handling the events. + def handle_event(self, solver, event_info): + """ + Event handling. This functions is called when Assimulo finds an event as + specified by the event functions. + """ + event_info = event_info[0] #We only look at the state events information. + while True: #Event Iteration + self.event_switch(solver, event_info) #Turns the switches + + b_mode = self.state_events(solver.t, solver.y, solver.sw) + self.init_mode(solver) #Pass in the solver to the problem specified init_mode + a_mode = self.state_events(solver.t, solver.y, solver.sw) + + event_info = self.check_eIter(b_mode, a_mode) + + if not True in event_info: #Breaks the iteration loop + break + + #Helper function for handle_event + def event_switch(self, solver, event_info): + """ + Turns the switches. + """ + for i in range(len(event_info)): #Loop across all event functions + if event_info[i] != 0: + solver.sw[i] = not solver.sw[i] #Turn the switch + + #Helper function for handle_event + def check_eIter(self, before, after): + """ + Helper function for handle_event to determine if we have event + iteration. + + Input: Values of the event indicator functions (state_events) + before and after we have changed mode of operations. + """ + + eIter = [False]*len(before) + + for i in range(len(before)): + if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): + eIter[i] = True + + return eIter + + def init_mode(self, solver): + """ + Initialize the DAE with the new conditions. + """ + solver.y[1] = (-1.0 if solver.sw[1] else 3.0) + solver.y[2] = (0.0 if solver.sw[2] else 2.0) + + + +def run_example(with_plots=True): + #Create an instance of the problem + exp_mod = Extended_Problem() #Create the problem + + exp_sim = Radau5ODE(exp_mod) #Create the solver + + exp_sim.verbosity = 0 + exp_sim.report_continuously = True + + #Simulate + t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points + + #Basic test + nose.tools.assert_almost_equal(y[-1][0],8.0) + nose.tools.assert_almost_equal(y[-1][1],3.0) + nose.tools.assert_almost_equal(y[-1][2],2.0) + + #Plot + if with_plots: + import pylab as P + P.plot(t,y) + P.title("Solution of a differential equation with discontinuities") + P.ylabel('States') + P.xlabel('Time') + P.show() + + return exp_mod, exp_sim + +if __name__=="__main__": + mod,sim = run_example() + + + diff --git a/examples/rodasode_vanderpol.py b/examples/rodasode_vanderpol.py index 1cf4ea22..3d86a7c6 100644 --- a/examples/rodasode_vanderpol.py +++ b/examples/rodasode_vanderpol.py @@ -1,95 +1,95 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import RodasODE -from assimulo.problem import Explicit_Problem - -def run_example(with_plots=True): - r""" - Example for the use of RODAS to solve - Van der Pol's equation - - .. math:: - - \dot y_1 &= y_2 \\ - \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) - - with :math:`\mu=1 10^6`. - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - - """ - - #Define the rhs - def f(t,y): - eps = 1.e-6 - my = 1./eps - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - return N.array([yd_0,yd_1]) - - #Define the jacobian - def jac(t,y): - eps = 1.e-6 - my = 1./eps - j = N.array([[0.0,1.0],[my*((-2.0*y[0])*y[1]-1.0), my*(1.0-y[0]**2)]]) - return j - - y0 = [2.0,-0.6] #Initial conditions - - #Define an Assimulo problem - exp_mod = Explicit_Problem(f,y0, name = 'Van der Pol (explicit)') - exp_mod.jac = jac - - #Define an explicit solver - exp_sim = RodasODE(exp_mod) #Create a Rodas solver - - #Sets the parameters - exp_sim.atol = 1e-4 #Default 1e-6 - exp_sim.rtol = 1e-4 #Default 1e-6 - exp_sim.inith = 1.e-4 #Initial step-size - exp_sim.usejac = True - - #Simulate - t, y = exp_sim.simulate(2.) #Simulate 2 seconds - - #Plot - if with_plots: - import pylab as P - P.plot(t,y[:,0], marker='o') - P.title(exp_mod.name) - P.ylabel("State: $y_1$") - P.xlabel("Time") - P.show() - - - #Basic test - x1 = y[:,0] - assert N.abs(x1[-1]-1.706168035) < 1e-3 #For test purpose - return exp_mod, exp_sim - - -if __name__=='__main__': - mod,sim = run_example() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import RodasODE +from assimulo.problem import Explicit_Problem + +def run_example(with_plots=True): + r""" + Example for the use of RODAS to solve + Van der Pol's equation + + .. math:: + + \dot y_1 &= y_2 \\ + \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) + + with :math:`\mu=1 10^6`. + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + + """ + + #Define the rhs + def f(t,y): + eps = 1.e-6 + my = 1./eps + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + return N.array([yd_0,yd_1]) + + #Define the jacobian + def jac(t,y): + eps = 1.e-6 + my = 1./eps + j = N.array([[0.0,1.0],[my*((-2.0*y[0])*y[1]-1.0), my*(1.0-y[0]**2)]]) + return j + + y0 = [2.0,-0.6] #Initial conditions + + #Define an Assimulo problem + exp_mod = Explicit_Problem(f,y0, name = 'Van der Pol (explicit)') + exp_mod.jac = jac + + #Define an explicit solver + exp_sim = RodasODE(exp_mod) #Create a Rodas solver + + #Sets the parameters + exp_sim.atol = 1e-4 #Default 1e-6 + exp_sim.rtol = 1e-4 #Default 1e-6 + exp_sim.inith = 1.e-4 #Initial step-size + exp_sim.usejac = True + + #Simulate + t, y = exp_sim.simulate(2.) #Simulate 2 seconds + + #Plot + if with_plots: + import pylab as P + P.plot(t,y[:,0], marker='o') + P.title(exp_mod.name) + P.ylabel("State: $y_1$") + P.xlabel("Time") + P.show() + + + #Basic test + x1 = y[:,0] + assert N.abs(x1[-1]-1.706168035) < 1e-3 #For test purpose + return exp_mod, exp_sim + + +if __name__=='__main__': + mod,sim = run_example() + diff --git a/examples/rungekutta34_basic.py b/examples/rungekutta34_basic.py index e7ee5438..f04e160c 100644 --- a/examples/rungekutta34_basic.py +++ b/examples/rungekutta34_basic.py @@ -1,66 +1,66 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import RungeKutta34 -from assimulo.problem import Explicit_Problem - -def run_example(with_plots=True): - r""" - Demonstration of the use of the use of Runge-Kutta 34 by solving the - linear test equation :math:`\dot y = - y` - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - - """ - - #Defines the rhs - def f(t,y): - ydot = -y[0] - return N.array([ydot]) - - #Define an Assimulo problem - exp_mod = Explicit_Problem(f, 4.0, - name = 'RK34 Example: $\dot y = - y$') - - exp_sim = RungeKutta34(exp_mod) #Create a RungeKutta34 solver - exp_sim.inith = 0.1 #Sets the initial step, default = 0.01 - - #Simulate - t, y = exp_sim.simulate(5) #Simulate 5 seconds - - #Basic test - nose.tools.assert_almost_equal(float(y[-1]),0.02695199,5) - - #Plot - if with_plots: - import pylab as P - P.plot(t, y) - P.title(exp_mod.name) - P.ylabel('y') - P.xlabel('Time') - P.show() - - return exp_mod, exp_sim - -if __name__=='__main__': - mod,sim = run_example() +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import RungeKutta34 +from assimulo.problem import Explicit_Problem + +def run_example(with_plots=True): + r""" + Demonstration of the use of the use of Runge-Kutta 34 by solving the + linear test equation :math:`\dot y = - y` + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + + """ + + #Defines the rhs + def f(t,y): + ydot = -y[0] + return N.array([ydot]) + + #Define an Assimulo problem + exp_mod = Explicit_Problem(f, 4.0, + name = 'RK34 Example: $\dot y = - y$') + + exp_sim = RungeKutta34(exp_mod) #Create a RungeKutta34 solver + exp_sim.inith = 0.1 #Sets the initial step, default = 0.01 + + #Simulate + t, y = exp_sim.simulate(5) #Simulate 5 seconds + + #Basic test + nose.tools.assert_almost_equal(float(y[-1]),0.02695199,5) + + #Plot + if with_plots: + import pylab as P + P.plot(t, y) + P.title(exp_mod.name) + P.ylabel('y') + P.xlabel('Time') + P.show() + + return exp_mod, exp_sim + +if __name__=='__main__': + mod,sim = run_example() diff --git a/examples/rungekutta34_with_disc.py b/examples/rungekutta34_with_disc.py index 1b49779e..84753e77 100644 --- a/examples/rungekutta34_with_disc.py +++ b/examples/rungekutta34_with_disc.py @@ -1,158 +1,158 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import RungeKutta34 -from assimulo.problem import Explicit_Problem - -""" -An example with event iteration and with three switches. - -t=0 , [False, True, True] (Start of simulation) -t=1 (1) , [False, True, False] (Found a root at t=1) -t=1 (2) , [False, False, False] (Second iteration at t=1) -t=1 (3) , [True, False, False] (Third iteration at t=1) -t=10 , [True, False, False] (End of simulation) - -""" - -#Extend Assimulos problem definition -class Extended_Problem(Explicit_Problem): - - #Sets the initial conditons directly into the problem - y0 = [0.0, -1.0, 0.0] - sw0 = [False,True,True] - - #The right-hand-side function (rhs) - def rhs(self,t,y,sw): - """ - This is our function we are trying to simulate. During simulation - the parameter sw should be fixed so that our function is continuous - over the interval. The parameters sw should only be changed when the - integrator has stopped. - """ - yd_0 = (1.0 if sw[0] else -1.0) - yd_1 = 0.0 - yd_2 = 0.0 - - return N.array([yd_0,yd_1,yd_2]) - - #Sets a name to our function - name = 'ODE with discontinuities and a function with consistency problem' - - #The event function - def state_events(self,t,y,sw): - """ - This is our function that keeps track of our events. When the sign - of any of the events has changed, we have an event. - """ - event_0 = y[1] - 1.0 - event_1 = -y[2] + 1.0 - event_2 = -t + 1.0 - - return N.array([event_0,event_1,event_2]) - - - #Responsible for handling the events. - def handle_event(self, solver, event_info): - """ - Event handling. This functions is called when Assimulo finds an event as - specified by the event functions. - """ - event_info = event_info[0] #We only look at the state events information. - while True: #Event Iteration - self.event_switch(solver, event_info) #Turns the switches - - b_mode = self.state_events(solver.t, solver.y, solver.sw) - self.init_mode(solver) #Pass in the solver to the problem specified init_mode - a_mode = self.state_events(solver.t, solver.y, solver.sw) - - event_info = self.check_eIter(b_mode, a_mode) - - if not True in event_info: #Breaks the iteration loop - break - - #Helper function for handle_event - def event_switch(self, solver, event_info): - """ - Turns the switches. - """ - for i in range(len(event_info)): #Loop across all event functions - if event_info[i] != 0: - solver.sw[i] = not solver.sw[i] #Turn the switch - - #Helper function for handle_event - def check_eIter(self, before, after): - """ - Helper function for handle_event to determine if we have event - iteration. - - Input: Values of the event indicator functions (state_events) - before and after we have changed mode of operations. - """ - - eIter = [False]*len(before) - - for i in range(len(before)): - if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): - eIter[i] = True - - return eIter - - def init_mode(self, solver): - """ - Initialize the DAE with the new conditions. - """ - solver.y[1] = (-1.0 if solver.sw[1] else 3.0) - solver.y[2] = (0.0 if solver.sw[2] else 2.0) - - - -def run_example(with_plots=True): - #Create an instance of the problem - exp_mod = Extended_Problem() #Create the problem - - exp_sim = RungeKutta34(exp_mod) #Create the solver - - exp_sim.verbosity = 0 - exp_sim.report_continuously = True - - #Simulate - t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points - - #Basic test - nose.tools.assert_almost_equal(y[-1][0],8.0) - nose.tools.assert_almost_equal(y[-1][1],3.0) - nose.tools.assert_almost_equal(y[-1][2],2.0) - - #Plot - if with_plots: - import pylab as P - P.plot(t,y) - P.title(exp_mod.name) - P.ylabel('States') - P.xlabel('Time') - P.show() - - return exp_mod, exp_sim - -if __name__=="__main__": - mod,sim = run_example() - - - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import RungeKutta34 +from assimulo.problem import Explicit_Problem + +""" +An example with event iteration and with three switches. + +t=0 , [False, True, True] (Start of simulation) +t=1 (1) , [False, True, False] (Found a root at t=1) +t=1 (2) , [False, False, False] (Second iteration at t=1) +t=1 (3) , [True, False, False] (Third iteration at t=1) +t=10 , [True, False, False] (End of simulation) + +""" + +#Extend Assimulos problem definition +class Extended_Problem(Explicit_Problem): + + #Sets the initial conditons directly into the problem + y0 = [0.0, -1.0, 0.0] + sw0 = [False,True,True] + + #The right-hand-side function (rhs) + def rhs(self,t,y,sw): + """ + This is our function we are trying to simulate. During simulation + the parameter sw should be fixed so that our function is continuous + over the interval. The parameters sw should only be changed when the + integrator has stopped. + """ + yd_0 = (1.0 if sw[0] else -1.0) + yd_1 = 0.0 + yd_2 = 0.0 + + return N.array([yd_0,yd_1,yd_2]) + + #Sets a name to our function + name = 'ODE with discontinuities and a function with consistency problem' + + #The event function + def state_events(self,t,y,sw): + """ + This is our function that keeps track of our events. When the sign + of any of the events has changed, we have an event. + """ + event_0 = y[1] - 1.0 + event_1 = -y[2] + 1.0 + event_2 = -t + 1.0 + + return N.array([event_0,event_1,event_2]) + + + #Responsible for handling the events. + def handle_event(self, solver, event_info): + """ + Event handling. This functions is called when Assimulo finds an event as + specified by the event functions. + """ + event_info = event_info[0] #We only look at the state events information. + while True: #Event Iteration + self.event_switch(solver, event_info) #Turns the switches + + b_mode = self.state_events(solver.t, solver.y, solver.sw) + self.init_mode(solver) #Pass in the solver to the problem specified init_mode + a_mode = self.state_events(solver.t, solver.y, solver.sw) + + event_info = self.check_eIter(b_mode, a_mode) + + if not True in event_info: #Breaks the iteration loop + break + + #Helper function for handle_event + def event_switch(self, solver, event_info): + """ + Turns the switches. + """ + for i in range(len(event_info)): #Loop across all event functions + if event_info[i] != 0: + solver.sw[i] = not solver.sw[i] #Turn the switch + + #Helper function for handle_event + def check_eIter(self, before, after): + """ + Helper function for handle_event to determine if we have event + iteration. + + Input: Values of the event indicator functions (state_events) + before and after we have changed mode of operations. + """ + + eIter = [False]*len(before) + + for i in range(len(before)): + if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): + eIter[i] = True + + return eIter + + def init_mode(self, solver): + """ + Initialize the DAE with the new conditions. + """ + solver.y[1] = (-1.0 if solver.sw[1] else 3.0) + solver.y[2] = (0.0 if solver.sw[2] else 2.0) + + + +def run_example(with_plots=True): + #Create an instance of the problem + exp_mod = Extended_Problem() #Create the problem + + exp_sim = RungeKutta34(exp_mod) #Create the solver + + exp_sim.verbosity = 0 + exp_sim.report_continuously = True + + #Simulate + t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points + + #Basic test + nose.tools.assert_almost_equal(y[-1][0],8.0) + nose.tools.assert_almost_equal(y[-1][1],3.0) + nose.tools.assert_almost_equal(y[-1][2],2.0) + + #Plot + if with_plots: + import pylab as P + P.plot(t,y) + P.title(exp_mod.name) + P.ylabel('States') + P.xlabel('Time') + P.show() + + return exp_mod, exp_sim + +if __name__=="__main__": + mod,sim = run_example() + + + diff --git a/examples/rungekutta4_basic.py b/examples/rungekutta4_basic.py index b07f2546..9c2256e4 100644 --- a/examples/rungekutta4_basic.py +++ b/examples/rungekutta4_basic.py @@ -1,66 +1,66 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import nose -from assimulo.solvers import RungeKutta4 -from assimulo.problem import Explicit_Problem - -def run_example(with_plots=True): - r""" - Demonstration of the use of the use of Runge-Kutta 4 by solving the - linear test equation :math:`\dot y = - y` - - on return: - - - :dfn:`exp_mod` problem instance - - - :dfn:`exp_sim` solver instance - - """ - - - #Defines the rhs - def f(t,y): - ydot = -y[0] - return N.array([ydot]) - - #Define an Assimulo problem - exp_mod = Explicit_Problem(f, 4.0, - name = 'RK4 Example: $\dot y = - y$') - - exp_sim = RungeKutta4(exp_mod) #Create a RungeKutta4 solver - - #Simulate - t, y = exp_sim.simulate(5, 100) #Simulate 5 seconds - - #Basic test - nose.tools.assert_almost_equal(float(y[-1]),0.02695179) - - #Plot - if with_plots: - import pylab as P - P.plot(t, y) - P.title(exp_mod.name) - P.ylabel('y') - P.xlabel('Time') - P.show() - - return exp_mod, exp_sim - -if __name__=='__main__': - mod,sim = run_example() +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import nose +from assimulo.solvers import RungeKutta4 +from assimulo.problem import Explicit_Problem + +def run_example(with_plots=True): + r""" + Demonstration of the use of the use of Runge-Kutta 4 by solving the + linear test equation :math:`\dot y = - y` + + on return: + + - :dfn:`exp_mod` problem instance + + - :dfn:`exp_sim` solver instance + + """ + + + #Defines the rhs + def f(t,y): + ydot = -y[0] + return N.array([ydot]) + + #Define an Assimulo problem + exp_mod = Explicit_Problem(f, 4.0, + name = 'RK4 Example: $\dot y = - y$') + + exp_sim = RungeKutta4(exp_mod) #Create a RungeKutta4 solver + + #Simulate + t, y = exp_sim.simulate(5, 100) #Simulate 5 seconds + + #Basic test + nose.tools.assert_almost_equal(float(y[-1]),0.02695179) + + #Plot + if with_plots: + import pylab as P + P.plot(t, y) + P.title(exp_mod.name) + P.ylabel('y') + P.xlabel('Time') + P.show() + + return exp_mod, exp_sim + +if __name__=='__main__': + mod,sim = run_example() diff --git a/setup.py b/setup.py index 4de35777..d1289482 100644 --- a/setup.py +++ b/setup.py @@ -387,7 +387,7 @@ def check_SUNDIALS(self): with open(os.path.join(os.path.join(self.incdirs,'sundials'), 'sundials_config.h')) as f: for line in f: if "SUNDIALS_PACKAGE_VERSION" in line or "SUNDIALS_VERSION" in line: - sundials_version = tuple([int(f) for f in line.split()[-1][1:-1].split(".")]) + sundials_version = tuple([int(f) for f in line.split()[-1][1:-1].split('-dev')[0].split(".")]) L.debug('SUNDIALS %d.%d found.'%(sundials_version[0], sundials_version[1])) break with open(os.path.join(os.path.join(self.incdirs,'sundials'), 'sundials_config.h')) as f: diff --git a/src/algebraic.pxd b/src/algebraic.pxd index 67a15dae..285b46df 100644 --- a/src/algebraic.pxd +++ b/src/algebraic.pxd @@ -1,38 +1,38 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -cimport numpy as N - -cdef class Algebraic: - cdef public object problem - cdef public dict options, solver_options, problem_info - cdef public dict statistics - - cdef public N.ndarray y - cdef public N.ndarray y0 - - cdef _reset_solution_variables(self) - - cdef double elapsed_step_time - cdef double clock_start - - cpdef log_message(self, message, int level) - cpdef solve(self, object y=*) - - cpdef finalize(self) - cpdef initialize(self) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +cimport numpy as N + +cdef class Algebraic: + cdef public object problem + cdef public dict options, solver_options, problem_info + cdef public dict statistics + + cdef public N.ndarray y + cdef public N.ndarray y0 + + cdef _reset_solution_variables(self) + + cdef double elapsed_step_time + cdef double clock_start + + cpdef log_message(self, message, int level) + cpdef solve(self, object y=*) + + cpdef finalize(self) + cpdef initialize(self) diff --git a/src/algebraic.pyx b/src/algebraic.pyx index 0b6b797a..379ce717 100644 --- a/src/algebraic.pyx +++ b/src/algebraic.pyx @@ -1,157 +1,157 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -cimport numpy as N -from timeit import default_timer as timer - -import itertools - -from exception import * -from problem import Algebraic_Problem - -include "constants.pxi" #Includes the constants (textual include) - -realtype = float - -cdef class Algebraic: - - def __init__(self, problem): - """ - Defines general starting attributes for a simulation - problem. - """ - self.statistics = {} #Initialize the statistics dictionary - self.options = {"verbosity":NORMAL, "y_nominal":None, "y_min":None, "y_max":None} - self.problem_info = {"dim":0,"jac_fcn":False,"jacv_fcn":False,'prec_solve':False,'prec_setup':False} - - if problem is None: - raise Algebraic_Exception('The problem needs to be a subclass of a Problem.') - self.problem = problem - - if hasattr(problem, 'y0'): - self.y0 = N.array(problem.y0,dtype=realtype) if len(N.array(problem.y0,dtype=realtype).shape)>0 else N.array([problem.y0],dtype=realtype) - self.y = N.array(problem.y0,dtype=realtype) if len(N.array(problem.y0,dtype=realtype).shape)>0 else N.array([problem.y0],dtype=realtype) - self.problem_info["dim"] = len(self.y0) - else: - raise Algebraic_Exception('y0 must be specified in the problem.') - - if hasattr(problem, "jac"): - self.problem_info["jac_fcn"] = True - if hasattr(problem, "jacv"): - self.problem_info["jacv_fcn"] = True - if hasattr(problem, "prec_solve"): - self.problem_info["prec_solve"] = True - if hasattr(problem, "prec_setup"): - self.problem_info["prec_setup"] = True - - if hasattr(problem, 'y0_nominal'): - self.options["y_nominal"] = problem.y0_nominal - if hasattr(problem, 'y0_min'): - self.options["y_min"] = problem.y0_min - if hasattr(problem, 'y0_max'): - self.options["y_max"] = problem.y0_max - - - #Reset solution variables - self._reset_solution_variables() - - #Initialize timer - self.elapsed_step_time = -1.0 - self.clock_start = -1.0 - - cdef _reset_solution_variables(self): - pass - - cpdef initialize(self): - pass - - cpdef finalize(self): - pass - - cpdef solve(self, y=None): - """ - Calls the solver to solve the problem at hand. - - Parameters:: - - y - - The initial guess. If none, the stored value - of y will be used as initial guess. - """ - #Reset solution variables - self._reset_solution_variables() - - #Simulation starting, call initialize - self.problem.initialize(self) - self.initialize() - - #Start of simulation, start the clock - time_start = timer() - - #Start the simulation - self.y = self._solve(y) - - #End of simulation, stop the clock - time_stop = timer() - - #Simulation complete, call finalize - self.finalize() - self.problem.finalize(self) - - #Print the simulation statistics - self.print_statistics(NORMAL) - - #Log elapsed time - self.log_message('Elapsed simulation time: ' + str(time_stop-time_start) + ' seconds.', NORMAL) - - #Return the results - return self.y - - cpdef log_message(self, message,int level): - if level >= self.options["verbosity"]: - print(message) - - def _set_verbosity(self, verb): - try: - self.options["verbosity"] = int(verb) - except: - raise AssimuloException("Verbosity must be an integer.") - - def _get_verbosity(self): - """ - This determines the level of the output. A smaller value - means more output. The following values can be set: - - QUIET = 50 - WHISPER = 40 - NORMAL = 30 - LOUD = 20 - SCREAM = 10 - - Parameters:: - - verb - - Default 30 (NORMAL) - - - Should be a integer. - - """ - return self.options["verbosity"] - - verbosity = property(_get_verbosity,_set_verbosity) - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +cimport numpy as N +from timeit import default_timer as timer + +import itertools + +from exception import * +from problem import Algebraic_Problem + +include "constants.pxi" #Includes the constants (textual include) + +realtype = float + +cdef class Algebraic: + + def __init__(self, problem): + """ + Defines general starting attributes for a simulation + problem. + """ + self.statistics = {} #Initialize the statistics dictionary + self.options = {"verbosity":NORMAL, "y_nominal":None, "y_min":None, "y_max":None} + self.problem_info = {"dim":0,"jac_fcn":False,"jacv_fcn":False,'prec_solve':False,'prec_setup':False} + + if problem is None: + raise Algebraic_Exception('The problem needs to be a subclass of a Problem.') + self.problem = problem + + if hasattr(problem, 'y0'): + self.y0 = N.array(problem.y0,dtype=realtype) if len(N.array(problem.y0,dtype=realtype).shape)>0 else N.array([problem.y0],dtype=realtype) + self.y = N.array(problem.y0,dtype=realtype) if len(N.array(problem.y0,dtype=realtype).shape)>0 else N.array([problem.y0],dtype=realtype) + self.problem_info["dim"] = len(self.y0) + else: + raise Algebraic_Exception('y0 must be specified in the problem.') + + if hasattr(problem, "jac"): + self.problem_info["jac_fcn"] = True + if hasattr(problem, "jacv"): + self.problem_info["jacv_fcn"] = True + if hasattr(problem, "prec_solve"): + self.problem_info["prec_solve"] = True + if hasattr(problem, "prec_setup"): + self.problem_info["prec_setup"] = True + + if hasattr(problem, 'y0_nominal'): + self.options["y_nominal"] = problem.y0_nominal + if hasattr(problem, 'y0_min'): + self.options["y_min"] = problem.y0_min + if hasattr(problem, 'y0_max'): + self.options["y_max"] = problem.y0_max + + + #Reset solution variables + self._reset_solution_variables() + + #Initialize timer + self.elapsed_step_time = -1.0 + self.clock_start = -1.0 + + cdef _reset_solution_variables(self): + pass + + cpdef initialize(self): + pass + + cpdef finalize(self): + pass + + cpdef solve(self, y=None): + """ + Calls the solver to solve the problem at hand. + + Parameters:: + + y + - The initial guess. If none, the stored value + of y will be used as initial guess. + """ + #Reset solution variables + self._reset_solution_variables() + + #Simulation starting, call initialize + self.problem.initialize(self) + self.initialize() + + #Start of simulation, start the clock + time_start = timer() + + #Start the simulation + self.y = self._solve(y) + + #End of simulation, stop the clock + time_stop = timer() + + #Simulation complete, call finalize + self.finalize() + self.problem.finalize(self) + + #Print the simulation statistics + self.print_statistics(NORMAL) + + #Log elapsed time + self.log_message('Elapsed simulation time: ' + str(time_stop-time_start) + ' seconds.', NORMAL) + + #Return the results + return self.y + + cpdef log_message(self, message,int level): + if level >= self.options["verbosity"]: + print(message) + + def _set_verbosity(self, verb): + try: + self.options["verbosity"] = int(verb) + except: + raise AssimuloException("Verbosity must be an integer.") + + def _get_verbosity(self): + """ + This determines the level of the output. A smaller value + means more output. The following values can be set: + + QUIET = 50 + WHISPER = 40 + NORMAL = 30 + LOUD = 20 + SCREAM = 10 + + Parameters:: + + verb + - Default 30 (NORMAL) + + - Should be a integer. + + """ + return self.options["verbosity"] + + verbosity = property(_get_verbosity,_set_verbosity) + diff --git a/src/constants.pxi b/src/constants.pxi index d50a129f..cf8999e9 100644 --- a/src/constants.pxi +++ b/src/constants.pxi @@ -1,60 +1,60 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . -""" -This file contains the constants used in Assimulo. -""" - -#LARGE VALUES -MIN_VALUE = 1e-100 -MAX_VALUE = 1e100 - -#Return flags -""" -DEF ID_OK = 0 -DEF ID_DISCARD = 1 -DEF ID_EVENT = 2 -DEF ID_COMPLETE = 3 -DEF ID_FAIL = -1 -""" - -#Verbosity levels -DEF DEBUG = 10 -DEF INFO = 20 -DEF WARNING = 30 -DEF ERROR = 40 -DEF CRITICAL = 50 - -#Backward compability -QUIET = CRITICAL -WHISPER = ERROR -NORMAL = WARNING -LOUD = INFO -SCREAM = DEBUG - - -DEF ID_OK = 0 -DEF ID_DISCARD = 1 -DEF ID_EVENT = 2 -DEF ID_COMPLETE = 3 -DEF ID_FAIL = -1 - -ID_PY_OK = 0 -ID_PY_DISCARD = 1 -ID_PY_EVENT = 2 -ID_PY_COMPLETE = 3 -ID_PY_FAIL = -1 - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +This file contains the constants used in Assimulo. +""" + +#LARGE VALUES +MIN_VALUE = 1e-100 +MAX_VALUE = 1e100 + +#Return flags +""" +DEF ID_OK = 0 +DEF ID_DISCARD = 1 +DEF ID_EVENT = 2 +DEF ID_COMPLETE = 3 +DEF ID_FAIL = -1 +""" + +#Verbosity levels +DEF DEBUG = 10 +DEF INFO = 20 +DEF WARNING = 30 +DEF ERROR = 40 +DEF CRITICAL = 50 + +#Backward compability +QUIET = CRITICAL +WHISPER = ERROR +NORMAL = WARNING +LOUD = INFO +SCREAM = DEBUG + + +DEF ID_OK = 0 +DEF ID_DISCARD = 1 +DEF ID_EVENT = 2 +DEF ID_COMPLETE = 3 +DEF ID_FAIL = -1 + +ID_PY_OK = 0 +ID_PY_DISCARD = 1 +ID_PY_EVENT = 2 +ID_PY_COMPLETE = 3 +ID_PY_FAIL = -1 + diff --git a/src/explicit_ode.pxd b/src/explicit_ode.pxd index e3f8ee8b..54b1c285 100644 --- a/src/explicit_ode.pxd +++ b/src/explicit_ode.pxd @@ -1,27 +1,27 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - - -from ode cimport ODE -import numpy as N -cimport numpy as N - - -cdef class Explicit_ODE(ODE): - - cpdef _simulate(self, double t0, double tfinal,N.ndarray output_list,int COMPLETE_STEP, int INTERPOLATE_OUTPUT,int TIME_EVENT) - cpdef report_solution(self, double t, N.ndarray y, opts) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + + +from ode cimport ODE +import numpy as N +cimport numpy as N + + +cdef class Explicit_ODE(ODE): + + cpdef _simulate(self, double t0, double tfinal,N.ndarray output_list,int COMPLETE_STEP, int INTERPOLATE_OUTPUT,int TIME_EVENT) + cpdef report_solution(self, double t, N.ndarray y, opts) diff --git a/src/explicit_ode.pyx b/src/explicit_ode.pyx index 1c6d91a0..a3cfcc78 100644 --- a/src/explicit_ode.pyx +++ b/src/explicit_ode.pyx @@ -1,432 +1,432 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -from ode cimport ODE -from problem import Explicit_Problem, Delay_Explicit_Problem, SingPerturbed_Problem, cExplicit_Problem - -import itertools -import sys -import numpy as N -cimport numpy as N - -from exception import * -from timeit import default_timer as timer - -include "constants.pxi" #Includes the constants (textual include) - -realtype = float - -cdef class Explicit_ODE(ODE): - """ - Baseclass for our explicit ODE integrators. - """ - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Explicit_Problem' or 'cExplicit_Problem' - class. - """ - ODE.__init__(self, problem) #Sets general attributes - - if isinstance(problem, cExplicit_Problem) or isinstance(problem, Delay_Explicit_Problem) or isinstance(problem, SingPerturbed_Problem): - self.problem = problem - else: - raise Explicit_ODE_Exception('The problem needs to be a subclass of a Explicit_Problem.') - - #Check the dimension of the state event function - if self.problem_info["state_events"]: - self.problem_info["dimRoot"] = len(problem.state_events(self.t0,self.y0, self.sw0)) - - self.t = self.t0 - self.y = self.y0.copy() - - def reset(self): - """ - Resets the problem. If the problem is defined with a reset method, its called - and then the method re_init. The re_init method is called with the initial - values provided to the solver (solver.t0 and solver.y0). - - """ - self.problem.reset() - - self.re_init(self.t0, self.y0, self.sw0 if self.problem_info["switches"] else None) - - def re_init(self,t0, y0, sw0=None): - """ - Reinitiates the solver. - - Parameters:: - - t0 - - The initial time. - y0 - - The initial values for the states - - See information in the __init__ method. - """ - y0 = N.array(y0) if len(N.array(y0).shape)>0 else N.array([y0]) - - if len(self.y) != len(y0): - raise Explicit_ODE_Exception('y0 must be of the same length as the original problem.') - - #Set the new values as the current values - self.t = float(t0) - self.y = y0 - - if sw0 is not None: - self.sw = (N.array(sw0,dtype=N.bool) if len(N.array(sw0,dtype=N.bool).shape)>0 else N.array([sw0],dtype=N.bool)).tolist() - - #Clear logs - self.clear_logs() - - cpdef _simulate(self, double t0, double tfinal, N.ndarray output_list, int REPORT_CONTINUOUSLY, int INTERPOLATE_OUTPUT, - int TIME_EVENT): - """ - INTERNAL FUNCTION, FOR SIMULATION USE METHOD SIMULATE. - - Calls the integrator to perform the simulation over the given time-interval. - If a second call to simulate is performed, the simulation starts from the last - given final time. - - Parameters:: - - - t0 - - Initial time - - - Should be float or integer less than final time. - - tfinal - - Final time for the simulation - - - Should be a float or integer greater than the initial time. - - output_list - - - List of communication points - - - Should be an array. - - REPORT_CONTINUOUSLY - - - integer flag: 1 indicates that results are reported at every internal time step - - INTERPOLATE_OUTPUT - - - integer flag: 1 indicates that results at output points should be obtained by interpolation - 0 indicates that results at output points should be computed exactly. - This might slow down integration. - - - TIME_EVENT - - - integer flag: 1 indicates that time events were defined in problem else 0 - - """ - cdef double tevent - cdef int flag, output_index - cdef dict opts - cdef double eps = N.finfo(float).eps*100 #Machine Epsilon - cdef backward = 1 if self.backward else 0 - - y0 = self.y - - #Log the first point - self.problem.handle_result(self,t0,y0) - - #Reinitiate the solver - flag_initialize = True - - #Start flag - flag = ID_OK - tevent = tfinal - - #Internal solver options - opts = {} - opts["initialize"] = flag_initialize - opts["output_list"] = output_list - opts["output_index"] = 0 - opts["report_continuously"] = 1 if REPORT_CONTINUOUSLY else 0 - output_index = 0 - - self.display_progress_activated = 1 if self.display_progress else 0 - self.time_limit_activated = 1 if self.time_limit > 0 else 0 - self.time_integration_start = timer() - - while (flag == ID_COMPLETE and tevent == tfinal) is False and (self.t-eps > tfinal) if backward else (self.t+eps < tfinal): - - #Time event function is specified - if TIME_EVENT == 1: - tret = self.problem.time_events(self.t, self.y, self.sw) - tevent = tfinal if tret is None else (tret if tret < tfinal else tfinal) - else: - tevent = tfinal - - #Initialize the clock, enabling storing elapsed time for each step - if REPORT_CONTINUOUSLY and self.options["clock_step"]: - self.clock_start = timer() - - flag, tlist, ylist = self.integrate(self.t, self.y, tevent, opts) - - #Store data if not done after each step - if REPORT_CONTINUOUSLY is False and len(tlist) > 0: - self.t, self.y = tlist[-1], ylist[-1].copy() - list(map(self.problem.handle_result,itertools.repeat(self,len(tlist)), tlist, ylist)) - - #Initialize flag to false - flag_initialize = False - - #Event handling - if flag == ID_EVENT or (flag == ID_COMPLETE and tevent != tfinal) or (flag == ID_COMPLETE and TIME_EVENT and tret==tevent): #Event has been detected - - if self.store_event_points and output_list is not None and abs(output_list[opts["output_index"]-1]-self.t) > eps: - self.problem.handle_result(self, self.t, self.y.copy()) - - #Get and store event information - event_info = [[],flag == ID_COMPLETE] - if flag == ID_COMPLETE: - self.statistics["ntimeevents"] += 1#Time event detected - if flag == ID_EVENT: - event_info[0] = self.state_event_info() - if REPORT_CONTINUOUSLY: - self._chattering_check(event_info) - - #Log the information - if LOUD >= self.options["verbosity"]: - self.log_event(self.t, event_info, LOUD) - if SCREAM >= self.options["verbosity"]: - self.log_message("A discontinuity occured at t = %e."%self.t,SCREAM) - self.log_message("Current switches: " + str(self.sw), SCREAM) - self.log_message('Event info: ' + str(event_info), SCREAM) - - #Print statistics - self.print_statistics(LOUD) - - try: - self.problem.handle_event(self, event_info) #self corresponds to the solver - except TerminateSimulation: #Terminating the simulation after indication from handle event - self.log_message("Terminating simulation at t = %f after signal from handle_event."%self.t, NORMAL) - break - - flag_initialize = True - - #Update options - opts["initialize"] = flag_initialize - - #Logg after the event handling if there was a communication point there. - if flag_initialize and (output_list is None or self.store_event_points):#output_list[opts["output_index"]] == self.t): - self.problem.handle_result(self, self.t, self.y.copy()) - - if self.t == tfinal: #Finished simulation (might occur due to event at the final time) - break - - cpdef report_solution(self, double t, N.ndarray y, opts): - '''Is called after each successful step in case the complete step - option is active. Here possible interpolation is done and the result - handeled. Furthermore possible step events are checked. - ''' - self.t = t - self.y = y - - #Store the elapsed time for a single step - if self.options["clock_step"]: - self.elapsed_step_time = timer() - self.clock_start - self.clock_start = timer() - - #Check elapsed timed - if self.time_limit_activated: - if self.time_limit-(timer()-self.time_integration_start) < 0.0: - raise TimeLimitExceeded("The time limit was exceeded at integration time %.8E."%self.t) - - self.chattering_clear_counter += 1 - if self.chattering_clear_counter > 3: - self.chattering_check = None - self.chattering_ok_print = 1 - - if self.display_progress_activated: - if (timer() - self.time_integration_start) > self.display_counter*10: - self.display_counter += 1 - - sys.stdout.write(" Integrator time: %e" % self.t) - sys.stdout.write('\r') - sys.stdout.flush() - - #Store data depending on situation - if opts["output_list"] is not None: - output_list = opts["output_list"] - output_index = opts["output_index"] - try: - while output_list[output_index] <= t: - self.problem.handle_result(self, output_list[output_index], - self.interpolate(output_list[output_index])) - output_index = output_index + 1 - except IndexError: - pass - opts["output_index"] = output_index - else: - self.problem.handle_result(self,t,y.copy()) - - #Callback to the problem - if self.problem_info["step_events"]: - flag_initialize = self.problem.step_events(self) #completed step returned to the problem - if flag_initialize: - self.statistics["nstepevents"] += 1 - else: - flag_initialize = False - - return flag_initialize - - def event_locator(self, t_low, t_high, y_high): - '''Checks if an event occurs in [t_low, t_high], if that is the case event - localization is started. Event localization finds the earliest small interval - that contains a change in domain. The right endpoint of this interval is then - returned as the time to restart the integration at. - ''' - - g_high = N.array(self.event_func(t_high, y_high)).copy() - g_low = self.g_old - self.statistics["nstatefcns"] += 1 - n_g = self.problem_info["dimRoot"] - TOL = max(abs(t_low), abs(t_high)) * 1e-13 - #Check for events in [t_low, t_high]. - for i in xrange(n_g): - if (g_low[i] > 0) != (g_high[i] > 0): - break - else: - self.g_old = g_high - return (ID_PY_OK, t_high, y_high) - - side = 0 - sideprev = -1 - - while abs(t_high - t_low) > TOL: - #Adjust alpha if the same side is choosen more than once in a row. - if (sideprev == side): - if side == 2: - alpha = alpha * 2.0 - else: - alpha = alpha / 2.0 - #Otherwise alpha = 1 and the secant rule is used. - else: - alpha = 1 - - #Decide which event function to iterate with. - maxfrac = 0 - imax = 0 #Avoid compilation problem - for i in xrange(n_g): - if ((g_low[i] > 0) != (g_high[i] > 0)): - gfrac = abs(g_high[i]/(g_low[i] - g_high[i])) - if gfrac >= maxfrac: - maxfrac = gfrac - imax = i - - #Hack for solving the slow converging case when g is zero for a large part of [t_low, t_high]. - if g_high[imax] == 0 or g_low[imax] == 0: - t_mid = (t_low + t_high)/2 - else: - t_mid = t_high - (t_high - t_low)*g_high[imax]/ \ - (g_high[imax] - alpha*g_low[imax]) - - #Check if t_mid is to close to current brackets and adjust inwards if so is the case. - if (abs(t_mid - t_low) < TOL/2): - fracint = abs(t_low - t_high)/TOL - if fracint > 5: - delta = (t_high - t_low) / 10.0 - else: - delta = (t_high - t_low) / (2.0 * fracint) - t_mid = t_low + delta - - if (abs(t_mid - t_high) < TOL/2): - fracint = abs(t_low - t_high)/TOL - if fracint > 5: - delta = (t_high - t_low) / 10.0 - else: - delta = (t_high - t_low) / (2.0 * fracint) - t_mid = t_high - delta - - #Calculate g at t_mid and check for events in [t_low, t_mid]. - g_mid = N.array(self.event_func(t_mid, self.interpolate(t_mid))).copy() - self.statistics["nstatefcns"] += 1 - sideprev = side - for i in xrange(n_g): - if (g_low[i] > 0) != (g_mid[i] > 0): - (t_high, g_high) = (t_mid, g_mid[0:n_g]) - side = 1 - break - #If there are no events in [t_low, t_mid] there must be some event in [t_mid, t_high]. - else: - (t_low, g_low) = (t_mid, g_mid[0:n_g]) - side = 2 - - event_info = N.array([0] * n_g) - for i in xrange(n_g): - if (g_low[i] > 0) != (g_high[i] > 0): - event_info[i] = 1 if g_high[i] > 0 else -1 - - self.set_event_info(event_info) - self.statistics["nstateevents"] += 1 - self.g_old = g_high - return (ID_PY_EVENT, t_high, self.interpolate(t_high)) - - def plot(self, mask=None, **kwargs): - """ - Plot the computed solution. - - Parameters:: - - mask - - Default 'None'. Used to determine which solution components should be plotted. - Used as a list of integers, ones represent the components to be - plotted and zeros that is not. - - - Should be a list of integers. - - Example: - mask = [1,0] , plots the first variable. - - **kwargs - - See http://matplotlib.sourceforge.net/api/pyplot_api.html#matplotlib.pyplot.plot - for information about the available options for **kwargs. - """ - import pylab as P - - if len(self.t_sol) > 0: - P.xlabel('time') - P.ylabel('state') - P.title(self.problem.name) - - if not mask: - P.plot(self.t_sol, self.y_sol, **kwargs) - else: - if not isinstance(mask, list): - raise Explicit_ODE_Exception('Mask must be a list of integers') - if not len(mask)==len(self.y_sol[-1]): - raise Explicit_ODE_Exception('Mask must be a list of integers of equal length as '\ - 'the number of variables.') - for i in range(len(mask)): - if mask[i]: - P.plot(self.t_sol, N.array(self.y_sol)[:,i],**kwargs) - - - P.show() - else: - self.log_message("No result for plotting found.",NORMAL) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +from ode cimport ODE +from problem import Explicit_Problem, Delay_Explicit_Problem, SingPerturbed_Problem, cExplicit_Problem + +import itertools +import sys +import numpy as N +cimport numpy as N + +from exception import * +from timeit import default_timer as timer + +include "constants.pxi" #Includes the constants (textual include) + +realtype = float + +cdef class Explicit_ODE(ODE): + """ + Baseclass for our explicit ODE integrators. + """ + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Explicit_Problem' or 'cExplicit_Problem' + class. + """ + ODE.__init__(self, problem) #Sets general attributes + + if isinstance(problem, cExplicit_Problem) or isinstance(problem, Delay_Explicit_Problem) or isinstance(problem, SingPerturbed_Problem): + self.problem = problem + else: + raise Explicit_ODE_Exception('The problem needs to be a subclass of a Explicit_Problem.') + + #Check the dimension of the state event function + if self.problem_info["state_events"]: + self.problem_info["dimRoot"] = len(problem.state_events(self.t0,self.y0, self.sw0)) + + self.t = self.t0 + self.y = self.y0.copy() + + def reset(self): + """ + Resets the problem. If the problem is defined with a reset method, its called + and then the method re_init. The re_init method is called with the initial + values provided to the solver (solver.t0 and solver.y0). + + """ + self.problem.reset() + + self.re_init(self.t0, self.y0, self.sw0 if self.problem_info["switches"] else None) + + def re_init(self,t0, y0, sw0=None): + """ + Reinitiates the solver. + + Parameters:: + + t0 + - The initial time. + y0 + - The initial values for the states + + See information in the __init__ method. + """ + y0 = N.array(y0) if len(N.array(y0).shape)>0 else N.array([y0]) + + if len(self.y) != len(y0): + raise Explicit_ODE_Exception('y0 must be of the same length as the original problem.') + + #Set the new values as the current values + self.t = float(t0) + self.y = y0 + + if sw0 is not None: + self.sw = (N.array(sw0,dtype=N.bool) if len(N.array(sw0,dtype=N.bool).shape)>0 else N.array([sw0],dtype=N.bool)).tolist() + + #Clear logs + self.clear_logs() + + cpdef _simulate(self, double t0, double tfinal, N.ndarray output_list, int REPORT_CONTINUOUSLY, int INTERPOLATE_OUTPUT, + int TIME_EVENT): + """ + INTERNAL FUNCTION, FOR SIMULATION USE METHOD SIMULATE. + + Calls the integrator to perform the simulation over the given time-interval. + If a second call to simulate is performed, the simulation starts from the last + given final time. + + Parameters:: + + + t0 + - Initial time + + - Should be float or integer less than final time. + + tfinal + - Final time for the simulation + + - Should be a float or integer greater than the initial time. + + output_list + + - List of communication points + + - Should be an array. + + REPORT_CONTINUOUSLY + + - integer flag: 1 indicates that results are reported at every internal time step + + INTERPOLATE_OUTPUT + + - integer flag: 1 indicates that results at output points should be obtained by interpolation + 0 indicates that results at output points should be computed exactly. + This might slow down integration. + + + TIME_EVENT + + - integer flag: 1 indicates that time events were defined in problem else 0 + + """ + cdef double tevent + cdef int flag, output_index + cdef dict opts + cdef double eps = N.finfo(float).eps*100 #Machine Epsilon + cdef backward = 1 if self.backward else 0 + + y0 = self.y + + #Log the first point + self.problem.handle_result(self,t0,y0) + + #Reinitiate the solver + flag_initialize = True + + #Start flag + flag = ID_OK + tevent = tfinal + + #Internal solver options + opts = {} + opts["initialize"] = flag_initialize + opts["output_list"] = output_list + opts["output_index"] = 0 + opts["report_continuously"] = 1 if REPORT_CONTINUOUSLY else 0 + output_index = 0 + + self.display_progress_activated = 1 if self.display_progress else 0 + self.time_limit_activated = 1 if self.time_limit > 0 else 0 + self.time_integration_start = timer() + + while (flag == ID_COMPLETE and tevent == tfinal) is False and (self.t-eps > tfinal) if backward else (self.t+eps < tfinal): + + #Time event function is specified + if TIME_EVENT == 1: + tret = self.problem.time_events(self.t, self.y, self.sw) + tevent = tfinal if tret is None else (tret if tret < tfinal else tfinal) + else: + tevent = tfinal + + #Initialize the clock, enabling storing elapsed time for each step + if REPORT_CONTINUOUSLY and self.options["clock_step"]: + self.clock_start = timer() + + flag, tlist, ylist = self.integrate(self.t, self.y, tevent, opts) + + #Store data if not done after each step + if REPORT_CONTINUOUSLY is False and len(tlist) > 0: + self.t, self.y = tlist[-1], ylist[-1].copy() + list(map(self.problem.handle_result,itertools.repeat(self,len(tlist)), tlist, ylist)) + + #Initialize flag to false + flag_initialize = False + + #Event handling + if flag == ID_EVENT or (flag == ID_COMPLETE and tevent != tfinal) or (flag == ID_COMPLETE and TIME_EVENT and tret==tevent): #Event has been detected + + if self.store_event_points and output_list is not None and abs(output_list[opts["output_index"]-1]-self.t) > eps: + self.problem.handle_result(self, self.t, self.y.copy()) + + #Get and store event information + event_info = [[],flag == ID_COMPLETE] + if flag == ID_COMPLETE: + self.statistics["ntimeevents"] += 1#Time event detected + if flag == ID_EVENT: + event_info[0] = self.state_event_info() + if REPORT_CONTINUOUSLY: + self._chattering_check(event_info) + + #Log the information + if LOUD >= self.options["verbosity"]: + self.log_event(self.t, event_info, LOUD) + if SCREAM >= self.options["verbosity"]: + self.log_message("A discontinuity occured at t = %e."%self.t,SCREAM) + self.log_message("Current switches: " + str(self.sw), SCREAM) + self.log_message('Event info: ' + str(event_info), SCREAM) + + #Print statistics + self.print_statistics(LOUD) + + try: + self.problem.handle_event(self, event_info) #self corresponds to the solver + except TerminateSimulation: #Terminating the simulation after indication from handle event + self.log_message("Terminating simulation at t = %f after signal from handle_event."%self.t, NORMAL) + break + + flag_initialize = True + + #Update options + opts["initialize"] = flag_initialize + + #Logg after the event handling if there was a communication point there. + if flag_initialize and (output_list is None or self.store_event_points):#output_list[opts["output_index"]] == self.t): + self.problem.handle_result(self, self.t, self.y.copy()) + + if self.t == tfinal: #Finished simulation (might occur due to event at the final time) + break + + cpdef report_solution(self, double t, N.ndarray y, opts): + '''Is called after each successful step in case the complete step + option is active. Here possible interpolation is done and the result + handeled. Furthermore possible step events are checked. + ''' + self.t = t + self.y = y + + #Store the elapsed time for a single step + if self.options["clock_step"]: + self.elapsed_step_time = timer() - self.clock_start + self.clock_start = timer() + + #Check elapsed timed + if self.time_limit_activated: + if self.time_limit-(timer()-self.time_integration_start) < 0.0: + raise TimeLimitExceeded("The time limit was exceeded at integration time %.8E."%self.t) + + self.chattering_clear_counter += 1 + if self.chattering_clear_counter > 3: + self.chattering_check = None + self.chattering_ok_print = 1 + + if self.display_progress_activated: + if (timer() - self.time_integration_start) > self.display_counter*10: + self.display_counter += 1 + + sys.stdout.write(" Integrator time: %e" % self.t) + sys.stdout.write('\r') + sys.stdout.flush() + + #Store data depending on situation + if opts["output_list"] is not None: + output_list = opts["output_list"] + output_index = opts["output_index"] + try: + while output_list[output_index] <= t: + self.problem.handle_result(self, output_list[output_index], + self.interpolate(output_list[output_index])) + output_index = output_index + 1 + except IndexError: + pass + opts["output_index"] = output_index + else: + self.problem.handle_result(self,t,y.copy()) + + #Callback to the problem + if self.problem_info["step_events"]: + flag_initialize = self.problem.step_events(self) #completed step returned to the problem + if flag_initialize: + self.statistics["nstepevents"] += 1 + else: + flag_initialize = False + + return flag_initialize + + def event_locator(self, t_low, t_high, y_high): + '''Checks if an event occurs in [t_low, t_high], if that is the case event + localization is started. Event localization finds the earliest small interval + that contains a change in domain. The right endpoint of this interval is then + returned as the time to restart the integration at. + ''' + + g_high = N.array(self.event_func(t_high, y_high)).copy() + g_low = self.g_old + self.statistics["nstatefcns"] += 1 + n_g = self.problem_info["dimRoot"] + TOL = max(abs(t_low), abs(t_high)) * 1e-13 + #Check for events in [t_low, t_high]. + for i in xrange(n_g): + if (g_low[i] > 0) != (g_high[i] > 0): + break + else: + self.g_old = g_high + return (ID_PY_OK, t_high, y_high) + + side = 0 + sideprev = -1 + + while abs(t_high - t_low) > TOL: + #Adjust alpha if the same side is choosen more than once in a row. + if (sideprev == side): + if side == 2: + alpha = alpha * 2.0 + else: + alpha = alpha / 2.0 + #Otherwise alpha = 1 and the secant rule is used. + else: + alpha = 1 + + #Decide which event function to iterate with. + maxfrac = 0 + imax = 0 #Avoid compilation problem + for i in xrange(n_g): + if ((g_low[i] > 0) != (g_high[i] > 0)): + gfrac = abs(g_high[i]/(g_low[i] - g_high[i])) + if gfrac >= maxfrac: + maxfrac = gfrac + imax = i + + #Hack for solving the slow converging case when g is zero for a large part of [t_low, t_high]. + if g_high[imax] == 0 or g_low[imax] == 0: + t_mid = (t_low + t_high)/2 + else: + t_mid = t_high - (t_high - t_low)*g_high[imax]/ \ + (g_high[imax] - alpha*g_low[imax]) + + #Check if t_mid is to close to current brackets and adjust inwards if so is the case. + if (abs(t_mid - t_low) < TOL/2): + fracint = abs(t_low - t_high)/TOL + if fracint > 5: + delta = (t_high - t_low) / 10.0 + else: + delta = (t_high - t_low) / (2.0 * fracint) + t_mid = t_low + delta + + if (abs(t_mid - t_high) < TOL/2): + fracint = abs(t_low - t_high)/TOL + if fracint > 5: + delta = (t_high - t_low) / 10.0 + else: + delta = (t_high - t_low) / (2.0 * fracint) + t_mid = t_high - delta + + #Calculate g at t_mid and check for events in [t_low, t_mid]. + g_mid = N.array(self.event_func(t_mid, self.interpolate(t_mid))).copy() + self.statistics["nstatefcns"] += 1 + sideprev = side + for i in xrange(n_g): + if (g_low[i] > 0) != (g_mid[i] > 0): + (t_high, g_high) = (t_mid, g_mid[0:n_g]) + side = 1 + break + #If there are no events in [t_low, t_mid] there must be some event in [t_mid, t_high]. + else: + (t_low, g_low) = (t_mid, g_mid[0:n_g]) + side = 2 + + event_info = N.array([0] * n_g) + for i in xrange(n_g): + if (g_low[i] > 0) != (g_high[i] > 0): + event_info[i] = 1 if g_high[i] > 0 else -1 + + self.set_event_info(event_info) + self.statistics["nstateevents"] += 1 + self.g_old = g_high + return (ID_PY_EVENT, t_high, self.interpolate(t_high)) + + def plot(self, mask=None, **kwargs): + """ + Plot the computed solution. + + Parameters:: + + mask + - Default 'None'. Used to determine which solution components should be plotted. + Used as a list of integers, ones represent the components to be + plotted and zeros that is not. + + - Should be a list of integers. + + Example: + mask = [1,0] , plots the first variable. + + **kwargs + - See http://matplotlib.sourceforge.net/api/pyplot_api.html#matplotlib.pyplot.plot + for information about the available options for **kwargs. + """ + import pylab as P + + if len(self.t_sol) > 0: + P.xlabel('time') + P.ylabel('state') + P.title(self.problem.name) + + if not mask: + P.plot(self.t_sol, self.y_sol, **kwargs) + else: + if not isinstance(mask, list): + raise Explicit_ODE_Exception('Mask must be a list of integers') + if not len(mask)==len(self.y_sol[-1]): + raise Explicit_ODE_Exception('Mask must be a list of integers of equal length as '\ + 'the number of variables.') + for i in range(len(mask)): + if mask[i]: + P.plot(self.t_sol, N.array(self.y_sol)[:,i],**kwargs) + + + P.show() + else: + self.log_message("No result for plotting found.",NORMAL) diff --git a/src/implicit_ode.pxd b/src/implicit_ode.pxd index ab1adadd..9cdbd392 100644 --- a/src/implicit_ode.pxd +++ b/src/implicit_ode.pxd @@ -1,26 +1,26 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - - -from ode cimport ODE -import numpy as N -cimport numpy as N - - -cdef class Implicit_ODE(ODE): - cpdef _simulate(self, double t0, double tfinal,N.ndarray output_list,int COMPLETE_STEP, int INTERPOLATE_OUTPUT,int TIME_EVENT) - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + + +from ode cimport ODE +import numpy as N +cimport numpy as N + + +cdef class Implicit_ODE(ODE): + cpdef _simulate(self, double t0, double tfinal,N.ndarray output_list,int COMPLETE_STEP, int INTERPOLATE_OUTPUT,int TIME_EVENT) + diff --git a/src/implicit_ode.pyx b/src/implicit_ode.pyx index bcfe107f..2013b92e 100644 --- a/src/implicit_ode.pyx +++ b/src/implicit_ode.pyx @@ -1,502 +1,502 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -from ode cimport ODE -from problem import Implicit_Problem, cImplicit_Problem, Overdetermined_Problem -from problem import cExplicit_Problem - -import itertools -import sys -import numpy as N -cimport numpy as N - -from exception import * -from timeit import default_timer as timer -import warnings - -realtype = float - -include "constants.pxi" #Includes the constants (textual include) - -class Implicit_ODE_Exception(Exception): - """An integrator exception""" - pass - -cdef class Implicit_ODE(ODE): - """ - Baseclass for our implicit ODE integrators. - """ - - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Implicit_Problem' class. - """ - ODE.__init__(self, problem) #Sets general attributes - self.problem = problem - self.check_instance() - - #Set type of problem - self.problem_info["type"] = 1 #Implicit - - - if hasattr(problem, 'yd0'): - self.yd0 = N.array(problem.yd0,dtype=realtype) if len(N.array(problem.yd0,dtype=realtype).shape)>0 else N.array([problem.yd0],dtype=realtype) - else: - if isinstance(self.problem, cExplicit_Problem): #The problem is an explicit, get the yd0 values from the right-hand-side - self.problem_info["type"] = 0 #Change to explicit problem - if self.problem_info["state_events"]: - self.yd0 = problem.rhs(self.t0, self.y0, self.sw0) - else: - self.yd0 = problem.rhs(self.t0, self.y0) - else: - raise Implicit_ODE_Exception('yd0 must be specified in the problem.') - - #Check the dimension of the state event function - if self.problem_info["state_events"]: - if self.problem_info["type"] == 1: - self.problem_info["dimRoot"] = len(problem.state_events(self.t0,self.y0, self.yd0, self.sw0)) - else: - self.problem_info["dimRoot"] = len(problem.state_events(self.t0,self.y0, self.sw0)) - self.t = self.t0 - self.y = self.y0.copy() - self.yd = self.yd0.copy() - - def check_instance(self): - if not isinstance(self.problem, cImplicit_Problem) and not isinstance(self.problem, cExplicit_Problem): - raise Implicit_ODE_Exception('The problem needs to be a subclass of Implicit_Problem (or Explicit_Problem).') - - def reset(self): - """ - - Resets the problem. If the problem is defined with a reset method, its called - and then the method re_init. The re_init method is called with the initial - values provided to the solver (solver.t0, solver.y0, solver.yd0). - - """ - self.problem.reset() - - self.re_init(self.t0, self.y0, self.yd0, self.sw0 if self.problem_info["switches"] else None) - - def re_init(self,t0, y0, yd0, sw0=None): - """ - Reinitiates the solver. - - Parameters:: - - t0 - The initial time. - y0 - The initial values for the states - yd0 - The initial values for the state derivatives. - - See information in the __init__ method. - """ - y0 = N.array(y0) if len(N.array(y0).shape)>0 else N.array([y0]) - yd0 = N.array(yd0) if len(N.array(yd0).shape)>0 else N.array([yd0]) - - if len(self.y) != len(y0) or len(self.yd) != len(yd0): - raise Implicit_ODE_Exception('y0/yd0 must be of the same length as the original problem.') - - #Set the new values as the current values - self.t = float(t0) - self.y = y0 - self.yd = yd0 - - if sw0 is not None: - self.sw = (N.array(sw0,dtype=N.bool) if len(N.array(sw0,dtype=N.bool).shape)>0 else N.array([sw0],dtype=N.bool)).tolist() - - #Clear logs - self.clear_logs() - - cpdef _simulate(self, double t0, double tfinal,N.ndarray output_list,int REPORT_CONTINUOUSLY, int INTERPOLATE_OUTPUT, - int TIME_EVENT): - """ - INTERNAL FUNCTION, FOR SIMULATION USE METHOD SIMULATE. - - Calls the integrator to perform the simulation over the given time-interval. - If a second call to simulate is performed, the simulation starts from the last - given final time. - - Parameters:: - - tfinal - - Final time for the simulation - - - Should be a float or integer greater than the initial time. - - ncp - - Default '0'. Number of communication points where the - solution is returned. If '0', the integrator will return - at its internal steps. - - - Should be an integer. - - Example: - - __call__(10.0, 100), 10.0 is the final time and 100 is the number - communication points. - """ - cdef double tevent - cdef int flag, output_index - cdef dict opts - cdef int type = self.problem_info["type"] - cdef double eps = N.finfo(float).eps*100 #Machine Epsilon - cdef backward = 1 if self.backward else 0 - - y0 = self.y - yd0 = self.yd - - #Logg the first point - if type == 0: - self.problem.handle_result(self,t0,y0) - else: - self.problem.handle_result(self,t0,y0,yd0) - - #Reinitiate the solver - flag_initialize = True - - #Start flag - flag = ID_OK - tevent = tfinal - - #Internal solver options - opts = {} - opts["initialize"] = flag_initialize - opts["output_list"] = output_list - opts["output_index"] = 0 - opts["report_continuously"] = 1 if REPORT_CONTINUOUSLY else 0 - output_index = 0 - - self.time_limit_activated = 1 if self.time_limit > 0 else 0 - self.time_integration_start = timer() - - while (flag == ID_COMPLETE and tevent == tfinal) is False and (self.t-eps > tfinal) if backward else (self.t+eps < tfinal): - - #Time event function is specified - if TIME_EVENT == 1: - if type == 0: - tret = self.problem.time_events(self.t, self.y, self.sw) - else: - tret = self.problem.time_events(self.t, self.y, self.yd, self.sw) - tevent = tfinal if tret is None else (tret if tret < tfinal else tfinal) - else: - tevent = tfinal - - #Initialize the clock, enabling storing elapsed time for each step - if REPORT_CONTINUOUSLY and self.options["clock_step"]: - self.clock_start = timer() - - [flag, tlist, ylist, ydlist] = self.integrate(self.t, self.y, self.yd, tevent, opts) - - #Store data if not done in report_solution - if REPORT_CONTINUOUSLY is False and len(tlist) > 0: - self.t, self.y, self.yd = tlist[-1], ylist[-1].copy(), ydlist[-1].copy() - if type == 0: - list(map(self.problem.handle_result,itertools.repeat(self,len(tlist)), tlist, ylist)) - else: - list(map(self.problem.handle_result,itertools.repeat(self,len(tlist)), tlist, ylist, ydlist)) - - #Initialize flag to false - flag_initialize = False - - #Event handling - if flag == ID_EVENT or (flag == ID_COMPLETE and tevent != tfinal): #Event have been detected - - if self.store_event_points and output_list is not None and abs(output_list[opts["output_index"]-1]-self.t) > eps: - self.problem.handle_result(self, self.t, self.y.copy(), self.yd.copy()) - - #Get and store event information - event_info = [[],flag == ID_COMPLETE] - if flag == ID_COMPLETE: - self.statistics["ntimeevents"] += 1#Time event detected - if flag == ID_EVENT: - event_info[0] = self.state_event_info() - if REPORT_CONTINUOUSLY: - self._chattering_check(event_info) - - #Log the information - if LOUD >= self.options["verbosity"]: - self.log_event(self.t, event_info, LOUD) - if SCREAM >= self.options["verbosity"]: - self.log_message("A discontinuity occured at t = %e."%self.t,SCREAM) - self.log_message("Current switches: " + str(self.sw), SCREAM) - self.log_message('Event info: ' + str(event_info), SCREAM) - - #Print statistics - self.print_statistics(LOUD) - - try: - self.problem.handle_event(self, event_info) #self corresponds to the solver - except TerminateSimulation: #Terminating the simulation after indication from handle event - self.log_message("Terminating simulation at t = %f after signal from handle_event."%self.t, NORMAL) - break - - flag_initialize = True - - #Update options - opts["initialize"] = flag_initialize - - #Logg after the event handling if there was a communication point there. - if flag_initialize and (output_list is None or self.store_event_points): - if type == 0: - self.problem.handle_result(self, self.t, self.y.copy()) - else: - self.problem.handle_result(self, self.t, self.y.copy(), self.yd.copy()) - - if self.t == tfinal: #Finished simulation (might occur due to event at the final time) - break - - def report_solution(self, t, y, yd, opts): - '''Is called after each successful step in case the report continuously - option is active. Here possible interpolation is done and the result - handeled. Furthermore possible step events are checked. - ''' - self.t, self.y, self.yd = t, y.copy(), yd.copy() - - #Store the elapsed time for a single step - if self.options["clock_step"]: - self.elapsed_step_time = timer() - self.clock_start - self.clock_start = timer() - - #Check elapsed timed - if self.time_limit_activated: - if self.time_limit-(timer()-self.time_integration_start) < 0.0: - raise TimeLimitExceeded("The time limit was exceeded at integration time %.8E."%self.t) - - if self.display_progress: - if (timer() - self.time_integration_start) > self.display_counter*10: - self.display_counter += 1 - - sys.stdout.write(" Integrator time: %e" % self.t) - sys.stdout.write('\r') - sys.stdout.flush() - - self.chattering_clear_counter += 1 - if self.chattering_clear_counter > 3: - self.chattering_check = None - self.chattering_ok_print = 1 - - #Store data depending on situation - if opts["output_list"] is not None: - output_list = opts["output_list"] - output_index = opts["output_index"] - try: - while output_list[output_index] <= t: - if self.problem_info["type"] == 0: - self.problem.handle_result(self, output_list[output_index], - self.interpolate(output_list[output_index])) - else: - self.problem.handle_result(self, output_list[output_index], - self.interpolate(output_list[output_index]), - self.interpolate(output_list[output_index],1)) - output_index = output_index + 1 - except IndexError: - pass - opts["output_index"] = output_index - else: - if self.problem_info["type"] == 0: - self.problem.handle_result(self,t,y.copy()) - else: - self.problem.handle_result(self,t,y.copy(),yd.copy()) - - #Callback to FMU - if self.problem_info["step_events"]: - flag_initialize = self.problem.step_events(self) #completed step returned to FMU - if flag_initialize: - self.statistics["nstepevents"] += 1 - else: - flag_initialize = False - - return flag_initialize - - def event_locator(self, t_low, t_high, y_high, yd_high): - '''Checks if an event occurs in [t_low, t_high], if that is the case event - localization is started. Event localization finds the earliest small interval - that contains a change in domain. The right endpoint of this interval is then - returned as the time to restart the integration at. - ''' - - g_high = self.event_func(t_high, y_high, yd_high) - g_low = self.g_old - self.statistics["nstatefcns"] += 1 - n_g = self.problem_info["dimRoot"] - TOL = max(t_low, t_high) * 1e-13 - #Check for events in [t_low, t_high]. - for i in xrange(n_g): - if (g_low[i] > 0) != (g_high[i] > 0): - break - else: - self.g_old = g_high - return (ID_PY_OK, t_high, y_high, yd_high) - - side = 0 - sideprev = -1 - - while abs(t_high - t_low) > TOL: - #Adjust alpha if the same side is choosen more than once in a row. - if (sideprev == side): - if side == 2: - alpha = alpha * 2.0 - else: - alpha = alpha / 2.0 - #Otherwise alpha = 1 and the secant rule is used. - else: - alpha = 1 - - #Decide which event function to iterate with. - maxfrac = 0 - imax = 0 #Avoid compilation problem - for i in xrange(n_g): - if ((g_low[i] > 0) != (g_high[i] > 0)): - gfrac = abs(g_high[i]/(g_low[i] - g_high[i])) - if gfrac >= maxfrac: - maxfrac = gfrac - imax = i - - #Hack for solving the slow converging case when g is zero for a large part of [t_low, t_high]. - if g_high[imax] == 0 or g_low[imax] == 0: - t_mid = (t_low + t_high)/2 - else: - t_mid = t_high - (t_high - t_low)*g_high[imax]/ \ - (g_high[imax] - alpha*g_low[imax]) - - #Check if t_mid is to close to current brackets and adjust inwards if so is the case. - if (abs(t_mid - t_low) < TOL/2): - fracint = abs(t_low - t_high)/TOL - if fracint > 5: - delta = (t_high - t_low) / 10.0 - else: - delta = (t_high - t_low) / (2.0 * fracint) - t_mid = t_low + delta - - if (abs(t_mid - t_high) < TOL/2): - fracint = abs(t_low - t_high)/TOL - if fracint > 5: - delta = (t_high - t_low) / 10.0 - else: - delta = (t_high - t_low) / (2.0 * fracint) - t_mid = t_high - delta - - #Calculate g at t_mid and check for events in [t_low, t_mid]. - g_mid = self.event_func(t_mid, self.interpolate(t_mid), self.interpolate(t_mid, 1)) - self.statistics["nstatefcns"] += 1 - sideprev = side - for i in xrange(n_g): - if (g_low[i] > 0) != (g_mid[i] > 0): - (t_high, g_high) = (t_mid, g_mid[0:n_g]) - side = 1 - break - #If there are no events in [t_low, t_mid] there must be some event in [t_mid, t_high]. - else: - (t_low, g_low) = (t_mid, g_mid[0:n_g]) - side = 2 - - event_info = N.array([0] * n_g) - for i in xrange(n_g): - if (g_low[i] > 0) != (g_high[i] > 0): - event_info[i] = 1 if g_high[i] > 0 else -1 - - self.set_event_info(event_info) - self.statistics["nstateevents"] += 1 - self.g_old = g_high - return (ID_PY_EVENT, t_high, self.interpolate(t_high), self.interpolate(t_high, 1)) - - def plot(self, mask=None, der=False, **kwargs): - """ - Plot the computed solution. - - Parameters:: - - mask - - Default 'None'. Used to determine which variables that is to be plotted. - Used as a list of integers, ones represents the variable that is to be - plotted and zeros that is not. - - - Should be a list of integers. - - Example: - mask = [1,0] , plots the first variable. - - der - - Default 'False'. When 'True' plots the derivative variables also. - - - Should be a Boolean. - - Example: - der = True - **kwargs - - See http://matplotlib.sourceforge.net/api/pyplot_api.html#matplotlib.pyplot.plot - for information about the available options for **kwargs. - """ - import pylab as P - - if len(self.t_sol) > 0: - P.figure(1) - if not mask: - P.plot(self.t_sol, self.y_sol, **kwargs) - else: - if not isinstance(mask, list): - raise Implicit_ODE_Exception('Mask must be a list of integers') - if not len(mask)==len(self.y_sol[-1]): - raise Implicit_ODE_Exception('Mask must be a list of integers of equal length as '\ - 'the number of variables.') - for i in range(len(mask)): - if mask[i]: - P.plot(self.t_sol, N.array(self.y_sol)[:,i], **kwargs) - - P.xlabel('time') - P.ylabel('state') - P.title(self.problem.name) - - - if der and not mask: - P.figure(2) - P.plot(self.t_sol, self.yd_sol, **kwargs) - P.xlabel('time') - P.ylabel('state derivatives') - P.title(self.problem.name) - elif mask and der: - P.figure(2) - if not isinstance(mask, list): - raise Implicit_ODE_Exception('Mask must be a list of integers') - if not len(mask)==len(self.yd_sol[-1]): - raise Implicit_ODE_Exception('Mask must be a list of integers of equal length as '\ - 'the number of variables.') - for i in range(len(mask)): - if mask[i]: - P.plot(self.t_sol, N.array(self.yd_sol)[:,i], **kwargs) - - P.xlabel('time') - P.ylabel('state derivatives') - P.title(self.problem.name) - - P.show() - else: - self.log_message("No result for plotting found.",NORMAL) - - -cdef class OverdeterminedDAE(Implicit_ODE): - def check_instance(self): - if not isinstance(self.problem, Overdetermined_Problem) and not isinstance(self.problem, Implicit_Problem): - raise Implicit_ODE_Exception('The problem needs to be a subclass of Overdetermined_Problem or of Implicit_Problem.') - - - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +from ode cimport ODE +from problem import Implicit_Problem, cImplicit_Problem, Overdetermined_Problem +from problem import cExplicit_Problem + +import itertools +import sys +import numpy as N +cimport numpy as N + +from exception import * +from timeit import default_timer as timer +import warnings + +realtype = float + +include "constants.pxi" #Includes the constants (textual include) + +class Implicit_ODE_Exception(Exception): + """An integrator exception""" + pass + +cdef class Implicit_ODE(ODE): + """ + Baseclass for our implicit ODE integrators. + """ + + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Implicit_Problem' class. + """ + ODE.__init__(self, problem) #Sets general attributes + self.problem = problem + self.check_instance() + + #Set type of problem + self.problem_info["type"] = 1 #Implicit + + + if hasattr(problem, 'yd0'): + self.yd0 = N.array(problem.yd0,dtype=realtype) if len(N.array(problem.yd0,dtype=realtype).shape)>0 else N.array([problem.yd0],dtype=realtype) + else: + if isinstance(self.problem, cExplicit_Problem): #The problem is an explicit, get the yd0 values from the right-hand-side + self.problem_info["type"] = 0 #Change to explicit problem + if self.problem_info["state_events"]: + self.yd0 = problem.rhs(self.t0, self.y0, self.sw0) + else: + self.yd0 = problem.rhs(self.t0, self.y0) + else: + raise Implicit_ODE_Exception('yd0 must be specified in the problem.') + + #Check the dimension of the state event function + if self.problem_info["state_events"]: + if self.problem_info["type"] == 1: + self.problem_info["dimRoot"] = len(problem.state_events(self.t0,self.y0, self.yd0, self.sw0)) + else: + self.problem_info["dimRoot"] = len(problem.state_events(self.t0,self.y0, self.sw0)) + self.t = self.t0 + self.y = self.y0.copy() + self.yd = self.yd0.copy() + + def check_instance(self): + if not isinstance(self.problem, cImplicit_Problem) and not isinstance(self.problem, cExplicit_Problem): + raise Implicit_ODE_Exception('The problem needs to be a subclass of Implicit_Problem (or Explicit_Problem).') + + def reset(self): + """ + + Resets the problem. If the problem is defined with a reset method, its called + and then the method re_init. The re_init method is called with the initial + values provided to the solver (solver.t0, solver.y0, solver.yd0). + + """ + self.problem.reset() + + self.re_init(self.t0, self.y0, self.yd0, self.sw0 if self.problem_info["switches"] else None) + + def re_init(self,t0, y0, yd0, sw0=None): + """ + Reinitiates the solver. + + Parameters:: + + t0 - The initial time. + y0 - The initial values for the states + yd0 - The initial values for the state derivatives. + + See information in the __init__ method. + """ + y0 = N.array(y0) if len(N.array(y0).shape)>0 else N.array([y0]) + yd0 = N.array(yd0) if len(N.array(yd0).shape)>0 else N.array([yd0]) + + if len(self.y) != len(y0) or len(self.yd) != len(yd0): + raise Implicit_ODE_Exception('y0/yd0 must be of the same length as the original problem.') + + #Set the new values as the current values + self.t = float(t0) + self.y = y0 + self.yd = yd0 + + if sw0 is not None: + self.sw = (N.array(sw0,dtype=N.bool) if len(N.array(sw0,dtype=N.bool).shape)>0 else N.array([sw0],dtype=N.bool)).tolist() + + #Clear logs + self.clear_logs() + + cpdef _simulate(self, double t0, double tfinal,N.ndarray output_list,int REPORT_CONTINUOUSLY, int INTERPOLATE_OUTPUT, + int TIME_EVENT): + """ + INTERNAL FUNCTION, FOR SIMULATION USE METHOD SIMULATE. + + Calls the integrator to perform the simulation over the given time-interval. + If a second call to simulate is performed, the simulation starts from the last + given final time. + + Parameters:: + + tfinal + - Final time for the simulation + + - Should be a float or integer greater than the initial time. + + ncp + - Default '0'. Number of communication points where the + solution is returned. If '0', the integrator will return + at its internal steps. + + - Should be an integer. + + Example: + + __call__(10.0, 100), 10.0 is the final time and 100 is the number + communication points. + """ + cdef double tevent + cdef int flag, output_index + cdef dict opts + cdef int type = self.problem_info["type"] + cdef double eps = N.finfo(float).eps*100 #Machine Epsilon + cdef backward = 1 if self.backward else 0 + + y0 = self.y + yd0 = self.yd + + #Logg the first point + if type == 0: + self.problem.handle_result(self,t0,y0) + else: + self.problem.handle_result(self,t0,y0,yd0) + + #Reinitiate the solver + flag_initialize = True + + #Start flag + flag = ID_OK + tevent = tfinal + + #Internal solver options + opts = {} + opts["initialize"] = flag_initialize + opts["output_list"] = output_list + opts["output_index"] = 0 + opts["report_continuously"] = 1 if REPORT_CONTINUOUSLY else 0 + output_index = 0 + + self.time_limit_activated = 1 if self.time_limit > 0 else 0 + self.time_integration_start = timer() + + while (flag == ID_COMPLETE and tevent == tfinal) is False and (self.t-eps > tfinal) if backward else (self.t+eps < tfinal): + + #Time event function is specified + if TIME_EVENT == 1: + if type == 0: + tret = self.problem.time_events(self.t, self.y, self.sw) + else: + tret = self.problem.time_events(self.t, self.y, self.yd, self.sw) + tevent = tfinal if tret is None else (tret if tret < tfinal else tfinal) + else: + tevent = tfinal + + #Initialize the clock, enabling storing elapsed time for each step + if REPORT_CONTINUOUSLY and self.options["clock_step"]: + self.clock_start = timer() + + [flag, tlist, ylist, ydlist] = self.integrate(self.t, self.y, self.yd, tevent, opts) + + #Store data if not done in report_solution + if REPORT_CONTINUOUSLY is False and len(tlist) > 0: + self.t, self.y, self.yd = tlist[-1], ylist[-1].copy(), ydlist[-1].copy() + if type == 0: + list(map(self.problem.handle_result,itertools.repeat(self,len(tlist)), tlist, ylist)) + else: + list(map(self.problem.handle_result,itertools.repeat(self,len(tlist)), tlist, ylist, ydlist)) + + #Initialize flag to false + flag_initialize = False + + #Event handling + if flag == ID_EVENT or (flag == ID_COMPLETE and tevent != tfinal): #Event have been detected + + if self.store_event_points and output_list is not None and abs(output_list[opts["output_index"]-1]-self.t) > eps: + self.problem.handle_result(self, self.t, self.y.copy(), self.yd.copy()) + + #Get and store event information + event_info = [[],flag == ID_COMPLETE] + if flag == ID_COMPLETE: + self.statistics["ntimeevents"] += 1#Time event detected + if flag == ID_EVENT: + event_info[0] = self.state_event_info() + if REPORT_CONTINUOUSLY: + self._chattering_check(event_info) + + #Log the information + if LOUD >= self.options["verbosity"]: + self.log_event(self.t, event_info, LOUD) + if SCREAM >= self.options["verbosity"]: + self.log_message("A discontinuity occured at t = %e."%self.t,SCREAM) + self.log_message("Current switches: " + str(self.sw), SCREAM) + self.log_message('Event info: ' + str(event_info), SCREAM) + + #Print statistics + self.print_statistics(LOUD) + + try: + self.problem.handle_event(self, event_info) #self corresponds to the solver + except TerminateSimulation: #Terminating the simulation after indication from handle event + self.log_message("Terminating simulation at t = %f after signal from handle_event."%self.t, NORMAL) + break + + flag_initialize = True + + #Update options + opts["initialize"] = flag_initialize + + #Logg after the event handling if there was a communication point there. + if flag_initialize and (output_list is None or self.store_event_points): + if type == 0: + self.problem.handle_result(self, self.t, self.y.copy()) + else: + self.problem.handle_result(self, self.t, self.y.copy(), self.yd.copy()) + + if self.t == tfinal: #Finished simulation (might occur due to event at the final time) + break + + def report_solution(self, t, y, yd, opts): + '''Is called after each successful step in case the report continuously + option is active. Here possible interpolation is done and the result + handeled. Furthermore possible step events are checked. + ''' + self.t, self.y, self.yd = t, y.copy(), yd.copy() + + #Store the elapsed time for a single step + if self.options["clock_step"]: + self.elapsed_step_time = timer() - self.clock_start + self.clock_start = timer() + + #Check elapsed timed + if self.time_limit_activated: + if self.time_limit-(timer()-self.time_integration_start) < 0.0: + raise TimeLimitExceeded("The time limit was exceeded at integration time %.8E."%self.t) + + if self.display_progress: + if (timer() - self.time_integration_start) > self.display_counter*10: + self.display_counter += 1 + + sys.stdout.write(" Integrator time: %e" % self.t) + sys.stdout.write('\r') + sys.stdout.flush() + + self.chattering_clear_counter += 1 + if self.chattering_clear_counter > 3: + self.chattering_check = None + self.chattering_ok_print = 1 + + #Store data depending on situation + if opts["output_list"] is not None: + output_list = opts["output_list"] + output_index = opts["output_index"] + try: + while output_list[output_index] <= t: + if self.problem_info["type"] == 0: + self.problem.handle_result(self, output_list[output_index], + self.interpolate(output_list[output_index])) + else: + self.problem.handle_result(self, output_list[output_index], + self.interpolate(output_list[output_index]), + self.interpolate(output_list[output_index],1)) + output_index = output_index + 1 + except IndexError: + pass + opts["output_index"] = output_index + else: + if self.problem_info["type"] == 0: + self.problem.handle_result(self,t,y.copy()) + else: + self.problem.handle_result(self,t,y.copy(),yd.copy()) + + #Callback to FMU + if self.problem_info["step_events"]: + flag_initialize = self.problem.step_events(self) #completed step returned to FMU + if flag_initialize: + self.statistics["nstepevents"] += 1 + else: + flag_initialize = False + + return flag_initialize + + def event_locator(self, t_low, t_high, y_high, yd_high): + '''Checks if an event occurs in [t_low, t_high], if that is the case event + localization is started. Event localization finds the earliest small interval + that contains a change in domain. The right endpoint of this interval is then + returned as the time to restart the integration at. + ''' + + g_high = self.event_func(t_high, y_high, yd_high) + g_low = self.g_old + self.statistics["nstatefcns"] += 1 + n_g = self.problem_info["dimRoot"] + TOL = max(t_low, t_high) * 1e-13 + #Check for events in [t_low, t_high]. + for i in xrange(n_g): + if (g_low[i] > 0) != (g_high[i] > 0): + break + else: + self.g_old = g_high + return (ID_PY_OK, t_high, y_high, yd_high) + + side = 0 + sideprev = -1 + + while abs(t_high - t_low) > TOL: + #Adjust alpha if the same side is choosen more than once in a row. + if (sideprev == side): + if side == 2: + alpha = alpha * 2.0 + else: + alpha = alpha / 2.0 + #Otherwise alpha = 1 and the secant rule is used. + else: + alpha = 1 + + #Decide which event function to iterate with. + maxfrac = 0 + imax = 0 #Avoid compilation problem + for i in xrange(n_g): + if ((g_low[i] > 0) != (g_high[i] > 0)): + gfrac = abs(g_high[i]/(g_low[i] - g_high[i])) + if gfrac >= maxfrac: + maxfrac = gfrac + imax = i + + #Hack for solving the slow converging case when g is zero for a large part of [t_low, t_high]. + if g_high[imax] == 0 or g_low[imax] == 0: + t_mid = (t_low + t_high)/2 + else: + t_mid = t_high - (t_high - t_low)*g_high[imax]/ \ + (g_high[imax] - alpha*g_low[imax]) + + #Check if t_mid is to close to current brackets and adjust inwards if so is the case. + if (abs(t_mid - t_low) < TOL/2): + fracint = abs(t_low - t_high)/TOL + if fracint > 5: + delta = (t_high - t_low) / 10.0 + else: + delta = (t_high - t_low) / (2.0 * fracint) + t_mid = t_low + delta + + if (abs(t_mid - t_high) < TOL/2): + fracint = abs(t_low - t_high)/TOL + if fracint > 5: + delta = (t_high - t_low) / 10.0 + else: + delta = (t_high - t_low) / (2.0 * fracint) + t_mid = t_high - delta + + #Calculate g at t_mid and check for events in [t_low, t_mid]. + g_mid = self.event_func(t_mid, self.interpolate(t_mid), self.interpolate(t_mid, 1)) + self.statistics["nstatefcns"] += 1 + sideprev = side + for i in xrange(n_g): + if (g_low[i] > 0) != (g_mid[i] > 0): + (t_high, g_high) = (t_mid, g_mid[0:n_g]) + side = 1 + break + #If there are no events in [t_low, t_mid] there must be some event in [t_mid, t_high]. + else: + (t_low, g_low) = (t_mid, g_mid[0:n_g]) + side = 2 + + event_info = N.array([0] * n_g) + for i in xrange(n_g): + if (g_low[i] > 0) != (g_high[i] > 0): + event_info[i] = 1 if g_high[i] > 0 else -1 + + self.set_event_info(event_info) + self.statistics["nstateevents"] += 1 + self.g_old = g_high + return (ID_PY_EVENT, t_high, self.interpolate(t_high), self.interpolate(t_high, 1)) + + def plot(self, mask=None, der=False, **kwargs): + """ + Plot the computed solution. + + Parameters:: + + mask + - Default 'None'. Used to determine which variables that is to be plotted. + Used as a list of integers, ones represents the variable that is to be + plotted and zeros that is not. + + - Should be a list of integers. + + Example: + mask = [1,0] , plots the first variable. + + der + - Default 'False'. When 'True' plots the derivative variables also. + + - Should be a Boolean. + + Example: + der = True + **kwargs + - See http://matplotlib.sourceforge.net/api/pyplot_api.html#matplotlib.pyplot.plot + for information about the available options for **kwargs. + """ + import pylab as P + + if len(self.t_sol) > 0: + P.figure(1) + if not mask: + P.plot(self.t_sol, self.y_sol, **kwargs) + else: + if not isinstance(mask, list): + raise Implicit_ODE_Exception('Mask must be a list of integers') + if not len(mask)==len(self.y_sol[-1]): + raise Implicit_ODE_Exception('Mask must be a list of integers of equal length as '\ + 'the number of variables.') + for i in range(len(mask)): + if mask[i]: + P.plot(self.t_sol, N.array(self.y_sol)[:,i], **kwargs) + + P.xlabel('time') + P.ylabel('state') + P.title(self.problem.name) + + + if der and not mask: + P.figure(2) + P.plot(self.t_sol, self.yd_sol, **kwargs) + P.xlabel('time') + P.ylabel('state derivatives') + P.title(self.problem.name) + elif mask and der: + P.figure(2) + if not isinstance(mask, list): + raise Implicit_ODE_Exception('Mask must be a list of integers') + if not len(mask)==len(self.yd_sol[-1]): + raise Implicit_ODE_Exception('Mask must be a list of integers of equal length as '\ + 'the number of variables.') + for i in range(len(mask)): + if mask[i]: + P.plot(self.t_sol, N.array(self.yd_sol)[:,i], **kwargs) + + P.xlabel('time') + P.ylabel('state derivatives') + P.title(self.problem.name) + + P.show() + else: + self.log_message("No result for plotting found.",NORMAL) + + +cdef class OverdeterminedDAE(Implicit_ODE): + def check_instance(self): + if not isinstance(self.problem, Overdetermined_Problem) and not isinstance(self.problem, Implicit_Problem): + raise Implicit_ODE_Exception('The problem needs to be a subclass of Overdetermined_Problem or of Implicit_Problem.') + + + diff --git a/src/lib/sundials_callbacks.pxi b/src/lib/sundials_callbacks.pxi index a1f1de2e..d8e3a920 100644 --- a/src/lib/sundials_callbacks.pxi +++ b/src/lib/sundials_callbacks.pxi @@ -1,77 +1,77 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import cython - -#================= -# Module functions -#================= - -cdef N_Vector N_VNewEmpty_Euclidean(long int n): - cdef N_Vector v = N_VNew_Serial(n) - v.ops.nvwrmsnorm = v.ops.nvwl2norm #Overwrite the WRMS norm to the 2-Norm - return v - -cdef inline N_Vector arr2nv(x): - x=N.array(x) - cdef long int n = len(x) - cdef N.ndarray[realtype, ndim=1,mode='c'] ndx=x - cdef void* data_ptr=PyArray_DATA(ndx) - cdef N_Vector v=N_VNew_Serial(n) - memcpy((v.content).data, data_ptr, n*sizeof(realtype)) - return v - -cdef inline N_Vector arr2nv_euclidean(x): - x=N.array(x) - cdef long int n = len(x) - cdef N.ndarray[realtype, ndim=1,mode='c'] ndx=x - cdef void* data_ptr=PyArray_DATA(ndx) - cdef N_Vector v=N_VNewEmpty_Euclidean(n) - memcpy((v.content).data, data_ptr, n*sizeof(realtype)) - return v - -cdef inline void arr2nv_inplace(x, N_Vector out): - x=N.array(x) - cdef long int n = len(x) - cdef N.ndarray[realtype, ndim=1,mode='c'] ndx=x - cdef void* data_ptr=PyArray_DATA(ndx) - memcpy((out.content).data, data_ptr, n*sizeof(realtype)) - -cdef inline N.ndarray nv2arr(N_Vector v): - cdef long int n = (v.content).length - cdef realtype* v_data = (v.content).data - cdef N.ndarray[realtype, ndim=1, mode='c'] x=N.empty(n) - memcpy(x.data, v_data, n*sizeof(realtype)) - return x - -cdef inline void nv2arr_inplace(N_Vector v, N.ndarray o): - cdef long int n = (v.content).length - cdef realtype* v_data = (v.content).data - memcpy(o.data, v_data, n*sizeof(realtype)) - -cdef inline void nv2mat_inplace(int Ns, N_Vector *v, N.ndarray o): - cdef long int i,j, Nf - for i in range(Ns): - Nf = (v[i].content).length - for j in range(Nf): - o[j,i] = (v[i].content).data[j] - -cdef inline realtype2arr(realtype *data, int n): - """Create new numpy array from realtype*""" - cdef N.ndarray[realtype, ndim=1, mode='c'] x=N.empty(n) - memcpy(x.data, data, n*sizeof(realtype)) - return x +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import cython + +#================= +# Module functions +#================= + +cdef N_Vector N_VNewEmpty_Euclidean(long int n): + cdef N_Vector v = N_VNew_Serial(n) + v.ops.nvwrmsnorm = v.ops.nvwl2norm #Overwrite the WRMS norm to the 2-Norm + return v + +cdef inline N_Vector arr2nv(x): + x=N.array(x) + cdef long int n = len(x) + cdef N.ndarray[realtype, ndim=1,mode='c'] ndx=x + cdef void* data_ptr=PyArray_DATA(ndx) + cdef N_Vector v=N_VNew_Serial(n) + memcpy((v.content).data, data_ptr, n*sizeof(realtype)) + return v + +cdef inline N_Vector arr2nv_euclidean(x): + x=N.array(x) + cdef long int n = len(x) + cdef N.ndarray[realtype, ndim=1,mode='c'] ndx=x + cdef void* data_ptr=PyArray_DATA(ndx) + cdef N_Vector v=N_VNewEmpty_Euclidean(n) + memcpy((v.content).data, data_ptr, n*sizeof(realtype)) + return v + +cdef inline void arr2nv_inplace(x, N_Vector out): + x=N.array(x) + cdef long int n = len(x) + cdef N.ndarray[realtype, ndim=1,mode='c'] ndx=x + cdef void* data_ptr=PyArray_DATA(ndx) + memcpy((out.content).data, data_ptr, n*sizeof(realtype)) + +cdef inline N.ndarray nv2arr(N_Vector v): + cdef long int n = (v.content).length + cdef realtype* v_data = (v.content).data + cdef N.ndarray[realtype, ndim=1, mode='c'] x=N.empty(n) + memcpy(x.data, v_data, n*sizeof(realtype)) + return x + +cdef inline void nv2arr_inplace(N_Vector v, N.ndarray o): + cdef long int n = (v.content).length + cdef realtype* v_data = (v.content).data + memcpy(o.data, v_data, n*sizeof(realtype)) + +cdef inline void nv2mat_inplace(int Ns, N_Vector *v, N.ndarray o): + cdef long int i,j, Nf + for i in range(Ns): + Nf = (v[i].content).length + for j in range(Nf): + o[j,i] = (v[i].content).data[j] + +cdef inline realtype2arr(realtype *data, int n): + """Create new numpy array from realtype*""" + cdef N.ndarray[realtype, ndim=1, mode='c'] x=N.empty(n) + memcpy(x.data, data, n*sizeof(realtype)) + return x diff --git a/src/lib/sundials_constants.pxi b/src/lib/sundials_constants.pxi index 374ec367..c5069917 100644 --- a/src/lib/sundials_constants.pxi +++ b/src/lib/sundials_constants.pxi @@ -1,241 +1,241 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -""" -This file contains the constants from Sundials. -""" - -#========== -# CVode -#========== - -#CVode Input -#--------- -# Main solver module -DEF CV_ADAMS = 1 -DEF CV_BDF = 2 -DEF CV_FUNCTIONAL = 1 -DEF CV_NEWTON = 2 -DEF CV_NORMAL = 1 -DEF CV_ONE_STEP = 2 -DEF CV_SIMULTANEOUS = 1 -DEF CV_STAGGERED = 2 -DEF CV_STAGGERED1 = 3 -DEF CV_CENTERED = 1 -DEF CV_FORWARD = 2 -# Iterative solver module -DEF PREC_NONE = 0 -DEF PREC_LEFT = 1 -DEF PREC_RIGHT = 2 -DEF PREC_BOTH = 3 - -#CVode Output -#---------- -# Main solver module -DEF CV_SUCCESS = 0 -DEF CV_TSTOP_RETURN = 1 -DEF CV_REC_ERR = 1 # Recoverable error. -DEF CV_ROOT_RETURN = 2 # CVSolve succeeded and found one or more roots. -DEF CV_STEP_RETURN = 3 -DEF CV_WARNING = 99 -DEF CV_TOO_MUCH_WORK = -1 -DEF CV_TOO_MUCH_ACC = -2 -DEF CV_ERR_FAIL = -3 -DEF CV_CONV_FAIL = -4 -DEF CV_LINIT_FAIL = -5 -DEF CV_LSETUP_FAIL = -6 -DEF CV_LSOLVE_FAIL = -7 -DEF CV_RHSFUNC_FAIL = -8 -DEF CV_FIRST_RHSFUNC_ERR = -9 -DEF CV_REPTD_RHSFUNC_ERR = -10 -DEF CV_UNREC_RHSFUNC_ERR = -11 -DEF CV_RTFUNC_FAIL = -12 # The rootfinding function failed in an unrecoverable manner. -DEF CV_NLS_INIT_FAIL = -13 -DEF CV_MEM_FAIL = -20 -DEF CV_MEM_NULL = -21 -DEF CV_ILL_INPUT = -22 -DEF CV_NO_MALLOC = -23 -DEF CV_BAD_K = -24 -DEF CV_BAD_T = -25 -DEF CV_BAD_DKY = -26 -DEF CV_TOO_CLOSE = -27 -# Linear solver module -DEF CVDLS_SUCCESS = 0 #Successful function return -DEF CVDLS_MEM_NULL = -1 #The cvode_mem argument was NULL -DEF CVDLS_LMEM_NULL = -2 -DEF CVDLS_ILL_INPUT = -3 -DEF CVDLS_MEM_FAIL = -4 #A memory allocation request failed. -DEF CVDLS_JACFUNC_UNRECVR = -5 -DEF CVDLS_JACFUNC_RECVR = -6 -# Iterative -DEF SPGMR_SUCCESS = 0 #Success, Converged -DEF SPGMR_ATIMES_FAIL_REC = 5 #The Jacobian-vector function failed recoverably -DEF SPGMR_PSOLVE_FAIL_UNREC = -3 #The preconditioner solve function failed unrecoverably - -DEF CVSPILS_SUCCESS = 0 #Success - -# Sensitivity constants -DEF CV_SRHSFUNC_FAIL = -41 #The sensitivity right-hand side function failed unrecoverable -DEF CV_FIRST_SRHSFUNC_ERR = -42 #The sensitivity right-hand side function failed at first call -DEF CV_REPTD_SRHSFUNC_ERR = -43 #Covergence tests occurred too many times. -DEF CV_UNREC_SRHSFUNC_ERR = -44 #The sensitivity right-hand side function had a recoverable error but was unable to recover -DEF CV_BAD_IS = -45 - - -DEF CSC_MAT = 0 -DEF CSR_MAT = 1 - -#========== -# IDA -#========== - -#IDA Input -#--------- -# Main solver module -DEF IDA_NORMAL = 1 # Solver returns at specified output time. -DEF IDA_ONE_STEP = 2 # Solver returns after each successful step. -DEF IDA_SIMULTANEOUS = 1 # Simultaneous corrector forward sensitivity method. -DEF IDA_STAGGERED = 2 # Staggered corrector forward sensitivity method. -DEF IDA_CENTERED = 1 # Central difference quotient approximation (2nd order) of the sensitivity RHS. -DEF IDA_FORWARD = 2 # Forward difference quotient approximation (1st order) of the sensitivity RHS. -DEF IDA_YA_YDP_INIT = 1 # See IDA Documentation 4.5.4 -DEF IDA_Y_INIT = 2 # See IDA Documentation 4.5.4 -# Iterative solver module -DEF PREC_NONE = 0 -DEF PREC_LEFT = 1 -DEF MODIFIED_GS = 1 -DEF CLASSICAL_GS = 2 - -#IDA Output -#---------- -# Main solver module -DEF IDA_SUCCESS = 0 # Successful function return. -DEF IDA_REC_ERR = 1 # Recoverable error. -DEF IDA_TSTOP_RETURN = 1 # IDASolve succeeded by reaching the specified stopping point. -DEF IDA_ROOT_RETURN = 2 # IDASolve succeeded and found one or more roots. -DEF IDA_WARNING = 99 -DEF IDA_TOO_MUCH_WORK = -1 -DEF IDA_TOO_MUCH_ACC = -2 -DEF IDA_ERR_FAIL = -3 -DEF IDA_CONV_FAIL = -4 -DEF IDA_LINIT_FAIL = -5 -DEF IDA_LSETUP_FAIL = -6 -DEF IDA_LSOLVE_FAIL = -7 -DEF IDA_RES_FAIL = -8 -DEF IDA_REP_RES_FAIL = -9 -DEF IDA_RTFUNC_FAIL = -10 # The rootfinding function failed in an unrecoverable manner. -DEF IDA_CONSTR_FAIL = -11 -DEF IDA_FIRST_RES_FAIL = -12 -DEF IDA_LINESEARCH_FAIL = -13 -DEF IDA_NO_RECOVERY = -14 -DEF IDA_MEM_NULL = -20 -DEF IDA_MEM_FAIL = -21 -DEF IDA_ILL_INPUT = -22 -DEF IDA_NO_MALLOC = -23 -DEF IDA_BAD_EWT = -24 -DEF IDA_BAD_K = -25 -DEF IDA_BAD_T = -26 -DEF IDA_BAD_DKY = -27 -DEF IDA_NO_QUAD = -30 -DEF IDA_QRHS_FAIL = -31 -DEF IDA_FIRST_QRHS_ERR = -32 -DEF IDA_REP_QRHS_ERR = -33 -DEF IDA_NO_SENS = -40 -DEF IDA_SRES_FAIL = -41 -DEF IDA_REP_SRES_ERR = -42 -DEF IDA_BAD_IS = -43 -DEF IDA_NO_QUADSENS = -50 -DEF IDA_QSRHS_FAIL = -51 -DEF IDA_FIRST_QSRHS_ERR = -52 -DEF IDA_REP_QSRHS_ERR = -53 -# Linear solver module -DEF IDADLS_SUCCESS = 0 -DEF IDADLS_MEM_NULL = -1 -DEF IDADLS_LMEM_NULL = -2 -DEF IDADLS_ILL_INPUT = -3 -DEF IDADLS_MEM_FAIL = -4 -DEF IDADLS_JACFUNC_UNRECVR = -5 -DEF IDADLS_JACFUNC_RECVR = -6 -DEF IDADLS_NO_ADJ = -101 -DEF IDADLS_LMEMB_NULL = -102 -DEF IDASPILS_SUCCESS = 0 -DEF IDASPILS_MEM_NULL = -1 -DEF IDASPILS_LMEM_NULL = -2 -DEF IDASPILS_ILL_INPUT = -3 -DEF IDASPILS_MEM_FAIL = -4 -DEF IDASPILS_PMEM_NULL = -5 -DEF IDASPILS_NO_ADJ = -101 -DEF IDASPILS_LMEMB_NULL = -102 - -### -# KINSOL -### -DEF KIN_SUCCESS =0 -DEF KIN_INITIAL_GUESS_OK =1 -DEF KIN_STEP_LT_STPTOL =2 -DEF KIN_REC_ERR =5 -DEF KIN_WARNING =99 -DEF KIN_MEM_NULL =-1 -DEF KIN_ILL_INPUT =-2 -DEF KIN_NO_MALLOC =-3 -DEF KIN_MEM_FAIL =-4 -DEF KIN_LINESEARCH_NONCONV =-5 -DEF KIN_MAXITER_REACHED =-6 -DEF KIN_MXNEWT_5X_EXCEEDED =-7 -DEF KIN_LINESEARCH_BCFAIL =-8 -DEF KIN_LINSOLV_NO_RECOVERY =-9 -DEF KIN_LINIT_FAIL =-10 -DEF KIN_LSETUP_FAIL =-11 -DEF KIN_LSOLVE_FAIL =-12 -DEF KIN_SYSFUNC_FAIL =-13 -DEF KIN_FIRST_SYSFUNC_ERR =-14 -DEF KIN_REPTD_SYSFUNC_ERR =-15 - -DEF KIN_NONE =0 -DEF KIN_LINESEARCH =1 - -# ----------------------------------------------------------------- -# KINDirect constants -# ----------------------------------------------------------------- - -DEF KINDLS_SUCCESS =0 - -DEF KINDLS_MEM_NULL =-1 -DEF KINDLS_LMEM_NULL =-2 -DEF KINDLS_ILL_INPUT =-3 -DEF KINDLS_MEM_FAIL =-4 -DEF KINDLS_JACFUNC_UNRECVR =-5 -DEF KINDLS_JACFUNC_RECVR =-6 - - -DEF CV_RHS_IND = 0 # Index to user data rhs handling -DEF CV_RHSF_IND = 0 # Index to user data rhs -DEF CV_JAC_IND = 1 # Index to user data jacobian -DEF CV_ROOT_IND = 1 # Index to user data root handling -DEF CV_ROOTF_IND = 0 # Index to user data root function -DEF CV_SW_IND = 1 # Index to user data root switches - -DEF IDA_NORMAL = 1 # Solver returns at specified output time. -DEF IDA_ONE_STEP = 2 # Solver returns after each successful step. -DEF IDA_RES_IND = 0 # Index to user data residual handling -DEF IDA_SENS_IND = 0 # Index to indicator for sensitivites -DEF IDA_RESF_IND = 1 # Index to user data residual -DEF IDA_JAC_IND = 2 # Index to user data jacobian -DEF IDA_ROOT_IND = 1 # Index to user data root handling -DEF IDA_ROOTF_IND = 0 # Index to user data root function -DEF IDA_SW_IND = 1 # Index to user data root switches - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +""" +This file contains the constants from Sundials. +""" + +#========== +# CVode +#========== + +#CVode Input +#--------- +# Main solver module +DEF CV_ADAMS = 1 +DEF CV_BDF = 2 +DEF CV_FUNCTIONAL = 1 +DEF CV_NEWTON = 2 +DEF CV_NORMAL = 1 +DEF CV_ONE_STEP = 2 +DEF CV_SIMULTANEOUS = 1 +DEF CV_STAGGERED = 2 +DEF CV_STAGGERED1 = 3 +DEF CV_CENTERED = 1 +DEF CV_FORWARD = 2 +# Iterative solver module +DEF PREC_NONE = 0 +DEF PREC_LEFT = 1 +DEF PREC_RIGHT = 2 +DEF PREC_BOTH = 3 + +#CVode Output +#---------- +# Main solver module +DEF CV_SUCCESS = 0 +DEF CV_TSTOP_RETURN = 1 +DEF CV_REC_ERR = 1 # Recoverable error. +DEF CV_ROOT_RETURN = 2 # CVSolve succeeded and found one or more roots. +DEF CV_STEP_RETURN = 3 +DEF CV_WARNING = 99 +DEF CV_TOO_MUCH_WORK = -1 +DEF CV_TOO_MUCH_ACC = -2 +DEF CV_ERR_FAIL = -3 +DEF CV_CONV_FAIL = -4 +DEF CV_LINIT_FAIL = -5 +DEF CV_LSETUP_FAIL = -6 +DEF CV_LSOLVE_FAIL = -7 +DEF CV_RHSFUNC_FAIL = -8 +DEF CV_FIRST_RHSFUNC_ERR = -9 +DEF CV_REPTD_RHSFUNC_ERR = -10 +DEF CV_UNREC_RHSFUNC_ERR = -11 +DEF CV_RTFUNC_FAIL = -12 # The rootfinding function failed in an unrecoverable manner. +DEF CV_NLS_INIT_FAIL = -13 +DEF CV_MEM_FAIL = -20 +DEF CV_MEM_NULL = -21 +DEF CV_ILL_INPUT = -22 +DEF CV_NO_MALLOC = -23 +DEF CV_BAD_K = -24 +DEF CV_BAD_T = -25 +DEF CV_BAD_DKY = -26 +DEF CV_TOO_CLOSE = -27 +# Linear solver module +DEF CVDLS_SUCCESS = 0 #Successful function return +DEF CVDLS_MEM_NULL = -1 #The cvode_mem argument was NULL +DEF CVDLS_LMEM_NULL = -2 +DEF CVDLS_ILL_INPUT = -3 +DEF CVDLS_MEM_FAIL = -4 #A memory allocation request failed. +DEF CVDLS_JACFUNC_UNRECVR = -5 +DEF CVDLS_JACFUNC_RECVR = -6 +# Iterative +DEF SPGMR_SUCCESS = 0 #Success, Converged +DEF SPGMR_ATIMES_FAIL_REC = 5 #The Jacobian-vector function failed recoverably +DEF SPGMR_PSOLVE_FAIL_UNREC = -3 #The preconditioner solve function failed unrecoverably + +DEF CVSPILS_SUCCESS = 0 #Success + +# Sensitivity constants +DEF CV_SRHSFUNC_FAIL = -41 #The sensitivity right-hand side function failed unrecoverable +DEF CV_FIRST_SRHSFUNC_ERR = -42 #The sensitivity right-hand side function failed at first call +DEF CV_REPTD_SRHSFUNC_ERR = -43 #Covergence tests occurred too many times. +DEF CV_UNREC_SRHSFUNC_ERR = -44 #The sensitivity right-hand side function had a recoverable error but was unable to recover +DEF CV_BAD_IS = -45 + + +DEF CSC_MAT = 0 +DEF CSR_MAT = 1 + +#========== +# IDA +#========== + +#IDA Input +#--------- +# Main solver module +DEF IDA_NORMAL = 1 # Solver returns at specified output time. +DEF IDA_ONE_STEP = 2 # Solver returns after each successful step. +DEF IDA_SIMULTANEOUS = 1 # Simultaneous corrector forward sensitivity method. +DEF IDA_STAGGERED = 2 # Staggered corrector forward sensitivity method. +DEF IDA_CENTERED = 1 # Central difference quotient approximation (2nd order) of the sensitivity RHS. +DEF IDA_FORWARD = 2 # Forward difference quotient approximation (1st order) of the sensitivity RHS. +DEF IDA_YA_YDP_INIT = 1 # See IDA Documentation 4.5.4 +DEF IDA_Y_INIT = 2 # See IDA Documentation 4.5.4 +# Iterative solver module +DEF PREC_NONE = 0 +DEF PREC_LEFT = 1 +DEF MODIFIED_GS = 1 +DEF CLASSICAL_GS = 2 + +#IDA Output +#---------- +# Main solver module +DEF IDA_SUCCESS = 0 # Successful function return. +DEF IDA_REC_ERR = 1 # Recoverable error. +DEF IDA_TSTOP_RETURN = 1 # IDASolve succeeded by reaching the specified stopping point. +DEF IDA_ROOT_RETURN = 2 # IDASolve succeeded and found one or more roots. +DEF IDA_WARNING = 99 +DEF IDA_TOO_MUCH_WORK = -1 +DEF IDA_TOO_MUCH_ACC = -2 +DEF IDA_ERR_FAIL = -3 +DEF IDA_CONV_FAIL = -4 +DEF IDA_LINIT_FAIL = -5 +DEF IDA_LSETUP_FAIL = -6 +DEF IDA_LSOLVE_FAIL = -7 +DEF IDA_RES_FAIL = -8 +DEF IDA_REP_RES_FAIL = -9 +DEF IDA_RTFUNC_FAIL = -10 # The rootfinding function failed in an unrecoverable manner. +DEF IDA_CONSTR_FAIL = -11 +DEF IDA_FIRST_RES_FAIL = -12 +DEF IDA_LINESEARCH_FAIL = -13 +DEF IDA_NO_RECOVERY = -14 +DEF IDA_MEM_NULL = -20 +DEF IDA_MEM_FAIL = -21 +DEF IDA_ILL_INPUT = -22 +DEF IDA_NO_MALLOC = -23 +DEF IDA_BAD_EWT = -24 +DEF IDA_BAD_K = -25 +DEF IDA_BAD_T = -26 +DEF IDA_BAD_DKY = -27 +DEF IDA_NO_QUAD = -30 +DEF IDA_QRHS_FAIL = -31 +DEF IDA_FIRST_QRHS_ERR = -32 +DEF IDA_REP_QRHS_ERR = -33 +DEF IDA_NO_SENS = -40 +DEF IDA_SRES_FAIL = -41 +DEF IDA_REP_SRES_ERR = -42 +DEF IDA_BAD_IS = -43 +DEF IDA_NO_QUADSENS = -50 +DEF IDA_QSRHS_FAIL = -51 +DEF IDA_FIRST_QSRHS_ERR = -52 +DEF IDA_REP_QSRHS_ERR = -53 +# Linear solver module +DEF IDADLS_SUCCESS = 0 +DEF IDADLS_MEM_NULL = -1 +DEF IDADLS_LMEM_NULL = -2 +DEF IDADLS_ILL_INPUT = -3 +DEF IDADLS_MEM_FAIL = -4 +DEF IDADLS_JACFUNC_UNRECVR = -5 +DEF IDADLS_JACFUNC_RECVR = -6 +DEF IDADLS_NO_ADJ = -101 +DEF IDADLS_LMEMB_NULL = -102 +DEF IDASPILS_SUCCESS = 0 +DEF IDASPILS_MEM_NULL = -1 +DEF IDASPILS_LMEM_NULL = -2 +DEF IDASPILS_ILL_INPUT = -3 +DEF IDASPILS_MEM_FAIL = -4 +DEF IDASPILS_PMEM_NULL = -5 +DEF IDASPILS_NO_ADJ = -101 +DEF IDASPILS_LMEMB_NULL = -102 + +### +# KINSOL +### +DEF KIN_SUCCESS =0 +DEF KIN_INITIAL_GUESS_OK =1 +DEF KIN_STEP_LT_STPTOL =2 +DEF KIN_REC_ERR =5 +DEF KIN_WARNING =99 +DEF KIN_MEM_NULL =-1 +DEF KIN_ILL_INPUT =-2 +DEF KIN_NO_MALLOC =-3 +DEF KIN_MEM_FAIL =-4 +DEF KIN_LINESEARCH_NONCONV =-5 +DEF KIN_MAXITER_REACHED =-6 +DEF KIN_MXNEWT_5X_EXCEEDED =-7 +DEF KIN_LINESEARCH_BCFAIL =-8 +DEF KIN_LINSOLV_NO_RECOVERY =-9 +DEF KIN_LINIT_FAIL =-10 +DEF KIN_LSETUP_FAIL =-11 +DEF KIN_LSOLVE_FAIL =-12 +DEF KIN_SYSFUNC_FAIL =-13 +DEF KIN_FIRST_SYSFUNC_ERR =-14 +DEF KIN_REPTD_SYSFUNC_ERR =-15 + +DEF KIN_NONE =0 +DEF KIN_LINESEARCH =1 + +# ----------------------------------------------------------------- +# KINDirect constants +# ----------------------------------------------------------------- + +DEF KINDLS_SUCCESS =0 + +DEF KINDLS_MEM_NULL =-1 +DEF KINDLS_LMEM_NULL =-2 +DEF KINDLS_ILL_INPUT =-3 +DEF KINDLS_MEM_FAIL =-4 +DEF KINDLS_JACFUNC_UNRECVR =-5 +DEF KINDLS_JACFUNC_RECVR =-6 + + +DEF CV_RHS_IND = 0 # Index to user data rhs handling +DEF CV_RHSF_IND = 0 # Index to user data rhs +DEF CV_JAC_IND = 1 # Index to user data jacobian +DEF CV_ROOT_IND = 1 # Index to user data root handling +DEF CV_ROOTF_IND = 0 # Index to user data root function +DEF CV_SW_IND = 1 # Index to user data root switches + +DEF IDA_NORMAL = 1 # Solver returns at specified output time. +DEF IDA_ONE_STEP = 2 # Solver returns after each successful step. +DEF IDA_RES_IND = 0 # Index to user data residual handling +DEF IDA_SENS_IND = 0 # Index to indicator for sensitivites +DEF IDA_RESF_IND = 1 # Index to user data residual +DEF IDA_JAC_IND = 2 # Index to user data jacobian +DEF IDA_ROOT_IND = 1 # Index to user data root handling +DEF IDA_ROOTF_IND = 0 # Index to user data root function +DEF IDA_SW_IND = 1 # Index to user data root switches + diff --git a/src/lib/sundials_includes.pxd b/src/lib/sundials_includes.pxd index 20dac618..b2af67fb 100644 --- a/src/lib/sundials_includes.pxd +++ b/src/lib/sundials_includes.pxd @@ -1,697 +1,699 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -""" -Cython Wrapper for interfacing Python with CVode and IDA (Sundials Version 2.4.0) -Claus Fuhrer, Lund University -Christian Andersson, Lund University - -see also Jon Olav Vik: -http://codespeak.net/pipermail/cython-dev/2009-June/005947.html - -""" -#import numpy as N -#cimport numpy as N - -from numpy cimport NPY_DOUBLE, npy_intp, NPY_INT - -#============================================== -#External definitions from Sundials headers -#============================================== - -cdef extern from "sundials/sundials_types.h": - ctypedef double realtype - ctypedef bint booleantype # should be bool instead of bint, but there is a bug in Cython - -cdef extern from "sundials/sundials_nvector.h": - ctypedef _generic_N_Vector* N_Vector - - cdef struct _generic_N_Vector_Ops: - realtype (*nvwrmsnorm)(N_Vector, N_Vector) - realtype (*nvwl2norm)(N_Vector, N_Vector) - ctypedef _generic_N_Vector_Ops *N_Vector_Ops - - cdef struct _generic_N_Vector: - void* content - N_Vector_Ops ops - -cdef extern from "nvector/nvector_serial.h": - cdef struct _N_VectorContent_Serial: - long int length - booleantype own_data - realtype* data - ctypedef _N_VectorContent_Serial* N_VectorContent_Serial - cdef N_Vector N_VMake_Serial(long int vec_length, realtype *v_data) - N_Vector *N_VCloneVectorArray_Serial(int count, N_Vector w) - N_Vector *N_VCloneVectorArrayEmpty_Serial(int count, N_Vector w) - void N_VSetArrayPointer_Serial(realtype *v_data, N_Vector v) - void N_VConst_Serial(realtype c, N_Vector z) - N_Vector N_VNew_Serial(long int vec_length) - void N_VDestroy_Serial(N_Vector v) - void N_VPrint_Serial(N_Vector v) - -IF SUNDIALS_VERSION >= (4,0,0): - cdef extern from "sundials/sundials_nonlinearsolver.h": - ctypedef _generic_SUNNonlinearSolver *SUNNonlinearSolver - - cdef struct _generic_SUNNonlinearSolver: - pass -ELSE: - #Dummy defines - ctypedef void *SUNNonlinearSolver - -IF SUNDIALS_VERSION >= (3,0,0): - cdef extern from "sundials/sundials_types.h": - IF SUNDIALS_VECTOR_SIZE == "64": - ctypedef long int sunindextype - ELSE: - ctypedef int sunindextype - cdef extern from "sundials/sundials_matrix.h": - ctypedef _generic_SUNMatrix *SUNMatrix - void SUNMatDestroy(SUNMatrix A) - - cdef struct _generic_SUNMatrix_Ops: - SUNMatrix_ID (*getid)(SUNMatrix) - SUNMatrix (*clone)(SUNMatrix) - void (*destroy)(SUNMatrix) - int (*zero)(SUNMatrix) - int (*copy)(SUNMatrix, SUNMatrix) - int (*scaleadd)(realtype, SUNMatrix, SUNMatrix) - int (*scaleaddi)(realtype, SUNMatrix) - int (*matvec)(SUNMatrix, N_Vector, N_Vector) - int (*space)(SUNMatrix, long int*, long int*) - - cdef struct _generic_SUNMatrix: - void *content - _generic_SUNMatrix_Ops *ops - - cdef enum SUNMatrix_ID: - SUNMATRIX_DENSE, - SUNMATRIX_BAND, - SUNMATRIX_SPARSE, - SUNMATRIX_CUSTOM - - cdef extern from "sundials/sundials_linearsolver.h": - ctypedef _generic_SUNLinearSolver *SUNLinearSolver - int SUNLinSolFree(SUNLinearSolver S) - - cdef struct _generic_SUNLinearSolver_Ops: - SUNLinearSolver_Type (*gettype)(SUNLinearSolver) - int (*setatimes)(SUNLinearSolver, void*, ATimesFn) - int (*setpreconditioner)(SUNLinearSolver, void*, - PSetupFn, PSolveFn) - int (*setscalingvectors)(SUNLinearSolver, - N_Vector, N_Vector) - int (*initialize)(SUNLinearSolver) - int (*setup)(SUNLinearSolver, SUNMatrix) - int (*solve)(SUNLinearSolver, SUNMatrix, N_Vector, - N_Vector, realtype) - int (*numiters)(SUNLinearSolver) - realtype (*resnorm)(SUNLinearSolver) - long int (*lastflag)(SUNLinearSolver) - int (*space)(SUNLinearSolver, long int*, long int*) - N_Vector (*resid)(SUNLinearSolver) - int (*free)(SUNLinearSolver) - - cdef struct _generic_SUNLinearSolver: - void *content - _generic_SUNLinearSolver_Ops *ops - - cdef enum SUNLinearSolver_Type: - SUNLINEARSOLVER_DIRECT, - SUNLINEARSOLVER_ITERATIVE, - SUNLINEARSOLVER_CUSTOM - - cdef extern from "sunmatrix/sunmatrix_dense.h": - ctypedef _SUNMatrixContent_Dense *SUNMatrixContent_Dense - cdef struct _SUNMatrixContent_Dense: - sunindextype M - sunindextype N - realtype *data - sunindextype ldata - realtype **cols - SUNMatrix SUNDenseMatrix(sunindextype M, sunindextype N) - cdef extern from "sunmatrix/sunmatrix_sparse.h": - ctypedef _SUNMatrixContent_Sparse *SUNMatrixContent_Sparse - cdef struct _SUNMatrixContent_Sparse: - sunindextype M - sunindextype N - sunindextype NNZ - sunindextype NP - realtype *data - int sparsetype - sunindextype *indexvals - sunindextype *indexptrs - sunindextype **rowvals - sunindextype **colptrs - sunindextype **colvals - sunindextype **rowptrs - SUNMatrix SUNSparseMatrix(sunindextype M, sunindextype N, sunindextype NNZ, int sparsetype) - cdef extern from "sunlinsol/sunlinsol_dense.h": - SUNLinearSolver SUNDenseLinearSolver(N_Vector y, SUNMatrix A) - cdef extern from "sunlinsol/sunlinsol_spgmr.h": - SUNLinearSolver SUNSPGMR(N_Vector y, int pretype, int maxl) - -ELSE: - #Dummy defines - ctypedef void *SUNLinearSolver - ctypedef void *SUNMatrix - ctypedef void *SUNMatrixContent_Dense - ctypedef void *SUNMatrixContent_Sparse - ctypedef int sunindextype - - -#Struct for handling the Jacobian data -cdef extern from "sundials/sundials_direct.h": - cdef struct _DlsMat: - int type - int M - int N - int ldim - int mu - int ml - int s_mu - realtype *data - int ldata - realtype **cols - ctypedef _DlsMat* DlsMat - cdef realtype* DENSE_COL(DlsMat A, int j) - -IF SUNDIALS_VERSION >= (2,6,3): - cdef extern from "sundials/sundials_sparse.h": - cdef struct _SlsMat: - int M - int N - int NNZ - int NP - realtype *data - int sparsetype - int *indexvals - int *indexptrs - int **rowvals - int **colptrs - int **colvals - int **rowptrs - ctypedef _SlsMat* SlsMat -ELIF SUNDIALS_VERSION >= (2,6,0): - cdef extern from "sundials/sundials_sparse.h": - cdef struct _SlsMat: - int M - int N - int NNZ - realtype *data - int *rowvals - int *colptrs - ctypedef _SlsMat* SlsMat -ELSE: - cdef struct _SlsMat: - int M - int N - int NNZ - realtype *data - int *rowvals - int *colptrs - ctypedef _SlsMat* SlsMat - -#============================================== -# C headers -#============================================== -cdef extern from "string.h": - void *memcpy(void *s1, void *s2, int n) -cdef extern from "stdlib.h": - void *malloc(int size) - void free(void *ptr) - -#============================================== -#External definitions from Sundials headers -#============================================== - -IF SUNDIALS_WITH_SUPERLU: - cdef inline int with_superlu(): return 1 -ELSE: - cdef inline int with_superlu(): return 0 - -IF SUNDIALS_VERSION >= (4,0,0): - cdef extern from "cvodes/cvodes.h": - void* CVodeCreate(int lmm) - - int CVodeSetNonlinearSolver(void *cvode_mem, SUNNonlinearSolver NLS) - int CVodeSetNonlinearSolverSensSim(void *cvode_mem, SUNNonlinearSolver NLS) - int CVodeSetNonlinearSolverSensStg(void *cvode_mem, SUNNonlinearSolver NLS) - - cdef extern from "sunnonlinsol/sunnonlinsol_newton.h": - SUNNonlinearSolver SUNNonlinSol_Newton(N_Vector y) - SUNNonlinearSolver SUNNonlinSol_NewtonSens(int count, N_Vector y) - cdef extern from "sunnonlinsol/sunnonlinsol_fixedpoint.h": - SUNNonlinearSolver SUNNonlinSol_FixedPoint(N_Vector y, int m) - SUNNonlinearSolver SUNNonlinSol_FixedPointSens(int count, N_Vector y, int m) -ELSE: - cdef extern from "cvodes/cvodes.h": - void* CVodeCreate(int lmm, int iter) - -cdef extern from "cvodes/cvodes.h": - ctypedef int (*CVRhsFn)(realtype t, N_Vector y, N_Vector ydot, void *f_data) - int CVodeInit(void *cvode_mem, CVRhsFn f, realtype t0, N_Vector y0) - int CVodeReInit(void *cvode_mem, realtype t0, N_Vector y0) - void CVodeFree(void **cvode_mem) - int CVode(void *cvode_mem, realtype tout, N_Vector yout, realtype *tret, int itask) - - #Functions for settings options - int CVodeSetMaxOrd(void *cvode_mem, int maxord) - int CVodeSetMaxNumSteps(void *cvode_mem, long int mxsteps) - int CVodeSetMaxStep(void *cvode_mem, realtype hmax) - int CVodeSetMinStep(void *cvode_mem, realtype hmin) - int CVodeSetInitStep(void *cvode_mem, realtype hin) - int CVodeSStolerances(void *cvode_mem, realtype reltol, realtype abstol) - int CVodeSVtolerances(void *cvode_mem, realtype reltol, N_Vector abstol) - int CVodeSetStopTime(void *cvode_mem, realtype tstop) - int CVodeSetUserData(void *cvode_mem,void *user_data) - int CVodeSetMaxConvFails(void *cvode_mem, int maxncf) - int CVodeSetMaxErrTestFails(void *cvode_mem, int maxnef) - int CVodeSetMaxNonlinIters(void *cvode_mem, int maxcor) - - #Functions for retrieving results - int CVodeGetDky(void *cvode_mem, realtype t, int k, N_Vector dky) - - #Functions for error handling - ctypedef void (*CVErrHandlerFn)(int error_code, const char *module, const char *function, char *msg, - void *eh_data) - int CVodeSetErrHandlerFn(void *cvode_mem, CVErrHandlerFn ehfun, void* eh_data) - - #Functions for discontinuity handling - ctypedef int (*CVRootFn)(realtype tt, N_Vector yy, realtype *gout, void *user_data) - int CVodeRootDirection(void *cvode_mem, int *rootdir) - int CVodeSetNoInactiveRootWarn(void *cvode_mem) - int CVodeRootInit(void *cvode_mem, int nrtfn, CVRootFn g) - int CVodeGetRootInfo(void *cvode_mem, int *rootsfound) - - #Functions for retrieving statistics - int CVodeGetLastOrder(void * cvode_mem,int *qlast) - int CVodeGetLastStep(void * cvode_mem, realtype *hlast) - int CVodeGetCurrentOrder(void * cvode_mem,int *qcurrent) - int CVodeGetActualInitStep(void * cvode_mem, realtype *hinused) - int CVodeGetNumSteps(void *cvode_mem, long int *nsteps) #Number of steps - int CVodeGetNumRhsEvals(void *cvode_mem, long int *nrevals) #Number of function evals - int CVDlsGetNumJacEvals(void *cvode_mem, long int *njevals) #Number of jac evals - int CVDlsGetNumRhsEvals(void *cvode_mem, long int *nrevalsLS) #Number of res evals due to jac evals - int CVodeGetNumGEvals(void *cvode_mem, long int *ngevals) #Number of root evals - int CVodeGetNumErrTestFails(void *cvode_mem, long int *netfails) #Number of local error test failures - int CVodeGetNumNonlinSolvIters(void *cvode_mem, long int *nniters) #Number of nonlinear iteration - int CVodeGetNumNonlinSolvConvFails(void *cvode_mem, long int *nncfails) #Number of nonlinear conv failures - int CVodeGetNonlinSolvStats(void *cvode_mem, long int *nniters, long int *nncfails) - int CVodeGetIntegratorStats(void* cvode_mem, long int *nsteps, long int *nfevals, - long int *nlinsetups, long int *netfails, int *qlast, int *qcur, - realtype *hinused, realtype *hlast, realtype *hcur, realtype *tcur) - int CVodeGetNumStabLimOrderReds(void *cvode_mem, long int *nslred) - - #Sensitivity methods - ctypedef int (*CVSensRhsFn)(int Ns, realtype t, N_Vector y, N_Vector ydot, N_Vector *yS, - N_Vector *ySdot, void *user_data, N_Vector tmp1, N_Vector tmp2) - ctypedef int (*CVSensRhs1Fn)(int Ns, realtype t, N_Vector y, N_Vector ydot, int iS, N_Vector *yS, - N_Vector *ySdot, void *user_data, N_Vector tmp1, N_Vector tmp2) - int CVodeSensInit(void *cvode_mem, int Ns, int ism, CVSensRhsFn fS, N_Vector *ySO) - int CVodeSensInit1(void *cvode_mem, int Ns, int ism, CVSensRhs1Fn fS1, N_Vector *ySO) - int CVodeSensReInit(void *cvode_mem, int ism, N_Vector *ySO) - int CVodeSensFree(void *cvode_mem) - int CVodeSensToggleOff(void *cvode_mem) - int CVodeSensSStolerances(void *cvode_mem, realtype reltolS, realtype *abstolS) - int CVodeSensSVtolerances(void *cvode_mem, realtype reltolS, N_Vector *abstolS) - int CVodeSensEEtolerances(void *cvode_mem) - int CVodeGetSens(void *cvode_mem, realtype *tret, N_Vector *yS) - int CVodeGetSensDky(void *cvode_mem, realtype t, int k, N_Vector *dkyS) - int CVodeGetSens1(void *cvode_mem, realtype *tret, int iss, N_Vector yS) - int CVodeGetSensDky1(void *cvode_mem, realtype t, int k, int iss, N_Vector dkyS) - int CVodeSetSensParams(void *cvode_mem, realtype *p, realtype *pbar, int *plist) - int CVodeSetSensDQMethod(void *cvode_mem, int DQtype, realtype DQrhomax) - int CVodeSetSensErrCon(void *cvode_mem, booleantype errconS) - int CVodeSetSensMaxNonlinIters(void *cvode_mem, int maxcorS) - int CVodeSetStabLimDet(void *cvode_mem, booleantype stldet) - - - - #Statistics - int CVodeGetEstLocalErrors(void *cvode_mem, N_Vector ele) #Estimated local errors - int CVodeGetErrWeights(void *cvode_mem, N_Vector eweight) #Estimated local errors - int CVodeGetSensNumRhsEvals(void *cvode_mem, long int *nfSevals) - int CVodeGetNumRhsEvalsSens(void *cvode_mem, long int *nfevalsS) - int CVodeGetSensNumErrTestFails(void *cvode_mem, long int *nSetfails) - int CVodeGetSensNumLinSolvSetups(void *cvode_mem, long int *nlinsetupsS) - int CVodeGetSensStats(void *cvode_mem, long int *nfSevals, long int *nfevalsS, - long int *nSetfails, long int *nlinsetupsS) - int CVodeGetSensErrWeights(void *cvode_mem, N_Vector *eSweight) - int CVodeGetSensNumNonlinSolvIters(void *cvode_mem, long int *nSniters) - int CVodeGetSensNumNonlinSolvConvFails(void *cvode_mem, long int *nSncfails) - int CVodeGetSensNonlinSolvStats(void *cvode_mem, long int *nSniters, long int *nSncfails) - int CVodeGetStgrSensNumNonlinSolvIters(void *cvode_mem, long int *nSTGR1niters) - int CVodeGetStgrSensNumNonlinSolvConvFails(void *cvode_mem, long int *nSTGR1ncfails) - -cdef extern from "cvodes/cvodes_spils.h": - ctypedef int (*CVSpilsJacTimesVecFn)(N_Vector v, N_Vector Jv, realtype t, - N_Vector y, N_Vector fy, void *user_data, N_Vector tmp) - -IF SUNDIALS_VERSION >= (3,0,0): - cdef extern from "cvodes/cvodes_direct.h": - ctypedef int (*CVDlsDenseJacFn)(realtype t, N_Vector y, N_Vector fy, - SUNMatrix Jac, void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) - int CVDlsSetLinearSolver(void *cvode_mem, SUNLinearSolver LS, SUNMatrix A) - int CVDlsSetJacFn(void *cvode_mem, CVDlsDenseJacFn djac) - cdef extern from "cvodes/cvodes_spils.h": - int CVSpilsSetLinearSolver(void *cvode_mem, SUNLinearSolver LS) - ctypedef int (*CVSpilsJacTimesSetupFn)(realtype t, N_Vector y, N_Vector fy, void *user_data) - int CVSpilsSetJacTimes(void *cvode_mem, CVSpilsJacTimesSetupFn jtsetup, CVSpilsJacTimesVecFn jtimes) - - ctypedef int (*CVSpilsPrecSetupFn)(realtype t, N_Vector y, N_Vector fy, - booleantype jok, booleantype *jcurPtr, realtype gamma, void *user_data) - ctypedef int (*CVSpilsPrecSolveFn)(realtype t, N_Vector y, N_Vector fy, - N_Vector r, N_Vector z, - realtype gamma, realtype delta, int lr, void *user_data) - - - IF SUNDIALS_WITH_SUPERLU: - cdef extern from "sunlinsol/sunlinsol_superlumt.h": - SUNLinearSolver SUNSuperLUMT(N_Vector y, SUNMatrix A, int num_threads) - ELSE: - cdef inline SUNLinearSolver SUNSuperLUMT(N_Vector y, SUNMatrix A, int num_threads): return NULL - - cdef inline int cv_spils_jtsetup_dummy(realtype t, N_Vector y, N_Vector fy, void *user_data): return 0 - cdef inline tuple version(): return (3,0,0) -ELSE: - cdef extern from "cvodes/cvodes_dense.h": - int CVDense(void *cvode_mem, long int n) - ctypedef int (*CVDlsDenseJacFn)(long int n, realtype t, N_Vector y, N_Vector fy, - DlsMat Jac, void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) - int CVDlsSetDenseJacFn(void *cvode_mem, CVDlsDenseJacFn djac) - - cdef extern from "cvodes/cvodes_spgmr.h": - int CVSpgmr(void *cvode_mem, int pretype, int max1) - - cdef extern from "cvodes/cvodes_spils.h": - int CVSpilsSetJacTimesVecFn(void *cvode_mem, CVSpilsJacTimesVecFn jtv) - ctypedef int (*CVSpilsPrecSetupFn)(realtype t, N_Vector y, N_Vector fy, - booleantype jok, booleantype *jcurPtr, - realtype gamma, void *user_data, - N_Vector tmp1, N_Vector tmp2, - N_Vector tmp3) - ctypedef int (*CVSpilsPrecSolveFn)(realtype t, N_Vector y, N_Vector fy, - N_Vector r, N_Vector z, - realtype gamma, realtype delta, - int lr, void *user_data, N_Vector tmp) - - IF SUNDIALS_VERSION >= (2,6,0): - cdef extern from "cvodes/cvodes_sparse.h": - ctypedef int (*CVSlsSparseJacFn)(realtype t, N_Vector y, N_Vector fy, - SlsMat Jac, void *user_data, N_Vector tmp1, - N_Vector tmp2, N_Vector tmp3) - int CVSlsSetSparseJacFn(void *cvode_mem, CVSlsSparseJacFn jac) - int CVSlsGetNumJacEvals(void *cvode_mem, long int *njevals) - cdef inline tuple version(): return (2,6,0) - IF SUNDIALS_WITH_SUPERLU: - cdef extern from "cvodes/cvodes_superlumt.h": - int CVSuperLUMT(void *cvode_mem, int numthreads, int n, int nnz) - ELSE: - cdef inline int CVSuperLUMT(void *cvode_mem, int numthreads, int n, int nnz): return -1 - ELSE: - cdef inline int CVSuperLUMT(void *cvode_mem, int numthreads, int n, int nnz): return -1 - ctypedef int (*CVSlsSparseJacFn)(realtype t, N_Vector y, N_Vector fy, - SlsMat Jac, void *user_data, N_Vector tmp1, - N_Vector tmp2, N_Vector tmp3) - cdef inline int CVSlsSetSparseJacFn(void *cvode_mem, CVSlsSparseJacFn jac): return -1 - cdef inline int CVSlsGetNumJacEvals(void *cvode_mem, long int *njevals): return -1 - cdef inline tuple version(): return (2,5,0) - -cdef extern from "cvodes/cvodes_spils.h": - int CVSpilsSetPreconditioner(void *cvode_mem, CVSpilsPrecSetupFn psetup, CVSpilsPrecSolveFn psolve) - int CVSpilsGetNumJtimesEvals(void *cvode_mem, long int *njvevals) #Number of jac*vector evals - int CVSpilsGetNumRhsEvals(void *cvode_mem, long int *nfevalsLS) #Number of res evals due to jacÄvector evals - int CVSpilsGetNumPrecEvals(void *cvode_mem, long int *npevals) - int CVSpilsGetNumPrecSolves(void *cvode_mem, long int *npsolves) - -cdef extern from "idas/idas.h": - ctypedef int (*IDAResFn)(realtype tt, N_Vector yy, N_Vector yp, N_Vector rr, void *user_data) - void* IDACreate() - int IDAInit(void* ida_mem, IDAResFn res, realtype t0, N_Vector y0, N_Vector yp0) - int IDAReInit(void* ida_mem, realtype t0, N_Vector y0, N_Vector yp0) - void IDAFree(void **ida_mem) - int IDASolve(void* ida_mem, realtype tout,realtype *tret, N_Vector yret, - N_Vector ypret, int itask) - - #Functions for settings options - int IDASStolerances(void *ida_mem, realtype reltol, realtype abstol) - int IDASVtolerances(void *ida_mem, realtype reltol, N_Vector abstol) - int IDASetSuppressAlg(void *ida_mem, booleantype suppressalg) - int IDASetId(void *ida_mem, N_Vector id) - int IDASetUserData(void *ida_mem,void *user_data) - int IDASetInitStep(void *ida_mem, realtype hin) - int IDASetStopTime(void *ida_mem, realtype tstop) - int IDASetMaxErrTestFails(void *ida_mem, int maxnef) - int IDASetMaxNumSteps(void *ida_mem, long int mxsteps) - int IDASetMaxOrd(void *ida_mem, int maxord) - int IDASetMaxStep(void* ida_mem, realtype hmax) - - #Functions for retrieving results - int IDAGetDky(void *ida_mem, realtype t, int k, N_Vector dky) - - #Functions for error handling - ctypedef void (*IDAErrHandlerFn)(int error_code, const char *module, const char *function, char *msg, - void *eh_data) - int IDASetErrHandlerFn(void *ida_mem,IDAErrHandlerFn ehfun, void* eh_data) - - - #Functions for discontinuity handling - ctypedef int (*IDARootFn)(realtype tt, N_Vector yy, N_Vector yp, realtype *gout, void *user_data) - int IDASetRootDirection(void *ida_mem, int *rootdir) - int IDASetNoInactiveRootWarn(void *ida_mem) - int IDARootInit(void *ida_mem, int nrtfn, IDARootFn g) - int IDAGetRootInfo(void *ida_mem, int *rootsfound) - int IDACalcIC(void *ida_men, int icopt, realtype tout1) - int IDAGetConsistentIC(void *ida_mem, N_Vector y0, N_Vector yp0) - int IDASetLineSearchOffIC(void *ida_mem, booleantype lsoff) - - #Functions for retrieving statistics - int IDAGetEstLocalErrors(void *ida_mem, N_Vector ele) #Estimated local errors - int IDAGetErrWeights(void *ida_mem, N_Vector eweight) - int IDAGetLastStep(void *ida_mem, realtype *hlast) - int IDAGetLastOrder(void *ida_mem,int *qlast) #Last order used - int IDAGetCurrentOrder(void *ida_mem,int *qcurrent) #Order that is about to be tried - int IDAGetNumSteps(void *ida_mem, long int *nsteps) #Number of steps - int IDAGetNumResEvals(void *ida_mem, long int *nrevals) #Number of res evals - int IDADlsGetNumJacEvals(void *ida_mem, long int *njevals) #Number of jac evals - int IDADlsGetNumResEvals(void *ida_mem, long int *nrevalsLS) #Number of res evals due to jac evals - int IDAGetNumGEvals(void *ida_mem, long int *ngevals) #Number of root evals - int IDAGetNumErrTestFails(void *ida_mem, long int *netfails) #Number of local error test failures - int IDAGetNumNonlinSolvIters(void *ida_mem, long int *nniters) #Number of nonlinear iteration - int IDAGetNumNonlinSolvConvFails(void *ida_mem, long int *nncfails) #Number of nonlinear conv failures - int IDAGetNonlinSolvStats(void *ida_mem, long int *nniters, long int *nncfails) - int IDAGetIntegratorStats(void* ida_mem,long int *nsteps, long int *nrevals, - long int *nlinsetups, long int *netfails, int *klast, - int *kcur, realtype *hinused, realtype *hlast, - realtype *hcur, realtype *tcur) - - #Start Sensitivities - #=================== - ctypedef int (*IDASensResFn)(int Ns, realtype t, N_Vector yy, N_Vector yp, - N_Vector *yS, N_Vector *ypS, N_Vector *resvalS, - void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) - int IDASensInit(void *ida_mem, int Ns, int ism, IDASensResFn resS, N_Vector *ySO, N_Vector *ypSO) - int IDASensReInit(void *ida_mem, int ism, N_Vector *ySO, N_Vector *ypSO) - - #Options - int IDASensToggleOff(void *ida_mem) - int IDASensSStolerances(void *ida_mem, realtype reltolS, realtype *abstolS) - int IDASensSVtolerances(void *ida_mem, realtype reltolS, N_Vector *abstolS) - int IDASensEEtolerances(void *ida_mem) - - #Results - int IDAGetSens(void *ida_mem, realtype tret, N_Vector *yS) - int IDAGetSensDky(void *ida_mem, realtype t, int k, N_Vector *dkyS) - int IDAGetSensDky1(void *ida_mem, realtype t, int k, int i, N_Vector dkyS) - - #Options (optional) - int IDASetSensParams(void *ida_mem, realtype *p, realtype *pbar, int *plist) - int IDASetSensDQMethod(void *ida_mem, int DQtype, realtype DQrhomax) - int IDASetSensErrCon(void *ida_mem, booleantype errconS) - int IDASetSensMaxNonlinIters(void *ida_mem, int maxcorS) - - #Statistics - int IDAGetSensNumResEvals(void *ida_mem, long int nfSevals) - int IDAGetNumResEvalsSens(void *ida_mem, long int nfevalsS) - int IDAGetSensNumErrTestFails(void *ida_mem, long int nSetfails) - int IDAGetSensNumLinSolvSetups(void *ida_mem, long int nlinsetupsS) - int IDAGetSensStats(void *ida_mem, long int *nfSevals, long int *nfevalsS, - long int *nSetfails, long int *nlinsetupsS) - int IDAGetSensNumNonlinSolvIters(void *ida_mem, long int nSniters) - int IDAGetSeonsNumNonlinSolvConvFails(void *ida_mem, long int nSncfails) - int IDAGetSensNonlinSolvStats(void *ida_mem, long int *nSniters, long int *nSncfails) - - #End Sensitivities - #================= - -cdef extern from "idas/idas_spils.h": - ctypedef int (*IDASpilsJacTimesVecFn)(realtype tt, N_Vector yy, N_Vector yp, N_Vector rr, - N_Vector v, N_Vector Jv, realtype cj, void *user_data,N_Vector tmp1, N_Vector tmp2) - -IF SUNDIALS_VERSION >= (3,0,0): - cdef extern from "idas/idas_direct.h": - ctypedef int (*IDADlsDenseJacFn)(realtype tt, realtype cj, N_Vector yy, - N_Vector yp, N_Vector rr, SUNMatrix Jac, void *user_data, - N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) - int IDADlsSetJacFn(void *ida_mem, IDADlsDenseJacFn djac) - int IDADlsSetLinearSolver(void *ida_mem, SUNLinearSolver LS, SUNMatrix A) - - cdef extern from "idas/idas_spils.h": - int IDASpilsSetLinearSolver(void *ida_mem, SUNLinearSolver LS) - ctypedef int (*IDASpilsJacTimesSetupFn)(realtype tt, N_Vector yy, - N_Vector yp, N_Vector rr, realtype c_j, void *user_data) - int IDASpilsSetJacTimes(void *ida_mem, - IDASpilsJacTimesSetupFn jtsetup, IDASpilsJacTimesVecFn jtimes) - - cdef inline int ida_spils_jtsetup_dummy(realtype tt, N_Vector yy, N_Vector yp, N_Vector rr, realtype c_j, void *user_data): return 0 -ELSE: - cdef extern from "idas/idas_dense.h": - int IDADense(void *ida_mem, long int n) - ctypedef int (*IDADlsDenseJacFn)(long int Neq, realtype tt, realtype cj, N_Vector yy, - N_Vector yp, N_Vector rr, DlsMat Jac, void *user_data, - N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) - int IDADlsSetDenseJacFn(void *ida_mem, IDADlsDenseJacFn djac) - - cdef extern from "idas/idas_spgmr.h": - int IDASpgmr(void *ida_mem, int max1) - - cdef extern from "idas/idas_spils.h": - int IDASpilsSetJacTimesVecFn(void *ida_mem, IDASpilsJacTimesVecFn ida_jacv) - -cdef extern from "idas/idas_spils.h": - int IDASpilsGetNumJtimesEvals(void *ida_mem, long int *njvevals) #Number of jac*vector - int IDASpilsGetNumResEvals(void *ida_mem, long int *nfevalsLS) #Number of rhs due to jac*vector - - -#################### -# KINSOL -#################### - - -# KINSOL functions and routines -cdef extern from "kinsol/kinsol.h": - # user defined functions - ctypedef int (*KINSysFn)(N_Vector uu, N_Vector fval, void *user_data ) - ctypedef void (*KINErrHandlerFn)(int error_code, char *module, char *function, char *msg, void *user_data) - ctypedef void (*KINInfoHandlerFn)(const char *module, const char *function, char *msg, void *user_data) - # initialization routines - void *KINCreate() - int KINInit(void *kinmem, KINSysFn func, N_Vector tmpl) - - # optional input spec. functions, - # for specificationsdocumentation cf. kinsol.h line 218-449 - int KINSetErrHandlerFn(void *kinmem, KINErrHandlerFn ehfun, void *eh_data) - int KINSetInfoHandlerFn(void *kinmem, KINInfoHandlerFn ihfun, void *ih_data) - int KINSetUserData(void *kinmem, void *user_data) - int KINSetPrintLevel(void *kinmemm, int printfl) - int KINSetNumMaxIters(void *kinmem, long int mxiter) - int KINSetNoInitSetup(void *kinmem, booleantype noInitSetup) - int KINSetNoResMon(void *kinmem, booleantype noNNIResMon) - int KINSetMaxSetupCalls(void *kinmem, long int msbset) - int KINSetMaxSubSetupCalls(void *kinmem, long int msbsetsub) - int KINSetEtaForm(void *kinmem, int etachoice) - int KINSetEtaConstValue(void *kinmem, realtype eta) - int KINSetEtaParams(void *kinmem, realtype egamma, realtype ealpha) - int KINSetResMonParams(void *kinmem, realtype omegamin, realtype omegamax) - int KINSetResMonConstValue(void *kinmem, realtype omegaconst) - int KINSetNoMinEps(void *kinmem, booleantype noMinEps) - int KINSetMaxNewtonStep(void *kinmem, realtype mxnewtstep) - int KINSetMaxBetaFails(void *kinmem, long int mxnbcf) - int KINSetRelErrFunc(void *kinmem, realtype relfunc) - int KINSetFuncNormTol(void *kinmem, realtype fnormtol) - int KINSetScaledStepTol(void *kinmem, realtype scsteptol) - int KINSetConstraints(void *kinmem, N_Vector constraints) - int KINSetSysFunc(void *kinmem, KINSysFn func) - - # solver routine - int KINSol(void *kinmem, N_Vector uu, int strategy, N_Vector u_scale, N_Vector f_scale) - - # optional output routines. - # Documentation see kinsol.h line 670-735 - int KINGetWorkSpace(void *kinmem, long int *lenrw, long int *leniw) - int KINGetNumNonlinSolvIters(void *kinmem, long int *nniters) - int KINGetNumFuncEvals(void *kinmem, long int *nfevals) - int KINGetNumBetaCondFails(void *kinmem, long int *nbcfails) - int KINGetNumBacktrackOps(void *kinmem, long int *nbacktr) - int KINGetFuncNorm(void *kinmem, realtype *fnorm) - int KINGetStepLength(void *kinmem, realtype *steplength) - char *KINGetReturnFlagName(int flag) - - # fuction used to deallocate memory used by KINSOL - void KINFree(void **kinmem) - - -IF SUNDIALS_VERSION >= (3,0,0): - cdef extern from "kinsol/kinsol_direct.h": - ctypedef int (*KINDlsDenseJacFn)(N_Vector u, N_Vector fu, SUNMatrix J, void *user_data, N_Vector tmp1, N_Vector tmp2) - int KINDlsSetLinearSolver(void *kinmem, SUNLinearSolver LS, SUNMatrix A) - int KINDlsSetJacFn(void *kinmem, KINDlsDenseJacFn djac) - - cdef extern from "kinsol/kinsol_spils.h": - int KINSpilsSetLinearSolver(void *kinsol_mem, SUNLinearSolver LS) - - ctypedef int (*KINSpilsPrecSolveFn)(N_Vector u, N_Vector uscale, - N_Vector fval, N_Vector fscale, N_Vector v, void *problem_data) - ctypedef int (*KINSpilsPrecSetupFn)(N_Vector u, N_Vector uscale, - N_Vector fval, N_Vector fscale, void *problem_data) -ELSE: - # functions used for supplying jacobian, and receiving info from linear solver - cdef extern from "kinsol/kinsol_direct.h": - # user functions - ctypedef int (*KINDlsDenseJacFn)(long int dim, N_Vector u, N_Vector fu, DlsMat J, void *user_data, N_Vector tmp1, N_Vector tmp2) - - # function used to link user functions to KINSOL - int KINDlsSetDenseJacFn(void *kinmem, KINDlsDenseJacFn jac) - - cdef extern from "kinsol/kinsol_dense.h": - int KINDense(void *kinmem, int dim) - - cdef extern from "kinsol/kinsol_spgmr.h": - int KINSpgmr(void *kinmem, int maxl) - - cdef extern from "kinsol/kinsol_spils.h": - ctypedef int (*KINSpilsPrecSolveFn)(N_Vector u, N_Vector uscale, - N_Vector fval, N_Vector fscale, N_Vector v, void *problem_data, N_Vector tmp) - ctypedef int (*KINSpilsPrecSetupFn)(N_Vector u, N_Vector uscale, - N_Vector fval, N_Vector fscale, void *problem_data, N_Vector tmp1, N_Vector tmp2) - -cdef extern from "kinsol/kinsol_direct.h": - # optional output fcts for linear direct solver - int KINDlsGetWorkSpace(void *kinmem, long int *lenrwB, long int *leniwB) - int KINDlsGetNumJacEvals(void *kinmem, long int *njevalsB) - int KINDlsGetNumFuncEvals(void *kinmem, long int *nfevalsB) - int KINDlsGetLastFlag(void *kinmem, long int *flag) - char *KINDlsGetReturnFlagName(int flag) - -cdef extern from "kinsol/kinsol_spils.h": - ctypedef int (*KINSpilsJacTimesVecFn)(N_Vector vv, N_Vector Jv, N_Vector vx, int* new_u, - void *problem_data) - - int KINSpilsSetJacTimesVecFn(void *kinmem, KINSpilsJacTimesVecFn jacv) - int KINSpilsGetNumLinIters(void *kinmem, long int *nliters) - int KINSpilsGetNumConvFails(void *kinmem, long int *nlcfails) - int KINSpilsGetNumPrecEvals(void *kinmem, long int *npevals) - int KINSpilsGetNumPrecSolves(void *kinmem, long int *npsolves) - int KINSpilsGetNumJtimesEvals(void *kinmem, long int *njevals) - int KINSpilsGetNumFuncEvals(void *kinmem, long int *nfevalsLS) - int KINSpilsSetPreconditioner(void *kinmem, KINSpilsPrecSetupFn psetup, KINSpilsPrecSolveFn psolve) - -#========================= -# END SUNDIALS DEFINITIONS -#========================= +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +""" +Cython Wrapper for interfacing Python with CVode and IDA (Sundials Version 2.4.0) +Claus Fuhrer, Lund University +Christian Andersson, Lund University + +see also Jon Olav Vik: +http://codespeak.net/pipermail/cython-dev/2009-June/005947.html + +""" +#import numpy as N +#cimport numpy as N + +from numpy cimport NPY_DOUBLE, npy_intp, NPY_INT + +#============================================== +#External definitions from Sundials headers +#============================================== + +cdef extern from "sundials/sundials_types.h": + ctypedef double realtype + ctypedef bint booleantype # should be bool instead of bint, but there is a bug in Cython + +cdef extern from "sundials/sundials_nvector.h": + ctypedef _generic_N_Vector* N_Vector + + cdef struct _generic_N_Vector_Ops: + realtype (*nvwrmsnorm)(N_Vector, N_Vector) + realtype (*nvwl2norm)(N_Vector, N_Vector) + ctypedef _generic_N_Vector_Ops *N_Vector_Ops + + cdef struct _generic_N_Vector: + void* content + N_Vector_Ops ops + +cdef extern from "nvector/nvector_serial.h": + cdef struct _N_VectorContent_Serial: + long int length + booleantype own_data + realtype* data + ctypedef _N_VectorContent_Serial* N_VectorContent_Serial + cdef N_Vector N_VMake_Serial(long int vec_length, realtype *v_data) + N_Vector *N_VCloneVectorArray_Serial(int count, N_Vector w) + N_Vector *N_VCloneVectorArrayEmpty_Serial(int count, N_Vector w) + void N_VSetArrayPointer_Serial(realtype *v_data, N_Vector v) + void N_VConst_Serial(realtype c, N_Vector z) + N_Vector N_VNew_Serial(long int vec_length) + void N_VDestroy_Serial(N_Vector v) + void N_VPrint_Serial(N_Vector v) + +IF SUNDIALS_VERSION >= (4,0,0): + cdef extern from "sundials/sundials_nonlinearsolver.h": + ctypedef _generic_SUNNonlinearSolver *SUNNonlinearSolver + + cdef struct _generic_SUNNonlinearSolver: + pass +ELSE: + #Dummy defines + ctypedef void *SUNNonlinearSolver + +IF SUNDIALS_VERSION >= (3,0,0): + cdef extern from "sundials/sundials_types.h": + IF SUNDIALS_VECTOR_SIZE == "64": + ctypedef long int sunindextype + ELSE: + ctypedef int sunindextype + cdef extern from "sundials/sundials_matrix.h": + ctypedef _generic_SUNMatrix *SUNMatrix + void SUNMatDestroy(SUNMatrix A) + + cdef struct _generic_SUNMatrix_Ops: + SUNMatrix_ID (*getid)(SUNMatrix) + SUNMatrix (*clone)(SUNMatrix) + void (*destroy)(SUNMatrix) + int (*zero)(SUNMatrix) + int (*copy)(SUNMatrix, SUNMatrix) + int (*scaleadd)(realtype, SUNMatrix, SUNMatrix) + int (*scaleaddi)(realtype, SUNMatrix) + int (*matvec)(SUNMatrix, N_Vector, N_Vector) + int (*space)(SUNMatrix, long int*, long int*) + + cdef struct _generic_SUNMatrix: + void *content + _generic_SUNMatrix_Ops *ops + + cdef enum SUNMatrix_ID: + SUNMATRIX_DENSE, + SUNMATRIX_BAND, + SUNMATRIX_SPARSE, + SUNMATRIX_CUSTOM + + cdef extern from "sundials/sundials_linearsolver.h": + ctypedef _generic_SUNLinearSolver *SUNLinearSolver + int SUNLinSolFree(SUNLinearSolver S) + + cdef struct _generic_SUNLinearSolver_Ops: + SUNLinearSolver_Type (*gettype)(SUNLinearSolver) + int (*setatimes)(SUNLinearSolver, void*, ATimesFn) + int (*setpreconditioner)(SUNLinearSolver, void*, + PSetupFn, PSolveFn) + int (*setscalingvectors)(SUNLinearSolver, + N_Vector, N_Vector) + int (*initialize)(SUNLinearSolver) + int (*setup)(SUNLinearSolver, SUNMatrix) + int (*solve)(SUNLinearSolver, SUNMatrix, N_Vector, + N_Vector, realtype) + int (*numiters)(SUNLinearSolver) + realtype (*resnorm)(SUNLinearSolver) + long int (*lastflag)(SUNLinearSolver) + int (*space)(SUNLinearSolver, long int*, long int*) + N_Vector (*resid)(SUNLinearSolver) + int (*free)(SUNLinearSolver) + + cdef struct _generic_SUNLinearSolver: + void *content + _generic_SUNLinearSolver_Ops *ops + + cdef enum SUNLinearSolver_Type: + SUNLINEARSOLVER_DIRECT, + SUNLINEARSOLVER_ITERATIVE, + SUNLINEARSOLVER_CUSTOM + + cdef extern from "sunmatrix/sunmatrix_dense.h": + ctypedef _SUNMatrixContent_Dense *SUNMatrixContent_Dense + cdef struct _SUNMatrixContent_Dense: + sunindextype M + sunindextype N + realtype *data + sunindextype ldata + realtype **cols + SUNMatrix SUNDenseMatrix(sunindextype M, sunindextype N) + cdef extern from "sunmatrix/sunmatrix_sparse.h": + ctypedef _SUNMatrixContent_Sparse *SUNMatrixContent_Sparse + cdef struct _SUNMatrixContent_Sparse: + sunindextype M + sunindextype N + sunindextype NNZ + sunindextype NP + realtype *data + int sparsetype + sunindextype *indexvals + sunindextype *indexptrs + sunindextype **rowvals + sunindextype **colptrs + sunindextype **colvals + sunindextype **rowptrs + SUNMatrix SUNSparseMatrix(sunindextype M, sunindextype N, sunindextype NNZ, int sparsetype) + cdef extern from "sunlinsol/sunlinsol_dense.h": + SUNLinearSolver SUNDenseLinearSolver(N_Vector y, SUNMatrix A) + cdef extern from "sunlinsol/sunlinsol_spgmr.h": + SUNLinearSolver SUNSPGMR(N_Vector y, int pretype, int maxl) + +ELSE: + #Dummy defines + ctypedef void *SUNLinearSolver + ctypedef void *SUNMatrix + ctypedef void *SUNMatrixContent_Dense + ctypedef void *SUNMatrixContent_Sparse + ctypedef int sunindextype + + +#Struct for handling the Jacobian data +cdef extern from "sundials/sundials_direct.h": + cdef struct _DlsMat: + int type + int M + int N + int ldim + int mu + int ml + int s_mu + realtype *data + int ldata + realtype **cols + ctypedef _DlsMat* DlsMat + cdef realtype* DENSE_COL(DlsMat A, int j) + +IF SUNDIALS_VERSION >= (5,0,0): + pass +ELIF SUNDIALS_VERSION >= (2,6,3): + cdef extern from "sundials/sundials_sparse.h": + cdef struct _SlsMat: + int M + int N + int NNZ + int NP + realtype *data + int sparsetype + int *indexvals + int *indexptrs + int **rowvals + int **colptrs + int **colvals + int **rowptrs + ctypedef _SlsMat* SlsMat +ELIF SUNDIALS_VERSION >= (2,6,0): + cdef extern from "sundials/sundials_sparse.h": + cdef struct _SlsMat: + int M + int N + int NNZ + realtype *data + int *rowvals + int *colptrs + ctypedef _SlsMat* SlsMat +ELSE: + cdef struct _SlsMat: + int M + int N + int NNZ + realtype *data + int *rowvals + int *colptrs + ctypedef _SlsMat* SlsMat + +#============================================== +# C headers +#============================================== +cdef extern from "string.h": + void *memcpy(void *s1, void *s2, int n) +cdef extern from "stdlib.h": + void *malloc(int size) + void free(void *ptr) + +#============================================== +#External definitions from Sundials headers +#============================================== + +IF SUNDIALS_WITH_SUPERLU: + cdef inline int with_superlu(): return 1 +ELSE: + cdef inline int with_superlu(): return 0 + +IF SUNDIALS_VERSION >= (4,0,0): + cdef extern from "cvodes/cvodes.h": + void* CVodeCreate(int lmm) + + int CVodeSetNonlinearSolver(void *cvode_mem, SUNNonlinearSolver NLS) + int CVodeSetNonlinearSolverSensSim(void *cvode_mem, SUNNonlinearSolver NLS) + int CVodeSetNonlinearSolverSensStg(void *cvode_mem, SUNNonlinearSolver NLS) + + cdef extern from "sunnonlinsol/sunnonlinsol_newton.h": + SUNNonlinearSolver SUNNonlinSol_Newton(N_Vector y) + SUNNonlinearSolver SUNNonlinSol_NewtonSens(int count, N_Vector y) + cdef extern from "sunnonlinsol/sunnonlinsol_fixedpoint.h": + SUNNonlinearSolver SUNNonlinSol_FixedPoint(N_Vector y, int m) + SUNNonlinearSolver SUNNonlinSol_FixedPointSens(int count, N_Vector y, int m) +ELSE: + cdef extern from "cvodes/cvodes.h": + void* CVodeCreate(int lmm, int iter) + +cdef extern from "cvodes/cvodes.h": + ctypedef int (*CVRhsFn)(realtype t, N_Vector y, N_Vector ydot, void *f_data) + int CVodeInit(void *cvode_mem, CVRhsFn f, realtype t0, N_Vector y0) + int CVodeReInit(void *cvode_mem, realtype t0, N_Vector y0) + void CVodeFree(void **cvode_mem) + int CVode(void *cvode_mem, realtype tout, N_Vector yout, realtype *tret, int itask) + + #Functions for settings options + int CVodeSetMaxOrd(void *cvode_mem, int maxord) + int CVodeSetMaxNumSteps(void *cvode_mem, long int mxsteps) + int CVodeSetMaxStep(void *cvode_mem, realtype hmax) + int CVodeSetMinStep(void *cvode_mem, realtype hmin) + int CVodeSetInitStep(void *cvode_mem, realtype hin) + int CVodeSStolerances(void *cvode_mem, realtype reltol, realtype abstol) + int CVodeSVtolerances(void *cvode_mem, realtype reltol, N_Vector abstol) + int CVodeSetStopTime(void *cvode_mem, realtype tstop) + int CVodeSetUserData(void *cvode_mem,void *user_data) + int CVodeSetMaxConvFails(void *cvode_mem, int maxncf) + int CVodeSetMaxErrTestFails(void *cvode_mem, int maxnef) + int CVodeSetMaxNonlinIters(void *cvode_mem, int maxcor) + + #Functions for retrieving results + int CVodeGetDky(void *cvode_mem, realtype t, int k, N_Vector dky) + + #Functions for error handling + ctypedef void (*CVErrHandlerFn)(int error_code, const char *module, const char *function, char *msg, + void *eh_data) + int CVodeSetErrHandlerFn(void *cvode_mem, CVErrHandlerFn ehfun, void* eh_data) + + #Functions for discontinuity handling + ctypedef int (*CVRootFn)(realtype tt, N_Vector yy, realtype *gout, void *user_data) + int CVodeRootDirection(void *cvode_mem, int *rootdir) + int CVodeSetNoInactiveRootWarn(void *cvode_mem) + int CVodeRootInit(void *cvode_mem, int nrtfn, CVRootFn g) + int CVodeGetRootInfo(void *cvode_mem, int *rootsfound) + + #Functions for retrieving statistics + int CVodeGetLastOrder(void * cvode_mem,int *qlast) + int CVodeGetLastStep(void * cvode_mem, realtype *hlast) + int CVodeGetCurrentOrder(void * cvode_mem,int *qcurrent) + int CVodeGetActualInitStep(void * cvode_mem, realtype *hinused) + int CVodeGetNumSteps(void *cvode_mem, long int *nsteps) #Number of steps + int CVodeGetNumRhsEvals(void *cvode_mem, long int *nrevals) #Number of function evals + int CVDlsGetNumJacEvals(void *cvode_mem, long int *njevals) #Number of jac evals + int CVDlsGetNumRhsEvals(void *cvode_mem, long int *nrevalsLS) #Number of res evals due to jac evals + int CVodeGetNumGEvals(void *cvode_mem, long int *ngevals) #Number of root evals + int CVodeGetNumErrTestFails(void *cvode_mem, long int *netfails) #Number of local error test failures + int CVodeGetNumNonlinSolvIters(void *cvode_mem, long int *nniters) #Number of nonlinear iteration + int CVodeGetNumNonlinSolvConvFails(void *cvode_mem, long int *nncfails) #Number of nonlinear conv failures + int CVodeGetNonlinSolvStats(void *cvode_mem, long int *nniters, long int *nncfails) + int CVodeGetIntegratorStats(void* cvode_mem, long int *nsteps, long int *nfevals, + long int *nlinsetups, long int *netfails, int *qlast, int *qcur, + realtype *hinused, realtype *hlast, realtype *hcur, realtype *tcur) + int CVodeGetNumStabLimOrderReds(void *cvode_mem, long int *nslred) + + #Sensitivity methods + ctypedef int (*CVSensRhsFn)(int Ns, realtype t, N_Vector y, N_Vector ydot, N_Vector *yS, + N_Vector *ySdot, void *user_data, N_Vector tmp1, N_Vector tmp2) + ctypedef int (*CVSensRhs1Fn)(int Ns, realtype t, N_Vector y, N_Vector ydot, int iS, N_Vector *yS, + N_Vector *ySdot, void *user_data, N_Vector tmp1, N_Vector tmp2) + int CVodeSensInit(void *cvode_mem, int Ns, int ism, CVSensRhsFn fS, N_Vector *ySO) + int CVodeSensInit1(void *cvode_mem, int Ns, int ism, CVSensRhs1Fn fS1, N_Vector *ySO) + int CVodeSensReInit(void *cvode_mem, int ism, N_Vector *ySO) + int CVodeSensFree(void *cvode_mem) + int CVodeSensToggleOff(void *cvode_mem) + int CVodeSensSStolerances(void *cvode_mem, realtype reltolS, realtype *abstolS) + int CVodeSensSVtolerances(void *cvode_mem, realtype reltolS, N_Vector *abstolS) + int CVodeSensEEtolerances(void *cvode_mem) + int CVodeGetSens(void *cvode_mem, realtype *tret, N_Vector *yS) + int CVodeGetSensDky(void *cvode_mem, realtype t, int k, N_Vector *dkyS) + int CVodeGetSens1(void *cvode_mem, realtype *tret, int iss, N_Vector yS) + int CVodeGetSensDky1(void *cvode_mem, realtype t, int k, int iss, N_Vector dkyS) + int CVodeSetSensParams(void *cvode_mem, realtype *p, realtype *pbar, int *plist) + int CVodeSetSensDQMethod(void *cvode_mem, int DQtype, realtype DQrhomax) + int CVodeSetSensErrCon(void *cvode_mem, booleantype errconS) + int CVodeSetSensMaxNonlinIters(void *cvode_mem, int maxcorS) + int CVodeSetStabLimDet(void *cvode_mem, booleantype stldet) + + + + #Statistics + int CVodeGetEstLocalErrors(void *cvode_mem, N_Vector ele) #Estimated local errors + int CVodeGetErrWeights(void *cvode_mem, N_Vector eweight) #Estimated local errors + int CVodeGetSensNumRhsEvals(void *cvode_mem, long int *nfSevals) + int CVodeGetNumRhsEvalsSens(void *cvode_mem, long int *nfevalsS) + int CVodeGetSensNumErrTestFails(void *cvode_mem, long int *nSetfails) + int CVodeGetSensNumLinSolvSetups(void *cvode_mem, long int *nlinsetupsS) + int CVodeGetSensStats(void *cvode_mem, long int *nfSevals, long int *nfevalsS, + long int *nSetfails, long int *nlinsetupsS) + int CVodeGetSensErrWeights(void *cvode_mem, N_Vector *eSweight) + int CVodeGetSensNumNonlinSolvIters(void *cvode_mem, long int *nSniters) + int CVodeGetSensNumNonlinSolvConvFails(void *cvode_mem, long int *nSncfails) + int CVodeGetSensNonlinSolvStats(void *cvode_mem, long int *nSniters, long int *nSncfails) + int CVodeGetStgrSensNumNonlinSolvIters(void *cvode_mem, long int *nSTGR1niters) + int CVodeGetStgrSensNumNonlinSolvConvFails(void *cvode_mem, long int *nSTGR1ncfails) + +cdef extern from "cvodes/cvodes_spils.h": + ctypedef int (*CVSpilsJacTimesVecFn)(N_Vector v, N_Vector Jv, realtype t, + N_Vector y, N_Vector fy, void *user_data, N_Vector tmp) + +IF SUNDIALS_VERSION >= (3,0,0): + cdef extern from "cvodes/cvodes_direct.h": + ctypedef int (*CVDlsDenseJacFn)(realtype t, N_Vector y, N_Vector fy, + SUNMatrix Jac, void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) + int CVDlsSetLinearSolver(void *cvode_mem, SUNLinearSolver LS, SUNMatrix A) + int CVDlsSetJacFn(void *cvode_mem, CVDlsDenseJacFn djac) + cdef extern from "cvodes/cvodes_spils.h": + int CVSpilsSetLinearSolver(void *cvode_mem, SUNLinearSolver LS) + ctypedef int (*CVSpilsJacTimesSetupFn)(realtype t, N_Vector y, N_Vector fy, void *user_data) + int CVSpilsSetJacTimes(void *cvode_mem, CVSpilsJacTimesSetupFn jtsetup, CVSpilsJacTimesVecFn jtimes) + + ctypedef int (*CVSpilsPrecSetupFn)(realtype t, N_Vector y, N_Vector fy, + booleantype jok, booleantype *jcurPtr, realtype gamma, void *user_data) + ctypedef int (*CVSpilsPrecSolveFn)(realtype t, N_Vector y, N_Vector fy, + N_Vector r, N_Vector z, + realtype gamma, realtype delta, int lr, void *user_data) + + + IF SUNDIALS_WITH_SUPERLU: + cdef extern from "sunlinsol/sunlinsol_superlumt.h": + SUNLinearSolver SUNSuperLUMT(N_Vector y, SUNMatrix A, int num_threads) + ELSE: + cdef inline SUNLinearSolver SUNSuperLUMT(N_Vector y, SUNMatrix A, int num_threads): return NULL + + cdef inline int cv_spils_jtsetup_dummy(realtype t, N_Vector y, N_Vector fy, void *user_data): return 0 + cdef inline tuple version(): return (3,0,0) +ELSE: + cdef extern from "cvodes/cvodes_dense.h": + int CVDense(void *cvode_mem, long int n) + ctypedef int (*CVDlsDenseJacFn)(long int n, realtype t, N_Vector y, N_Vector fy, + DlsMat Jac, void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) + int CVDlsSetDenseJacFn(void *cvode_mem, CVDlsDenseJacFn djac) + + cdef extern from "cvodes/cvodes_spgmr.h": + int CVSpgmr(void *cvode_mem, int pretype, int max1) + + cdef extern from "cvodes/cvodes_spils.h": + int CVSpilsSetJacTimesVecFn(void *cvode_mem, CVSpilsJacTimesVecFn jtv) + ctypedef int (*CVSpilsPrecSetupFn)(realtype t, N_Vector y, N_Vector fy, + booleantype jok, booleantype *jcurPtr, + realtype gamma, void *user_data, + N_Vector tmp1, N_Vector tmp2, + N_Vector tmp3) + ctypedef int (*CVSpilsPrecSolveFn)(realtype t, N_Vector y, N_Vector fy, + N_Vector r, N_Vector z, + realtype gamma, realtype delta, + int lr, void *user_data, N_Vector tmp) + + IF SUNDIALS_VERSION >= (2,6,0): + cdef extern from "cvodes/cvodes_sparse.h": + ctypedef int (*CVSlsSparseJacFn)(realtype t, N_Vector y, N_Vector fy, + SlsMat Jac, void *user_data, N_Vector tmp1, + N_Vector tmp2, N_Vector tmp3) + int CVSlsSetSparseJacFn(void *cvode_mem, CVSlsSparseJacFn jac) + int CVSlsGetNumJacEvals(void *cvode_mem, long int *njevals) + cdef inline tuple version(): return (2,6,0) + IF SUNDIALS_WITH_SUPERLU: + cdef extern from "cvodes/cvodes_superlumt.h": + int CVSuperLUMT(void *cvode_mem, int numthreads, int n, int nnz) + ELSE: + cdef inline int CVSuperLUMT(void *cvode_mem, int numthreads, int n, int nnz): return -1 + ELSE: + cdef inline int CVSuperLUMT(void *cvode_mem, int numthreads, int n, int nnz): return -1 + ctypedef int (*CVSlsSparseJacFn)(realtype t, N_Vector y, N_Vector fy, + SlsMat Jac, void *user_data, N_Vector tmp1, + N_Vector tmp2, N_Vector tmp3) + cdef inline int CVSlsSetSparseJacFn(void *cvode_mem, CVSlsSparseJacFn jac): return -1 + cdef inline int CVSlsGetNumJacEvals(void *cvode_mem, long int *njevals): return -1 + cdef inline tuple version(): return (2,5,0) + +cdef extern from "cvodes/cvodes_spils.h": + int CVSpilsSetPreconditioner(void *cvode_mem, CVSpilsPrecSetupFn psetup, CVSpilsPrecSolveFn psolve) + int CVSpilsGetNumJtimesEvals(void *cvode_mem, long int *njvevals) #Number of jac*vector evals + int CVSpilsGetNumRhsEvals(void *cvode_mem, long int *nfevalsLS) #Number of res evals due to jacÄvector evals + int CVSpilsGetNumPrecEvals(void *cvode_mem, long int *npevals) + int CVSpilsGetNumPrecSolves(void *cvode_mem, long int *npsolves) + +cdef extern from "idas/idas.h": + ctypedef int (*IDAResFn)(realtype tt, N_Vector yy, N_Vector yp, N_Vector rr, void *user_data) + void* IDACreate() + int IDAInit(void* ida_mem, IDAResFn res, realtype t0, N_Vector y0, N_Vector yp0) + int IDAReInit(void* ida_mem, realtype t0, N_Vector y0, N_Vector yp0) + void IDAFree(void **ida_mem) + int IDASolve(void* ida_mem, realtype tout,realtype *tret, N_Vector yret, + N_Vector ypret, int itask) + + #Functions for settings options + int IDASStolerances(void *ida_mem, realtype reltol, realtype abstol) + int IDASVtolerances(void *ida_mem, realtype reltol, N_Vector abstol) + int IDASetSuppressAlg(void *ida_mem, booleantype suppressalg) + int IDASetId(void *ida_mem, N_Vector id) + int IDASetUserData(void *ida_mem,void *user_data) + int IDASetInitStep(void *ida_mem, realtype hin) + int IDASetStopTime(void *ida_mem, realtype tstop) + int IDASetMaxErrTestFails(void *ida_mem, int maxnef) + int IDASetMaxNumSteps(void *ida_mem, long int mxsteps) + int IDASetMaxOrd(void *ida_mem, int maxord) + int IDASetMaxStep(void* ida_mem, realtype hmax) + + #Functions for retrieving results + int IDAGetDky(void *ida_mem, realtype t, int k, N_Vector dky) + + #Functions for error handling + ctypedef void (*IDAErrHandlerFn)(int error_code, const char *module, const char *function, char *msg, + void *eh_data) + int IDASetErrHandlerFn(void *ida_mem,IDAErrHandlerFn ehfun, void* eh_data) + + + #Functions for discontinuity handling + ctypedef int (*IDARootFn)(realtype tt, N_Vector yy, N_Vector yp, realtype *gout, void *user_data) + int IDASetRootDirection(void *ida_mem, int *rootdir) + int IDASetNoInactiveRootWarn(void *ida_mem) + int IDARootInit(void *ida_mem, int nrtfn, IDARootFn g) + int IDAGetRootInfo(void *ida_mem, int *rootsfound) + int IDACalcIC(void *ida_men, int icopt, realtype tout1) + int IDAGetConsistentIC(void *ida_mem, N_Vector y0, N_Vector yp0) + int IDASetLineSearchOffIC(void *ida_mem, booleantype lsoff) + + #Functions for retrieving statistics + int IDAGetEstLocalErrors(void *ida_mem, N_Vector ele) #Estimated local errors + int IDAGetErrWeights(void *ida_mem, N_Vector eweight) + int IDAGetLastStep(void *ida_mem, realtype *hlast) + int IDAGetLastOrder(void *ida_mem,int *qlast) #Last order used + int IDAGetCurrentOrder(void *ida_mem,int *qcurrent) #Order that is about to be tried + int IDAGetNumSteps(void *ida_mem, long int *nsteps) #Number of steps + int IDAGetNumResEvals(void *ida_mem, long int *nrevals) #Number of res evals + int IDADlsGetNumJacEvals(void *ida_mem, long int *njevals) #Number of jac evals + int IDADlsGetNumResEvals(void *ida_mem, long int *nrevalsLS) #Number of res evals due to jac evals + int IDAGetNumGEvals(void *ida_mem, long int *ngevals) #Number of root evals + int IDAGetNumErrTestFails(void *ida_mem, long int *netfails) #Number of local error test failures + int IDAGetNumNonlinSolvIters(void *ida_mem, long int *nniters) #Number of nonlinear iteration + int IDAGetNumNonlinSolvConvFails(void *ida_mem, long int *nncfails) #Number of nonlinear conv failures + int IDAGetNonlinSolvStats(void *ida_mem, long int *nniters, long int *nncfails) + int IDAGetIntegratorStats(void* ida_mem,long int *nsteps, long int *nrevals, + long int *nlinsetups, long int *netfails, int *klast, + int *kcur, realtype *hinused, realtype *hlast, + realtype *hcur, realtype *tcur) + + #Start Sensitivities + #=================== + ctypedef int (*IDASensResFn)(int Ns, realtype t, N_Vector yy, N_Vector yp, + N_Vector *yS, N_Vector *ypS, N_Vector *resvalS, + void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) + int IDASensInit(void *ida_mem, int Ns, int ism, IDASensResFn resS, N_Vector *ySO, N_Vector *ypSO) + int IDASensReInit(void *ida_mem, int ism, N_Vector *ySO, N_Vector *ypSO) + + #Options + int IDASensToggleOff(void *ida_mem) + int IDASensSStolerances(void *ida_mem, realtype reltolS, realtype *abstolS) + int IDASensSVtolerances(void *ida_mem, realtype reltolS, N_Vector *abstolS) + int IDASensEEtolerances(void *ida_mem) + + #Results + int IDAGetSens(void *ida_mem, realtype tret, N_Vector *yS) + int IDAGetSensDky(void *ida_mem, realtype t, int k, N_Vector *dkyS) + int IDAGetSensDky1(void *ida_mem, realtype t, int k, int i, N_Vector dkyS) + + #Options (optional) + int IDASetSensParams(void *ida_mem, realtype *p, realtype *pbar, int *plist) + int IDASetSensDQMethod(void *ida_mem, int DQtype, realtype DQrhomax) + int IDASetSensErrCon(void *ida_mem, booleantype errconS) + int IDASetSensMaxNonlinIters(void *ida_mem, int maxcorS) + + #Statistics + int IDAGetSensNumResEvals(void *ida_mem, long int nfSevals) + int IDAGetNumResEvalsSens(void *ida_mem, long int nfevalsS) + int IDAGetSensNumErrTestFails(void *ida_mem, long int nSetfails) + int IDAGetSensNumLinSolvSetups(void *ida_mem, long int nlinsetupsS) + int IDAGetSensStats(void *ida_mem, long int *nfSevals, long int *nfevalsS, + long int *nSetfails, long int *nlinsetupsS) + int IDAGetSensNumNonlinSolvIters(void *ida_mem, long int nSniters) + int IDAGetSeonsNumNonlinSolvConvFails(void *ida_mem, long int nSncfails) + int IDAGetSensNonlinSolvStats(void *ida_mem, long int *nSniters, long int *nSncfails) + + #End Sensitivities + #================= + +cdef extern from "idas/idas_spils.h": + ctypedef int (*IDASpilsJacTimesVecFn)(realtype tt, N_Vector yy, N_Vector yp, N_Vector rr, + N_Vector v, N_Vector Jv, realtype cj, void *user_data,N_Vector tmp1, N_Vector tmp2) + +IF SUNDIALS_VERSION >= (3,0,0): + cdef extern from "idas/idas_direct.h": + ctypedef int (*IDADlsDenseJacFn)(realtype tt, realtype cj, N_Vector yy, + N_Vector yp, N_Vector rr, SUNMatrix Jac, void *user_data, + N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) + int IDADlsSetJacFn(void *ida_mem, IDADlsDenseJacFn djac) + int IDADlsSetLinearSolver(void *ida_mem, SUNLinearSolver LS, SUNMatrix A) + + cdef extern from "idas/idas_spils.h": + int IDASpilsSetLinearSolver(void *ida_mem, SUNLinearSolver LS) + ctypedef int (*IDASpilsJacTimesSetupFn)(realtype tt, N_Vector yy, + N_Vector yp, N_Vector rr, realtype c_j, void *user_data) + int IDASpilsSetJacTimes(void *ida_mem, + IDASpilsJacTimesSetupFn jtsetup, IDASpilsJacTimesVecFn jtimes) + + cdef inline int ida_spils_jtsetup_dummy(realtype tt, N_Vector yy, N_Vector yp, N_Vector rr, realtype c_j, void *user_data): return 0 +ELSE: + cdef extern from "idas/idas_dense.h": + int IDADense(void *ida_mem, long int n) + ctypedef int (*IDADlsDenseJacFn)(long int Neq, realtype tt, realtype cj, N_Vector yy, + N_Vector yp, N_Vector rr, DlsMat Jac, void *user_data, + N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) + int IDADlsSetDenseJacFn(void *ida_mem, IDADlsDenseJacFn djac) + + cdef extern from "idas/idas_spgmr.h": + int IDASpgmr(void *ida_mem, int max1) + + cdef extern from "idas/idas_spils.h": + int IDASpilsSetJacTimesVecFn(void *ida_mem, IDASpilsJacTimesVecFn ida_jacv) + +cdef extern from "idas/idas_spils.h": + int IDASpilsGetNumJtimesEvals(void *ida_mem, long int *njvevals) #Number of jac*vector + int IDASpilsGetNumResEvals(void *ida_mem, long int *nfevalsLS) #Number of rhs due to jac*vector + + +#################### +# KINSOL +#################### + + +# KINSOL functions and routines +cdef extern from "kinsol/kinsol.h": + # user defined functions + ctypedef int (*KINSysFn)(N_Vector uu, N_Vector fval, void *user_data ) + ctypedef void (*KINErrHandlerFn)(int error_code, char *module, char *function, char *msg, void *user_data) + ctypedef void (*KINInfoHandlerFn)(const char *module, const char *function, char *msg, void *user_data) + # initialization routines + void *KINCreate() + int KINInit(void *kinmem, KINSysFn func, N_Vector tmpl) + + # optional input spec. functions, + # for specificationsdocumentation cf. kinsol.h line 218-449 + int KINSetErrHandlerFn(void *kinmem, KINErrHandlerFn ehfun, void *eh_data) + int KINSetInfoHandlerFn(void *kinmem, KINInfoHandlerFn ihfun, void *ih_data) + int KINSetUserData(void *kinmem, void *user_data) + int KINSetPrintLevel(void *kinmemm, int printfl) + int KINSetNumMaxIters(void *kinmem, long int mxiter) + int KINSetNoInitSetup(void *kinmem, booleantype noInitSetup) + int KINSetNoResMon(void *kinmem, booleantype noNNIResMon) + int KINSetMaxSetupCalls(void *kinmem, long int msbset) + int KINSetMaxSubSetupCalls(void *kinmem, long int msbsetsub) + int KINSetEtaForm(void *kinmem, int etachoice) + int KINSetEtaConstValue(void *kinmem, realtype eta) + int KINSetEtaParams(void *kinmem, realtype egamma, realtype ealpha) + int KINSetResMonParams(void *kinmem, realtype omegamin, realtype omegamax) + int KINSetResMonConstValue(void *kinmem, realtype omegaconst) + int KINSetNoMinEps(void *kinmem, booleantype noMinEps) + int KINSetMaxNewtonStep(void *kinmem, realtype mxnewtstep) + int KINSetMaxBetaFails(void *kinmem, long int mxnbcf) + int KINSetRelErrFunc(void *kinmem, realtype relfunc) + int KINSetFuncNormTol(void *kinmem, realtype fnormtol) + int KINSetScaledStepTol(void *kinmem, realtype scsteptol) + int KINSetConstraints(void *kinmem, N_Vector constraints) + int KINSetSysFunc(void *kinmem, KINSysFn func) + + # solver routine + int KINSol(void *kinmem, N_Vector uu, int strategy, N_Vector u_scale, N_Vector f_scale) + + # optional output routines. + # Documentation see kinsol.h line 670-735 + int KINGetWorkSpace(void *kinmem, long int *lenrw, long int *leniw) + int KINGetNumNonlinSolvIters(void *kinmem, long int *nniters) + int KINGetNumFuncEvals(void *kinmem, long int *nfevals) + int KINGetNumBetaCondFails(void *kinmem, long int *nbcfails) + int KINGetNumBacktrackOps(void *kinmem, long int *nbacktr) + int KINGetFuncNorm(void *kinmem, realtype *fnorm) + int KINGetStepLength(void *kinmem, realtype *steplength) + char *KINGetReturnFlagName(int flag) + + # fuction used to deallocate memory used by KINSOL + void KINFree(void **kinmem) + + +IF SUNDIALS_VERSION >= (3,0,0): + cdef extern from "kinsol/kinsol_direct.h": + ctypedef int (*KINDlsDenseJacFn)(N_Vector u, N_Vector fu, SUNMatrix J, void *user_data, N_Vector tmp1, N_Vector tmp2) + int KINDlsSetLinearSolver(void *kinmem, SUNLinearSolver LS, SUNMatrix A) + int KINDlsSetJacFn(void *kinmem, KINDlsDenseJacFn djac) + + cdef extern from "kinsol/kinsol_spils.h": + int KINSpilsSetLinearSolver(void *kinsol_mem, SUNLinearSolver LS) + + ctypedef int (*KINSpilsPrecSolveFn)(N_Vector u, N_Vector uscale, + N_Vector fval, N_Vector fscale, N_Vector v, void *problem_data) + ctypedef int (*KINSpilsPrecSetupFn)(N_Vector u, N_Vector uscale, + N_Vector fval, N_Vector fscale, void *problem_data) +ELSE: + # functions used for supplying jacobian, and receiving info from linear solver + cdef extern from "kinsol/kinsol_direct.h": + # user functions + ctypedef int (*KINDlsDenseJacFn)(long int dim, N_Vector u, N_Vector fu, DlsMat J, void *user_data, N_Vector tmp1, N_Vector tmp2) + + # function used to link user functions to KINSOL + int KINDlsSetDenseJacFn(void *kinmem, KINDlsDenseJacFn jac) + + cdef extern from "kinsol/kinsol_dense.h": + int KINDense(void *kinmem, int dim) + + cdef extern from "kinsol/kinsol_spgmr.h": + int KINSpgmr(void *kinmem, int maxl) + + cdef extern from "kinsol/kinsol_spils.h": + ctypedef int (*KINSpilsPrecSolveFn)(N_Vector u, N_Vector uscale, + N_Vector fval, N_Vector fscale, N_Vector v, void *problem_data, N_Vector tmp) + ctypedef int (*KINSpilsPrecSetupFn)(N_Vector u, N_Vector uscale, + N_Vector fval, N_Vector fscale, void *problem_data, N_Vector tmp1, N_Vector tmp2) + +cdef extern from "kinsol/kinsol_direct.h": + # optional output fcts for linear direct solver + int KINDlsGetWorkSpace(void *kinmem, long int *lenrwB, long int *leniwB) + int KINDlsGetNumJacEvals(void *kinmem, long int *njevalsB) + int KINDlsGetNumFuncEvals(void *kinmem, long int *nfevalsB) + int KINDlsGetLastFlag(void *kinmem, long int *flag) + char *KINDlsGetReturnFlagName(int flag) + +cdef extern from "kinsol/kinsol_spils.h": + ctypedef int (*KINSpilsJacTimesVecFn)(N_Vector vv, N_Vector Jv, N_Vector vx, int* new_u, + void *problem_data) + + int KINSpilsSetJacTimesVecFn(void *kinmem, KINSpilsJacTimesVecFn jacv) + int KINSpilsGetNumLinIters(void *kinmem, long int *nliters) + int KINSpilsGetNumConvFails(void *kinmem, long int *nlcfails) + int KINSpilsGetNumPrecEvals(void *kinmem, long int *npevals) + int KINSpilsGetNumPrecSolves(void *kinmem, long int *npsolves) + int KINSpilsGetNumJtimesEvals(void *kinmem, long int *njevals) + int KINSpilsGetNumFuncEvals(void *kinmem, long int *nfevalsLS) + int KINSpilsSetPreconditioner(void *kinmem, KINSpilsPrecSetupFn psetup, KINSpilsPrecSolveFn psolve) + +#========================= +# END SUNDIALS DEFINITIONS +#========================= diff --git a/src/ode.pxd b/src/ode.pxd index eedcd3fd..4c5a494a 100644 --- a/src/ode.pxd +++ b/src/ode.pxd @@ -1,59 +1,59 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -cimport numpy as N -from support cimport Statistics - -cdef class ODE: - cdef public dict options, solver_options, problem_info - cdef public dict supports, - cdef public Statistics statistics - - cdef public list event_data - - cdef public object problem - cdef public object chattering_check - - cdef public double t, t0 - cdef public int display_counter - cdef public int chattering_clear_counter - cdef public int chattering_ok_print - cdef public N.ndarray y,yd, p - cdef public N.ndarray y0, yd0, p0, sw0 - cdef double elapsed_step_time, time_integration_start - cdef int time_limit_activated, display_progress_activated - cdef double clock_start - cdef public object _event_info - - #cdef public list t,y,yd,p,sw_cur - cdef public list t_sol, y_sol, yd_sol, p_sol, sw - - cpdef log_message(self, message, int level) - cpdef log_event(self, double time, object event_info, int level) - cpdef clear_logs(self) - cpdef simulate(self, double tfinal, int ncp=*, object ncp_list=*) - cpdef get_options(self) - cpdef get_supports(self) - cpdef get_statistics(self) - cpdef get_event_data(self) - cpdef print_event_data(self) - cpdef finalize(self) - cpdef initialize(self) - cdef _reset_solution_variables(self) - cpdef get_elapsed_step_time(self) - cpdef _chattering_check(self, object event_info) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +cimport numpy as N +from support cimport Statistics + +cdef class ODE: + cdef public dict options, solver_options, problem_info + cdef public dict supports, + cdef public Statistics statistics + + cdef public list event_data + + cdef public object problem + cdef public object chattering_check + + cdef public double t, t0 + cdef public int display_counter + cdef public int chattering_clear_counter + cdef public int chattering_ok_print + cdef public N.ndarray y,yd, p + cdef public N.ndarray y0, yd0, p0, sw0 + cdef double elapsed_step_time, time_integration_start + cdef int time_limit_activated, display_progress_activated + cdef double clock_start + cdef public object _event_info + + #cdef public list t,y,yd,p,sw_cur + cdef public list t_sol, y_sol, yd_sol, p_sol, sw + + cpdef log_message(self, message, int level) + cpdef log_event(self, double time, object event_info, int level) + cpdef clear_logs(self) + cpdef simulate(self, double tfinal, int ncp=*, object ncp_list=*) + cpdef get_options(self) + cpdef get_supports(self) + cpdef get_statistics(self) + cpdef get_event_data(self) + cpdef print_event_data(self) + cpdef finalize(self) + cpdef initialize(self) + cdef _reset_solution_variables(self) + cpdef get_elapsed_step_time(self) + cpdef _chattering_check(self, object event_info) diff --git a/src/ode.pyx b/src/ode.pyx index 99822869..09259e1b 100644 --- a/src/ode.pyx +++ b/src/ode.pyx @@ -1,589 +1,589 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -cimport numpy as N -from timeit import default_timer as timer - -import itertools -import multiprocessing - -from exception import * -from problem import Explicit_Problem, Delay_Explicit_Problem, Implicit_Problem, SingPerturbed_Problem -from support import Statistics - -include "constants.pxi" #Includes the constants (textual include) - -realtype = float - -cdef class ODE: - """ - Base class for all our integrators. - """ - - def __init__(self, problem): - """ - Defines general starting attributes for a simulation - problem. - """ - self.statistics = Statistics() #Initialize the statistics dictionary - self.options = {"report_continuously":False, - "display_progress":True, - "verbosity":NORMAL, - "backward":False, - "store_event_points":True, - "time_limit":0, - "clock_step":False, - "num_threads":1} #multiprocessing.cpu_count() - #self.internal_flags = {"state_events":False,"step_events":False,"time_events":False} #Flags for checking the problem (Does the problem have state events?) - self.supports = {"state_events":False,"interpolated_output":False,"report_continuously":False,"sensitivity_calculations":False,"interpolated_sensitivity_output":False} #Flags for determining what the solver supports - self.problem_info = {"dim":0,"dimRoot":0,"dimSens":0,"state_events":False,"step_events":False,"time_events":False - ,"jac_fcn":False, "sens_fcn":False, "jacv_fcn":False,"switches":False,"type":0,"jaclag_fcn":False,'prec_solve':False,'prec_setup':False - ,"jac_fcn_nnz": -1} - #Type of the problem - #0 = Explicit - #1 = Implicit - - #Data object for storing the event data - self.event_data = [] - self._event_info = N.array([]) - - if problem is None: - raise ODE_Exception('The problem needs to be a subclass of a Problem.') - - #Check Problem for event functions - if hasattr(problem, 'time_events'): - if hasattr(problem, 'time_events_use'): - self.problem_info["time_events"] = problem.time_events_use - else: - self.problem_info["time_events"] = True - - if hasattr(problem, 'state_events'): - if hasattr(problem, 'state_events_use'): - self.problem_info["state_events"] = problem.state_events_use - else: - self.problem_info["state_events"] = True - - if hasattr(problem, 'step_events'): - if hasattr(problem, 'step_events_use'): - self.problem_info["step_events"] = problem.step_events_use - else: - self.problem_info["step_events"] = True - - if hasattr(problem, 'y0'): - self.y0 = N.array(problem.y0,dtype=realtype) if len(N.array(problem.y0,dtype=realtype).shape)>0 else N.array([problem.y0],dtype=realtype) - self.problem_info["dim"] = len(self.y0) - else: - raise ODE_Exception('y0 must be specified in the problem.') - - if hasattr(problem, 'neq'): # relevant for overdetermined problems: neq=number of equations >= dim - self.problem_info["neq"] = problem.neq - else: - self.problem_info["neq"] = self.problem_info["dim"] - - if hasattr(problem, "p0"): - self.p0 = N.array(problem.p0,dtype=realtype) if len(N.array(problem.p0,dtype=realtype).shape)>0 else N.array([problem.p0],dtype=realtype) - self.problem_info["dimSens"] = len(self.p0) - self.p = self.p0.copy() - - if hasattr(problem, "sw0"): - self.sw0 = N.array(problem.sw0,dtype=N.bool) if len(N.array(problem.sw0,dtype=N.bool).shape)>0 else N.array([problem.sw0],dtype=N.bool) - self.problem_info["switches"] = True - self.sw = self.sw0.tolist() - - if hasattr(problem, 't0'): - self.t0 = float(problem.t0) - else: - self.t0 = 0.0 - - if hasattr(problem, "jac"): - if hasattr(problem, "jac_use"): - self.problem_info["jac_fcn"] = problem.jac_use - else: - self.problem_info["jac_fcn"] = True - if hasattr(problem, "jac_nnz"): - self.problem_info["jac_fcn_nnz"] = problem.jac_nnz - if hasattr(problem, "jacv"): - self.problem_info["jacv_fcn"] = True - if hasattr(problem, "jaclag"): - self.problem_info["jaclag_fcn"] = True - if hasattr(problem, "prec_solve"): - self.problem_info["prec_solve"] = True - if hasattr(problem, "prec_setup"): - self.problem_info["prec_setup"] = True - if hasattr(problem, "rhs_sens"): - self.problem_info["sens_fcn"] = True - - #Reset solution variables - self._reset_solution_variables() - - #Specify storing of sensitivity to 0 - problem._sensitivity_result = 0 - - #Initialize timer - self.elapsed_step_time = -1.0 - self.clock_start = -1.0 - self.display_counter = 1 - self.chattering_clear_counter = 0 - self.chattering_ok_print = 1 - - #Add common statistics - self.statistics.add_key("nsteps", "Number of steps") - self.statistics.add_key("nfcns", "Number of function evaluations") - self.statistics.add_key("njacs", "Number of Jacobian evaluations") - self.statistics.add_key("njacvecs", "Number of Jacobian*vector evaluations") - self.statistics.add_key("nfcnjacs", "Number of function eval. due to Jacobian eval.") - self.statistics.add_key("nerrfails", "Number of error test failures") - self.statistics.add_key("nlus", "Number of LU decompositions") - self.statistics.add_key("nniters", "Number of nonlinear iterations") - self.statistics.add_key("nnfails", "Number of nonlinear convergence failures") - self.statistics.add_key("nstatefcns", "Number of state function evaluations") - self.statistics.add_key("nstateevents", "Number of state events") - self.statistics.add_key("ntimeevents", "Number of time events") - self.statistics.add_key("nstepevents", "Number of step events") - self.statistics.add_key("nprecs", "Number of pre-conditioner solves") - self.statistics.add_key("nprecsetups", "Number of pre-conditioner setups") - self.statistics.add_key("nsensfcns", "Number of sensitivity evaluations") - self.statistics.add_key("nsensfcnfcns", "Number of function eval. due to sensitivity eval.") - self.statistics.add_key("nsensniters", "Number of sensitivity nonlinear iterations") - self.statistics.add_key("nsensnfails", "Number of sensitivity nonlinear convergence failures") - self.statistics.add_key("nsenserrfails", "Number of sensitivity error test failures") - - - def __call__(self, double tfinal, int ncp=0, list cpts=None): - return self.simulate(tfinal, ncp, cpts) - - cdef _reset_solution_variables(self): - """ - Resets solution variables. - """ - self.t_sol = [] - self.y_sol = [] - self.yd_sol = [] - self.p_sol = [[] for i in range(self.problem_info["dimSens"])] - - - cpdef simulate(self, double tfinal, int ncp=0, object ncp_list=None): - """ - Calls the integrator to perform the simulation over the given time-interval. - If a second call to simulate is performed, the simulation starts from the last - given final time. - - Parameters:: - - tfinal - - Final time for the simulation - - - Should be a float or integer greater than the initial time. - - ncp - - Default '0'. Number of communication points where the - solution is returned. If '0', the integrator will return - at its internal steps. - - - Should be an integer. - - ncp_list - - Default None. A list of time points where the solution - should be returned. Note, requires that ncp == 0. - - Example: - - simulate(10.0, 100), 10.0 is the final time and 100 is the number - communication points. - - """ - t0 = self.t - - #Reset solution variables - self._reset_solution_variables() - - #Error checking - try: - tfinal = float(tfinal) - except ValueError: - raise AssimuloException('Final time must be an integer or float.') - - if (self.t > tfinal) and not self.options["backward"]: - raise AssimuloException('Final time {} must be greater than start time {}.\n Perhaps you should consider to reset the integration. If the intention is to integrate backwards, please use the backwards option.'.format(tfinal,self.t)) - - if not isinstance(ncp, int): - raise AssimuloException('Number of communication points must be an integer') - - if ncp < 0: - ncp = 0 - self.log_message('Number of communication points must be a positive integer, setting ncp = 0.',WARNING) - - #Check solver support against current problem - if self.problem_info["state_events"] and self.supports["state_events"] is False: - self.log_message("The current solver does not support state events (root functions). Disabling and continues.", WHISPER) - self.problem_info["state_events"] = False - - if self.problem_info["step_events"] and self.supports["report_continuously"] is False: - self.log_message("The current solver does not support step events (report continuously). Disabling step events and continues.", WHISPER) - self.problem_info["step_events"] = False - - if self.supports["report_continuously"] is False and self.options["report_continuously"]: - self.log_message("The current solver does not support to report continuously. Setting report_continuously to False and continues.", WHISPER) - self.options["report_continuously"] = False - - if (ncp != 0 or ncp_list is not None) and (self.options["report_continuously"] or self.problem_info["step_events"]) and self.supports["interpolated_output"] is False: - self.log_message("The current solver does not support interpolated output. Setting ncp to 0 and ncp_list to None and continues.", WHISPER) - ncp = 0 - ncp_list = None - - if (ncp != 0 or ncp_list is not None) and self.problem_info["state_events"] and self.supports["report_continuously"] is False: - self.log_message("The current solver does not support interpolated output together with state events. Setting ncp to 0 and ncp_list to None and continues.", WHISPER) - ncp = 0 - ncp_list = None - elif (ncp != 0 or ncp_list is not None) and self.problem_info["step_events"] and self.supports["report_continuously"]: - if not self.report_continuously: - self.log_message("The problem contains step events: report_continuously is set to True", WHISPER) - self.report_continuously = True - - #Determine the output list - if ncp != 0: - output_list = N.linspace(t0,tfinal,ncp+1)[1:] - output_index = 0 - elif ncp_list is not None: - if self.options["backward"]: - output_list = N.array(ncp_list, dtype=realtype, ndmin=1)[N.logical_and(N.array(ncp_list, dtype=realtype, ndmin=1)=tfinal)] - output_list = -N.sort(-output_list) - if output_list[-1] > tfinal: #Add the last point if necessary! - output_list = N.append(output_list, tfinal) - else: - output_list = N.array(ncp_list, dtype=realtype, ndmin=1)[N.logical_and(N.array(ncp_list, dtype=realtype, ndmin=1)>t0,N.array(ncp_list, dtype=realtype, ndmin=1)<=tfinal)] - if output_list[-1] < tfinal: #Add the last point if necessary! - output_list = N.append(output_list, tfinal) - output_index = 0 - else: - output_list = None - output_index = 0 - - #Determine if we are using one step mode or normal mode - if self.problem_info['step_events'] or self.options['report_continuously']: - REPORT_CONTINUOUSLY = 1 - else: - REPORT_CONTINUOUSLY = 0 - - #Determine if the output should be interpolated or not - if output_list is None: - INTERPOLATE_OUTPUT = 0 - else: - INTERPOLATE_OUTPUT = 1 - - #Time and Step events - TIME_EVENT = 1 if self.problem_info['time_events'] is True else 0 - - #Simulation starting, call initialize - self.problem.initialize(self) - self.initialize() - - #Start of simulation, start the clock - time_start = timer() - - #Start the simulation - self._simulate(t0, tfinal, output_list, REPORT_CONTINUOUSLY, INTERPOLATE_OUTPUT, TIME_EVENT) - - #End of simulation, stop the clock - time_stop = timer() - - #Simulation complete, call finalize - self.finalize() - self.problem.finalize(self) - - #Print the simulation statistics - self.print_statistics(NORMAL) - - #Log elapsed time - self.log_message('Simulation interval : ' + str(t0) + ' - ' + str(self.t) + ' seconds.', NORMAL) - self.log_message('Elapsed simulation time: ' + str(time_stop-time_start) + ' seconds.', NORMAL) - - #Return the results - if isinstance(self.problem, Explicit_Problem) or isinstance(self.problem, Delay_Explicit_Problem) or isinstance(self.problem, SingPerturbed_Problem): - return self.t_sol, N.array(self.y_sol) - else: - return self.t_sol, N.array(self.y_sol), N.array(self.yd_sol) - - def _simulate(self,t0, tfinal, output_list, REPORT_CONTINUOUSLY, INTERPOLATE_OUTPUT, TIME_EVENT): - pass - - cpdef initialize(self): - pass - - cpdef finalize(self): - pass - - def _set_verbosity(self, verb): - try: - self.options["verbosity"] = int(verb) - except: - raise AssimuloException("Verbosity must be an integer.") - - def _get_verbosity(self): - """ - This determines the level of the output. A smaller value - means more output. The following values can be set: - - QUIET = 50 - WHISPER = 40 - NORMAL = 30 - LOUD = 20 - SCREAM = 10 - - Parameters:: - - verb - - Default 30 (NORMAL) - - - Should be a integer. - - """ - return self.options["verbosity"] - - verbosity = property(_get_verbosity,_set_verbosity) - - def _set_time_limit(self, time_limit): - if time_limit < 0: - raise AssimuloException("The time limit must be positive or zero.") - self.options["time_limit"] = time_limit - - def _get_time_limit(self): - """ - This option can be used to limit the time of an integration. I.e - to set an upper bound on the time allowed for the integration - to be completed. The time limit is specified in seconds. For the - limit to be checked, the option report_continuously must be True. - - Parameters:: - - time_limit - - Default 0, i.e. NO limit. - """ - return self.options["time_limit"] - - time_limit = property(_get_time_limit, _set_time_limit) - - def _set_display_progress(self, display_progress): - self.options["display_progress"] = bool(display_progress) - - def _get_display_progress(self): - """ - This option actives output during the integration in terms of - that the current integration is periodically printed to the - stdout. Note though that report_continuously needs to be - activated. - - Parameters:: - - display_progress - - Default True - """ - return self.options["display_progress"] - - display_progress = property(_get_display_progress, _set_display_progress) - - def _set_report_continuously(self, report_continuously): - self.options["report_continuously"] = bool(report_continuously) - - def _get_report_continuously(self): - """ - This options specifies if the solver should report the solution - continuously after steps. - - Parameters:: - - report_continuously - - - Default False - - - Should be a boolean. - - """ - return self.options["report_continuously"] - - report_continuously = property(_get_report_continuously,_set_report_continuously) - - def _set_number_threads(self, num_threads): - self.options["num_threads"] = int(num_threads) - - def _get_number_threads(self): - """ - This options specifies the number of threads to be used for those - solvers that supports it. - - Parameters:: - - num_threads - - - Default is the number of cores - - - Should be a integer. - - """ - return self.options["num_threads"] - - num_threads = property(_get_number_threads,_set_number_threads) - - - def _set_store_event_points(self, store_event_points): - self.options["store_event_points"] = bool(store_event_points) - - def _get_store_event_points(self): - """ - This options specifies if the solver should save additional points - at the events, :math:`t_e^-, t_e^+`. - - Parameters:: - - store_event_points - - - Default True - - - Should be a Boolean. - - """ - return self.options["store_event_points"] - - store_event_points = property(_get_store_event_points,_set_store_event_points) - - def _set_clock_step(self, clock_step): - self.options["clock_step"] = clock_step - - def _get_clock_step(self): - """ - Specifies if the elapsed time of an integrator step should be - timed or not. Not that this is only possible if running in - report continuously mode. - """ - return self.options["clock_step"] - - clock_step = property(_get_clock_step, _set_clock_step) - - def _set_backward(self, backward): - self.options["backward"] = bool(backward) - - def _get_backward(self): - """ - Specifies if the simulation is done in reverse time. (NOTE: - experimental!) - - Parameters:: - - backward - - - Default False - - Boolean - """ - return self.options["backward"] - - backward = property(_get_backward,_set_backward) - - cpdef log_message(self, message,int level): - if level >= self.options["verbosity"]: - print(message) - - cpdef log_event(self,double time,object event_info, int level): - if level >= self.options["verbosity"]: - self.event_data.append([time,event_info]) - - cpdef clear_logs(self): - """ - Clears the currently stored log messages. - """ - self.event_data = [] - - cpdef get_options(self): - """ - Returns the current solver options. - """ - return self.options - - cpdef get_supports(self): - """ - Returns the functionality which the solver supports. - """ - return self.supports - - cpdef get_statistics(self): - """ - Returns the run-time statistics (if any). - """ - return self.statistics - - cpdef get_event_data(self): - """ - Returns the event information (if any). If there exists information - about events, it will be returned as a list of tuples where the - first value is the time of the occured event and the second is the - event information. - """ - return self.event_data - - cpdef print_event_data(self): - """ - Prints the event information (if any). - """ - cdef i = 0 - for i in self.event_data: - print 'Time, t = %e'%i[0] - print ' Event info, ', i[1] - print 'Number of events: ', len(self.event_data) - - def print_statistics(self, verbose=NORMAL): - """ - This method should print the statistics of the solver. - """ - if verbose >= self.options["verbosity"]: - self.log_message('Final Run Statistics: %s ' % self.problem.name, verbose) - self.statistics.print_stats() - - cpdef get_elapsed_step_time(self): - """ - Returns the elapsed time of a step. I.e. how long a step took. - Note that this is only possible if running in report_continuously - mode and should only be used to get a general idea of how long - a step really took. - - Returns:: - - Elapsed time (note -1.0 indicates that it was not used) - """ - return self.elapsed_step_time - - def _compact_atol(self): - """ - Reduces atol to a scalar if it is an ndarray and all entries are the same. - Used for print solver options in a more compact way - """ - if isinstance(self.atol,N.ndarray) and (self.atol==self.atol[0]).all(): - return self.atol[0] - else: - return self.atol - - cpdef _chattering_check(self, object event_info): - self.chattering_clear_counter = 0 - if event_info[0] is not None and event_info[0] != []: - if self.chattering_check is None: - self.chattering_check = abs(N.array(event_info[0])) - else: - self.chattering_check += abs(N.array(event_info[0])) - - if max(self.chattering_check) > 5 and self.chattering_ok_print: - self.chattering_ok_print = 0 - self.log_message("Warning: Possible chattering detected at t = %e in state event(s): "%self.t + - str(N.where(self.chattering_check == max(self.chattering_check))[0]), NORMAL) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +cimport numpy as N +from timeit import default_timer as timer + +import itertools +import multiprocessing + +from exception import * +from problem import Explicit_Problem, Delay_Explicit_Problem, Implicit_Problem, SingPerturbed_Problem +from support import Statistics + +include "constants.pxi" #Includes the constants (textual include) + +realtype = float + +cdef class ODE: + """ + Base class for all our integrators. + """ + + def __init__(self, problem): + """ + Defines general starting attributes for a simulation + problem. + """ + self.statistics = Statistics() #Initialize the statistics dictionary + self.options = {"report_continuously":False, + "display_progress":True, + "verbosity":NORMAL, + "backward":False, + "store_event_points":True, + "time_limit":0, + "clock_step":False, + "num_threads":1} #multiprocessing.cpu_count() + #self.internal_flags = {"state_events":False,"step_events":False,"time_events":False} #Flags for checking the problem (Does the problem have state events?) + self.supports = {"state_events":False,"interpolated_output":False,"report_continuously":False,"sensitivity_calculations":False,"interpolated_sensitivity_output":False} #Flags for determining what the solver supports + self.problem_info = {"dim":0,"dimRoot":0,"dimSens":0,"state_events":False,"step_events":False,"time_events":False + ,"jac_fcn":False, "sens_fcn":False, "jacv_fcn":False,"switches":False,"type":0,"jaclag_fcn":False,'prec_solve':False,'prec_setup':False + ,"jac_fcn_nnz": -1} + #Type of the problem + #0 = Explicit + #1 = Implicit + + #Data object for storing the event data + self.event_data = [] + self._event_info = N.array([]) + + if problem is None: + raise ODE_Exception('The problem needs to be a subclass of a Problem.') + + #Check Problem for event functions + if hasattr(problem, 'time_events'): + if hasattr(problem, 'time_events_use'): + self.problem_info["time_events"] = problem.time_events_use + else: + self.problem_info["time_events"] = True + + if hasattr(problem, 'state_events'): + if hasattr(problem, 'state_events_use'): + self.problem_info["state_events"] = problem.state_events_use + else: + self.problem_info["state_events"] = True + + if hasattr(problem, 'step_events'): + if hasattr(problem, 'step_events_use'): + self.problem_info["step_events"] = problem.step_events_use + else: + self.problem_info["step_events"] = True + + if hasattr(problem, 'y0'): + self.y0 = N.array(problem.y0,dtype=realtype) if len(N.array(problem.y0,dtype=realtype).shape)>0 else N.array([problem.y0],dtype=realtype) + self.problem_info["dim"] = len(self.y0) + else: + raise ODE_Exception('y0 must be specified in the problem.') + + if hasattr(problem, 'neq'): # relevant for overdetermined problems: neq=number of equations >= dim + self.problem_info["neq"] = problem.neq + else: + self.problem_info["neq"] = self.problem_info["dim"] + + if hasattr(problem, "p0"): + self.p0 = N.array(problem.p0,dtype=realtype) if len(N.array(problem.p0,dtype=realtype).shape)>0 else N.array([problem.p0],dtype=realtype) + self.problem_info["dimSens"] = len(self.p0) + self.p = self.p0.copy() + + if hasattr(problem, "sw0"): + self.sw0 = N.array(problem.sw0,dtype=N.bool) if len(N.array(problem.sw0,dtype=N.bool).shape)>0 else N.array([problem.sw0],dtype=N.bool) + self.problem_info["switches"] = True + self.sw = self.sw0.tolist() + + if hasattr(problem, 't0'): + self.t0 = float(problem.t0) + else: + self.t0 = 0.0 + + if hasattr(problem, "jac"): + if hasattr(problem, "jac_use"): + self.problem_info["jac_fcn"] = problem.jac_use + else: + self.problem_info["jac_fcn"] = True + if hasattr(problem, "jac_nnz"): + self.problem_info["jac_fcn_nnz"] = problem.jac_nnz + if hasattr(problem, "jacv"): + self.problem_info["jacv_fcn"] = True + if hasattr(problem, "jaclag"): + self.problem_info["jaclag_fcn"] = True + if hasattr(problem, "prec_solve"): + self.problem_info["prec_solve"] = True + if hasattr(problem, "prec_setup"): + self.problem_info["prec_setup"] = True + if hasattr(problem, "rhs_sens"): + self.problem_info["sens_fcn"] = True + + #Reset solution variables + self._reset_solution_variables() + + #Specify storing of sensitivity to 0 + problem._sensitivity_result = 0 + + #Initialize timer + self.elapsed_step_time = -1.0 + self.clock_start = -1.0 + self.display_counter = 1 + self.chattering_clear_counter = 0 + self.chattering_ok_print = 1 + + #Add common statistics + self.statistics.add_key("nsteps", "Number of steps") + self.statistics.add_key("nfcns", "Number of function evaluations") + self.statistics.add_key("njacs", "Number of Jacobian evaluations") + self.statistics.add_key("njacvecs", "Number of Jacobian*vector evaluations") + self.statistics.add_key("nfcnjacs", "Number of function eval. due to Jacobian eval.") + self.statistics.add_key("nerrfails", "Number of error test failures") + self.statistics.add_key("nlus", "Number of LU decompositions") + self.statistics.add_key("nniters", "Number of nonlinear iterations") + self.statistics.add_key("nnfails", "Number of nonlinear convergence failures") + self.statistics.add_key("nstatefcns", "Number of state function evaluations") + self.statistics.add_key("nstateevents", "Number of state events") + self.statistics.add_key("ntimeevents", "Number of time events") + self.statistics.add_key("nstepevents", "Number of step events") + self.statistics.add_key("nprecs", "Number of pre-conditioner solves") + self.statistics.add_key("nprecsetups", "Number of pre-conditioner setups") + self.statistics.add_key("nsensfcns", "Number of sensitivity evaluations") + self.statistics.add_key("nsensfcnfcns", "Number of function eval. due to sensitivity eval.") + self.statistics.add_key("nsensniters", "Number of sensitivity nonlinear iterations") + self.statistics.add_key("nsensnfails", "Number of sensitivity nonlinear convergence failures") + self.statistics.add_key("nsenserrfails", "Number of sensitivity error test failures") + + + def __call__(self, double tfinal, int ncp=0, list cpts=None): + return self.simulate(tfinal, ncp, cpts) + + cdef _reset_solution_variables(self): + """ + Resets solution variables. + """ + self.t_sol = [] + self.y_sol = [] + self.yd_sol = [] + self.p_sol = [[] for i in range(self.problem_info["dimSens"])] + + + cpdef simulate(self, double tfinal, int ncp=0, object ncp_list=None): + """ + Calls the integrator to perform the simulation over the given time-interval. + If a second call to simulate is performed, the simulation starts from the last + given final time. + + Parameters:: + + tfinal + - Final time for the simulation + + - Should be a float or integer greater than the initial time. + + ncp + - Default '0'. Number of communication points where the + solution is returned. If '0', the integrator will return + at its internal steps. + + - Should be an integer. + + ncp_list + - Default None. A list of time points where the solution + should be returned. Note, requires that ncp == 0. + + Example: + + simulate(10.0, 100), 10.0 is the final time and 100 is the number + communication points. + + """ + t0 = self.t + + #Reset solution variables + self._reset_solution_variables() + + #Error checking + try: + tfinal = float(tfinal) + except ValueError: + raise AssimuloException('Final time must be an integer or float.') + + if (self.t > tfinal) and not self.options["backward"]: + raise AssimuloException('Final time {} must be greater than start time {}.\n Perhaps you should consider to reset the integration. If the intention is to integrate backwards, please use the backwards option.'.format(tfinal,self.t)) + + if not isinstance(ncp, int): + raise AssimuloException('Number of communication points must be an integer') + + if ncp < 0: + ncp = 0 + self.log_message('Number of communication points must be a positive integer, setting ncp = 0.',WARNING) + + #Check solver support against current problem + if self.problem_info["state_events"] and self.supports["state_events"] is False: + self.log_message("The current solver does not support state events (root functions). Disabling and continues.", WHISPER) + self.problem_info["state_events"] = False + + if self.problem_info["step_events"] and self.supports["report_continuously"] is False: + self.log_message("The current solver does not support step events (report continuously). Disabling step events and continues.", WHISPER) + self.problem_info["step_events"] = False + + if self.supports["report_continuously"] is False and self.options["report_continuously"]: + self.log_message("The current solver does not support to report continuously. Setting report_continuously to False and continues.", WHISPER) + self.options["report_continuously"] = False + + if (ncp != 0 or ncp_list is not None) and (self.options["report_continuously"] or self.problem_info["step_events"]) and self.supports["interpolated_output"] is False: + self.log_message("The current solver does not support interpolated output. Setting ncp to 0 and ncp_list to None and continues.", WHISPER) + ncp = 0 + ncp_list = None + + if (ncp != 0 or ncp_list is not None) and self.problem_info["state_events"] and self.supports["report_continuously"] is False: + self.log_message("The current solver does not support interpolated output together with state events. Setting ncp to 0 and ncp_list to None and continues.", WHISPER) + ncp = 0 + ncp_list = None + elif (ncp != 0 or ncp_list is not None) and self.problem_info["step_events"] and self.supports["report_continuously"]: + if not self.report_continuously: + self.log_message("The problem contains step events: report_continuously is set to True", WHISPER) + self.report_continuously = True + + #Determine the output list + if ncp != 0: + output_list = N.linspace(t0,tfinal,ncp+1)[1:] + output_index = 0 + elif ncp_list is not None: + if self.options["backward"]: + output_list = N.array(ncp_list, dtype=realtype, ndmin=1)[N.logical_and(N.array(ncp_list, dtype=realtype, ndmin=1)=tfinal)] + output_list = -N.sort(-output_list) + if output_list[-1] > tfinal: #Add the last point if necessary! + output_list = N.append(output_list, tfinal) + else: + output_list = N.array(ncp_list, dtype=realtype, ndmin=1)[N.logical_and(N.array(ncp_list, dtype=realtype, ndmin=1)>t0,N.array(ncp_list, dtype=realtype, ndmin=1)<=tfinal)] + if output_list[-1] < tfinal: #Add the last point if necessary! + output_list = N.append(output_list, tfinal) + output_index = 0 + else: + output_list = None + output_index = 0 + + #Determine if we are using one step mode or normal mode + if self.problem_info['step_events'] or self.options['report_continuously']: + REPORT_CONTINUOUSLY = 1 + else: + REPORT_CONTINUOUSLY = 0 + + #Determine if the output should be interpolated or not + if output_list is None: + INTERPOLATE_OUTPUT = 0 + else: + INTERPOLATE_OUTPUT = 1 + + #Time and Step events + TIME_EVENT = 1 if self.problem_info['time_events'] is True else 0 + + #Simulation starting, call initialize + self.problem.initialize(self) + self.initialize() + + #Start of simulation, start the clock + time_start = timer() + + #Start the simulation + self._simulate(t0, tfinal, output_list, REPORT_CONTINUOUSLY, INTERPOLATE_OUTPUT, TIME_EVENT) + + #End of simulation, stop the clock + time_stop = timer() + + #Simulation complete, call finalize + self.finalize() + self.problem.finalize(self) + + #Print the simulation statistics + self.print_statistics(NORMAL) + + #Log elapsed time + self.log_message('Simulation interval : ' + str(t0) + ' - ' + str(self.t) + ' seconds.', NORMAL) + self.log_message('Elapsed simulation time: ' + str(time_stop-time_start) + ' seconds.', NORMAL) + + #Return the results + if isinstance(self.problem, Explicit_Problem) or isinstance(self.problem, Delay_Explicit_Problem) or isinstance(self.problem, SingPerturbed_Problem): + return self.t_sol, N.array(self.y_sol) + else: + return self.t_sol, N.array(self.y_sol), N.array(self.yd_sol) + + def _simulate(self,t0, tfinal, output_list, REPORT_CONTINUOUSLY, INTERPOLATE_OUTPUT, TIME_EVENT): + pass + + cpdef initialize(self): + pass + + cpdef finalize(self): + pass + + def _set_verbosity(self, verb): + try: + self.options["verbosity"] = int(verb) + except: + raise AssimuloException("Verbosity must be an integer.") + + def _get_verbosity(self): + """ + This determines the level of the output. A smaller value + means more output. The following values can be set: + + QUIET = 50 + WHISPER = 40 + NORMAL = 30 + LOUD = 20 + SCREAM = 10 + + Parameters:: + + verb + - Default 30 (NORMAL) + + - Should be a integer. + + """ + return self.options["verbosity"] + + verbosity = property(_get_verbosity,_set_verbosity) + + def _set_time_limit(self, time_limit): + if time_limit < 0: + raise AssimuloException("The time limit must be positive or zero.") + self.options["time_limit"] = time_limit + + def _get_time_limit(self): + """ + This option can be used to limit the time of an integration. I.e + to set an upper bound on the time allowed for the integration + to be completed. The time limit is specified in seconds. For the + limit to be checked, the option report_continuously must be True. + + Parameters:: + + time_limit + - Default 0, i.e. NO limit. + """ + return self.options["time_limit"] + + time_limit = property(_get_time_limit, _set_time_limit) + + def _set_display_progress(self, display_progress): + self.options["display_progress"] = bool(display_progress) + + def _get_display_progress(self): + """ + This option actives output during the integration in terms of + that the current integration is periodically printed to the + stdout. Note though that report_continuously needs to be + activated. + + Parameters:: + + display_progress + - Default True + """ + return self.options["display_progress"] + + display_progress = property(_get_display_progress, _set_display_progress) + + def _set_report_continuously(self, report_continuously): + self.options["report_continuously"] = bool(report_continuously) + + def _get_report_continuously(self): + """ + This options specifies if the solver should report the solution + continuously after steps. + + Parameters:: + + report_continuously + + - Default False + + - Should be a boolean. + + """ + return self.options["report_continuously"] + + report_continuously = property(_get_report_continuously,_set_report_continuously) + + def _set_number_threads(self, num_threads): + self.options["num_threads"] = int(num_threads) + + def _get_number_threads(self): + """ + This options specifies the number of threads to be used for those + solvers that supports it. + + Parameters:: + + num_threads + + - Default is the number of cores + + - Should be a integer. + + """ + return self.options["num_threads"] + + num_threads = property(_get_number_threads,_set_number_threads) + + + def _set_store_event_points(self, store_event_points): + self.options["store_event_points"] = bool(store_event_points) + + def _get_store_event_points(self): + """ + This options specifies if the solver should save additional points + at the events, :math:`t_e^-, t_e^+`. + + Parameters:: + + store_event_points + + - Default True + + - Should be a Boolean. + + """ + return self.options["store_event_points"] + + store_event_points = property(_get_store_event_points,_set_store_event_points) + + def _set_clock_step(self, clock_step): + self.options["clock_step"] = clock_step + + def _get_clock_step(self): + """ + Specifies if the elapsed time of an integrator step should be + timed or not. Not that this is only possible if running in + report continuously mode. + """ + return self.options["clock_step"] + + clock_step = property(_get_clock_step, _set_clock_step) + + def _set_backward(self, backward): + self.options["backward"] = bool(backward) + + def _get_backward(self): + """ + Specifies if the simulation is done in reverse time. (NOTE: + experimental!) + + Parameters:: + + backward + + - Default False + - Boolean + """ + return self.options["backward"] + + backward = property(_get_backward,_set_backward) + + cpdef log_message(self, message,int level): + if level >= self.options["verbosity"]: + print(message) + + cpdef log_event(self,double time,object event_info, int level): + if level >= self.options["verbosity"]: + self.event_data.append([time,event_info]) + + cpdef clear_logs(self): + """ + Clears the currently stored log messages. + """ + self.event_data = [] + + cpdef get_options(self): + """ + Returns the current solver options. + """ + return self.options + + cpdef get_supports(self): + """ + Returns the functionality which the solver supports. + """ + return self.supports + + cpdef get_statistics(self): + """ + Returns the run-time statistics (if any). + """ + return self.statistics + + cpdef get_event_data(self): + """ + Returns the event information (if any). If there exists information + about events, it will be returned as a list of tuples where the + first value is the time of the occured event and the second is the + event information. + """ + return self.event_data + + cpdef print_event_data(self): + """ + Prints the event information (if any). + """ + cdef i = 0 + for i in self.event_data: + print 'Time, t = %e'%i[0] + print ' Event info, ', i[1] + print 'Number of events: ', len(self.event_data) + + def print_statistics(self, verbose=NORMAL): + """ + This method should print the statistics of the solver. + """ + if verbose >= self.options["verbosity"]: + self.log_message('Final Run Statistics: %s ' % self.problem.name, verbose) + self.statistics.print_stats() + + cpdef get_elapsed_step_time(self): + """ + Returns the elapsed time of a step. I.e. how long a step took. + Note that this is only possible if running in report_continuously + mode and should only be used to get a general idea of how long + a step really took. + + Returns:: + + Elapsed time (note -1.0 indicates that it was not used) + """ + return self.elapsed_step_time + + def _compact_atol(self): + """ + Reduces atol to a scalar if it is an ndarray and all entries are the same. + Used for print solver options in a more compact way + """ + if isinstance(self.atol,N.ndarray) and (self.atol==self.atol[0]).all(): + return self.atol[0] + else: + return self.atol + + cpdef _chattering_check(self, object event_info): + self.chattering_clear_counter = 0 + if event_info[0] is not None and event_info[0] != []: + if self.chattering_check is None: + self.chattering_check = abs(N.array(event_info[0])) + else: + self.chattering_check += abs(N.array(event_info[0])) + + if max(self.chattering_check) > 5 and self.chattering_ok_print: + self.chattering_ok_print = 0 + self.log_message("Warning: Possible chattering detected at t = %e in state event(s): "%self.t + + str(N.where(self.chattering_check == max(self.chattering_check))[0]), NORMAL) diff --git a/src/problem.pxd b/src/problem.pxd index f1d3983d..36e58281 100644 --- a/src/problem.pxd +++ b/src/problem.pxd @@ -1,49 +1,49 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -cimport numpy as N - -cdef class cProblem: - cdef public int _sensitivity_result - - cpdef initialize(self, solver) - cpdef reset(self) - cpdef handle_event(self, object solver, event_info) - cpdef finalize(self,object solver) - -cdef class cImplicit_Problem(cProblem): - cpdef res_internal(self, N.ndarray[double, ndim=1] res, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd) - -cdef class cOverdetermined_Problem(cProblem): - cpdef res_internal(self, N.ndarray[double, ndim=1] res, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd) - -cdef class cExplicit_Problem(cProblem): - cpdef int rhs_internal(self, N.ndarray[double, ndim=1] yd, double t, N.ndarray[double, ndim=1] y) - cpdef N.ndarray res(self, t, y, yd, sw=*) - -cdef class cDelay_Explicit_Problem(cExplicit_Problem): - pass - -cdef class cSingPerturbed_Problem(cExplicit_Problem): - pass - - -cdef class cAlgebraic_Problem: - cpdef initialize(self, solver) - cpdef finalize(self,object solver) - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +cimport numpy as N + +cdef class cProblem: + cdef public int _sensitivity_result + + cpdef initialize(self, solver) + cpdef reset(self) + cpdef handle_event(self, object solver, event_info) + cpdef finalize(self,object solver) + +cdef class cImplicit_Problem(cProblem): + cpdef res_internal(self, N.ndarray[double, ndim=1] res, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd) + +cdef class cOverdetermined_Problem(cProblem): + cpdef res_internal(self, N.ndarray[double, ndim=1] res, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd) + +cdef class cExplicit_Problem(cProblem): + cpdef int rhs_internal(self, N.ndarray[double, ndim=1] yd, double t, N.ndarray[double, ndim=1] y) + cpdef N.ndarray res(self, t, y, yd, sw=*) + +cdef class cDelay_Explicit_Problem(cExplicit_Problem): + pass + +cdef class cSingPerturbed_Problem(cExplicit_Problem): + pass + + +cdef class cAlgebraic_Problem: + cpdef initialize(self, solver) + cpdef finalize(self,object solver) + diff --git a/src/problem.pyx b/src/problem.pyx index 4019f3e2..a9bfc23c 100644 --- a/src/problem.pyx +++ b/src/problem.pyx @@ -1,592 +1,592 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -cimport numpy as N - -from assimulo.support import set_type_shape_array - -include "constants.pxi" #Includes the constants (textual include) - - -cdef class cProblem: - - name = "---" - - def __init__(self, y0 = None, double t0 = 0.0, p0 = None, sw0 = None, name = None): - - if not y0 is None: - self.y0 = set_type_shape_array(y0) - if not p0 is None: - self.p0 = set_type_shape_array(p0) - if not sw0 is None: - self.sw0 = set_type_shape_array(sw0, bool) - if name: - self.name = name - self.t0 = t0 - - cpdef initialize(self, solver): - """ - Method for specializing initiation. - """ - solver.log_message("No initialization defined for the problem.", LOUD) - - cpdef reset(self): - """ - Resets a problem to its default values. - """ - pass - - cpdef handle_event(self, object solver, event_info): - """ - Defines how to handle a discontinuity. This functions gets called when - a discontinuity has been found in the supplied event functions. The solver - is the solver attribute while the event_info is a list of length 2 where - the first element is a list containing information about state events and - the second element is a Boolean for indicating if there has been a time - event. If there has not been a state event the first element is an empty - list. The state event list contains a set of integers of values (-1,0,1), - the values indicates which state event has triggered (determined from - state_event(...) ) and the value indicates to where the state event is 'headed'. - """ - solver.log_message("No event handling defined.", NORMAL) - - - cpdef finalize(self,object solver): - """ - Method for specifying the finalization options when the simulation have - finished. - """ - solver.log_message("No finalization defined for the problem.", LOUD) - -cdef class cImplicit_Problem(cProblem): - - def __init__(self, object res=None, y0=None, yd0=None,double t0=0.0, - p0=None, sw0=None, name = None): - cProblem.__init__(self, y0, t0, p0, sw0, name) - if res is not None: - self.res = res - if yd0 is not None: - self.yd0 = set_type_shape_array(yd0) - - - def handle_result(self, solver, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd): - """ - Method for specifying how the result is handled. By default the - data is stored in three vectors: solver.(t/y/yd). - """ - cdef int i = 0 - - solver.t_sol.extend([t]) - solver.y_sol.extend([y]) - solver.yd_sol.extend([yd]) - - #Store sensitivity result (variable _sensitivity_result are set from the solver by the solver) - if self._sensitivity_result == 1: - for i in range(solver.problem_info["dimSens"]): - solver.p_sol[i] += [solver.interpolate_sensitivity(t, i=i)] - - cpdef res_internal(self, N.ndarray[double, ndim=1] res, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd): - try: - res[:] = self.res(t,y,yd) - except: - return ID_FAIL - return ID_OK - -cdef class cOverdetermined_Problem(cProblem): - - def __init__(self, object res=None, y0=None, yd0=None,double t0=0.0, - p0=None, sw0=None, name=None): - cProblem.__init__(self, y0, t0, p0, sw0, name) - if res is not None: - self.res = res - if yd0 is not None: - self.yd0 = set_type_shape_array(yd0) - - - def handle_result(self, solver, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd): - """ - Method for specifying how the result is to be handled. As default the - data is stored in three vectors: solver.(t/y/yd). - """ - cdef int i = 0 - - solver.t_sol.extend([t]) - solver.y_sol.extend([y]) - solver.yd_sol.extend([yd]) - - cpdef res_internal(self, N.ndarray[double, ndim=1] res, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd): - try: - res[:] = self.res(t,y,yd) - except: - return ID_FAIL - return ID_OK - -cdef class cExplicit_Problem(cProblem): - - def __init__(self, object rhs=None, y0=None,double t0=0.0, p0=None, sw0=None, name = None): - - cProblem.__init__(self, y0, t0, p0, sw0, name) - if rhs is not None: - self.rhs = rhs - def handle_result(self, solver, double t, N.ndarray[double, ndim=1] y): - """ - Method for specifying how the result is to be handled. As default the - data is stored in two vectors: solver.(t/y). - """ - cdef int i = 0 - - solver.t_sol.extend([t]) - solver.y_sol.extend([y]) - - #Store sensitivity result (variable _sensitivity_result are set from the solver by the solver) - if self._sensitivity_result == 1: - for i in range(solver.problem_info["dimSens"]): - solver.p_sol[i] += [solver.interpolate_sensitivity(t, i=i)] - - cpdef int rhs_internal(self, N.ndarray[double, ndim=1] yd, double t, N.ndarray[double, ndim=1] y): - try: - yd[:] = self.rhs(t,y) - except: - return ID_FAIL - return ID_OK - - cpdef N.ndarray res(self, t, y, yd, sw=None): - if sw == None: - return yd-self.rhs(t,y) - else: - return yd-self.rhs(t, y, sw) - -cdef class cDelay_Explicit_Problem(cExplicit_Problem): - def __init__(self, object rhs=None, y0=None, phi = None, arglag = None, lagcompmap = None, jaclag = None, nlags = None, njacl = None, double t0=0.0, p0=None, sw0=None): - cExplicit_Problem.__init__(self, rhs, y0, t0, p0, sw0) - - if phi is not None: - self.phi = phi - if arglag is not None: - self.arglag = arglag - if jaclag is not None: - self.jaclag = jaclag - - self.lagcompmap = lagcompmap - - self.nlags = nlags # Number of delay arguments to differentiate for - self.njacl = njacl # Number of possible delay arguments that fcn can be differentiated with respect to - -cdef class cSingPerturbed_Problem(cExplicit_Problem): - - def __init__(self, rhs1=None, rhs2=None, yy0=None, zz0=None, double t0=0.0, eps=None, name = None): - if rhs1 is not None: - self.rhs1 = rhs1 - if rhs2 is not None: - self.rhs2 = rhs2 - if eps is not None: - self.eps = eps - if yy0 is not None: - self.yy0 = set_type_shape_array(yy0) - if zz0 is not None: - self.zz0 = set_type_shape_array(zz0) - # preparing things so that the problem also describes a - # classical explicit problem without exposing the structure - # of a singularly perturbed problem - if yy0 is not None and zz0 is not None: - y0 = N.hstack((self.yy0,self.zz0)) - elif yy0 is not None: - y0 = self.yy0 - elif zz0 is not None: - y0 = self.zz0 - self.n = len(self.yy0) if yy0 is not None else 0 - self.m = len(self.zz0) if zz0 is not None else 0 - cExplicit_Problem.__init__(self, y0=y0, t0=t0, name=name) - - def rhs(self,t,y): - yy=y[:self.n] - zz=y[self.n:] - yydot=self.rhs1(t,yy,zz) - zzdot=self.rhs2(t,yy,zz) - # componentwise division by eps as it is the diagonal of - # a diagonal matrix - if self.eps != None: - zzdot /= self.eps - return N.hstack((yydot,zzdot)) - -class Delay_Explicit_Problem(cDelay_Explicit_Problem): - pass - -class Implicit_Problem(cImplicit_Problem): - """ - Problem for our implicit integrators (DAEs). A problem - consists of the residual function and some initial conditions. - - Parameters :: - - res - Function that calculates the residual. Depending on - the problem and the support of the solver, this function can - have the following input parameters. - - res(t,y,yd) - Normal DAE - res(t,y,yd,sw) - An DAE with different modes, sw is a list of - switches (boolean list) which should be held - constant during the integration and only be - changed when an event have occured. Used together - with event functions. - res(t,y,yd,p) - An DAE with parameters for which sensitivities - should be calculated. - res(t,y,yd,sw,p) - An DAE with both parameters and switches. - - Returns: - A numpy array of size len(y). - y0 - Defines the starting values of y0. - yd0 - Defines the starting values of yd0. - t0 - Defines the starting time. - p0 (Depending on if the solver supports sensitivity calculations) - Parameters for which sensitivites are to be calculated - sw0 (Depending on if the solver supports state events) - Defines the starting values of the switches. - Should be a list of Booleans. - - Parameters (optionally contained in class) :: - - algvar - Defines the differential and algebraic components of the problem. - Should be a list of integers. For more information, see the - property algvar in IDA. - - Signature of default or user provided methods. Their use is solver dependent. :: - - def state_events(self ,t ,y ,yd, sw) - Defines the event (root) functions. - - Returns: - A numpy array. - - def time_events(self, t, y, yd, sw) - Defines the time events. This function should return - the next time-point for a time event. At a time-event - the usual method handle_event is called for the specific - handling. If there are no more time events. This function - should return None. - - Returns: - Float - The time-point for the next time-event. - None - No time-event. - - def jac(self, c, t, y, yd, sw) - Defines the Jacobian, which should be of the form - J = dF/dx + c*dF/dx'. - - Returns: - A numpy array of size len(y)*len(y). - - def handle_result(self, solver, t, y, yd) - Method for specifying how the result is handled. - By default the data is stored in three vectors, solver.(t_sol/y_sol/yd_sol). - If the problem to be solved also involve sensitivities these results are - stored in p_sol - - def handle_event(self, object solver, event_info): - Defines how to handle a discontinuity. This functions gets called when - a discontinuity has been found in the supplied event functions. The solver - is the solver attribute. The event_info is a list of length 2 where - the first element is a list containing information about state events and - the second element is a Boolean indicating if there has been a time - event. If there has not been a state event the first element is an empty - list. The state event list contains a set of integers of values (-1,0,1), - the values indicate which state event has triggered (determined from - state_event(...) ) and the value indicates to where the state event is 'headed'. - """ - pass -class Overdetermined_Problem(cOverdetermined_Problem): - """ - Problem for integrators for overdetermined DAES (ODAEs). A problem - consists of the residual function with more components than state variables - and some initial conditions. - - Parameters :: - - res - Function that calculates the residual. Depending on - the problem and the support of the solver, this function can - have the following input parameters. - - res(t,y,yd) - Normal ODAE - res(t,y,yd,sw) - An ODAE with different modes, sw is a list of - switches (boolean list) which should be held - constant during the integration and only be - changed when an event have occured. Used together - with event functions. - - Returns: - A numpy array of size neq > len(y). - y0 - Defines the starting values of y0. - yd0 - Defines the starting values of yd0. - t0 - Defines the starting time. - sw0 (Depending on if the solver supports state events) - Defines the starting values of the switches. - Should be a list of Booleans. - - Parameters (optionally contained in class) :: - - algvar - Defines the differential and algebraic components of the problem. - Should be a list of integers. For more information, see the - property algvar in IDA. - - Available (optional) options (depending on the solver support):: - - def state_events(self ,t ,y ,yd, sw) - Defines the event (root) functions. - - Returns: - A numpy array. - - def time_events(self, t, y, yd, sw) - Defines the time events. This function should return - the next time-point for a time event. At a time-event - the usual method handle_event is called for the specific - handling. If there are no more time events. This function - should return None. - - Returns: - Float - The time-point for the next time-event. - None - No time-event. - - def jac(self, c, t, y, yd, sw) - Defines the Jacobian, which should be of the form - J = dF/dx + c*dF/dx'. - - Returns: - A numpy array of size neq*len(y). - - def handle_result(self, solver, t, y, yd) - Method for specifying how the result is handled. - By default the data is stored in three vectors, solver.(t_sol/y_sol/yd_sol). - If the problem to be solved also involve sensitivities these results are - stored in p_sol - - def handle_event(self, object solver, event_info): - Defines how to handle a discontinuity. This functions gets called when - a discontinuity has been found in the supplied event functions. The solver - is the solver attribute. The event_info is a list of length 2 where - the first element is a list containing information about state events and - the second element is a Boolean indicating if there has been a time - event. If there have not been a state event the first element is an empty - list. The state event list contains a set of integers of values (-1,0,1), - the values indicate which state event has triggered (determined from - state_event(...) ) and the value indicates to where the state event is 'headed'. - """ - pass -class Explicit_Problem(cExplicit_Problem): - """ - Problem for our explicit integrators (ODEs). A problem - consists of the right-hand-side and some initial conditions. - - Parameters:: - - rhs - Function that calculates the right-hand-side. Depending on - the problem and the support of the solver, this function has - the following input parameters: - - rhs(t,y) - Normal ODE - rhs(t,y,sw) - An ODE with different modes, sw is a list of - switches (Boolean list) which should be held - constant during the integration and only be - changed when an event have occured. Used together - with event functions. - rhs(t,y,p) - An ODE with parameters for which sensitivities - should be calculated. - rhs(t,y,sw,p) - An ODE with both parameters and switches. - - Returns: - A numpy array of size len(y). - - y0 - Defines the starting values - t0 - Defines the starting time - p0 (Depending on if the solver supports sensitivity calculations) - Parameters for which sensitivites are to be calculated - sw0 (Depending on if the solver supports state events) - Defines the starting values of the switches. - Should be a list of Booleans. - - Signature of default or user provided methods. Their use is solver dependent. :: - - def state_events(self ,t ,y, sw) - Defines the event (root) functions. - - Returns: - A numpy array. - - def time_events(self, t, y, sw) - Defines the time events. This function should return - the next time-point for a time event. At a time-event - the usual method handle_event is called for the specific - handling. If there are no more time events. This function - should return None. - - Returns: - Float - The time-point for the next time-event. - None - No time-event. - - def jac(self, t, y, sw=None) - Defines the jacobian. J=df/dx. - - Returns: - A numpy matrix of size len(y)*len(y). - - def jacv(self, t, y, fy, v) - Defines a Jacobian Vector product. df/dx*v. - - Returns: - A numpy vector of size len(y). - - def handle_result(self, solver, t, y) - Method for specifying how the result is handled. - By default the data is stored in two vectors, solver.(t_sol/y_sol). If - the problem to be solved also involve sensitivities these results are - stored in p_sol - - def handle_event(self, object solver, event_info): - Defines how to handle a discontinuity. This functions is called when - a discontinuity has been found in the supplied event functions. The solver - is the solver attribute. The event_info is a list of length 2 where - the first element is a list containing information about state events and - the second element is a Boolean indicating if there has been a time - event. If there has not been a state event the first element is an empty - list. The state event list contains a set of integers of values (-1,0,1), - the values indicate which state event has triggered (determined from - state_event(...) ) and the value indicates to where the state event is 'headed'. - """ - pass -class SingPerturbed_Problem(cSingPerturbed_Problem): - """ - Problem for singularly perturbed problems of the form - The equation is in the form - .. math:: - :nowrap: - - \begin{eqnarray} - \dot{y} & = & \mathrm{rhs}_1(t,y,z) \\ - \vareps \dot{z} & = & \mathrm{rhs}_(t,y,z) - \end{eqnarray} - - Parameters:: - - rhs1 - Function that calculates the 'slow' right-hand-side: f (in formula above) - - rhs1(t,y,z) - 'slow' ODE - Returns: - A numpy array of size len(y). - rhs2 - Function that calculates the 'fast' right-hand-side: g (in formula above) - - rhs2(t,y,z) - 'fast' ODE - Returns: - A numpy array of size len(z). - eps diagonal of a len(z) x len(z) matrix with small numbers - A numpy array of size len(z) - - yy0 - Defines the starting values of y0 - zz0 - Defines the starting values of z0 - t0 - Defines the starting time - """ - pass - - -cdef class cAlgebraic_Problem: - - name = "---" - - def __init__(self, object res, y0, y0_min = None, y0_max=None, - y0_nominal=None, object jac=None, object jacv=None, - object prec_solve=None, object prec_setup=None, name = None): - - if res != None: - self.res = res - if jac != None: - self.jac = jac - if jacv != None: - self.jacv = jacv - if prec_solve != None: - self.prec_solve = prec_solve - if prec_setup != None: - self.prec_setup = prec_setup - - if not y0 is None: - self.y0 = set_type_shape_array(y0) - - if not y0_min is None: - self.y0_min = set_type_shape_array(y0_min) - else: - self.y0_min = set_type_shape_array([MIN_VALUE]*len(self.y0)) - - if not y0_max is None: - self.y0_max = set_type_shape_array(y0_max) - else: - self.y0_max = set_type_shape_array([MAX_VALUE]*len(self.y0)) - - if not y0_nominal is None: - self.y0_nominal = set_type_shape_array(y0_nominal) - else: - self.y0_nominal = set_type_shape_array([1.0]*len(self.y0)) - - if name: - self.name = name - - cpdef initialize(self, solver): - """ - Method for specializing initiation. - """ - solver.log_message("No initialization defined for the problem.", LOUD) - - cpdef finalize(self,object solver): - """ - Method for specifying the finalization options when the simulation have - finished. - """ - solver.log_message("No finalization defined for the problem.", LOUD) - -class Algebraic_Problem(cAlgebraic_Problem): - """ - Problem class for solving systems of algebraic equations. - - .. math:: - :nowrap: - - 0 = F(y) - - """ - pass +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +cimport numpy as N + +from assimulo.support import set_type_shape_array + +include "constants.pxi" #Includes the constants (textual include) + + +cdef class cProblem: + + name = "---" + + def __init__(self, y0 = None, double t0 = 0.0, p0 = None, sw0 = None, name = None): + + if not y0 is None: + self.y0 = set_type_shape_array(y0) + if not p0 is None: + self.p0 = set_type_shape_array(p0) + if not sw0 is None: + self.sw0 = set_type_shape_array(sw0, bool) + if name: + self.name = name + self.t0 = t0 + + cpdef initialize(self, solver): + """ + Method for specializing initiation. + """ + solver.log_message("No initialization defined for the problem.", LOUD) + + cpdef reset(self): + """ + Resets a problem to its default values. + """ + pass + + cpdef handle_event(self, object solver, event_info): + """ + Defines how to handle a discontinuity. This functions gets called when + a discontinuity has been found in the supplied event functions. The solver + is the solver attribute while the event_info is a list of length 2 where + the first element is a list containing information about state events and + the second element is a Boolean for indicating if there has been a time + event. If there has not been a state event the first element is an empty + list. The state event list contains a set of integers of values (-1,0,1), + the values indicates which state event has triggered (determined from + state_event(...) ) and the value indicates to where the state event is 'headed'. + """ + solver.log_message("No event handling defined.", NORMAL) + + + cpdef finalize(self,object solver): + """ + Method for specifying the finalization options when the simulation have + finished. + """ + solver.log_message("No finalization defined for the problem.", LOUD) + +cdef class cImplicit_Problem(cProblem): + + def __init__(self, object res=None, y0=None, yd0=None,double t0=0.0, + p0=None, sw0=None, name = None): + cProblem.__init__(self, y0, t0, p0, sw0, name) + if res is not None: + self.res = res + if yd0 is not None: + self.yd0 = set_type_shape_array(yd0) + + + def handle_result(self, solver, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd): + """ + Method for specifying how the result is handled. By default the + data is stored in three vectors: solver.(t/y/yd). + """ + cdef int i = 0 + + solver.t_sol.extend([t]) + solver.y_sol.extend([y]) + solver.yd_sol.extend([yd]) + + #Store sensitivity result (variable _sensitivity_result are set from the solver by the solver) + if self._sensitivity_result == 1: + for i in range(solver.problem_info["dimSens"]): + solver.p_sol[i] += [solver.interpolate_sensitivity(t, i=i)] + + cpdef res_internal(self, N.ndarray[double, ndim=1] res, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd): + try: + res[:] = self.res(t,y,yd) + except: + return ID_FAIL + return ID_OK + +cdef class cOverdetermined_Problem(cProblem): + + def __init__(self, object res=None, y0=None, yd0=None,double t0=0.0, + p0=None, sw0=None, name=None): + cProblem.__init__(self, y0, t0, p0, sw0, name) + if res is not None: + self.res = res + if yd0 is not None: + self.yd0 = set_type_shape_array(yd0) + + + def handle_result(self, solver, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd): + """ + Method for specifying how the result is to be handled. As default the + data is stored in three vectors: solver.(t/y/yd). + """ + cdef int i = 0 + + solver.t_sol.extend([t]) + solver.y_sol.extend([y]) + solver.yd_sol.extend([yd]) + + cpdef res_internal(self, N.ndarray[double, ndim=1] res, double t, N.ndarray[double, ndim=1] y, N.ndarray[double, ndim=1] yd): + try: + res[:] = self.res(t,y,yd) + except: + return ID_FAIL + return ID_OK + +cdef class cExplicit_Problem(cProblem): + + def __init__(self, object rhs=None, y0=None,double t0=0.0, p0=None, sw0=None, name = None): + + cProblem.__init__(self, y0, t0, p0, sw0, name) + if rhs is not None: + self.rhs = rhs + def handle_result(self, solver, double t, N.ndarray[double, ndim=1] y): + """ + Method for specifying how the result is to be handled. As default the + data is stored in two vectors: solver.(t/y). + """ + cdef int i = 0 + + solver.t_sol.extend([t]) + solver.y_sol.extend([y]) + + #Store sensitivity result (variable _sensitivity_result are set from the solver by the solver) + if self._sensitivity_result == 1: + for i in range(solver.problem_info["dimSens"]): + solver.p_sol[i] += [solver.interpolate_sensitivity(t, i=i)] + + cpdef int rhs_internal(self, N.ndarray[double, ndim=1] yd, double t, N.ndarray[double, ndim=1] y): + try: + yd[:] = self.rhs(t,y) + except: + return ID_FAIL + return ID_OK + + cpdef N.ndarray res(self, t, y, yd, sw=None): + if sw == None: + return yd-self.rhs(t,y) + else: + return yd-self.rhs(t, y, sw) + +cdef class cDelay_Explicit_Problem(cExplicit_Problem): + def __init__(self, object rhs=None, y0=None, phi = None, arglag = None, lagcompmap = None, jaclag = None, nlags = None, njacl = None, double t0=0.0, p0=None, sw0=None): + cExplicit_Problem.__init__(self, rhs, y0, t0, p0, sw0) + + if phi is not None: + self.phi = phi + if arglag is not None: + self.arglag = arglag + if jaclag is not None: + self.jaclag = jaclag + + self.lagcompmap = lagcompmap + + self.nlags = nlags # Number of delay arguments to differentiate for + self.njacl = njacl # Number of possible delay arguments that fcn can be differentiated with respect to + +cdef class cSingPerturbed_Problem(cExplicit_Problem): + + def __init__(self, rhs1=None, rhs2=None, yy0=None, zz0=None, double t0=0.0, eps=None, name = None): + if rhs1 is not None: + self.rhs1 = rhs1 + if rhs2 is not None: + self.rhs2 = rhs2 + if eps is not None: + self.eps = eps + if yy0 is not None: + self.yy0 = set_type_shape_array(yy0) + if zz0 is not None: + self.zz0 = set_type_shape_array(zz0) + # preparing things so that the problem also describes a + # classical explicit problem without exposing the structure + # of a singularly perturbed problem + if yy0 is not None and zz0 is not None: + y0 = N.hstack((self.yy0,self.zz0)) + elif yy0 is not None: + y0 = self.yy0 + elif zz0 is not None: + y0 = self.zz0 + self.n = len(self.yy0) if yy0 is not None else 0 + self.m = len(self.zz0) if zz0 is not None else 0 + cExplicit_Problem.__init__(self, y0=y0, t0=t0, name=name) + + def rhs(self,t,y): + yy=y[:self.n] + zz=y[self.n:] + yydot=self.rhs1(t,yy,zz) + zzdot=self.rhs2(t,yy,zz) + # componentwise division by eps as it is the diagonal of + # a diagonal matrix + if self.eps != None: + zzdot /= self.eps + return N.hstack((yydot,zzdot)) + +class Delay_Explicit_Problem(cDelay_Explicit_Problem): + pass + +class Implicit_Problem(cImplicit_Problem): + """ + Problem for our implicit integrators (DAEs). A problem + consists of the residual function and some initial conditions. + + Parameters :: + + res + Function that calculates the residual. Depending on + the problem and the support of the solver, this function can + have the following input parameters. + + res(t,y,yd) - Normal DAE + res(t,y,yd,sw) - An DAE with different modes, sw is a list of + switches (boolean list) which should be held + constant during the integration and only be + changed when an event have occured. Used together + with event functions. + res(t,y,yd,p) - An DAE with parameters for which sensitivities + should be calculated. + res(t,y,yd,sw,p) - An DAE with both parameters and switches. + + Returns: + A numpy array of size len(y). + y0 + Defines the starting values of y0. + yd0 + Defines the starting values of yd0. + t0 + Defines the starting time. + p0 (Depending on if the solver supports sensitivity calculations) + Parameters for which sensitivites are to be calculated + sw0 (Depending on if the solver supports state events) + Defines the starting values of the switches. + Should be a list of Booleans. + + Parameters (optionally contained in class) :: + + algvar + Defines the differential and algebraic components of the problem. + Should be a list of integers. For more information, see the + property algvar in IDA. + + Signature of default or user provided methods. Their use is solver dependent. :: + + def state_events(self ,t ,y ,yd, sw) + Defines the event (root) functions. + + Returns: + A numpy array. + + def time_events(self, t, y, yd, sw) + Defines the time events. This function should return + the next time-point for a time event. At a time-event + the usual method handle_event is called for the specific + handling. If there are no more time events. This function + should return None. + + Returns: + Float + The time-point for the next time-event. + None + No time-event. + + def jac(self, c, t, y, yd, sw) + Defines the Jacobian, which should be of the form + J = dF/dx + c*dF/dx'. + + Returns: + A numpy array of size len(y)*len(y). + + def handle_result(self, solver, t, y, yd) + Method for specifying how the result is handled. + By default the data is stored in three vectors, solver.(t_sol/y_sol/yd_sol). + If the problem to be solved also involve sensitivities these results are + stored in p_sol + + def handle_event(self, object solver, event_info): + Defines how to handle a discontinuity. This functions gets called when + a discontinuity has been found in the supplied event functions. The solver + is the solver attribute. The event_info is a list of length 2 where + the first element is a list containing information about state events and + the second element is a Boolean indicating if there has been a time + event. If there has not been a state event the first element is an empty + list. The state event list contains a set of integers of values (-1,0,1), + the values indicate which state event has triggered (determined from + state_event(...) ) and the value indicates to where the state event is 'headed'. + """ + pass +class Overdetermined_Problem(cOverdetermined_Problem): + """ + Problem for integrators for overdetermined DAES (ODAEs). A problem + consists of the residual function with more components than state variables + and some initial conditions. + + Parameters :: + + res + Function that calculates the residual. Depending on + the problem and the support of the solver, this function can + have the following input parameters. + + res(t,y,yd) - Normal ODAE + res(t,y,yd,sw) - An ODAE with different modes, sw is a list of + switches (boolean list) which should be held + constant during the integration and only be + changed when an event have occured. Used together + with event functions. + + Returns: + A numpy array of size neq > len(y). + y0 + Defines the starting values of y0. + yd0 + Defines the starting values of yd0. + t0 + Defines the starting time. + sw0 (Depending on if the solver supports state events) + Defines the starting values of the switches. + Should be a list of Booleans. + + Parameters (optionally contained in class) :: + + algvar + Defines the differential and algebraic components of the problem. + Should be a list of integers. For more information, see the + property algvar in IDA. + + Available (optional) options (depending on the solver support):: + + def state_events(self ,t ,y ,yd, sw) + Defines the event (root) functions. + + Returns: + A numpy array. + + def time_events(self, t, y, yd, sw) + Defines the time events. This function should return + the next time-point for a time event. At a time-event + the usual method handle_event is called for the specific + handling. If there are no more time events. This function + should return None. + + Returns: + Float + The time-point for the next time-event. + None + No time-event. + + def jac(self, c, t, y, yd, sw) + Defines the Jacobian, which should be of the form + J = dF/dx + c*dF/dx'. + + Returns: + A numpy array of size neq*len(y). + + def handle_result(self, solver, t, y, yd) + Method for specifying how the result is handled. + By default the data is stored in three vectors, solver.(t_sol/y_sol/yd_sol). + If the problem to be solved also involve sensitivities these results are + stored in p_sol + + def handle_event(self, object solver, event_info): + Defines how to handle a discontinuity. This functions gets called when + a discontinuity has been found in the supplied event functions. The solver + is the solver attribute. The event_info is a list of length 2 where + the first element is a list containing information about state events and + the second element is a Boolean indicating if there has been a time + event. If there have not been a state event the first element is an empty + list. The state event list contains a set of integers of values (-1,0,1), + the values indicate which state event has triggered (determined from + state_event(...) ) and the value indicates to where the state event is 'headed'. + """ + pass +class Explicit_Problem(cExplicit_Problem): + """ + Problem for our explicit integrators (ODEs). A problem + consists of the right-hand-side and some initial conditions. + + Parameters:: + + rhs + Function that calculates the right-hand-side. Depending on + the problem and the support of the solver, this function has + the following input parameters: + + rhs(t,y) - Normal ODE + rhs(t,y,sw) - An ODE with different modes, sw is a list of + switches (Boolean list) which should be held + constant during the integration and only be + changed when an event have occured. Used together + with event functions. + rhs(t,y,p) - An ODE with parameters for which sensitivities + should be calculated. + rhs(t,y,sw,p) - An ODE with both parameters and switches. + + Returns: + A numpy array of size len(y). + + y0 + Defines the starting values + t0 + Defines the starting time + p0 (Depending on if the solver supports sensitivity calculations) + Parameters for which sensitivites are to be calculated + sw0 (Depending on if the solver supports state events) + Defines the starting values of the switches. + Should be a list of Booleans. + + Signature of default or user provided methods. Their use is solver dependent. :: + + def state_events(self ,t ,y, sw) + Defines the event (root) functions. + + Returns: + A numpy array. + + def time_events(self, t, y, sw) + Defines the time events. This function should return + the next time-point for a time event. At a time-event + the usual method handle_event is called for the specific + handling. If there are no more time events. This function + should return None. + + Returns: + Float + The time-point for the next time-event. + None + No time-event. + + def jac(self, t, y, sw=None) + Defines the jacobian. J=df/dx. + + Returns: + A numpy matrix of size len(y)*len(y). + + def jacv(self, t, y, fy, v) + Defines a Jacobian Vector product. df/dx*v. + + Returns: + A numpy vector of size len(y). + + def handle_result(self, solver, t, y) + Method for specifying how the result is handled. + By default the data is stored in two vectors, solver.(t_sol/y_sol). If + the problem to be solved also involve sensitivities these results are + stored in p_sol + + def handle_event(self, object solver, event_info): + Defines how to handle a discontinuity. This functions is called when + a discontinuity has been found in the supplied event functions. The solver + is the solver attribute. The event_info is a list of length 2 where + the first element is a list containing information about state events and + the second element is a Boolean indicating if there has been a time + event. If there has not been a state event the first element is an empty + list. The state event list contains a set of integers of values (-1,0,1), + the values indicate which state event has triggered (determined from + state_event(...) ) and the value indicates to where the state event is 'headed'. + """ + pass +class SingPerturbed_Problem(cSingPerturbed_Problem): + """ + Problem for singularly perturbed problems of the form + The equation is in the form + .. math:: + :nowrap: + + \begin{eqnarray} + \dot{y} & = & \mathrm{rhs}_1(t,y,z) \\ + \vareps \dot{z} & = & \mathrm{rhs}_(t,y,z) + \end{eqnarray} + + Parameters:: + + rhs1 + Function that calculates the 'slow' right-hand-side: f (in formula above) + + rhs1(t,y,z) - 'slow' ODE + Returns: + A numpy array of size len(y). + rhs2 + Function that calculates the 'fast' right-hand-side: g (in formula above) + + rhs2(t,y,z) - 'fast' ODE + Returns: + A numpy array of size len(z). + eps diagonal of a len(z) x len(z) matrix with small numbers + A numpy array of size len(z) + + yy0 + Defines the starting values of y0 + zz0 + Defines the starting values of z0 + t0 + Defines the starting time + """ + pass + + +cdef class cAlgebraic_Problem: + + name = "---" + + def __init__(self, object res, y0, y0_min = None, y0_max=None, + y0_nominal=None, object jac=None, object jacv=None, + object prec_solve=None, object prec_setup=None, name = None): + + if res != None: + self.res = res + if jac != None: + self.jac = jac + if jacv != None: + self.jacv = jacv + if prec_solve != None: + self.prec_solve = prec_solve + if prec_setup != None: + self.prec_setup = prec_setup + + if not y0 is None: + self.y0 = set_type_shape_array(y0) + + if not y0_min is None: + self.y0_min = set_type_shape_array(y0_min) + else: + self.y0_min = set_type_shape_array([MIN_VALUE]*len(self.y0)) + + if not y0_max is None: + self.y0_max = set_type_shape_array(y0_max) + else: + self.y0_max = set_type_shape_array([MAX_VALUE]*len(self.y0)) + + if not y0_nominal is None: + self.y0_nominal = set_type_shape_array(y0_nominal) + else: + self.y0_nominal = set_type_shape_array([1.0]*len(self.y0)) + + if name: + self.name = name + + cpdef initialize(self, solver): + """ + Method for specializing initiation. + """ + solver.log_message("No initialization defined for the problem.", LOUD) + + cpdef finalize(self,object solver): + """ + Method for specifying the finalization options when the simulation have + finished. + """ + solver.log_message("No finalization defined for the problem.", LOUD) + +class Algebraic_Problem(cAlgebraic_Problem): + """ + Problem class for solving systems of algebraic equations. + + .. math:: + :nowrap: + + 0 = F(y) + + """ + pass diff --git a/src/solvers/__init__.py b/src/solvers/__init__.py index 0d40eaf2..38eb504e 100644 --- a/src/solvers/__init__.py +++ b/src/solvers/__init__.py @@ -1,74 +1,74 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -__all__ = ["euler","radau5","sundials","runge_kutta","rosenbrock", - "glimda","odepack","radar5","dasp3","odassl"] - -import sys - -#Import all the solvers from the different modules -try: - from .euler import ExplicitEuler - from .euler import ImplicitEuler -except ImportError as ie: - sys.stderr.write("Could not find " + str(ie) + "\n") -try: - from .radau5 import Radau5ODE - from .radau5 import Radau5DAE - from .radau5 import _Radau5ODE - from .radau5 import _Radau5DAE -except ImportError as ie: - sys.stderr.write("Could not find " + str(ie) + "\n") -try: - from .sundials import IDA - from .sundials import CVode -except ImportError as ie: - sys.stderr.write("Could not find " + str(ie) + "\n") -try: - from .kinsol import KINSOL -except ImportError as ie: - sys.stderr.write("Could not find " + str(ie) + "\n") -try: - from .runge_kutta import RungeKutta34 - from .runge_kutta import RungeKutta4 - from .runge_kutta import Dopri5 -except ImportError as ie: - sys.stderr.write("Could not find " + str(ie) + "\n") -try: - from .rosenbrock import RodasODE -except ImportError as ie: - sys.stderr.write("Could not find " + str(ie) + "\n") -try: - from .odassl import ODASSL -except ImportError as ie: - sys.stderr.write("Could not find " + str(ie) + "\n") -try: - from .odepack import LSODAR -except ImportError as ie: - sys.stderr.write("Could not find " + str(ie) + "\n") -try: - from .radar5 import Radar5ODE -except ImportError as ie: - sys.stderr.write("Could not find " + str(ie) + "\n") -try: - from .dasp3 import DASP3ODE -except ImportError as ie: - sys.stderr.write("Could not find " + str(ie) + "\n") -try: - from .glimda import GLIMDA -except ImportError as ie: - sys.stderr.write("Could not find " + str(ie) + "\n") +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +__all__ = ["euler","radau5","sundials","runge_kutta","rosenbrock", + "glimda","odepack","radar5","dasp3","odassl"] + +import sys + +#Import all the solvers from the different modules +try: + from .euler import ExplicitEuler + from .euler import ImplicitEuler +except ImportError as ie: + sys.stderr.write("Could not find " + str(ie) + "\n") +try: + from .radau5 import Radau5ODE + from .radau5 import Radau5DAE + from .radau5 import _Radau5ODE + from .radau5 import _Radau5DAE +except ImportError as ie: + sys.stderr.write("Could not find " + str(ie) + "\n") +try: + from .sundials import IDA + from .sundials import CVode +except ImportError as ie: + sys.stderr.write("Could not find " + str(ie) + "\n") +try: + from .kinsol import KINSOL +except ImportError as ie: + sys.stderr.write("Could not find " + str(ie) + "\n") +try: + from .runge_kutta import RungeKutta34 + from .runge_kutta import RungeKutta4 + from .runge_kutta import Dopri5 +except ImportError as ie: + sys.stderr.write("Could not find " + str(ie) + "\n") +try: + from .rosenbrock import RodasODE +except ImportError as ie: + sys.stderr.write("Could not find " + str(ie) + "\n") +try: + from .odassl import ODASSL +except ImportError as ie: + sys.stderr.write("Could not find " + str(ie) + "\n") +try: + from .odepack import LSODAR +except ImportError as ie: + sys.stderr.write("Could not find " + str(ie) + "\n") +try: + from .radar5 import Radar5ODE +except ImportError as ie: + sys.stderr.write("Could not find " + str(ie) + "\n") +try: + from .dasp3 import DASP3ODE +except ImportError as ie: + sys.stderr.write("Could not find " + str(ie) + "\n") +try: + from .glimda import GLIMDA +except ImportError as ie: + sys.stderr.write("Could not find " + str(ie) + "\n") diff --git a/src/solvers/dasp3.py b/src/solvers/dasp3.py index 8f40e053..51cbf1d9 100644 --- a/src/solvers/dasp3.py +++ b/src/solvers/dasp3.py @@ -1,212 +1,212 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2011 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import numpy as N -import scipy as S -import scipy.linalg as LIN - -from assimulo.problem import SingPerturbed_Problem -from assimulo.exception import * -from assimulo.ode import * - -from assimulo.explicit_ode import Explicit_ODE - -try: - from assimulo.lib import dasp3dp -except ImportError: - pass - -class DASP3ODE(Explicit_ODE): - """ - DASP3 Solver by Gustaf Söderlind (1980-10-22). Originally published - in,:: - - DASP3 - A Program for the Numerical Integration of Partitioned: - Stiff Ode:s and Differential-Algebraic Systems. - - By, Gustaf Söderlind, Department of Numerical Analysis, and - Computing Science, The Royal Institute of Technology, 1980. - Stockholm, Sweden. - - DASP3 solves system on the form, - - .. math:: - - \\frac{\mathrm{d}y}{\mathrm{d}t} &= f(t,y,z) \;\;\; \\text{(N equations)} \\\\ - \\varepsilon\\frac{\mathrm{d}z}{\mathrm{d}t} &= G(t,y,z)\;\;\; \\text{(M equations)} - - If is assumed that the first system is non-stiff and that - the stiffness of the second system is due to the parameter - epsilon, possibly a diagonal matrix. - """ - - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Explicit_Problem' class. - """ - Explicit_ODE.__init__(self, problem) #Calls the base class - if not isinstance(problem, SingPerturbed_Problem): - raise Explicit_ODE_Exception('The problem needs to be a subclass of a SingPerturbed_Problem.') - self.n=self.problem.n - self.m=self.problem.m - - # Set initial values - self.wsy=N.empty((10*self.n,)) - self.wsy[:self.n]=self.problem.yy0 - self.wsz=N.empty((max(9*self.m,1),)) # array must be at least 1 element long - self.wsz[:self.m]=self.problem.zz0 - - # - Default values - self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - - self.statistics.add_key("nyder", "Number of slow function evaluations (Y)") - self.statistics.add_key("nzder", "Number of fast function evaluations (Z)") - - def initialize(self): - #Reset statistics - self.statistics.reset() - - self._tlist = [] - self._ylist = [] - - def _solout(self, t, wsy, wsz, n, m, jstop): - """ - This method is called after every successful step taken by DASP3 - """ - self._tlist.append(t) - self._ylist.append(N.hstack((wsy[:n],wsz[:m]))) - - if self._opts["report_continuously"]: - initialize_flag = self.report_solution(t, N.hstack((wsy[:n],wsz[:m])), self._opts) - if initialize_flag: - jstop = -1 - else: - self._tlist.append(t) - self._ylist.append(N.hstack((wsy[:n],wsz[:m]))) - - return jstop - - def integrate(self, t, y, tf, opts): - atol=self.options["atol"] - tol=self.options["rtol"] - absrel=atol/tol - - m = self.problem.m - n = self.problem.n - - a = N.empty((m,m)) - w = N.empty((m,m)) - slu= N.empty((2*m,)) - ips= N.empty((m,),'int32') - ind = N.empty((2*m,),'int32') - eq= N.empty((m,),'bool') - wght=N.ones((m+n,)) - - #Store the opts - self._opts = opts - - t,lflag=dasp3dp.dasp3(self.problem.rhs1,self.problem.rhs2,self._solout,t,tf,self.wsy,self.wsz,n,m,tol, - absrel,wght,self.problem.eps,a,w,slu,ips,eq,ind) - - #Checking return - if lflag == 0: - flag = ID_PY_COMPLETE - else: - raise Exception("DASP3 failed with flag %d"%lflag) - - #Retrieving statistics - self.statistics["nsteps"] += dasp3dp.COUNTS.NSTEP - self.statistics["nyder"] += dasp3dp.COUNTS.NYDER - self.statistics["nzder"] += dasp3dp.COUNTS.NZDER - self.statistics["nerrfails"] += dasp3dp.COUNTS.NREJ - self.statistics["nlus"] += 0 - - return flag, self._tlist, self._ylist - - def print_statistics(self, verbose=NORMAL): - """ - Prints the run-time statistics for the problem. - """ - self.log_message('Final Run Statistics: %s \n' % self.problem.name, verbose) - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : DASP3 ', verbose) - self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) - self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) - self.log_message('', verbose) - - def _set_atol(self,atol): - - self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) - - if len(self.options["atol"]) == 1: - self.options["atol"] = self.options["atol"]*N.ones(self.problem_info["dim"]) - elif len(self.options["atol"]) != self.problem_info["dim"]: - raise DASP3_Exception("atol must be of length one or same as the dimension of the problem.") - - def _get_atol(self): - """ - Defines the absolute tolerance(s) that is to be used by the solver. - Can be set differently for each variable. - - Parameters:: - - atol - - Default '1.0e-6'. - - - Should be a positive float or a numpy vector - of floats. - - Example: - atol = [1.0e-4, 1.0e-6] - """ - return self.options["atol"] - - atol=property(_get_atol,_set_atol) - - def _set_rtol(self,rtol): - try: - self.options["rtol"] = float(rtol) - except (ValueError, TypeError): - raise DASP3_Exception('Relative tolerance must be a (scalar) float.') - if self.options["rtol"] <= 0.0: - raise DASP3_Exception('Relative tolerance must be a positive (scalar) float.') - - def _get_rtol(self): - """ - Defines the relative tolerance that is to be used by the solver. - - Parameters:: - - rtol - - Default '1.0e-6'. - - - Should be a positive float. - - Example: - rtol = 1.0e-4 - """ - return self.options["rtol"] - - rtol=property(_get_rtol,_set_rtol) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2011 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import numpy as N +import scipy as S +import scipy.linalg as LIN + +from assimulo.problem import SingPerturbed_Problem +from assimulo.exception import * +from assimulo.ode import * + +from assimulo.explicit_ode import Explicit_ODE + +try: + from assimulo.lib import dasp3dp +except ImportError: + pass + +class DASP3ODE(Explicit_ODE): + """ + DASP3 Solver by Gustaf Söderlind (1980-10-22). Originally published + in,:: + + DASP3 - A Program for the Numerical Integration of Partitioned: + Stiff Ode:s and Differential-Algebraic Systems. + + By, Gustaf Söderlind, Department of Numerical Analysis, and + Computing Science, The Royal Institute of Technology, 1980. + Stockholm, Sweden. + + DASP3 solves system on the form, + + .. math:: + + \\frac{\mathrm{d}y}{\mathrm{d}t} &= f(t,y,z) \;\;\; \\text{(N equations)} \\\\ + \\varepsilon\\frac{\mathrm{d}z}{\mathrm{d}t} &= G(t,y,z)\;\;\; \\text{(M equations)} + + If is assumed that the first system is non-stiff and that + the stiffness of the second system is due to the parameter + epsilon, possibly a diagonal matrix. + """ + + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Explicit_Problem' class. + """ + Explicit_ODE.__init__(self, problem) #Calls the base class + if not isinstance(problem, SingPerturbed_Problem): + raise Explicit_ODE_Exception('The problem needs to be a subclass of a SingPerturbed_Problem.') + self.n=self.problem.n + self.m=self.problem.m + + # Set initial values + self.wsy=N.empty((10*self.n,)) + self.wsy[:self.n]=self.problem.yy0 + self.wsz=N.empty((max(9*self.m,1),)) # array must be at least 1 element long + self.wsz[:self.m]=self.problem.zz0 + + # - Default values + self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + + self.statistics.add_key("nyder", "Number of slow function evaluations (Y)") + self.statistics.add_key("nzder", "Number of fast function evaluations (Z)") + + def initialize(self): + #Reset statistics + self.statistics.reset() + + self._tlist = [] + self._ylist = [] + + def _solout(self, t, wsy, wsz, n, m, jstop): + """ + This method is called after every successful step taken by DASP3 + """ + self._tlist.append(t) + self._ylist.append(N.hstack((wsy[:n],wsz[:m]))) + + if self._opts["report_continuously"]: + initialize_flag = self.report_solution(t, N.hstack((wsy[:n],wsz[:m])), self._opts) + if initialize_flag: + jstop = -1 + else: + self._tlist.append(t) + self._ylist.append(N.hstack((wsy[:n],wsz[:m]))) + + return jstop + + def integrate(self, t, y, tf, opts): + atol=self.options["atol"] + tol=self.options["rtol"] + absrel=atol/tol + + m = self.problem.m + n = self.problem.n + + a = N.empty((m,m)) + w = N.empty((m,m)) + slu= N.empty((2*m,)) + ips= N.empty((m,),'int32') + ind = N.empty((2*m,),'int32') + eq= N.empty((m,),'bool') + wght=N.ones((m+n,)) + + #Store the opts + self._opts = opts + + t,lflag=dasp3dp.dasp3(self.problem.rhs1,self.problem.rhs2,self._solout,t,tf,self.wsy,self.wsz,n,m,tol, + absrel,wght,self.problem.eps,a,w,slu,ips,eq,ind) + + #Checking return + if lflag == 0: + flag = ID_PY_COMPLETE + else: + raise Exception("DASP3 failed with flag %d"%lflag) + + #Retrieving statistics + self.statistics["nsteps"] += dasp3dp.COUNTS.NSTEP + self.statistics["nyder"] += dasp3dp.COUNTS.NYDER + self.statistics["nzder"] += dasp3dp.COUNTS.NZDER + self.statistics["nerrfails"] += dasp3dp.COUNTS.NREJ + self.statistics["nlus"] += 0 + + return flag, self._tlist, self._ylist + + def print_statistics(self, verbose=NORMAL): + """ + Prints the run-time statistics for the problem. + """ + self.log_message('Final Run Statistics: %s \n' % self.problem.name, verbose) + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : DASP3 ', verbose) + self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) + self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) + self.log_message('', verbose) + + def _set_atol(self,atol): + + self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) + + if len(self.options["atol"]) == 1: + self.options["atol"] = self.options["atol"]*N.ones(self.problem_info["dim"]) + elif len(self.options["atol"]) != self.problem_info["dim"]: + raise DASP3_Exception("atol must be of length one or same as the dimension of the problem.") + + def _get_atol(self): + """ + Defines the absolute tolerance(s) that is to be used by the solver. + Can be set differently for each variable. + + Parameters:: + + atol + - Default '1.0e-6'. + + - Should be a positive float or a numpy vector + of floats. + + Example: + atol = [1.0e-4, 1.0e-6] + """ + return self.options["atol"] + + atol=property(_get_atol,_set_atol) + + def _set_rtol(self,rtol): + try: + self.options["rtol"] = float(rtol) + except (ValueError, TypeError): + raise DASP3_Exception('Relative tolerance must be a (scalar) float.') + if self.options["rtol"] <= 0.0: + raise DASP3_Exception('Relative tolerance must be a positive (scalar) float.') + + def _get_rtol(self): + """ + Defines the relative tolerance that is to be used by the solver. + + Parameters:: + + rtol + - Default '1.0e-6'. + + - Should be a positive float. + + Example: + rtol = 1.0e-4 + """ + return self.options["rtol"] + + rtol=property(_get_rtol,_set_rtol) diff --git a/src/solvers/euler.pyx b/src/solvers/euler.pyx index b68eaf2f..e78b8d0e 100644 --- a/src/solvers/euler.pyx +++ b/src/solvers/euler.pyx @@ -1,677 +1,677 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -cimport numpy as N -import numpy as N -import numpy.linalg as LIN -import scipy.sparse as sp - -#from assimulo.ode import * -from assimulo.explicit_ode cimport Explicit_ODE -from assimulo.exception import * - -include "constants.pxi" #Includes the constants (textual include) - -cdef class ImplicitEuler(Explicit_ODE): - """ - This solver solves an explicit ordinary differential equation using - the implicit Euler method. - - We want to approximate the solution to the ordinary differential - equation of the form, - - .. math:: - - \dot{y} = f(t,y), \quad y(t_0) = y_0 . - - Using the implicit Euler method, the approximation is defined as - follow, - - .. math:: - - y_{n+1} = y_n + hf(t_{n+1},y_{n+1}) - - with :math:`h` being the step-size and :math:`y_n` the previous - solution to the equation. - """ - cdef N.ndarray yd1 - cdef N.ndarray _old_jac - cdef object f - cdef public object event_func - cdef int _leny - cdef double _eps - cdef int _needjac - cdef int _curjac - cdef int _steps_since_last_jac - cdef N.ndarray _yold - cdef N.ndarray _ynew - #cdef N.ndarray _event_info - cdef public N.ndarray g_old - cdef double _told - cdef double _h - cdef double _inith - - def __init__(self, problem): - Explicit_ODE.__init__(self, problem) #Calls the base class - - #Solver options - self.options["h"] = 0.01 - self.options["usejac"] = True if (self.problem_info["jac_fcn"]) else False - self.options["newt"] = 7 #Maximum number of newton iterations - self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - - #Internal temporary result vector - self.yd1 = N.array([0.0]*len(self.y0)) - self._yold = N.array([0.0]*len(self.y0)) - self._ynew = N.array([0.0]*len(self.y0)) - - #Solver support - self.supports["report_continuously"] = True - self.supports["interpolated_output"] = False - self.supports["state_events"] = True - - - self._leny = len(self.y) #Dimension of the problem - self._eps = N.finfo('double').eps - self._needjac = True #Do we need a new jacobian? - self._curjac = False #Is the current jacobian up to date? - self._steps_since_last_jac = 0 #Keep track on how long ago we updated the jacobian - self._inith = 0 #Used for taking an initial step of correct length after an event. - - def set_problem_data(self): - if self.problem_info["state_events"]: - def event_func(t, y): - return self.problem.state_events(t, y, self.sw) - def f(t, y): - return self.problem.rhs(t, y, self.sw) - self.f = f - self.event_func = event_func - self._event_info = N.array([0] * self.problem_info["dimRoot"]) - self.g_old = self.event_func(self.t, self.y) - else: - self.f = self.problem.rhs - - - def _set_usejac(self, jac): - self.options["usejac"] = bool(jac) - - def _get_usejac(self): - """ - This sets the option to use the user defined jacobian. If a - user provided jacobian is implemented into the problem the - default setting is to use that jacobian. If not, an - approximation is used. - - Parameters:: - - usejac - - True - use user defined jacobian - False - use an approximation - - - Should be a boolean. - - Example: - usejac = False - """ - return self.options["usejac"] - - usejac = property(_get_usejac,_set_usejac) - - cpdef step(self,double t,N.ndarray y,double tf,dict opts): - cdef double h - h = self.options["h"] - - if t+h < tf: - t, y = self._step(t,y,h) - return ID_OK, t, y - else: - h = min(h, abs(tf-t)) - t, y = self._step(t,y,h) - return ID_COMPLETE, t, y - - cpdef integrate(self, double t,N.ndarray y,double tf, dict opts): - cdef double h - cdef list tr,yr - - h = self.options["h"] - h = min(h, abs(tf-t)) - - if opts["initialize"]: - self.set_problem_data() - tr = [] - yr = [] - - flag = ID_PY_OK - if self._inith != 0: - t, y = self._step(t,y,self._inith) - if self.problem_info["state_events"]: - flag, t, y = self.event_locator(t-self._inith , t, y) - - if opts["report_continuously"]: - initialize_flag = self.report_solution(t, y, opts) - if initialize_flag: flag = ID_PY_EVENT - - else: - tr.append(t) - yr.append(y) - - #If an event was detected, calculate the length of the initial step to take after restarting. - if flag == ID_PY_EVENT: - self._inith = self._inith - (t - self._told) - else: - self._inith = 0 - h = min(h, abs(tf-t)) - - while t+h < tf and flag == ID_PY_OK: - t, y = self._step(t,y,h) - if self.problem_info["state_events"]: - flag, t, y = self.event_locator(t-h , t, y) - - if opts["report_continuously"]: - initialize_flag = self.report_solution(t, y, opts) - if initialize_flag: flag = ID_PY_EVENT - else: - tr.append(t) - yr.append(y) - - h = min(h, abs(tf-t)) - else: - #If no event was detected take the final step to tf. - if flag == ID_PY_OK: - t, y = self._step(t, y, h) - flag = ID_PY_COMPLETE - if self.problem_info["state_events"]: - flag, t, y = self.event_locator(t-h , t, y) - if flag == ID_PY_OK: flag = ID_PY_COMPLETE - - if opts["report_continuously"]: - initialize_flag = self.report_solution(t, y, opts) - if initialize_flag: flag = ID_PY_EVENT - else: - tr.append(t) - yr.append(y) - flag = ID_PY_COMPLETE - - #If an event was detected calculate the length of the initial step if not already done. - if flag == ID_PY_EVENT and self._inith == 0: - self._inith = h - (t - self._told) - - return flag, tr, yr - - def _set_newt(self, newt): - """ - Maximal number of Newton iterations. - - Parameters:: - - newt - - Default '7'. - - - Should be an integer. - - Example: - newt = 10 - """ - try: - self.options["newt"] = int(newt) - except (ValueError, TypeError): - raise AssimuloException('The newt must be an integer or float.') - - def _get_newt(self): - """ - Maximal number of Newton iterations. - - Parameters:: - - newt - - Default '7'. - - - Should be an integer. - - Example: - newt = 10 - """ - return self.options["newt"] - - newt = property(_get_newt,_set_newt) - - def _set_atol(self,atol): - - self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) - - if len(self.options["atol"]) == 1: - self.options["atol"] = self.options["atol"]*N.ones(self._leny) - elif len(self.options["atol"]) != self._leny: - raise AssimuloException("atol must be of length one or same as the dimension of the problem.") - - def _get_atol(self): - """ - Defines the absolute tolerance(s) that is to be used by the solver. - Can be set differently for each variable. - - Parameters:: - - atol - - Default '1.0e-6'. - - - Should be a positive float or a numpy vector - of floats. - - Example: - atol = [1.0e-4, 1.0e-6] - """ - return self.options["atol"] - - atol=property(_get_atol,_set_atol) - - def _set_rtol(self,rtol): - try: - self.options["rtol"] = float(rtol) - except (ValueError, TypeError): - raise AssimuloException('Relative tolerance must be a (scalar) float.') - if self.options["rtol"] <= 0.0: - raise AssimuloException('Relative tolerance must be a positive (scalar) float.') - - def _get_rtol(self): - """ - Defines the relative tolerance that is to be used by the solver. - - Parameters:: - - rtol - - Default '1.0e-6'. - - - Should be a positive float. - - Example: - rtol = 1.0e-4 - """ - return self.options["rtol"] - - rtol=property(_get_rtol,_set_rtol) - - cpdef initialize(self): - #Reset statistics - #for k in self.statistics.keys(): - # self.statistics[k] = 0 - self.statistics.reset() - - def _jacobian(self, t, y): - """ - Calculates the Jacobian, either by an approximation or by the user - defined (jac specified in the problem class). - """ - self._curjac = True #The jacobian is up to date - self._needjac = False #A new jacobian is not needed - self._steps_since_last_jac = 0 - - if self.usejac: #Retrieve the user-defined jacobian - jac = self.problem.jac(t,y) - - if isinstance(jac, sp.csc_matrix): - jac = jac.toarray() - else: #Calculate a numeric jacobian - delt = N.array([(self._eps*max(abs(yi),1.e-5))**0.5 for yi in y])*N.identity(self._leny) #Calculate a disturbance - Fdelt = N.array([self.f(t,y+e) for e in delt]) #Add the disturbance (row by row) - grad = ((Fdelt-self.f(t,y)).T/delt.diagonal()).T - jac = N.array(grad).T - - self.statistics["nfcnjacs"] += 1+self._leny #Add the number of function evaluations - - self.statistics["njacs"] += 1 #add the number of jacobian evaluation - return jac - - - - cdef double WRMS(self, N.ndarray x, N.ndarray w): - """ - Calculates the Weighted Root-mean-square. - """ - cdef double prod - cdef int N = self._leny - cdef double sum = 0.0 - - for i in range(N): - prod = x[i]*w[i] - sum += prod*prod - - return (sum/N)**0.5 - - cdef tuple _step(self,double t,N.ndarray y,double h): - """ - This calculates the next step in the integration. - """ - cdef double new_norm, old_norm - cdef double tn1 = t+h - cdef N.ndarray yn = y.copy() #Old y - #cdef N.ndarray yn1 = y.copy() #First newton guess - cdef N.ndarray yn1 = y+h*self.f(t,y) #First newton guess - cdef N.ndarray I = N.eye(self._leny) - self.statistics["nfcns"] += 1 - - FLAG_CONV = False - - for j in range(2): - FLAG_FAIL = False - - if self._needjac: #If the jacobian should be updated or not - jac = self._jacobian(tn1, yn1) - else: - jac = self._old_jac - - for i in range(self.newt): - self.statistics["nniters"] += 1 - - #jac = self._jacobian(tn1, yn1) - - #ynew = yn1 - N.dot(LIN.inv(h*jac-I),(yn-yn1+h*self.problem.rhs(tn1,yn1))) - ynew = yn1 - LIN.solve(h*jac-I, yn-yn1+h*self.f(tn1,yn1) ) - self.statistics["nfcns"] += 1 - - #print tn1, self.WRMS(ynew-yn1, 1.0/(self.rtol*N.abs(yn1)+self.atol)) - new_norm = self.WRMS(ynew-yn1, 1.0/(self.rtol*N.abs(yn1)+self.atol)) - - if new_norm < 0.1: #Newton converged - FLAG_CONV = True - break - - if i > 0: - if new_norm/old_norm > 2: #Newton iterations diverging - FLAG_FAIL = True - break - yn1 = ynew - old_norm = new_norm - else: - FLAG_FAIL = True - - if FLAG_FAIL: - self.statistics["nnfails"] += 1 - - if self._curjac: #The current Jacobian is updated (Newton failed) - raise AssimuloException("Newton iteration failed at %f"%t) - else: #Try with updated jacobian - self._needjac = True - - if FLAG_CONV: - self._steps_since_last_jac += 1 - self.statistics["nsteps"] += 1 - - if self._steps_since_last_jac > 20: #Need to update the jacobian - self._needjac = True - break - else: - raise AssimuloException("Newton iteration failed at %f"%t) - - self._curjac = False #The Jacobian is no longer current - self._old_jac = jac #Store the old jacobian - - #Internal values only used for defining the interpolation function. - self._yold = yn - self._ynew = ynew.copy() - self._told = t - self._h = h - - return tn1, ynew - - def interpolate(self, time): - return self._yold + (time - self._told) / self._h * (self._ynew - self._yold) - - def _set_h(self,h): - try: - self.options["h"] = float(h) - except: - raise AssimuloException("Step-size must be a (scalar) float.") - - def _get_h(self): - """ - Defines the step-size that is to be used by the solver. - - Parameters:: - - maxh - - Default '0.01'. - - - Should be a float. - - Example: - maxh = 0.01 - - """ - return self.options["h"] - - h=property(_get_h,_set_h) - - def state_event_info(self): - return self._event_info - - def set_event_info(self, event_info): - self._event_info = event_info - - def print_statistics(self, verbose=NORMAL): - """ - Should print the statistics. - """ - Explicit_ODE.print_statistics(self, verbose) #Calls the base class - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : ImplicitEuler', verbose) - self.log_message(' Solver type : fixed step size', verbose) - self.log_message(' Step size : {}\n'.format(self.h), verbose) - -cdef class ExplicitEuler(Explicit_ODE): - """ - This solver solves an explicit ordinary differential equation using - the explicit Euler method. - - We want to approximate the solution to the ordinary differential - equation of the form, - - .. math:: - - \dot{y} = f(t,y), \quad y(t_0) = y_0 . - - Using the explicit Euler method, the approximation is defined as - follow, - - .. math:: - - y_{n+1} = y_n + hf(t_n,y_n) - - with :math:`h` being the step-size and :math:`y_n` the previous - solution to the equation. - """ - cdef N.ndarray yd1 - cdef object f - cdef public object event_func - cdef N.ndarray _yold - cdef N.ndarray _ynew - #cdef N.ndarray _event_info - cdef public N.ndarray g_old - cdef double _told - cdef double _h - cdef double _inith - - def __init__(self, problem): - Explicit_ODE.__init__(self, problem) #Calls the base class - - #Solver options - self.options["h"] = 0.01 - - - - #Internal temporary result vector - self.yd1 = N.array([0.0]*len(self.y0)) - self._yold = N.array([0.0]*len(self.y0)) - self._ynew = N.array([0.0]*len(self.y0)) - self._inith = 0 #Used for taking an initial step of correct length after an event. - - #Solver support - self.supports["report_continuously"] = True - self.supports["interpolated_output"] = False - self.supports["state_events"] = True - - def set_problem_data(self): - if self.problem_info["state_events"]: - def event_func(t, y): - return self.problem.state_events(t, y, self.sw) - def f(t, y): - return self.problem.rhs(t, y, self.sw) - self.f = f - self.event_func = event_func - self._event_info = N.array([0] * self.problem_info["dimRoot"]) - self.g_old = self.event_func(self.t, self.y) - else: - self.f = self.problem.rhs - - cpdef step(self,double t,N.ndarray y,double tf,dict opts): - cdef double h - h = self.options["h"] - - if t+h < tf: - t, y = self._step(t,y,h) - return ID_OK, t, y - else: - h = min(h, abs(tf-t)) - t, y = self._step(t,y,h) - return ID_COMPLETE, t, y - - cpdef integrate(self, double t,N.ndarray y,double tf, dict opts): - cdef double h - cdef list tr,yr - - h = self.options["h"] - h = min(h, abs(tf-t)) - - if opts["initialize"]: - self.set_problem_data() - tr = [] - yr = [] - - flag = ID_PY_OK - if self._inith != 0: - t, y = self._step(t,y,self._inith) - if self.problem_info["state_events"]: - flag, t, y = self.event_locator(t-self._inith , t, y) - - if opts["report_continuously"]: - initialize_flag = self.report_solution(t, y, opts) - if initialize_flag: flag = ID_PY_EVENT - - else: - tr.append(t) - yr.append(y.copy()) - - #If an event was detected calculate the length of the initial step to take after restarting. - if flag == ID_PY_EVENT: - self._inith = self._inith - (t - self._told) - else: - self._inith = 0 - h = min(h, abs(tf-t)) - - while t+h < tf and flag == ID_PY_OK: - t, y = self._step(t,y,h) - if self.problem_info["state_events"]: - flag, t, y = self.event_locator(t-h , t, y) - - if opts["report_continuously"]: - initialize_flag = self.report_solution(t, y, opts) - if initialize_flag: flag = ID_PY_EVENT - else: - tr.append(t) - yr.append(y.copy()) - - h = min(h, abs(tf-t)) - else: - #If no event was detected take the final step to tf. - if flag == ID_PY_OK: - t, y = self._step(t, y, h) - flag = ID_PY_COMPLETE - if self.problem_info["state_events"]: - flag, t, y = self.event_locator(t-h , t, y) - if flag == ID_PY_OK: flag = ID_PY_COMPLETE - if opts["report_continuously"]: - initialize_flag = self.report_solution(t, y, opts) - if initialize_flag: flag = ID_PY_EVENT - else: - tr.append(t) - yr.append(y.copy()) - - #If an event was detected calculate the length of the initial step if not already done. - if flag == ID_PY_EVENT and self._inith == 0: - self._inith = h - (t - self._told) - self._inith = self._inith if abs(self._inith) > 1e-15 else 0.0 - - return flag, tr, yr - - cdef tuple _step(self,double t,N.ndarray y,double h): - """ - This calculates the next step in the integration. - """ - #self.f(self.yd1,t,y) #The output is stored in yd - - #Internal values only used for defining the interpolation function. - self._yold = y.copy() - self._ynew = y + h*self.f(t,y) - self._told = t - self._h = h - return t + h, self._ynew - - def interpolate(self, time): - return self._yold + (time - self._told) / self._h * (self._ynew - self._yold) - - def _set_h(self,h): - try: - self.options["h"] = float(h) - except: - raise AssimuloException("Step-size must be a (scalar) float.") - - def _get_h(self): - """ - Defines the step-size that is to be used by the solver. - - Parameters:: - - maxh - - Default '0.01'. - - - Should be a float. - - Example: - maxh = 0.01 - - """ - return self.options["h"] - - h=property(_get_h,_set_h) - - def state_event_info(self): - return self._event_info - - def set_event_info(self, event_info): - self._event_info = event_info - - def print_statistics(self, verbose=NORMAL): - """ - Should print the statistics. - """ - Explicit_ODE.print_statistics(self, verbose) #Calls the base class - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : ExplicitEuler', verbose) - self.log_message(' Solver type : fixed step size', verbose) - self.log_message(' Step size : {}\n'.format(self.h), verbose) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +cimport numpy as N +import numpy as N +import numpy.linalg as LIN +import scipy.sparse as sp + +#from assimulo.ode import * +from assimulo.explicit_ode cimport Explicit_ODE +from assimulo.exception import * + +include "constants.pxi" #Includes the constants (textual include) + +cdef class ImplicitEuler(Explicit_ODE): + """ + This solver solves an explicit ordinary differential equation using + the implicit Euler method. + + We want to approximate the solution to the ordinary differential + equation of the form, + + .. math:: + + \dot{y} = f(t,y), \quad y(t_0) = y_0 . + + Using the implicit Euler method, the approximation is defined as + follow, + + .. math:: + + y_{n+1} = y_n + hf(t_{n+1},y_{n+1}) + + with :math:`h` being the step-size and :math:`y_n` the previous + solution to the equation. + """ + cdef N.ndarray yd1 + cdef N.ndarray _old_jac + cdef object f + cdef public object event_func + cdef int _leny + cdef double _eps + cdef int _needjac + cdef int _curjac + cdef int _steps_since_last_jac + cdef N.ndarray _yold + cdef N.ndarray _ynew + #cdef N.ndarray _event_info + cdef public N.ndarray g_old + cdef double _told + cdef double _h + cdef double _inith + + def __init__(self, problem): + Explicit_ODE.__init__(self, problem) #Calls the base class + + #Solver options + self.options["h"] = 0.01 + self.options["usejac"] = True if (self.problem_info["jac_fcn"]) else False + self.options["newt"] = 7 #Maximum number of newton iterations + self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + + #Internal temporary result vector + self.yd1 = N.array([0.0]*len(self.y0)) + self._yold = N.array([0.0]*len(self.y0)) + self._ynew = N.array([0.0]*len(self.y0)) + + #Solver support + self.supports["report_continuously"] = True + self.supports["interpolated_output"] = False + self.supports["state_events"] = True + + + self._leny = len(self.y) #Dimension of the problem + self._eps = N.finfo('double').eps + self._needjac = True #Do we need a new jacobian? + self._curjac = False #Is the current jacobian up to date? + self._steps_since_last_jac = 0 #Keep track on how long ago we updated the jacobian + self._inith = 0 #Used for taking an initial step of correct length after an event. + + def set_problem_data(self): + if self.problem_info["state_events"]: + def event_func(t, y): + return self.problem.state_events(t, y, self.sw) + def f(t, y): + return self.problem.rhs(t, y, self.sw) + self.f = f + self.event_func = event_func + self._event_info = N.array([0] * self.problem_info["dimRoot"]) + self.g_old = self.event_func(self.t, self.y) + else: + self.f = self.problem.rhs + + + def _set_usejac(self, jac): + self.options["usejac"] = bool(jac) + + def _get_usejac(self): + """ + This sets the option to use the user defined jacobian. If a + user provided jacobian is implemented into the problem the + default setting is to use that jacobian. If not, an + approximation is used. + + Parameters:: + + usejac + - True - use user defined jacobian + False - use an approximation + + - Should be a boolean. + + Example: + usejac = False + """ + return self.options["usejac"] + + usejac = property(_get_usejac,_set_usejac) + + cpdef step(self,double t,N.ndarray y,double tf,dict opts): + cdef double h + h = self.options["h"] + + if t+h < tf: + t, y = self._step(t,y,h) + return ID_OK, t, y + else: + h = min(h, abs(tf-t)) + t, y = self._step(t,y,h) + return ID_COMPLETE, t, y + + cpdef integrate(self, double t,N.ndarray y,double tf, dict opts): + cdef double h + cdef list tr,yr + + h = self.options["h"] + h = min(h, abs(tf-t)) + + if opts["initialize"]: + self.set_problem_data() + tr = [] + yr = [] + + flag = ID_PY_OK + if self._inith != 0: + t, y = self._step(t,y,self._inith) + if self.problem_info["state_events"]: + flag, t, y = self.event_locator(t-self._inith , t, y) + + if opts["report_continuously"]: + initialize_flag = self.report_solution(t, y, opts) + if initialize_flag: flag = ID_PY_EVENT + + else: + tr.append(t) + yr.append(y) + + #If an event was detected, calculate the length of the initial step to take after restarting. + if flag == ID_PY_EVENT: + self._inith = self._inith - (t - self._told) + else: + self._inith = 0 + h = min(h, abs(tf-t)) + + while t+h < tf and flag == ID_PY_OK: + t, y = self._step(t,y,h) + if self.problem_info["state_events"]: + flag, t, y = self.event_locator(t-h , t, y) + + if opts["report_continuously"]: + initialize_flag = self.report_solution(t, y, opts) + if initialize_flag: flag = ID_PY_EVENT + else: + tr.append(t) + yr.append(y) + + h = min(h, abs(tf-t)) + else: + #If no event was detected take the final step to tf. + if flag == ID_PY_OK: + t, y = self._step(t, y, h) + flag = ID_PY_COMPLETE + if self.problem_info["state_events"]: + flag, t, y = self.event_locator(t-h , t, y) + if flag == ID_PY_OK: flag = ID_PY_COMPLETE + + if opts["report_continuously"]: + initialize_flag = self.report_solution(t, y, opts) + if initialize_flag: flag = ID_PY_EVENT + else: + tr.append(t) + yr.append(y) + flag = ID_PY_COMPLETE + + #If an event was detected calculate the length of the initial step if not already done. + if flag == ID_PY_EVENT and self._inith == 0: + self._inith = h - (t - self._told) + + return flag, tr, yr + + def _set_newt(self, newt): + """ + Maximal number of Newton iterations. + + Parameters:: + + newt + - Default '7'. + + - Should be an integer. + + Example: + newt = 10 + """ + try: + self.options["newt"] = int(newt) + except (ValueError, TypeError): + raise AssimuloException('The newt must be an integer or float.') + + def _get_newt(self): + """ + Maximal number of Newton iterations. + + Parameters:: + + newt + - Default '7'. + + - Should be an integer. + + Example: + newt = 10 + """ + return self.options["newt"] + + newt = property(_get_newt,_set_newt) + + def _set_atol(self,atol): + + self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) + + if len(self.options["atol"]) == 1: + self.options["atol"] = self.options["atol"]*N.ones(self._leny) + elif len(self.options["atol"]) != self._leny: + raise AssimuloException("atol must be of length one or same as the dimension of the problem.") + + def _get_atol(self): + """ + Defines the absolute tolerance(s) that is to be used by the solver. + Can be set differently for each variable. + + Parameters:: + + atol + - Default '1.0e-6'. + + - Should be a positive float or a numpy vector + of floats. + + Example: + atol = [1.0e-4, 1.0e-6] + """ + return self.options["atol"] + + atol=property(_get_atol,_set_atol) + + def _set_rtol(self,rtol): + try: + self.options["rtol"] = float(rtol) + except (ValueError, TypeError): + raise AssimuloException('Relative tolerance must be a (scalar) float.') + if self.options["rtol"] <= 0.0: + raise AssimuloException('Relative tolerance must be a positive (scalar) float.') + + def _get_rtol(self): + """ + Defines the relative tolerance that is to be used by the solver. + + Parameters:: + + rtol + - Default '1.0e-6'. + + - Should be a positive float. + + Example: + rtol = 1.0e-4 + """ + return self.options["rtol"] + + rtol=property(_get_rtol,_set_rtol) + + cpdef initialize(self): + #Reset statistics + #for k in self.statistics.keys(): + # self.statistics[k] = 0 + self.statistics.reset() + + def _jacobian(self, t, y): + """ + Calculates the Jacobian, either by an approximation or by the user + defined (jac specified in the problem class). + """ + self._curjac = True #The jacobian is up to date + self._needjac = False #A new jacobian is not needed + self._steps_since_last_jac = 0 + + if self.usejac: #Retrieve the user-defined jacobian + jac = self.problem.jac(t,y) + + if isinstance(jac, sp.csc_matrix): + jac = jac.toarray() + else: #Calculate a numeric jacobian + delt = N.array([(self._eps*max(abs(yi),1.e-5))**0.5 for yi in y])*N.identity(self._leny) #Calculate a disturbance + Fdelt = N.array([self.f(t,y+e) for e in delt]) #Add the disturbance (row by row) + grad = ((Fdelt-self.f(t,y)).T/delt.diagonal()).T + jac = N.array(grad).T + + self.statistics["nfcnjacs"] += 1+self._leny #Add the number of function evaluations + + self.statistics["njacs"] += 1 #add the number of jacobian evaluation + return jac + + + + cdef double WRMS(self, N.ndarray x, N.ndarray w): + """ + Calculates the Weighted Root-mean-square. + """ + cdef double prod + cdef int N = self._leny + cdef double sum = 0.0 + + for i in range(N): + prod = x[i]*w[i] + sum += prod*prod + + return (sum/N)**0.5 + + cdef tuple _step(self,double t,N.ndarray y,double h): + """ + This calculates the next step in the integration. + """ + cdef double new_norm, old_norm + cdef double tn1 = t+h + cdef N.ndarray yn = y.copy() #Old y + #cdef N.ndarray yn1 = y.copy() #First newton guess + cdef N.ndarray yn1 = y+h*self.f(t,y) #First newton guess + cdef N.ndarray I = N.eye(self._leny) + self.statistics["nfcns"] += 1 + + FLAG_CONV = False + + for j in range(2): + FLAG_FAIL = False + + if self._needjac: #If the jacobian should be updated or not + jac = self._jacobian(tn1, yn1) + else: + jac = self._old_jac + + for i in range(self.newt): + self.statistics["nniters"] += 1 + + #jac = self._jacobian(tn1, yn1) + + #ynew = yn1 - N.dot(LIN.inv(h*jac-I),(yn-yn1+h*self.problem.rhs(tn1,yn1))) + ynew = yn1 - LIN.solve(h*jac-I, yn-yn1+h*self.f(tn1,yn1) ) + self.statistics["nfcns"] += 1 + + #print tn1, self.WRMS(ynew-yn1, 1.0/(self.rtol*N.abs(yn1)+self.atol)) + new_norm = self.WRMS(ynew-yn1, 1.0/(self.rtol*N.abs(yn1)+self.atol)) + + if new_norm < 0.1: #Newton converged + FLAG_CONV = True + break + + if i > 0: + if new_norm/old_norm > 2: #Newton iterations diverging + FLAG_FAIL = True + break + yn1 = ynew + old_norm = new_norm + else: + FLAG_FAIL = True + + if FLAG_FAIL: + self.statistics["nnfails"] += 1 + + if self._curjac: #The current Jacobian is updated (Newton failed) + raise AssimuloException("Newton iteration failed at %f"%t) + else: #Try with updated jacobian + self._needjac = True + + if FLAG_CONV: + self._steps_since_last_jac += 1 + self.statistics["nsteps"] += 1 + + if self._steps_since_last_jac > 20: #Need to update the jacobian + self._needjac = True + break + else: + raise AssimuloException("Newton iteration failed at %f"%t) + + self._curjac = False #The Jacobian is no longer current + self._old_jac = jac #Store the old jacobian + + #Internal values only used for defining the interpolation function. + self._yold = yn + self._ynew = ynew.copy() + self._told = t + self._h = h + + return tn1, ynew + + def interpolate(self, time): + return self._yold + (time - self._told) / self._h * (self._ynew - self._yold) + + def _set_h(self,h): + try: + self.options["h"] = float(h) + except: + raise AssimuloException("Step-size must be a (scalar) float.") + + def _get_h(self): + """ + Defines the step-size that is to be used by the solver. + + Parameters:: + + maxh + - Default '0.01'. + + - Should be a float. + + Example: + maxh = 0.01 + + """ + return self.options["h"] + + h=property(_get_h,_set_h) + + def state_event_info(self): + return self._event_info + + def set_event_info(self, event_info): + self._event_info = event_info + + def print_statistics(self, verbose=NORMAL): + """ + Should print the statistics. + """ + Explicit_ODE.print_statistics(self, verbose) #Calls the base class + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : ImplicitEuler', verbose) + self.log_message(' Solver type : fixed step size', verbose) + self.log_message(' Step size : {}\n'.format(self.h), verbose) + +cdef class ExplicitEuler(Explicit_ODE): + """ + This solver solves an explicit ordinary differential equation using + the explicit Euler method. + + We want to approximate the solution to the ordinary differential + equation of the form, + + .. math:: + + \dot{y} = f(t,y), \quad y(t_0) = y_0 . + + Using the explicit Euler method, the approximation is defined as + follow, + + .. math:: + + y_{n+1} = y_n + hf(t_n,y_n) + + with :math:`h` being the step-size and :math:`y_n` the previous + solution to the equation. + """ + cdef N.ndarray yd1 + cdef object f + cdef public object event_func + cdef N.ndarray _yold + cdef N.ndarray _ynew + #cdef N.ndarray _event_info + cdef public N.ndarray g_old + cdef double _told + cdef double _h + cdef double _inith + + def __init__(self, problem): + Explicit_ODE.__init__(self, problem) #Calls the base class + + #Solver options + self.options["h"] = 0.01 + + + + #Internal temporary result vector + self.yd1 = N.array([0.0]*len(self.y0)) + self._yold = N.array([0.0]*len(self.y0)) + self._ynew = N.array([0.0]*len(self.y0)) + self._inith = 0 #Used for taking an initial step of correct length after an event. + + #Solver support + self.supports["report_continuously"] = True + self.supports["interpolated_output"] = False + self.supports["state_events"] = True + + def set_problem_data(self): + if self.problem_info["state_events"]: + def event_func(t, y): + return self.problem.state_events(t, y, self.sw) + def f(t, y): + return self.problem.rhs(t, y, self.sw) + self.f = f + self.event_func = event_func + self._event_info = N.array([0] * self.problem_info["dimRoot"]) + self.g_old = self.event_func(self.t, self.y) + else: + self.f = self.problem.rhs + + cpdef step(self,double t,N.ndarray y,double tf,dict opts): + cdef double h + h = self.options["h"] + + if t+h < tf: + t, y = self._step(t,y,h) + return ID_OK, t, y + else: + h = min(h, abs(tf-t)) + t, y = self._step(t,y,h) + return ID_COMPLETE, t, y + + cpdef integrate(self, double t,N.ndarray y,double tf, dict opts): + cdef double h + cdef list tr,yr + + h = self.options["h"] + h = min(h, abs(tf-t)) + + if opts["initialize"]: + self.set_problem_data() + tr = [] + yr = [] + + flag = ID_PY_OK + if self._inith != 0: + t, y = self._step(t,y,self._inith) + if self.problem_info["state_events"]: + flag, t, y = self.event_locator(t-self._inith , t, y) + + if opts["report_continuously"]: + initialize_flag = self.report_solution(t, y, opts) + if initialize_flag: flag = ID_PY_EVENT + + else: + tr.append(t) + yr.append(y.copy()) + + #If an event was detected calculate the length of the initial step to take after restarting. + if flag == ID_PY_EVENT: + self._inith = self._inith - (t - self._told) + else: + self._inith = 0 + h = min(h, abs(tf-t)) + + while t+h < tf and flag == ID_PY_OK: + t, y = self._step(t,y,h) + if self.problem_info["state_events"]: + flag, t, y = self.event_locator(t-h , t, y) + + if opts["report_continuously"]: + initialize_flag = self.report_solution(t, y, opts) + if initialize_flag: flag = ID_PY_EVENT + else: + tr.append(t) + yr.append(y.copy()) + + h = min(h, abs(tf-t)) + else: + #If no event was detected take the final step to tf. + if flag == ID_PY_OK: + t, y = self._step(t, y, h) + flag = ID_PY_COMPLETE + if self.problem_info["state_events"]: + flag, t, y = self.event_locator(t-h , t, y) + if flag == ID_PY_OK: flag = ID_PY_COMPLETE + if opts["report_continuously"]: + initialize_flag = self.report_solution(t, y, opts) + if initialize_flag: flag = ID_PY_EVENT + else: + tr.append(t) + yr.append(y.copy()) + + #If an event was detected calculate the length of the initial step if not already done. + if flag == ID_PY_EVENT and self._inith == 0: + self._inith = h - (t - self._told) + self._inith = self._inith if abs(self._inith) > 1e-15 else 0.0 + + return flag, tr, yr + + cdef tuple _step(self,double t,N.ndarray y,double h): + """ + This calculates the next step in the integration. + """ + #self.f(self.yd1,t,y) #The output is stored in yd + + #Internal values only used for defining the interpolation function. + self._yold = y.copy() + self._ynew = y + h*self.f(t,y) + self._told = t + self._h = h + return t + h, self._ynew + + def interpolate(self, time): + return self._yold + (time - self._told) / self._h * (self._ynew - self._yold) + + def _set_h(self,h): + try: + self.options["h"] = float(h) + except: + raise AssimuloException("Step-size must be a (scalar) float.") + + def _get_h(self): + """ + Defines the step-size that is to be used by the solver. + + Parameters:: + + maxh + - Default '0.01'. + + - Should be a float. + + Example: + maxh = 0.01 + + """ + return self.options["h"] + + h=property(_get_h,_set_h) + + def state_event_info(self): + return self._event_info + + def set_event_info(self, event_info): + self._event_info = event_info + + def print_statistics(self, verbose=NORMAL): + """ + Should print the statistics. + """ + Explicit_ODE.print_statistics(self, verbose) #Calls the base class + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : ExplicitEuler', verbose) + self.log_message(' Solver type : fixed step size', verbose) + self.log_message(' Step size : {}\n'.format(self.h), verbose) diff --git a/src/solvers/kinsol.pyx b/src/solvers/kinsol.pyx index 2ea61024..c6ae7011 100644 --- a/src/solvers/kinsol.pyx +++ b/src/solvers/kinsol.pyx @@ -1,697 +1,699 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -cimport numpy as N -from numpy cimport PyArray_DATA - -N.import_array() - -import numpy.linalg -import traceback - -from assimulo.exception import * -from assimulo.algebraic cimport Algebraic - -cimport sundials_includes as SUNDIALS - -#Various C includes transfered to namespace -from sundials_includes cimport N_Vector, realtype, N_VectorContent_Serial, DENSE_COL, sunindextype -from sundials_includes cimport memcpy, N_VNew_Serial, DlsMat, SlsMat, SUNMatrix, SUNMatrixContent_Dense, SUNMatrixContent_Sparse -from sundials_includes cimport malloc, free, N_VCloneVectorArray_Serial -from sundials_includes cimport N_VConst_Serial, N_VDestroy_Serial - -include "constants.pxi" #Includes the constants (textual include) -include "../lib/sundials_constants.pxi" #Sundials related constants -include "../lib/sundials_callbacks.pxi" -include "../lib/sundials_callbacks_kinsol.pxi" - -cdef class KINSOL(Algebraic): - """ - This class provides a connection to the Sundials - (https://computation.llnl.gov/casc/sundials/main.html) solver - KINSOL. - - .. math:: - - 0 = F(y) - - """ - cdef void* kinsol_mem - cdef ProblemDataEquationSolver pData #A struct containing information about the problem - cdef N_Vector y_temp, y_scale, f_scale - cdef public double _eps - - cdef object pt_fcn, pt_jac, pt_jacv, pt_prec_setup, pt_prec_solve - cdef object _added_linear_solver - cdef SUNDIALS.SUNMatrix sun_matrix - cdef SUNDIALS.SUNLinearSolver sun_linearsolver - - def __init__(self, problem): - Algebraic.__init__(self, problem) #Calls the base class - - self.pData = ProblemDataEquationSolver() - - self._eps = N.finfo('double').eps - self._added_linear_solver = False - - #Populate the ProblemData - self.set_problem_data() - - #Solver options - self.options["ftol"] = self._eps**(1.0/3.0) - self.options["stol"] = self._eps**(2.0/3.0) - self.options["strategy"] = KIN_LINESEARCH - self.options["y_scale"] = N.array([1.0]*self.problem_info["dim"]) - self.options["f_scale"] = N.array([1.0]*self.problem_info["dim"]) - #self.options["y_nominal"] = N.array([1.0]*self.problem_info["dim"]) - #self.options["y_min"] = N.array([MIN_VALUE]*self.problem_info["dim"]) - #self.options["y_max"] = N.array([MAX_VALUE]*self.problem_info["dim"]) - self.options["linear_solver"] = "DENSE" - self.options["max_iter"] = 200 #Maximum number of nonlinear iterations - self.options["no_initial_setup"] = False #Specifies wheter or not a call to the setup function should be made - self.options["max_solves_between_setup_calls"] = 10 #Specifies the maximum number of allowed calls to the solve function between setup calls - self.options["max_newton_step"] = 0.0 #Specifies the max allowed newton step - self.options["no_min_epsilon"] = False #Specifies wheter the scaled linear residual is bounded from below - self.options["max_beta_fails"] = 10 - self.options["max_krylov"] = 0 - self.options["precond"] = PREC_NONE - - #Statistics - self.statistics["nfevals"] = 0 #Function evaluations - self.statistics["nniters"] = 0 #Nonlinear iterations - self.statistics["njevals"] = 0 #Jacobian evaluations - self.statistics["nfevalsLS"] = 0 #Function evaluations due to Jac - self.statistics["nbacktr"] = 0 #The function KINGetNumBacktrackOps returns the number of backtrack operations (step length adjustments) performed by the line search algorithm. - self.statistics["nbcfails"] = 0 #The function KINGetNumBetaCondFails returns the number of β-condition failures. - self.statistics["nliters"] = 0 - self.statistics["nlcfails"] = 0 - - #Initialize Kinsol - self.initialize_kinsol() - - def __dealloc__(self): - - if self.y_temp != NULL: - #Deallocate N_Vector - N_VDestroy_Serial(self.y_temp) - - if self.kinsol_mem != NULL: - #Free Memory - SUNDIALS.KINFree(&self.kinsol_mem) - - IF SUNDIALS_VERSION >= (3,0,0): - if self.sun_matrix != NULL: - SUNDIALS.SUNMatDestroy(self.sun_matrix) - - if self.sun_linearsolver != NULL: - SUNDIALS.SUNLinSolFree(self.sun_linearsolver) - - def update_variable_scaling(self, value="Automatic"): - """ - Updates the variable scaling with either - """ - if isinstance(value, str) and value.upper() == "AUTOMATIC": - for i in range(self.problem_info["dim"]): - if self.options["y_nominal"]: - self.options["y_scale"][i] = self.options["y_nominal"][i] - elif self.y[i] != 0.0: - self.options["y_scale"][i] = self.y[i] - elif self.options["y_max"] and self.options["y_min"]: - self.options["y_scale"][i] = max(abs(self.options["y_max"][i]+self.options["y_min"][i])/2.0, 0.01*self.options["y_max"][i]) - elif self.options["y_max"]: - self.options["y_scale"][i] = max(1.0, abs(self.options["y_max"][i])) - elif self.options["y_min"]: - self.options["y_scale"][i] = max(1.0, abs(self.options["y_min"][i])) - else: - self.options["y_scale"] = N.array([value]) if isinstance(value, float) or isinstance(value, int) else N.array(value) - - arr2nv_inplace(self.options["y_scale"], self.y_scale) - - def update_residual_scaling(self, value="Automatic"): - """ - Updates the residual scaling. - """ - if isinstance(value, str) and value.upper() == "AUTOMATIC": - pass - else: - self.options["f_scale"] = N.array([value]) if isinstance(value, float) or isinstance(value, int) else N.array(value) - - arr2nv_inplace(self.options["f_scale"], self.f_scale) - - cdef set_problem_data(self): - - #Sets the f function - self.pt_fcn = self.problem.res - self.pData.RES = self.pt_fcn - self.pData.dim = self.problem_info["dim"] - self.pData.nl_fnorm = [] - self.pData.l_fnorm = [] - self.pData.log = [] - - if self.problem_info["jac_fcn"] is True: #Sets the jacobian - self.pt_jac = self.problem.jac - self.pData.JAC = self.pt_jac - - if self.problem_info["jacv_fcn"] is True: #Sets the jacobian*vector function - self.pt_jacv = self.problem.jacv - self.pData.JACV = self.pt_jacv - - if self.problem_info["prec_solve"] is True: #Sets the preconditioner solve function - self.pt_prec_solve = self.problem.prec_solve - self.pData.PREC_SOLVE = self.pt_prec_solve - - if self.problem_info["prec_setup"] is True: #Sets the preconditioner setup function - self.pt_prec_setup = self.problem.prec_setup - self.pData.PREC_SETUP = self.pt_prec_setup - - cdef initialize_kinsol(self): - cdef int flag #Used for return - - self.y_temp = arr2nv(self.y) - self.y_scale = arr2nv([1.0]*self.problem_info["dim"]) - self.f_scale = arr2nv([1.0]*self.problem_info["dim"]) - - if self.kinsol_mem == NULL: #The solver is not initialized - - #Create the solver - self.kinsol_mem = SUNDIALS.KINCreate() - if self.kinsol_mem == NULL: - raise KINSOLError(KIN_MEM_NULL) - self.pData.KIN_MEM = self.kinsol_mem - - #Specify the residual and the initial conditions to the solver - flag = SUNDIALS.KINInit(self.kinsol_mem, kin_res, self.y_temp) - if flag < 0: - raise KINSOLError(flag) - - #Specify the error handling - flag = SUNDIALS.KINSetErrHandlerFn(self.kinsol_mem, kin_err, self.pData) - if flag < 0: - raise KINSOLError(flag) - - #Specify the handling of info messages - flag = SUNDIALS.KINSetInfoHandlerFn(self.kinsol_mem, kin_info, self.pData); - if flag < 0: - raise KINSOLError(flag) - - else: #The solver needs not to be reinitialized - pass - - #Set the user data - flag = SUNDIALS.KINSetUserData(self.kinsol_mem, self.pData) - if flag < 0: - raise KINSOLError(flag) - - cpdef add_linear_solver(self): - if self.options["linear_solver"] == "DENSE": - IF SUNDIALS_VERSION >= (3,0,0): - #Create a dense Sundials matrix - self.sun_matrix = SUNDIALS.SUNDenseMatrix(self.pData.dim, self.pData.dim) - #Create a dense Sundials linear solver - self.sun_linearsolver = SUNDIALS.SUNDenseLinearSolver(self.y_temp, self.sun_matrix) - #Attach it to Kinsol - flag = SUNDIALS.KINDlsSetLinearSolver(self.kinsol_mem, self.sun_linearsolver, self.sun_matrix) - ELSE: - flag = SUNDIALS.KINDense(self.kinsol_mem, self.problem_info["dim"]) - if flag < 0: - raise KINSOLError(flag) - - if self.problem_info["jac_fcn"]: - IF SUNDIALS_VERSION >= (3,0,0): - flag = SUNDIALS.KINDlsSetJacFn(self.kinsol_mem, kin_jac); - ELSE: - flag = SUNDIALS.KINDlsSetDenseJacFn(self.kinsol_mem, kin_jac); - if flag < 0: - raise KINSOLError(flag) - elif self.options["linear_solver"] == "SPGMR": - IF SUNDIALS_VERSION >= (3,0,0): - #Create the linear solver - self.sun_linearsolver = SUNDIALS.SUNSPGMR(self.y_temp, self.options["precond"], self.options["max_krylov"]) - #Attach it to Kinsol - flag = SUNDIALS.KINSpilsSetLinearSolver(self.kinsol_mem, self.sun_linearsolver) - ELSE: - #Specify the use of KINSpgmr linear solver. - flag = SUNDIALS.KINSpgmr(self.kinsol_mem, self.options["max_krylov"]) - if flag < 0: - raise KINSOLError(flag) - - if self.problem_info["jacv_fcn"]: - flag = SUNDIALS.KINSpilsSetJacTimesVecFn(self.kinsol_mem, kin_jacv) - if flag < 0: - raise KINSOLError(flag) - - if self.problem_info["prec_setup"] or self.problem_info["prec_solve"]: - if not self.problem_info["prec_setup"]: - flag = SUNDIALS.KINSpilsSetPreconditioner(self.kinsol_mem, NULL,kin_prec_solve) - if flag < 0: - raise KINSOLError(flag) - elif not self.problem_info["prec_solve"]: - flag = SUNDIALS.KINSpilsSetPreconditioner(self.kinsol_mem, kin_prec_setup, NULL) - if flag < 0: - raise KINSOLError(flag) - else: - flag = SUNDIALS.KINSpilsSetPreconditioner(self.kinsol_mem, kin_prec_setup, kin_prec_solve) - if flag < 0: - raise KINSOLError(flag) - - - else: - raise KINSOLError(-100) - - self._added_linear_solver = True - - cpdef update_options(self): - """ - Updates the simulation options. - """ - cdef int flag - - #Update scaling - arr2nv_inplace(self.options["y_scale"], self.y_scale) - arr2nv_inplace(self.options["f_scale"], self.f_scale) - - flag = SUNDIALS.KINSetFuncNormTol(self.kinsol_mem, self.options["ftol"]); - if flag < 0: - raise KINSOLError(flag) - - flag = SUNDIALS.KINSetScaledStepTol(self.kinsol_mem, self.options["stol"]); - if flag < 0: - raise KINSOLError(flag) - - flag = SUNDIALS.KINSetMaxBetaFails(self.kinsol_mem, self.options["max_beta_fails"]); #The function KINSetMaxBetaFails specifies the maximum number of β-condition failures in the linesearch algorithm. - if flag < 0: - raise KINSOLError(flag) - - flag = SUNDIALS.KINSetMaxNewtonStep(self.kinsol_mem, self.options["max_newton_step"]); #The function KINSetMaxNewtonStep specifies the maximum allowable scaled length of the Newton step. - if flag < 0: - raise KINSOLError(flag) - - flag = SUNDIALS.KINSetNoMinEps(self.kinsol_mem, self.options["no_min_epsilon"]); #The function KINSetNoMinEps specifies a flag that controls whether or not the value of ǫ, the scaled linear residual tolerance, is bounded from below. - if flag < 0: - raise KINSOLError(flag) - - #flag = SUNDIALS.KINSetEtaParams(self.kinsol_mem, egamma, ealpha); #The function KINSetEtaParams specifies the parameters γ and α in the formula for η, in the case etachoice = KIN ETACHOICE2. - #if flag < 0: - # raise KINSOLError(flag) - - flag = SUNDIALS.KINSetMaxSetupCalls(self.kinsol_mem, self.options["max_solves_between_setup_calls"]); #The function KINSetMaxSetupCalls specifies the maximum number of nonlinear iterations that can be performed between calls to the preconditioner setup function. - if flag < 0: - raise KINSOLError(flag) - - flag = SUNDIALS.KINSetNoInitSetup(self.kinsol_mem, self.options["no_initial_setup"]); #The function KINSetNoInitSetup specifies whether an initial call to the preconditioner setup function should be made or not. - if flag < 0: - raise KINSOLError(flag) - - flag = SUNDIALS.KINSetNumMaxIters(self.kinsol_mem, self.options["max_iter"]); #The function KINSetNumMaxIters specifies the maximum number of nonlinear iterations allowed. - if flag < 0: - raise KINSOLError(flag) - - flag = SUNDIALS.KINSetPrintLevel(self.kinsol_mem, self._get_print_level()); #The function KINSetPrintLevel specifies the level of verbosity of the output. - if flag < 0: - raise KINSOLError(flag) - - def _get_print_level(self): - """ - Converts Assimulos verbosity level to Kinsol print level. Kinsol - print level:: - - 0 no information displayed. - - 1 for each nonlinear iteration display the following information: the scaled - Euclidean ℓ2 norm of the system function evaluated at the current iterate, - the scaled norm of the Newton step (only if using KIN NONE), and the - number of function evaluations performed so far. - - 2 display level 1 output and the following values for each iteration: - kF(u)kDF (only for KIN NONE). - kF(u)kDF ,∞ (for KIN NONE and KIN LINESEARCH). - - 3 display level 2 output plus additional values used by the global strategy - (only if using KIN LINESEARCH), and statistical information for the linear - solver. - """ - if self.verbosity >= NORMAL: - return 0 - elif self.verbosity >= LOUD: - return 2 - else: - return 3 - - cpdef _solve(self, y0=None): - """ - Solves the system. - """ - if y0 is not None: - arr2nv_inplace(y0, self.y_temp) - else: - arr2nv_inplace(self.y, self.y_temp) - - if not self._added_linear_solver: - self.add_linear_solver() - - #Update the solver options - self.update_options() - - flag = SUNDIALS.KINSol(self.kinsol_mem, self.y_temp, self.options["strategy"], self.y_scale, self.f_scale) - self.y = nv2arr(self.y_temp) - - if flag < 0: - raise KINSOLError(flag) - if flag == KIN_STEP_LT_STPTOL: - print 'Scaled step length too small. Either an approximate solution or a local minimum is reached. Check value of residual.' - - self.store_statistics() - - return nv2arr(self.y_temp) - - def get_last_flag(self): - """ - Returns the last flag reported by Kinsol. - """ - cdef int flag = 0, - cdef long int lsflag = 0 - - flag = SUNDIALS.KINDlsGetLastFlag(self.kinsol_mem, &lsflag) - if flag < 0: - raise KINSOLError(flag) - - return lsflag - - def store_statistics(self): - cdef int flag - cdef long int nfevalsLS, njevals, nbacktr, nbcfails, nniters - cdef long int nliters, nlcfails, npevals, npsolves - cdef long int nfevals - - flag = SUNDIALS.KINGetNumFuncEvals(self.kinsol_mem, &nfevals) - if flag < 0: - raise KINSOLError(flag) - self.statistics["nfevals"] = nfevals - - flag = SUNDIALS.KINGetNumNonlinSolvIters(self.kinsol_mem, &nniters) - if flag < 0: - raise KINSOLError(flag) - self.statistics["nniters"] = nniters - - flag = SUNDIALS.KINGetNumBacktrackOps(self.kinsol_mem, &nbacktr) #The function KINGetNumBacktrackOps returns the number of backtrack operations (step length adjustments) performed by the line search algorithm. - if flag < 0: - raise KINSOLError(flag) - self.statistics["nbacktr"] = nbacktr - - flag = SUNDIALS.KINGetNumBetaCondFails(self.kinsol_mem, &nbcfails) #The function KINGetNumBetaCondFails returns the number of β-condition failures. - if flag < 0: - raise KINSOLError(flag) - self.statistics["nbcfails"] = nbcfails - - if self.options["linear_solver"] == "SPGMR": - - flag = SUNDIALS.KINSpilsGetNumLinIters(self.kinsol_mem, &nliters) - if flag < 0: - raise KINSOLError(flag) - self.statistics["nliters"] = nliters - - flag = SUNDIALS.KINSpilsGetNumConvFails(self.kinsol_mem, &nlcfails) - if flag < 0: - raise KINSOLError(flag) - self.statistics["nlcfails"] = nlcfails - - flag = SUNDIALS.KINSpilsGetNumPrecEvals(self.kinsol_mem, &npevals) - if flag < 0: - raise KINSOLError(flag) - self.statistics["npevals"] = npevals - - flag = SUNDIALS.KINSpilsGetNumPrecSolves(self.kinsol_mem, &npsolves) - if flag < 0: - raise KINSOLError(flag) - self.statistics["npsolves"] = npsolves - - flag = SUNDIALS.KINSpilsGetNumJtimesEvals(self.kinsol_mem, &njevals) - if flag < 0: - raise KINSOLError(flag) - self.statistics["njevals"] = njevals - - flag = SUNDIALS.KINSpilsGetNumFuncEvals(self.kinsol_mem, &nfevalsLS) - if flag < 0: - raise KINSOLError(flag) - self.statistics["nfevalsLS"] = nfevalsLS - - elif self.options["linear_solver"] == "DENSE": - - flag = SUNDIALS.KINDlsGetNumJacEvals(self.kinsol_mem, &njevals) #The function KINDlsGetNumJacEvals returns the number of calls to the dense Jacobian approximation function. - if flag < 0: - raise KINSOLError(flag) - self.statistics["njevals"] = njevals - - flag = SUNDIALS.KINDlsGetNumFuncEvals(self.kinsol_mem, &nfevalsLS) #The function KINDlsGetNumFuncEvals returns the number of calls to the user system function used to compute the difference quotient approximation to the dense or banded Jacobian. - if flag < 0: - raise KINSOLError(flag) - self.statistics["nfevalsLS"] = nfevalsLS - - - def print_statistics(self, verbose=NORMAL): - """ - Should print the statistics. - """ - self.log_message('Final Run Statistics: %s \n' % self.problem.name, verbose) - - self.log_message(' Number of function evaluations : '+ str(self.statistics["nfevals"]), verbose) - self.log_message(' Number of Nonlinear Iterations : '+ str(self.statistics["nniters"]), verbose) - self.log_message(' Number of Backtrack Operations (Linesearch) : '+ str(self.statistics["nbacktr"]), verbose) #The function KINGetNumBacktrackOps returns the number of backtrack operations (step length adjustments) performed by the line search algorithm. - self.log_message(' Number of Beta-condition Failures : '+ str(self.statistics["nbcfails"]), verbose) #The function KINGetNumBetaCondFails returns the number of β-condition failures. - - if self.options["linear_solver"] == "SPGMR": - self.log_message(' Number of Jacobian*Vector Evaluations : '+ str(self.statistics["njevals"]), verbose) - self.log_message(' Number of F-Eval During Jac*Vec-Eval : '+ str(self.statistics["nfevalsLS"]), verbose) - self.log_message(' Number of Linear Iterations : '+ str(self.statistics["nliters"]), verbose) - self.log_message(' Number of Linear Convergence Failures : '+ str(self.statistics["nlcfails"]), verbose) - elif self.options["linear_solver"] == "DENSE": - self.log_message(' Number of Jacobian evaluations : '+ str(self.statistics["njevals"]), verbose) - self.log_message(' Number of F-eval during Jac-eval : '+ str(self.statistics["nfevalsLS"]), verbose) - - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : Kinsol', verbose) - self.log_message(' Linear Solver : ' + str(self.options["linear_solver"]), verbose) - self.log_message(' Globalization Strategy : ' + ("LINESEARCH" if self.options["strategy"]==1 else "NONE"), verbose) - self.log_message(' Function Tolerances : ' + str(self.options["ftol"]), verbose) - self.log_message(' Step Tolerances : ' + str(self.options["stol"]), verbose) - self.log_message(' Variable Scaling : ' + str(self.options["y_scale"]), verbose) - self.log_message(' Function Scaling : ' + str(self.options["f_scale"]), verbose) - self.log_message('', verbose) - - def _set_ftol_method(self,ftol): - self.options["ftol"] = ftol - - def _get_ftol_method(self): - """ - Specifies the scalar used as a stopping tolerance on the scaled - maximum norm of the residual. - """ - return self.options["ftol"] - - ftol = property(_get_ftol_method,_set_ftol_method) - - def _set_stol_method(self,stol): - self.options["stol"] = stol - - def _get_stol_method(self): - """ - Specifies the scalar used as a stopping tolerance on the - minimum scaled step length. - """ - return self.options["stol"] - - stol = property(_get_stol_method,_set_stol_method) - - def _set_max_iter_method(self, max_iter): - self.options["max_iter"] = max_iter - - def _get_max_iter_method(self): - """ - Specifies the maximum number of nonlinear iterations. - """ - return self.options["max_iter"] - - max_iter = property(_get_max_iter_method,_set_max_iter_method) - - def _set_no_initial_setup_method(self, no_initial_setup): - self.options["no_initial_setup"] = no_initial_setup - - def _get_no_initial_setup_method(self): - """ - Specifies whether or not an initial call to the preconditioner - setup call should be made. - """ - return self.options["no_initial_setup"] - - no_initial_setup = property(_get_no_initial_setup_method,_set_no_initial_setup_method) - - def _set_max_solves_between_setup_calls_method(self, max_solves_between_setup_calls): - self.options["max_solves_between_setup_calls"] = max_solves_between_setup_calls - - def _get_max_solves_between_setup_calls_method(self): - """ - Specifies the maximum number of allowed solve calls inbetween - setup calls to the linear solver. - """ - return self.options["max_solves_between_setup_calls"] - - max_solves_between_setup_calls = property(_get_max_solves_between_setup_calls_method,_set_max_solves_between_setup_calls_method) - - def _set_max_newton_step_method(self, max_newton_step): - self.options["max_newton_step"] = max_newton_step - - def _get_max_newton_step_method(self): - """ - Specifies the maximum allowed scaled length of the Newton step. - """ - return self.options["max_newton_step"] - - max_newton_step = property(_get_max_newton_step_method,_set_max_newton_step_method) - - def _set_no_min_epsilon_method(self, no_min_epsilon): - self.options["no_min_epsilon"] = no_min_epsilon - - def _get_no_min_epsilon_method(self): - """ - Specifies if the scaled linear residual tolerance is bounded - from below - """ - return self.options["no_min_epsilon"] - - no_min_epsilon = property(_get_no_min_epsilon_method,_set_no_min_epsilon_method) - - def _set_max_beta_fails_method(self, max_beta_fails): - self.options["max_beta_fails"] = max_beta_fails - - def _get_max_beta_fails_method(self): - """ - Specifies the maximum number of beta condition fails in the - linesearch algorithm. - """ - return self.options["max_beta_fails"] - - max_beta_fails = property(_get_max_beta_fails_method,_set_max_beta_fails_method) - - def _set_linear_solver(self, lsolver): - if lsolver.upper() == "DENSE" or lsolver.upper() == "SPGMR": - self.options["linear_solver"] = lsolver.upper() - else: - raise Exception('The linear solver must be either "DENSE" or "SPGMR".') - - def _get_linear_solver(self): - """ - Specifies the linear solver to be used. - - Parameters:: - - linearsolver - - Default 'DENSE'. Can also be 'SPGMR'. - """ - return self.options["linear_solver"] - - linear_solver = property(_get_linear_solver, _set_linear_solver) - - def _set_globalization_strategy(self, lsolver): - if lsolver.upper() == "LINESEARCH": - self.options["strategy"] = KIN_LINSEARCH - elif lsolver.upper() == "NONE": - self.options["strategy"] = KIN_NONE - else: - raise Exception('The linear solver must be either "LINESEARCH" or "NONE".') - - def _get_globalization_strategy(self): - """ - Specifies the globalization strategy to be used. - - Parameters:: - - linearsolver - - Default 'LINSEARCH'. Can also be 'NONE'. - """ - return self.options["strategy"] - - globalization_strategy = property(_get_globalization_strategy, _set_globalization_strategy) - - def _set_max_krylov(self, max_krylov): - try: - self.options["max_krylov"] = int(max_krylov) - except: - raise Exception("Maximum number of krylov dimension should be an integer.") - if self.options["max_krylov"] < 0: - raise Exception("Maximum number of krylov dimension should be an positive integer.") - - def _get_max_krylov(self): - """ - Specifies the maximum number of dimensions for the krylov subspace to be used. - - Parameters:: - - maxkrylov - - A positive integer. - - Default 0 - - Returns:: - - The current value of maxkrylov. - - See SUNDIALS documentation 'CVSpgmr' - """ - return self.options["max_krylov"] - - max_dim_krylov_subspace = property(_get_max_krylov, _set_max_krylov) - - def get_residual_norm_nonlinear_iterations(self): - return self.pData.nl_fnorm - - def get_residual_norm_linear_iterations(self): - return self.pData.l_fnorm - - def get_log(self): - return self.pData.log - -class KINSOLError(Exception): - """ - Defines the KINSOLError and provides the textual error message. - """ - msg = { KIN_MEM_FAIL : 'Memory allocation failed.', - KIN_MEM_NULL : 'KINSOL was not properly created.', - KIN_ILL_INPUT: 'There was an input error.', - KIN_NO_MALLOC: 'Memory not allocated, call KINSOL_init(...)', - KIN_LINESEARCH_NONCONV: 'Linesearch could not find a step big enough.', - KIN_MAXITER_REACHED: 'Max number of iterations reached', - KIN_MXNEWT_5X_EXCEEDED: 'Max size of Newton step exceeded 5 times.', - KIN_LINESEARCH_BCFAIL: 'Linesearch beta-test failed, probably because of too poor progress.', - KIN_LINSOLV_NO_RECOVERY: 'psolve encountered an error, but preconditioner is current', - KIN_LINIT_FAIL: 'Init of linear solver failed.', - KIN_LSETUP_FAIL: 'pset (preconditioner setup fct) encountered an error', - KIN_LSOLVE_FAIL: 'Error in either psolve or in linear solver routine.', - KIN_SYSFUNC_FAIL: 'Call to RES failed.', - KIN_FIRST_SYSFUNC_ERR: 'Call to RHS failed on first call', - KIN_REPTD_SYSFUNC_ERR: 'Call to RHS failed multiple times.', - KIN_STEP_LT_STPTOL: 'Scaled step length too small. Either an approximate solution or a local minimum is reached. Check value of residual.'} - - def __init__(self, value): - self.value = value - - def __str__(self): - try: - return repr(self.msg[self.value]) - except KeyError: - return repr('Sundials failed with flag %s.'%(self.value)) - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +cimport numpy as N +from numpy cimport PyArray_DATA + +N.import_array() + +import numpy.linalg +import traceback + +from assimulo.exception import * +from assimulo.algebraic cimport Algebraic + +cimport sundials_includes as SUNDIALS + +#Various C includes transfered to namespace +from sundials_includes cimport N_Vector, realtype, N_VectorContent_Serial, DENSE_COL, sunindextype +from sundials_includes cimport memcpy, N_VNew_Serial, DlsMat, SUNMatrix, SUNMatrixContent_Dense, SUNMatrixContent_Sparse +IF SUNDIALS_VERSION < (5,0,0): + from sundials_includes cimport SlsMat +from sundials_includes cimport malloc, free, N_VCloneVectorArray_Serial +from sundials_includes cimport N_VConst_Serial, N_VDestroy_Serial + +include "constants.pxi" #Includes the constants (textual include) +include "../lib/sundials_constants.pxi" #Sundials related constants +include "../lib/sundials_callbacks.pxi" +include "../lib/sundials_callbacks_kinsol.pxi" + +cdef class KINSOL(Algebraic): + """ + This class provides a connection to the Sundials + (https://computation.llnl.gov/casc/sundials/main.html) solver + KINSOL. + + .. math:: + + 0 = F(y) + + """ + cdef void* kinsol_mem + cdef ProblemDataEquationSolver pData #A struct containing information about the problem + cdef N_Vector y_temp, y_scale, f_scale + cdef public double _eps + + cdef object pt_fcn, pt_jac, pt_jacv, pt_prec_setup, pt_prec_solve + cdef object _added_linear_solver + cdef SUNDIALS.SUNMatrix sun_matrix + cdef SUNDIALS.SUNLinearSolver sun_linearsolver + + def __init__(self, problem): + Algebraic.__init__(self, problem) #Calls the base class + + self.pData = ProblemDataEquationSolver() + + self._eps = N.finfo('double').eps + self._added_linear_solver = False + + #Populate the ProblemData + self.set_problem_data() + + #Solver options + self.options["ftol"] = self._eps**(1.0/3.0) + self.options["stol"] = self._eps**(2.0/3.0) + self.options["strategy"] = KIN_LINESEARCH + self.options["y_scale"] = N.array([1.0]*self.problem_info["dim"]) + self.options["f_scale"] = N.array([1.0]*self.problem_info["dim"]) + #self.options["y_nominal"] = N.array([1.0]*self.problem_info["dim"]) + #self.options["y_min"] = N.array([MIN_VALUE]*self.problem_info["dim"]) + #self.options["y_max"] = N.array([MAX_VALUE]*self.problem_info["dim"]) + self.options["linear_solver"] = "DENSE" + self.options["max_iter"] = 200 #Maximum number of nonlinear iterations + self.options["no_initial_setup"] = False #Specifies wheter or not a call to the setup function should be made + self.options["max_solves_between_setup_calls"] = 10 #Specifies the maximum number of allowed calls to the solve function between setup calls + self.options["max_newton_step"] = 0.0 #Specifies the max allowed newton step + self.options["no_min_epsilon"] = False #Specifies wheter the scaled linear residual is bounded from below + self.options["max_beta_fails"] = 10 + self.options["max_krylov"] = 0 + self.options["precond"] = PREC_NONE + + #Statistics + self.statistics["nfevals"] = 0 #Function evaluations + self.statistics["nniters"] = 0 #Nonlinear iterations + self.statistics["njevals"] = 0 #Jacobian evaluations + self.statistics["nfevalsLS"] = 0 #Function evaluations due to Jac + self.statistics["nbacktr"] = 0 #The function KINGetNumBacktrackOps returns the number of backtrack operations (step length adjustments) performed by the line search algorithm. + self.statistics["nbcfails"] = 0 #The function KINGetNumBetaCondFails returns the number of β-condition failures. + self.statistics["nliters"] = 0 + self.statistics["nlcfails"] = 0 + + #Initialize Kinsol + self.initialize_kinsol() + + def __dealloc__(self): + + if self.y_temp != NULL: + #Deallocate N_Vector + N_VDestroy_Serial(self.y_temp) + + if self.kinsol_mem != NULL: + #Free Memory + SUNDIALS.KINFree(&self.kinsol_mem) + + IF SUNDIALS_VERSION >= (3,0,0): + if self.sun_matrix != NULL: + SUNDIALS.SUNMatDestroy(self.sun_matrix) + + if self.sun_linearsolver != NULL: + SUNDIALS.SUNLinSolFree(self.sun_linearsolver) + + def update_variable_scaling(self, value="Automatic"): + """ + Updates the variable scaling with either + """ + if isinstance(value, str) and value.upper() == "AUTOMATIC": + for i in range(self.problem_info["dim"]): + if self.options["y_nominal"]: + self.options["y_scale"][i] = self.options["y_nominal"][i] + elif self.y[i] != 0.0: + self.options["y_scale"][i] = self.y[i] + elif self.options["y_max"] and self.options["y_min"]: + self.options["y_scale"][i] = max(abs(self.options["y_max"][i]+self.options["y_min"][i])/2.0, 0.01*self.options["y_max"][i]) + elif self.options["y_max"]: + self.options["y_scale"][i] = max(1.0, abs(self.options["y_max"][i])) + elif self.options["y_min"]: + self.options["y_scale"][i] = max(1.0, abs(self.options["y_min"][i])) + else: + self.options["y_scale"] = N.array([value]) if isinstance(value, float) or isinstance(value, int) else N.array(value) + + arr2nv_inplace(self.options["y_scale"], self.y_scale) + + def update_residual_scaling(self, value="Automatic"): + """ + Updates the residual scaling. + """ + if isinstance(value, str) and value.upper() == "AUTOMATIC": + pass + else: + self.options["f_scale"] = N.array([value]) if isinstance(value, float) or isinstance(value, int) else N.array(value) + + arr2nv_inplace(self.options["f_scale"], self.f_scale) + + cdef set_problem_data(self): + + #Sets the f function + self.pt_fcn = self.problem.res + self.pData.RES = self.pt_fcn + self.pData.dim = self.problem_info["dim"] + self.pData.nl_fnorm = [] + self.pData.l_fnorm = [] + self.pData.log = [] + + if self.problem_info["jac_fcn"] is True: #Sets the jacobian + self.pt_jac = self.problem.jac + self.pData.JAC = self.pt_jac + + if self.problem_info["jacv_fcn"] is True: #Sets the jacobian*vector function + self.pt_jacv = self.problem.jacv + self.pData.JACV = self.pt_jacv + + if self.problem_info["prec_solve"] is True: #Sets the preconditioner solve function + self.pt_prec_solve = self.problem.prec_solve + self.pData.PREC_SOLVE = self.pt_prec_solve + + if self.problem_info["prec_setup"] is True: #Sets the preconditioner setup function + self.pt_prec_setup = self.problem.prec_setup + self.pData.PREC_SETUP = self.pt_prec_setup + + cdef initialize_kinsol(self): + cdef int flag #Used for return + + self.y_temp = arr2nv(self.y) + self.y_scale = arr2nv([1.0]*self.problem_info["dim"]) + self.f_scale = arr2nv([1.0]*self.problem_info["dim"]) + + if self.kinsol_mem == NULL: #The solver is not initialized + + #Create the solver + self.kinsol_mem = SUNDIALS.KINCreate() + if self.kinsol_mem == NULL: + raise KINSOLError(KIN_MEM_NULL) + self.pData.KIN_MEM = self.kinsol_mem + + #Specify the residual and the initial conditions to the solver + flag = SUNDIALS.KINInit(self.kinsol_mem, kin_res, self.y_temp) + if flag < 0: + raise KINSOLError(flag) + + #Specify the error handling + flag = SUNDIALS.KINSetErrHandlerFn(self.kinsol_mem, kin_err, self.pData) + if flag < 0: + raise KINSOLError(flag) + + #Specify the handling of info messages + flag = SUNDIALS.KINSetInfoHandlerFn(self.kinsol_mem, kin_info, self.pData); + if flag < 0: + raise KINSOLError(flag) + + else: #The solver needs not to be reinitialized + pass + + #Set the user data + flag = SUNDIALS.KINSetUserData(self.kinsol_mem, self.pData) + if flag < 0: + raise KINSOLError(flag) + + cpdef add_linear_solver(self): + if self.options["linear_solver"] == "DENSE": + IF SUNDIALS_VERSION >= (3,0,0): + #Create a dense Sundials matrix + self.sun_matrix = SUNDIALS.SUNDenseMatrix(self.pData.dim, self.pData.dim) + #Create a dense Sundials linear solver + self.sun_linearsolver = SUNDIALS.SUNDenseLinearSolver(self.y_temp, self.sun_matrix) + #Attach it to Kinsol + flag = SUNDIALS.KINDlsSetLinearSolver(self.kinsol_mem, self.sun_linearsolver, self.sun_matrix) + ELSE: + flag = SUNDIALS.KINDense(self.kinsol_mem, self.problem_info["dim"]) + if flag < 0: + raise KINSOLError(flag) + + if self.problem_info["jac_fcn"]: + IF SUNDIALS_VERSION >= (3,0,0): + flag = SUNDIALS.KINDlsSetJacFn(self.kinsol_mem, kin_jac); + ELSE: + flag = SUNDIALS.KINDlsSetDenseJacFn(self.kinsol_mem, kin_jac); + if flag < 0: + raise KINSOLError(flag) + elif self.options["linear_solver"] == "SPGMR": + IF SUNDIALS_VERSION >= (3,0,0): + #Create the linear solver + self.sun_linearsolver = SUNDIALS.SUNSPGMR(self.y_temp, self.options["precond"], self.options["max_krylov"]) + #Attach it to Kinsol + flag = SUNDIALS.KINSpilsSetLinearSolver(self.kinsol_mem, self.sun_linearsolver) + ELSE: + #Specify the use of KINSpgmr linear solver. + flag = SUNDIALS.KINSpgmr(self.kinsol_mem, self.options["max_krylov"]) + if flag < 0: + raise KINSOLError(flag) + + if self.problem_info["jacv_fcn"]: + flag = SUNDIALS.KINSpilsSetJacTimesVecFn(self.kinsol_mem, kin_jacv) + if flag < 0: + raise KINSOLError(flag) + + if self.problem_info["prec_setup"] or self.problem_info["prec_solve"]: + if not self.problem_info["prec_setup"]: + flag = SUNDIALS.KINSpilsSetPreconditioner(self.kinsol_mem, NULL,kin_prec_solve) + if flag < 0: + raise KINSOLError(flag) + elif not self.problem_info["prec_solve"]: + flag = SUNDIALS.KINSpilsSetPreconditioner(self.kinsol_mem, kin_prec_setup, NULL) + if flag < 0: + raise KINSOLError(flag) + else: + flag = SUNDIALS.KINSpilsSetPreconditioner(self.kinsol_mem, kin_prec_setup, kin_prec_solve) + if flag < 0: + raise KINSOLError(flag) + + + else: + raise KINSOLError(-100) + + self._added_linear_solver = True + + cpdef update_options(self): + """ + Updates the simulation options. + """ + cdef int flag + + #Update scaling + arr2nv_inplace(self.options["y_scale"], self.y_scale) + arr2nv_inplace(self.options["f_scale"], self.f_scale) + + flag = SUNDIALS.KINSetFuncNormTol(self.kinsol_mem, self.options["ftol"]); + if flag < 0: + raise KINSOLError(flag) + + flag = SUNDIALS.KINSetScaledStepTol(self.kinsol_mem, self.options["stol"]); + if flag < 0: + raise KINSOLError(flag) + + flag = SUNDIALS.KINSetMaxBetaFails(self.kinsol_mem, self.options["max_beta_fails"]); #The function KINSetMaxBetaFails specifies the maximum number of β-condition failures in the linesearch algorithm. + if flag < 0: + raise KINSOLError(flag) + + flag = SUNDIALS.KINSetMaxNewtonStep(self.kinsol_mem, self.options["max_newton_step"]); #The function KINSetMaxNewtonStep specifies the maximum allowable scaled length of the Newton step. + if flag < 0: + raise KINSOLError(flag) + + flag = SUNDIALS.KINSetNoMinEps(self.kinsol_mem, self.options["no_min_epsilon"]); #The function KINSetNoMinEps specifies a flag that controls whether or not the value of ǫ, the scaled linear residual tolerance, is bounded from below. + if flag < 0: + raise KINSOLError(flag) + + #flag = SUNDIALS.KINSetEtaParams(self.kinsol_mem, egamma, ealpha); #The function KINSetEtaParams specifies the parameters γ and α in the formula for η, in the case etachoice = KIN ETACHOICE2. + #if flag < 0: + # raise KINSOLError(flag) + + flag = SUNDIALS.KINSetMaxSetupCalls(self.kinsol_mem, self.options["max_solves_between_setup_calls"]); #The function KINSetMaxSetupCalls specifies the maximum number of nonlinear iterations that can be performed between calls to the preconditioner setup function. + if flag < 0: + raise KINSOLError(flag) + + flag = SUNDIALS.KINSetNoInitSetup(self.kinsol_mem, self.options["no_initial_setup"]); #The function KINSetNoInitSetup specifies whether an initial call to the preconditioner setup function should be made or not. + if flag < 0: + raise KINSOLError(flag) + + flag = SUNDIALS.KINSetNumMaxIters(self.kinsol_mem, self.options["max_iter"]); #The function KINSetNumMaxIters specifies the maximum number of nonlinear iterations allowed. + if flag < 0: + raise KINSOLError(flag) + + flag = SUNDIALS.KINSetPrintLevel(self.kinsol_mem, self._get_print_level()); #The function KINSetPrintLevel specifies the level of verbosity of the output. + if flag < 0: + raise KINSOLError(flag) + + def _get_print_level(self): + """ + Converts Assimulos verbosity level to Kinsol print level. Kinsol + print level:: + + 0 no information displayed. + + 1 for each nonlinear iteration display the following information: the scaled + Euclidean ℓ2 norm of the system function evaluated at the current iterate, + the scaled norm of the Newton step (only if using KIN NONE), and the + number of function evaluations performed so far. + + 2 display level 1 output and the following values for each iteration: + kF(u)kDF (only for KIN NONE). + kF(u)kDF ,∞ (for KIN NONE and KIN LINESEARCH). + + 3 display level 2 output plus additional values used by the global strategy + (only if using KIN LINESEARCH), and statistical information for the linear + solver. + """ + if self.verbosity >= NORMAL: + return 0 + elif self.verbosity >= LOUD: + return 2 + else: + return 3 + + cpdef _solve(self, y0=None): + """ + Solves the system. + """ + if y0 is not None: + arr2nv_inplace(y0, self.y_temp) + else: + arr2nv_inplace(self.y, self.y_temp) + + if not self._added_linear_solver: + self.add_linear_solver() + + #Update the solver options + self.update_options() + + flag = SUNDIALS.KINSol(self.kinsol_mem, self.y_temp, self.options["strategy"], self.y_scale, self.f_scale) + self.y = nv2arr(self.y_temp) + + if flag < 0: + raise KINSOLError(flag) + if flag == KIN_STEP_LT_STPTOL: + print 'Scaled step length too small. Either an approximate solution or a local minimum is reached. Check value of residual.' + + self.store_statistics() + + return nv2arr(self.y_temp) + + def get_last_flag(self): + """ + Returns the last flag reported by Kinsol. + """ + cdef int flag = 0, + cdef long int lsflag = 0 + + flag = SUNDIALS.KINDlsGetLastFlag(self.kinsol_mem, &lsflag) + if flag < 0: + raise KINSOLError(flag) + + return lsflag + + def store_statistics(self): + cdef int flag + cdef long int nfevalsLS, njevals, nbacktr, nbcfails, nniters + cdef long int nliters, nlcfails, npevals, npsolves + cdef long int nfevals + + flag = SUNDIALS.KINGetNumFuncEvals(self.kinsol_mem, &nfevals) + if flag < 0: + raise KINSOLError(flag) + self.statistics["nfevals"] = nfevals + + flag = SUNDIALS.KINGetNumNonlinSolvIters(self.kinsol_mem, &nniters) + if flag < 0: + raise KINSOLError(flag) + self.statistics["nniters"] = nniters + + flag = SUNDIALS.KINGetNumBacktrackOps(self.kinsol_mem, &nbacktr) #The function KINGetNumBacktrackOps returns the number of backtrack operations (step length adjustments) performed by the line search algorithm. + if flag < 0: + raise KINSOLError(flag) + self.statistics["nbacktr"] = nbacktr + + flag = SUNDIALS.KINGetNumBetaCondFails(self.kinsol_mem, &nbcfails) #The function KINGetNumBetaCondFails returns the number of β-condition failures. + if flag < 0: + raise KINSOLError(flag) + self.statistics["nbcfails"] = nbcfails + + if self.options["linear_solver"] == "SPGMR": + + flag = SUNDIALS.KINSpilsGetNumLinIters(self.kinsol_mem, &nliters) + if flag < 0: + raise KINSOLError(flag) + self.statistics["nliters"] = nliters + + flag = SUNDIALS.KINSpilsGetNumConvFails(self.kinsol_mem, &nlcfails) + if flag < 0: + raise KINSOLError(flag) + self.statistics["nlcfails"] = nlcfails + + flag = SUNDIALS.KINSpilsGetNumPrecEvals(self.kinsol_mem, &npevals) + if flag < 0: + raise KINSOLError(flag) + self.statistics["npevals"] = npevals + + flag = SUNDIALS.KINSpilsGetNumPrecSolves(self.kinsol_mem, &npsolves) + if flag < 0: + raise KINSOLError(flag) + self.statistics["npsolves"] = npsolves + + flag = SUNDIALS.KINSpilsGetNumJtimesEvals(self.kinsol_mem, &njevals) + if flag < 0: + raise KINSOLError(flag) + self.statistics["njevals"] = njevals + + flag = SUNDIALS.KINSpilsGetNumFuncEvals(self.kinsol_mem, &nfevalsLS) + if flag < 0: + raise KINSOLError(flag) + self.statistics["nfevalsLS"] = nfevalsLS + + elif self.options["linear_solver"] == "DENSE": + + flag = SUNDIALS.KINDlsGetNumJacEvals(self.kinsol_mem, &njevals) #The function KINDlsGetNumJacEvals returns the number of calls to the dense Jacobian approximation function. + if flag < 0: + raise KINSOLError(flag) + self.statistics["njevals"] = njevals + + flag = SUNDIALS.KINDlsGetNumFuncEvals(self.kinsol_mem, &nfevalsLS) #The function KINDlsGetNumFuncEvals returns the number of calls to the user system function used to compute the difference quotient approximation to the dense or banded Jacobian. + if flag < 0: + raise KINSOLError(flag) + self.statistics["nfevalsLS"] = nfevalsLS + + + def print_statistics(self, verbose=NORMAL): + """ + Should print the statistics. + """ + self.log_message('Final Run Statistics: %s \n' % self.problem.name, verbose) + + self.log_message(' Number of function evaluations : '+ str(self.statistics["nfevals"]), verbose) + self.log_message(' Number of Nonlinear Iterations : '+ str(self.statistics["nniters"]), verbose) + self.log_message(' Number of Backtrack Operations (Linesearch) : '+ str(self.statistics["nbacktr"]), verbose) #The function KINGetNumBacktrackOps returns the number of backtrack operations (step length adjustments) performed by the line search algorithm. + self.log_message(' Number of Beta-condition Failures : '+ str(self.statistics["nbcfails"]), verbose) #The function KINGetNumBetaCondFails returns the number of β-condition failures. + + if self.options["linear_solver"] == "SPGMR": + self.log_message(' Number of Jacobian*Vector Evaluations : '+ str(self.statistics["njevals"]), verbose) + self.log_message(' Number of F-Eval During Jac*Vec-Eval : '+ str(self.statistics["nfevalsLS"]), verbose) + self.log_message(' Number of Linear Iterations : '+ str(self.statistics["nliters"]), verbose) + self.log_message(' Number of Linear Convergence Failures : '+ str(self.statistics["nlcfails"]), verbose) + elif self.options["linear_solver"] == "DENSE": + self.log_message(' Number of Jacobian evaluations : '+ str(self.statistics["njevals"]), verbose) + self.log_message(' Number of F-eval during Jac-eval : '+ str(self.statistics["nfevalsLS"]), verbose) + + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : Kinsol', verbose) + self.log_message(' Linear Solver : ' + str(self.options["linear_solver"]), verbose) + self.log_message(' Globalization Strategy : ' + ("LINESEARCH" if self.options["strategy"]==1 else "NONE"), verbose) + self.log_message(' Function Tolerances : ' + str(self.options["ftol"]), verbose) + self.log_message(' Step Tolerances : ' + str(self.options["stol"]), verbose) + self.log_message(' Variable Scaling : ' + str(self.options["y_scale"]), verbose) + self.log_message(' Function Scaling : ' + str(self.options["f_scale"]), verbose) + self.log_message('', verbose) + + def _set_ftol_method(self,ftol): + self.options["ftol"] = ftol + + def _get_ftol_method(self): + """ + Specifies the scalar used as a stopping tolerance on the scaled + maximum norm of the residual. + """ + return self.options["ftol"] + + ftol = property(_get_ftol_method,_set_ftol_method) + + def _set_stol_method(self,stol): + self.options["stol"] = stol + + def _get_stol_method(self): + """ + Specifies the scalar used as a stopping tolerance on the + minimum scaled step length. + """ + return self.options["stol"] + + stol = property(_get_stol_method,_set_stol_method) + + def _set_max_iter_method(self, max_iter): + self.options["max_iter"] = max_iter + + def _get_max_iter_method(self): + """ + Specifies the maximum number of nonlinear iterations. + """ + return self.options["max_iter"] + + max_iter = property(_get_max_iter_method,_set_max_iter_method) + + def _set_no_initial_setup_method(self, no_initial_setup): + self.options["no_initial_setup"] = no_initial_setup + + def _get_no_initial_setup_method(self): + """ + Specifies whether or not an initial call to the preconditioner + setup call should be made. + """ + return self.options["no_initial_setup"] + + no_initial_setup = property(_get_no_initial_setup_method,_set_no_initial_setup_method) + + def _set_max_solves_between_setup_calls_method(self, max_solves_between_setup_calls): + self.options["max_solves_between_setup_calls"] = max_solves_between_setup_calls + + def _get_max_solves_between_setup_calls_method(self): + """ + Specifies the maximum number of allowed solve calls inbetween + setup calls to the linear solver. + """ + return self.options["max_solves_between_setup_calls"] + + max_solves_between_setup_calls = property(_get_max_solves_between_setup_calls_method,_set_max_solves_between_setup_calls_method) + + def _set_max_newton_step_method(self, max_newton_step): + self.options["max_newton_step"] = max_newton_step + + def _get_max_newton_step_method(self): + """ + Specifies the maximum allowed scaled length of the Newton step. + """ + return self.options["max_newton_step"] + + max_newton_step = property(_get_max_newton_step_method,_set_max_newton_step_method) + + def _set_no_min_epsilon_method(self, no_min_epsilon): + self.options["no_min_epsilon"] = no_min_epsilon + + def _get_no_min_epsilon_method(self): + """ + Specifies if the scaled linear residual tolerance is bounded + from below + """ + return self.options["no_min_epsilon"] + + no_min_epsilon = property(_get_no_min_epsilon_method,_set_no_min_epsilon_method) + + def _set_max_beta_fails_method(self, max_beta_fails): + self.options["max_beta_fails"] = max_beta_fails + + def _get_max_beta_fails_method(self): + """ + Specifies the maximum number of beta condition fails in the + linesearch algorithm. + """ + return self.options["max_beta_fails"] + + max_beta_fails = property(_get_max_beta_fails_method,_set_max_beta_fails_method) + + def _set_linear_solver(self, lsolver): + if lsolver.upper() == "DENSE" or lsolver.upper() == "SPGMR": + self.options["linear_solver"] = lsolver.upper() + else: + raise Exception('The linear solver must be either "DENSE" or "SPGMR".') + + def _get_linear_solver(self): + """ + Specifies the linear solver to be used. + + Parameters:: + + linearsolver + - Default 'DENSE'. Can also be 'SPGMR'. + """ + return self.options["linear_solver"] + + linear_solver = property(_get_linear_solver, _set_linear_solver) + + def _set_globalization_strategy(self, lsolver): + if lsolver.upper() == "LINESEARCH": + self.options["strategy"] = KIN_LINSEARCH + elif lsolver.upper() == "NONE": + self.options["strategy"] = KIN_NONE + else: + raise Exception('The linear solver must be either "LINESEARCH" or "NONE".') + + def _get_globalization_strategy(self): + """ + Specifies the globalization strategy to be used. + + Parameters:: + + linearsolver + - Default 'LINSEARCH'. Can also be 'NONE'. + """ + return self.options["strategy"] + + globalization_strategy = property(_get_globalization_strategy, _set_globalization_strategy) + + def _set_max_krylov(self, max_krylov): + try: + self.options["max_krylov"] = int(max_krylov) + except: + raise Exception("Maximum number of krylov dimension should be an integer.") + if self.options["max_krylov"] < 0: + raise Exception("Maximum number of krylov dimension should be an positive integer.") + + def _get_max_krylov(self): + """ + Specifies the maximum number of dimensions for the krylov subspace to be used. + + Parameters:: + + maxkrylov + - A positive integer. + - Default 0 + + Returns:: + + The current value of maxkrylov. + + See SUNDIALS documentation 'CVSpgmr' + """ + return self.options["max_krylov"] + + max_dim_krylov_subspace = property(_get_max_krylov, _set_max_krylov) + + def get_residual_norm_nonlinear_iterations(self): + return self.pData.nl_fnorm + + def get_residual_norm_linear_iterations(self): + return self.pData.l_fnorm + + def get_log(self): + return self.pData.log + +class KINSOLError(Exception): + """ + Defines the KINSOLError and provides the textual error message. + """ + msg = { KIN_MEM_FAIL : 'Memory allocation failed.', + KIN_MEM_NULL : 'KINSOL was not properly created.', + KIN_ILL_INPUT: 'There was an input error.', + KIN_NO_MALLOC: 'Memory not allocated, call KINSOL_init(...)', + KIN_LINESEARCH_NONCONV: 'Linesearch could not find a step big enough.', + KIN_MAXITER_REACHED: 'Max number of iterations reached', + KIN_MXNEWT_5X_EXCEEDED: 'Max size of Newton step exceeded 5 times.', + KIN_LINESEARCH_BCFAIL: 'Linesearch beta-test failed, probably because of too poor progress.', + KIN_LINSOLV_NO_RECOVERY: 'psolve encountered an error, but preconditioner is current', + KIN_LINIT_FAIL: 'Init of linear solver failed.', + KIN_LSETUP_FAIL: 'pset (preconditioner setup fct) encountered an error', + KIN_LSOLVE_FAIL: 'Error in either psolve or in linear solver routine.', + KIN_SYSFUNC_FAIL: 'Call to RES failed.', + KIN_FIRST_SYSFUNC_ERR: 'Call to RHS failed on first call', + KIN_REPTD_SYSFUNC_ERR: 'Call to RHS failed multiple times.', + KIN_STEP_LT_STPTOL: 'Scaled step length too small. Either an approximate solution or a local minimum is reached. Check value of residual.'} + + def __init__(self, value): + self.value = value + + def __str__(self): + try: + return repr(self.msg[self.value]) + except KeyError: + return repr('Sundials failed with flag %s.'%(self.value)) + diff --git a/src/solvers/odassl.py b/src/solvers/odassl.py index e164e491..99b3182e 100644 --- a/src/solvers/odassl.py +++ b/src/solvers/odassl.py @@ -1,372 +1,372 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as np - -from assimulo.ode import * -from assimulo.support import set_type_shape_array -from assimulo.implicit_ode import OverdeterminedDAE - -from assimulo.exception import * - -from assimulo.lib import odassl - -realtype = float - -class ODASSL_Exception(Exception): - pass - -class ODASSL_Common(object): - - def _set_atol(self,atol): - - self.options["atol"] = set_type_shape_array(atol) - if len(self.options["atol"]) == 1: - self.options["atol"] = self.options["atol"]*np.ones(self._leny) - elif len(self.options["atol"]) != self._leny: - raise ODASSL_Exception("atol must be of length one or same as the dimension of the problem.") - - def _get_atol(self): - """ - Defines the absolute tolerance(s) that is to be used by the solver. - Can be set differently for each variable. - - Parameters:: - - atol - - Default '1.0e-6'. - - - Should be a positive float or a numpy vector - of floats. - - Example: - atol = [1.0e-4, 1.0e-6] - """ - return self.options["atol"] - - atol=property(_get_atol,_set_atol) - - def _set_rtol(self,rtol): - self.options["rtol"] = set_type_shape_array(rtol) - if len(self.options["rtol"]) == 1: - self.options["rtol"] = self.options["rtol"]*np.ones(self._leny) - elif len(self.options["rtol"]) != self._leny: - raise ODASSL_Exception("rtol must be of length one or same as the dimension of the problem.") - - def _get_rtol(self): - """ - Defines the relative tolerance that is to be used by the solver. - - Parameters:: - - rtol - - Default '1.0e-6'. - - - Should be a positive float. - - Example: - rtol = 1.0e-4 - """ - return self.options["rtol"] - - rtol=property(_get_rtol,_set_rtol) - - - - def _set_initial_step(self, initstep): - try: - self.options["inith"] = float(initstep) - except (ValueError, TypeError): - raise ODASSL_Exception('The initial step size must be an integer or float.') - - def _get_initial_step(self): - """ - This determines the initial step-size to be used in the integration. - - Parameters:: - - inith - - Default '0.01'. - - - Should be float. - - Example: - inith = 0.001 - - The quantity should be always positive. It will internally be multiplied - by the sign(tout-t0) to account for the direction of integration. - """ - return self.options["inith"] - - inith = property(_get_initial_step,_set_initial_step) - - def _set_max_h(self,max_h): - try: - self.options["maxh"] = float(max_h) - except (ValueError,TypeError): - raise ODASSL_Exception('Maximal stepsize must be a (scalar) float.') - if self.options["maxh"] < 0: - raise ODASSL_Exception('Maximal stepsize must be a positiv (scalar) float.') - - def _get_max_h(self): - """ - Defines the maximal step-size that is to be used by the solver. - - Parameters:: - - maxh - - Default: maxh=0.0 or None ignores this option - - - Should be a float. - - Example: - maxh = 0.01 - - """ - return self.options["maxh"] - - maxh=property(_get_max_h,_set_max_h) - - def _set_maxord(self,maxord): - try: - self.options["maxord"] = int(maxord) - except (ValueError,TypeError): - raise ODASSL_Exception('Maximal order must be an integer.') - if 0 < self.maxord < 6 : - raise ODASSL_Exception('Maximal order must be a positive integer less than 6.') - - def _get_maxord(self): - """ - Defines the maximal order that is to be used by the solver. - - Parameters:: - - maxord - - Default: maxord=0 or None ignores this option - - - Should be a float. - - Example: - maxord = 4 - - """ - return self.options["maxord"] - - maxord=property(_get_maxord,_set_maxord) - - def _set_usejac(self, jac): - self.options["usejac"] = bool(jac) - - def _get_usejac(self): - """ - This sets the option to use the user defined Jacobian. If a - user provided jacobian is implemented into the problem the - default setting is to use that Jacobian. If not, an - approximation is used. - - Parameters:: - - usejac - - True - use user defined Jacobian - False - use an approximation - - Default: False - - - Should be a Boolean. - - Example: - usejac = False - """ - return self.options["usejac"] - - usejac = property(_get_usejac,_set_usejac) - - -class ODASSL(ODASSL_Common, OverdeterminedDAE): - """ - Modified version of DASSL for solving overdetermined systems - of (singularily) implicit ODEs. The main difference to DASSL - is in the corrector iteration part. :: - - ODASSL ad-ons : FUEHRER, CLAUS - DEUTSCHE FORSCHUNGSANSTALT - FUER LUFT- UND RAUMFAHRT (DLR) - INST. DYNAMIC DER FLUGSYSTEME - D-8031 WESSLING (F.R.G) - - Based on DASSL version dated to 900103 by:: - - DASSL-Author: PETZOLD, LINDA - APPLIED MATHEMATICS DIVISION 8331 - SANDIA NATIONAL LABORATORIES - LIVERMORE, CA. 94550 - - """ - - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Explicit_Problem' class. - """ - OverdeterminedDAE.__init__(self, problem) #Calls the base class - - #Default values - self.options["inith"] = 0.0 - self.options["maxh"] = 0.0 - self.options["safe"] = 0.9 #Safety factor - self.options["atol"] = 1.0e-6*np.ones(self.problem_info["dim"]) #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - self.options["usejac"] = True if self.problem_info["jac_fcn"] else False - self.options["maxsteps"] = 5000 - self.options["maxord"] = 0 - - #Solver support - self.supports["report_continuously"] = True - self.supports["interpolated_output"] = False - self.supports["state_events"] = False - - #Internal - self._leny = len(self.y) #Dimension of the problem - - def initialize(self): - #Reset statistics - self.statistics.reset() - - def integrate(self, t, y, yprime, tf, opts): - ny = self.problem_info["dim"] - - neq = len(set_type_shape_array(self.problem.res(t,y,yprime))) - #neq = self.problem_info["neq"] - lrw = 40+8*ny + neq**2 + 3*neq - rwork = np.zeros((lrw,)) - liw = 22+neq - iwork = np.zeros((liw,),np.int) - jac_dummy = lambda t,x,xp: x - info = np.zeros((15,),np.int) - info[1] = 1 # Tolerances are vectors - info[2] = normal_mode = 1 if opts["output_list"] is None or opts["report_continuously"] else 0 # intermediate output mode - info[6] = 1 if self.options["maxh"] > 0.0 else 0 - rwork[1] = self.options["maxh"] - info[7] = 1 if self.options["inith"] > 0.0 else 0 - rwork[2] = self.options["inith"] - info[8] = 1 if self.options["maxord"] > 0 else 0 - iwork[2] = self.options["maxord"] - #info[11] will be set later (see Ticket #230) - #iwork[0] = ML - #iwork[1] = MU - - atol = self.options["atol"] - rtol = set_type_shape_array(self.options["rtol"]) - - if len(rtol) == 1: - rtol = rtol*np.ones(self._leny) - - if hasattr(self.problem, "algvar"): - for i in range(ny): - if self.problem.algvar[i] == 0: - rtol[i]=1.e7 - atol[i]=1.e7 - tlist=[] - ylist=[] - ydlist=[] - - - #Store the opts - self._opts = opts - - - #THIS IS NOT SUPPOSE TO BE NECCESSARY, BUT DUE TO A PROBLEM - #REPORTED IN TICKET:244 THIS IS HOWEVER NECCESSARY AS A - #WORKAROUND FOR NOW... - def py_residual(t,y,yd): - return self.problem.res(t,y,yd) - callback_residual = py_residual - #---- - if opts["report_continuously"]: - idid = 1 - while idid==1: - t,y,yprime,tf,info,idid,rwork,iwork = \ - odassl.odassl(callback_residual,neq,ny,t,y,yprime, - tf,info,rtol,atol,rwork,iwork,jac_dummy) - - initialize_flag = self.report_solution(t, y, yprime, opts) - if initialize_flag: - flag = ID_PY_EVENT - break - if idid==2 or idid==3: - flag=ID_PY_COMPLETE - elif idid < 0: - raise ODASSL_Exception("ODASSL failed with flag IDID {IDID}".format(IDID=idid)) - else: - if normal_mode == 1: # intermediate output mode - idid = 1 - while idid==1: - t,y,yprime,tf,info,idid,rwork,iwork = \ - odassl.odassl(callback_residual,neq,ny,t,y,yprime, - tf,info,rtol,atol,rwork,iwork,jac_dummy) - - tlist.append(t) - ylist.append(y.copy()) - ydlist.append(yprime.copy()) - if idid==2 or idid==3: - flag=ID_PY_COMPLETE - elif idid < 0: - raise ODASSL_Exception("ODASSL failed with flag IDID {IDID}".format(IDID=idid)) - - else: # mode with output_list - output_list = opts["output_list"] - for tout in output_list: - t,y,yprime,tout,info,idid,rwork,iwork = \ - odassl.odassl(callback_residual,neq,ny,t,y,yprime, \ - tout,info,rtol,atol,rwork,iwork,jac_dummy) - tlist.append(t) - ylist.append(y.copy()) - ydlist.append(yprime.copy()) - if idid > 0 and t >= tf: - flag=ID_PY_COMPLETE - elif idid < 0: - raise ODASSL_Exception("ODASSL failed with flag IDID {IDID}".format(IDID=idid)) - - - - #Retrieving statistics - self.statistics["nsteps"] += iwork[10] - self.statistics["nfcns"] += iwork[11] - self.statistics["njacs"] += iwork[12] - self.statistics["nerrfails"] += iwork[13] - self.statistics["nnfails"] += iwork[14] - - return flag, tlist, ylist, ydlist - - def print_statistics(self, verbose=NORMAL): - """ - Prints the run-time statistics for the problem. - """ - OverdeterminedDAE.print_statistics(self, verbose) #Calls the base class - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : ODASSL ', verbose) - self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) - self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) - self.log_message('', verbose) - - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as np + +from assimulo.ode import * +from assimulo.support import set_type_shape_array +from assimulo.implicit_ode import OverdeterminedDAE + +from assimulo.exception import * + +from assimulo.lib import odassl + +realtype = float + +class ODASSL_Exception(Exception): + pass + +class ODASSL_Common(object): + + def _set_atol(self,atol): + + self.options["atol"] = set_type_shape_array(atol) + if len(self.options["atol"]) == 1: + self.options["atol"] = self.options["atol"]*np.ones(self._leny) + elif len(self.options["atol"]) != self._leny: + raise ODASSL_Exception("atol must be of length one or same as the dimension of the problem.") + + def _get_atol(self): + """ + Defines the absolute tolerance(s) that is to be used by the solver. + Can be set differently for each variable. + + Parameters:: + + atol + - Default '1.0e-6'. + + - Should be a positive float or a numpy vector + of floats. + + Example: + atol = [1.0e-4, 1.0e-6] + """ + return self.options["atol"] + + atol=property(_get_atol,_set_atol) + + def _set_rtol(self,rtol): + self.options["rtol"] = set_type_shape_array(rtol) + if len(self.options["rtol"]) == 1: + self.options["rtol"] = self.options["rtol"]*np.ones(self._leny) + elif len(self.options["rtol"]) != self._leny: + raise ODASSL_Exception("rtol must be of length one or same as the dimension of the problem.") + + def _get_rtol(self): + """ + Defines the relative tolerance that is to be used by the solver. + + Parameters:: + + rtol + - Default '1.0e-6'. + + - Should be a positive float. + + Example: + rtol = 1.0e-4 + """ + return self.options["rtol"] + + rtol=property(_get_rtol,_set_rtol) + + + + def _set_initial_step(self, initstep): + try: + self.options["inith"] = float(initstep) + except (ValueError, TypeError): + raise ODASSL_Exception('The initial step size must be an integer or float.') + + def _get_initial_step(self): + """ + This determines the initial step-size to be used in the integration. + + Parameters:: + + inith + - Default '0.01'. + + - Should be float. + + Example: + inith = 0.001 + + The quantity should be always positive. It will internally be multiplied + by the sign(tout-t0) to account for the direction of integration. + """ + return self.options["inith"] + + inith = property(_get_initial_step,_set_initial_step) + + def _set_max_h(self,max_h): + try: + self.options["maxh"] = float(max_h) + except (ValueError,TypeError): + raise ODASSL_Exception('Maximal stepsize must be a (scalar) float.') + if self.options["maxh"] < 0: + raise ODASSL_Exception('Maximal stepsize must be a positiv (scalar) float.') + + def _get_max_h(self): + """ + Defines the maximal step-size that is to be used by the solver. + + Parameters:: + + maxh + - Default: maxh=0.0 or None ignores this option + + - Should be a float. + + Example: + maxh = 0.01 + + """ + return self.options["maxh"] + + maxh=property(_get_max_h,_set_max_h) + + def _set_maxord(self,maxord): + try: + self.options["maxord"] = int(maxord) + except (ValueError,TypeError): + raise ODASSL_Exception('Maximal order must be an integer.') + if 0 < self.maxord < 6 : + raise ODASSL_Exception('Maximal order must be a positive integer less than 6.') + + def _get_maxord(self): + """ + Defines the maximal order that is to be used by the solver. + + Parameters:: + + maxord + - Default: maxord=0 or None ignores this option + + - Should be a float. + + Example: + maxord = 4 + + """ + return self.options["maxord"] + + maxord=property(_get_maxord,_set_maxord) + + def _set_usejac(self, jac): + self.options["usejac"] = bool(jac) + + def _get_usejac(self): + """ + This sets the option to use the user defined Jacobian. If a + user provided jacobian is implemented into the problem the + default setting is to use that Jacobian. If not, an + approximation is used. + + Parameters:: + + usejac + - True - use user defined Jacobian + False - use an approximation + + Default: False + + - Should be a Boolean. + + Example: + usejac = False + """ + return self.options["usejac"] + + usejac = property(_get_usejac,_set_usejac) + + +class ODASSL(ODASSL_Common, OverdeterminedDAE): + """ + Modified version of DASSL for solving overdetermined systems + of (singularily) implicit ODEs. The main difference to DASSL + is in the corrector iteration part. :: + + ODASSL ad-ons : FUEHRER, CLAUS + DEUTSCHE FORSCHUNGSANSTALT + FUER LUFT- UND RAUMFAHRT (DLR) + INST. DYNAMIC DER FLUGSYSTEME + D-8031 WESSLING (F.R.G) + + Based on DASSL version dated to 900103 by:: + + DASSL-Author: PETZOLD, LINDA + APPLIED MATHEMATICS DIVISION 8331 + SANDIA NATIONAL LABORATORIES + LIVERMORE, CA. 94550 + + """ + + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Explicit_Problem' class. + """ + OverdeterminedDAE.__init__(self, problem) #Calls the base class + + #Default values + self.options["inith"] = 0.0 + self.options["maxh"] = 0.0 + self.options["safe"] = 0.9 #Safety factor + self.options["atol"] = 1.0e-6*np.ones(self.problem_info["dim"]) #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + self.options["usejac"] = True if self.problem_info["jac_fcn"] else False + self.options["maxsteps"] = 5000 + self.options["maxord"] = 0 + + #Solver support + self.supports["report_continuously"] = True + self.supports["interpolated_output"] = False + self.supports["state_events"] = False + + #Internal + self._leny = len(self.y) #Dimension of the problem + + def initialize(self): + #Reset statistics + self.statistics.reset() + + def integrate(self, t, y, yprime, tf, opts): + ny = self.problem_info["dim"] + + neq = len(set_type_shape_array(self.problem.res(t,y,yprime))) + #neq = self.problem_info["neq"] + lrw = 40+8*ny + neq**2 + 3*neq + rwork = np.zeros((lrw,)) + liw = 22+neq + iwork = np.zeros((liw,),np.int) + jac_dummy = lambda t,x,xp: x + info = np.zeros((15,),np.int) + info[1] = 1 # Tolerances are vectors + info[2] = normal_mode = 1 if opts["output_list"] is None or opts["report_continuously"] else 0 # intermediate output mode + info[6] = 1 if self.options["maxh"] > 0.0 else 0 + rwork[1] = self.options["maxh"] + info[7] = 1 if self.options["inith"] > 0.0 else 0 + rwork[2] = self.options["inith"] + info[8] = 1 if self.options["maxord"] > 0 else 0 + iwork[2] = self.options["maxord"] + #info[11] will be set later (see Ticket #230) + #iwork[0] = ML + #iwork[1] = MU + + atol = self.options["atol"] + rtol = set_type_shape_array(self.options["rtol"]) + + if len(rtol) == 1: + rtol = rtol*np.ones(self._leny) + + if hasattr(self.problem, "algvar"): + for i in range(ny): + if self.problem.algvar[i] == 0: + rtol[i]=1.e7 + atol[i]=1.e7 + tlist=[] + ylist=[] + ydlist=[] + + + #Store the opts + self._opts = opts + + + #THIS IS NOT SUPPOSE TO BE NECCESSARY, BUT DUE TO A PROBLEM + #REPORTED IN TICKET:244 THIS IS HOWEVER NECCESSARY AS A + #WORKAROUND FOR NOW... + def py_residual(t,y,yd): + return self.problem.res(t,y,yd) + callback_residual = py_residual + #---- + if opts["report_continuously"]: + idid = 1 + while idid==1: + t,y,yprime,tf,info,idid,rwork,iwork = \ + odassl.odassl(callback_residual,neq,ny,t,y,yprime, + tf,info,rtol,atol,rwork,iwork,jac_dummy) + + initialize_flag = self.report_solution(t, y, yprime, opts) + if initialize_flag: + flag = ID_PY_EVENT + break + if idid==2 or idid==3: + flag=ID_PY_COMPLETE + elif idid < 0: + raise ODASSL_Exception("ODASSL failed with flag IDID {IDID}".format(IDID=idid)) + else: + if normal_mode == 1: # intermediate output mode + idid = 1 + while idid==1: + t,y,yprime,tf,info,idid,rwork,iwork = \ + odassl.odassl(callback_residual,neq,ny,t,y,yprime, + tf,info,rtol,atol,rwork,iwork,jac_dummy) + + tlist.append(t) + ylist.append(y.copy()) + ydlist.append(yprime.copy()) + if idid==2 or idid==3: + flag=ID_PY_COMPLETE + elif idid < 0: + raise ODASSL_Exception("ODASSL failed with flag IDID {IDID}".format(IDID=idid)) + + else: # mode with output_list + output_list = opts["output_list"] + for tout in output_list: + t,y,yprime,tout,info,idid,rwork,iwork = \ + odassl.odassl(callback_residual,neq,ny,t,y,yprime, \ + tout,info,rtol,atol,rwork,iwork,jac_dummy) + tlist.append(t) + ylist.append(y.copy()) + ydlist.append(yprime.copy()) + if idid > 0 and t >= tf: + flag=ID_PY_COMPLETE + elif idid < 0: + raise ODASSL_Exception("ODASSL failed with flag IDID {IDID}".format(IDID=idid)) + + + + #Retrieving statistics + self.statistics["nsteps"] += iwork[10] + self.statistics["nfcns"] += iwork[11] + self.statistics["njacs"] += iwork[12] + self.statistics["nerrfails"] += iwork[13] + self.statistics["nnfails"] += iwork[14] + + return flag, tlist, ylist, ydlist + + def print_statistics(self, verbose=NORMAL): + """ + Prints the run-time statistics for the problem. + """ + OverdeterminedDAE.print_statistics(self, verbose) #Calls the base class + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : ODASSL ', verbose) + self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) + self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) + self.log_message('', verbose) + + diff --git a/src/solvers/odepack.py b/src/solvers/odepack.py index a134594f..2ca0adb3 100644 --- a/src/solvers/odepack.py +++ b/src/solvers/odepack.py @@ -1,1033 +1,1033 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010-2021 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import scipy.linalg as Sc -import scipy.sparse as sp -import sys -from assimulo.exception import * -from assimulo.ode import * -import logging - -from assimulo.explicit_ode import Explicit_ODE - -try: - from assimulo.lib.odepack import dlsodar, dcfode, dintdy - from assimulo.lib.odepack import set_lsod_common, get_lsod_common -except ImportError: - sys.stderr.write("Could not find ODEPACK functions.\n") - -# Real work array -class common_like(object): - def __call__(self): - return self.__dict__ - -def g_dummy(t,y): - return y - -def jac_dummy(t,y): - return N.zeros((len(y),len(y))) - -class LSODAR(Explicit_ODE): - """ - LOSDAR is a multistep method for solving explicit ordinary - differential equations on the form, - - .. math:: - - \dot{y} = f(t,y), \quad y(t_0) = y_0. - - LSODAR automatically switches between using an ADAMS method - or an BDF method and is also able to monitor events. - - LSODAR is part of ODEPACK, http://www.netlib.org/odepack/opkd-sum - """ - - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Implicit_Problem' class. - """ - Explicit_ODE.__init__(self, problem) #Calls the base class - - #Default values - self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - self.options["usejac"] = False - self.options["maxsteps"] = 100000 - self.options["rkstarter"] = 1 - self.options["maxordn"] = 12 - self.options["maxords"] = 5 - self.options["maxh"] = 0. - - self._leny = len(self.y) #Dimension of the problem - self._nordsieck_array = [] - self._nordsieck_order = 0 - self._nordsieck_time = 0.0 - self._nordsieck_h = 0.0 - self._update_nordsieck = False - - # Solver support - self.supports["state_events"] = True - self.supports["report_continuously"] = True - self.supports["interpolated_output"] = True - - self._RWORK = N.array([0.0]*(22 + self.problem_info["dim"] * - max(16,self.problem_info["dim"]+9) + - 3*self.problem_info["dimRoot"])) - self._IWORK = N.array([0]*(20 + self.problem_info["dim"])) - - - def initialize(self): - """ - Initializes the overall simulation process - (called before _simulate) - """ - #Reset statistics - self.statistics.reset() - - #self._tlist = [] - #self._ylist = [] - - #starts simulation with classical multistep starting procedure - # Runge-Kutta starter will be started if desired (see options) - # only after an event occured. - self._rkstarter_active = False - - def interpolate(self, t): - """ - Helper method to interpolate the solution at time t using the Nordsieck history - array. Wrapper to ODEPACK's subroutine DINTDY. - """ - if self._update_nordsieck: - #Nordsieck start index - nordsieck_start_index = 21+3*self.problem_info["dimRoot"] - 1 - - hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() - self._nordsieck_array = \ - self._RWORK[nordsieck_start_index:nordsieck_start_index+(nq+1)*nyh].reshape((nyh,-1),order='F') - self._nyh = nyh - self._update_nordsieck = False - - dky, iflag = dintdy(t, 0, self._nordsieck_array, self._nyh) - - if iflag!= 0 and iflag!=-2: - raise ODEPACK_Exception("DINTDY returned with iflag={} (see ODEPACK documentation).".format(iflag)) - elif iflag==-2: - dky=self.y.copy() - return dky - - def autostart(self,t,y,sw0=[]): - """ - autostart determines the initial stepsize for Runge--Kutta solvers - """ - RWORK=self._RWORK.copy() - IWORK=self._IWORK.copy() - pr=self.rkstarter+1 - tol=self.options["atol"] - tolscale=tol[0]**(1./pr) - normscale=1. - f=self.problem.rhs - - t0=t - tf=RWORK[0] - T=abs(tf-t0) - direction=N.sign(tf-t0) - - #Perturb initial condition and compute rough Lipschitz constant - cent=Sc.norm(y)/normscale/100. - v0=y+cent*N.random.rand(len(y),1) - u0prime=f(t,y,sw0) - v0prime=f(t,v0,sw0) - Lip=Sc.norm(u0prime-v0prime)/Sc.norm(y-v0) - h=direction*min(1e-3*T,max(1e-8*T,0.05/Lip)) - #step 1: fwd Euler step - u1=y+h*u0prime - t1=t+h - #step 2: fwd Euler step in reverse - u1prime=f(t1,u1,sw0) - u0comp=u1-h*u1prime - #step 3: estimate of local error - du=u0comp-y - dunorm=Sc.norm(du) - errnorm=dunorm/normscale - #step 4: new estimate of Lipschitz constant - u0comprime=f(t0,u0comp,sw0) - L=Sc.norm(u0comprime-u0prime)/dunorm - M=N.dot(du,u0comprime-u0prime)/dunorm**2 - #step 5: construct a refined starting stepsize - theta1=tolscale/N.sqrt(errnorm) - theta2=tolscale/abs(h*(L+M/2)) - h=h*(theta1+theta2)/2 - h=direction*min(3e-3*T,abs(h)) - return h - - - def integrate_start(self, t, y): - """ - Helper program for the initialization of LSODAR - """ - #print ' We have rkstarter {} and rkstarter_active {}'.format(self.rkstarter, self._rkstarter_active) - if not(self.rkstarter>1 and self._rkstarter_active): - # first call or classical restart after a discontinuity - ISTATE=1 - # reset work arrays - RWORK = 0.0*self._RWORK - IWORK = 0.0*self._IWORK - else: #self.rkstarter and self._rkstarter_active - # RK restart - RWORK=self._RWORK.copy() - IWORK=self._IWORK.copy() - ISTATE=2 # should be 2 - dls001=common_like() - dlsr01=common_like() - # invoke rkstarter - # a) get previous stepsize if any - hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() - #H = hu if hu != 0. else 1.e-4 # this needs some reflections - #H =(abs(RWORK[0]-t)*((self.options["rtol"])**(1/(self.rkstarter+1))))/(100*Sc.norm(self.problem.rhs(t,y,self.sw))+10)#if hu != 0. else 1.e-4 - H=1e-2 - #H=self.autostart(t,y) - #H=3*H - # b) compute the Nordsieck array and put it into RWORK - rkNordsieck = RKStarterNordsieck(self.problem.rhs,H,number_of_steps=self.rkstarter) - t,nordsieck = rkNordsieck(t,y,self.sw) - nordsieck=nordsieck.T - nordsieck_start_index = 21+3*self.problem_info["dimRoot"] - 1 - RWORK[nordsieck_start_index:nordsieck_start_index+nordsieck.size] = \ - nordsieck.flatten(order='F') - - # c) compute method coefficients and update the common blocks - dls001.init = 1 - #dls001.jstart = -1.0 #take the next step with a new value of H,n,meth,.. - mf = 20 - nq = self.rkstarter - dls001.meth = meth = mf // 10 - dls001.miter =mf % 10 - elco,tesco =dcfode(meth) # - dls001.maxord= 5 #max order - dls001.nq= self.rkstarter #Next step order - dls001.nqu=self.rkstarter #Method order last used (check if this is needed) - dls001.meo= meth #meth - dls001.nqnyh= nq*self.problem_info["dim"] #nqnyh - dls001.conit= 0.5/(nq+2) #conit - dls001.el= elco[:,nq-1] # el0 is set internally - dls001.hu=H # this sets also hold and h internally - dls001.jstart=1 - - # IWORK[...] = - #IWORK[13]=dls001.nqu - IWORK[14]=dls001.nq - #IWORK[18]=dls001.meth - #IWORK[7]=dlsa01.mxordn #max allowed order for Adams methods - #IWORK[8]=dlsa01.mxords #max allowed order for BDF - IWORK[19]=meth #the current method indicator - #RWORK[...] - dls001.tn=t - RWORK[12]=t - RWORK[10]=hu #step-size used successfully - RWORK[11]=H #step-size to be attempted for the next step - #RWORK[6]=dls001.hmin - #RWORK[5]=dls001.hmxi - - number_of_fevals=N.array([1,2,4,7,11]) - # d) Reset statistics - IWORK[9:13]=[0]*4 - dls001.nst=1 - dls001.nfe=number_of_fevals[self.rkstarter-1] # from the starter - dls001.nje=0 - dlsr01.nge=0 - # set common block - commonblocks={} - commonblocks.update(dls001()) - commonblocks.update(dlsr01()) - set_lsod_common(**commonblocks) - - - return ISTATE, RWORK, IWORK - - def _jacobian(self, t, y): - """ - Calculates the Jacobian, either by an approximation or by the user - defined (jac specified in the problem class). - """ - jac = self.problem.jac(t,y) - - if isinstance(jac, sp.csc_matrix): - jac = jac.toarray() - - return jac - - def integrate(self, t, y, tf, opts): - ITOL = 2 #Only atol is a vector - ITASK = 5 #For one step mode and hitting exactly tcrit, normally tf - IOPT = 1 #optional inputs are used - - # provide work arrays and set common blocks (if needed) - ISTATE, RWORK, IWORK = self.integrate_start( t, y) - - JT = 1 if self.usejac else 2#Jacobian type indicator - JROOT = N.array([0]*self.problem_info["dimRoot"]) - - #Setting work options - RWORK[0] = tf #Do not integrate past tf - RWORK[5] = self.options["maxh"] - - #Setting iwork options - IWORK[5] = self.maxsteps - - #Setting maxord to IWORK - IWORK[7] = self.maxordn - IWORK[8] = self.maxords - - #Dummy methods - if self.problem_info["state_events"]: - if self.problem_info["switches"]: - def state_events(t,y,sw): - return self.problem.state_events(t,y,sw) - g_fcn = state_events - else: - def state_events(t,y): - return self.problem.state_events(t,y) - g_fcn = state_events - else: - g_fcn = g_dummy - - #jac_dummy = (lambda t,y:N.zeros((len(y),len(y)))) if not self.usejac else self.problem.jac - jac_fcn = jac_dummy if not self.usejac else self._jacobian - - #Extra args to rhs and state_events - rhs_extra_args = (self.sw,) if self.problem_info["switches"] else () - g_extra_args = (self.sw,) if self.problem_info["switches"] else () - - #Store the opts - self._opts = opts - - #Outputs - tlist = [] - ylist = [] - - - - #Run in normal mode? - normal_mode = 1 if opts["output_list"] is not None else 0 - - #Tolerances: - atol = self.atol - rtol = self.rtol*N.ones(self.problem_info["dim"]) - rhs = self.problem.rhs - - #if normal_mode == 0: - if opts["report_continuously"] or opts["output_list"] is None: - - while (ISTATE == 2 or ISTATE == 1) and t < tf: - - y, t, ISTATE, RWORK, IWORK, roots = dlsodar(rhs, y.copy(), t, tf, ITOL, - rtol, atol, - ITASK, ISTATE, IOPT, RWORK, IWORK, jac_fcn, JT, g_fcn, JROOT, - f_extra_args = rhs_extra_args, g_extra_args = g_extra_args) - - self._update_nordsieck = True - self._IWORK = IWORK - self._RWORK = RWORK - #hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() - #self._nordsieck_array = \ - # RWORK[nordsieck_start_index:nordsieck_start_index+(nq+1)*nyh].reshape((nyh,-1),order='F') - #self._nyh = nyh - self._event_info = roots - - if opts["report_continuously"]: - flag_initialize = self.report_solution(t, y, opts) - if flag_initialize: - #If a step event has occured the integration has to be reinitialized - ISTATE = 3 - else: - #Store results - tlist.append(t) - ylist.append(y.copy()) - - #Checking return - if ISTATE == 2: - flag = ID_PY_COMPLETE - elif ISTATE == 3: - flag = ID_PY_EVENT - else: - raise ODEPACK_Exception("LSODAR failed with flag %d"%ISTATE) - - else: - - #Change the ITASK - ITASK = 4 #For computation of yout - - output_index = opts["output_index"] - output_list = opts["output_list"][output_index:] - - flag = ID_PY_COMPLETE - - for tout in output_list: - output_index += 1 - - y, t, ISTATE, RWORK, IWORK, roots = dlsodar(rhs, y.copy(), t, tout, ITOL, - rtol, atol, - ITASK, ISTATE, IOPT, RWORK, IWORK, jac_fcn, JT, g_fcn, JROOT, - f_extra_args = rhs_extra_args, g_extra_args = g_extra_args) - - #Store results - tlist.append(t) - ylist.append(y.copy()) - self._event_info = roots - - #Checking return - if ISTATE == 2 and t >= tf: - flag = ID_PY_COMPLETE - break - elif ISTATE == 3: - flag = ID_PY_EVENT - break - elif ISTATE < 0: - raise ODEPACK_Exception("LSODAR failed with flag %d"%ISTATE) - - opts["output_index"] = output_index - # deciding on restarting options - self._rkstarter_active = True if ISTATE == 3 and self.rkstarter > 1 else False - #print 'rkstarter_active set to {} and ISTATE={}'.format(self._rkstarter_active, ISTATE) - - #Retrieving statistics - self.statistics["nstatefcns"] += IWORK[9] - self.statistics["nsteps"] += IWORK[10] - self.statistics["nfcns"] += IWORK[11] - self.statistics["njacs"] += IWORK[12] - self.statistics["nstateevents"] += 1 if flag == ID_PY_EVENT else 0 - # save RWORK, IWORK for restarting feature - if self.rkstarter>1: - self._RWORK=RWORK - self._IWORK=IWORK - - return flag, tlist, ylist - - def get_algorithm_data(self): - """ - Returns the order and step size used in the last successful step. - """ - hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() - - return hu, nqu - - def state_event_info(self): - """ - Returns the state events indicator as a list where a one (1) - indicates that the event function have been activated and a (0) - if not. - """ - return self._event_info - - def print_statistics(self, verbose=NORMAL): - """ - Prints the run-time statistics for the problem. - """ - Explicit_ODE.print_statistics(self, verbose) #Calls the base class - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : LSODAR ', verbose) - self.log_message(' Absolute tolerances : {}'.format(self.options["atol"]), verbose) - self.log_message(' Relative tolerances : {}'.format(self.options["rtol"]), verbose) - if self.rkstarter==1: - self.log_message(' Starter : {}'.format('classical'), verbose) - else: - self.log_message(' Starter : Runge-Kutta order {}'.format(self.rkstarter), verbose) - if self.maxordn < 12 or self.maxords < 5: - self.log_message(' Maximal order Adams : {}'.format(self.maxordn), verbose) - self.log_message(' Maximal order BDF : {}'.format(self.maxords), verbose) - if self.maxh > 0. : - self.log_message(' Maximal stepsize maxh : {}'.format(self.maxh), verbose) - self.log_message('', verbose) - - def _set_usejac(self, jac): - self.options["usejac"] = bool(jac) - - def _get_usejac(self): - """ - This sets the option to use the user defined jacobian. If a - user provided jacobian is implemented into the problem the - default setting is to use that jacobian. If not, an - approximation is used. - - Parameters:: - - usejac - - True - use user defined jacobian - False - use an approximation - - - Should be a boolean. - - Example: - usejac = False - """ - return self.options["usejac"] - - usejac = property(_get_usejac,_set_usejac) - - def _set_atol(self,atol): - - self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) - - if len(self.options["atol"]) == 1: - self.options["atol"] = self.options["atol"]*N.ones(self._leny) - elif len(self.options["atol"]) != self._leny: - raise ODEPACK_Exception("atol must be of length one or same as the dimension of the problem.") - - def _get_atol(self): - """ - Defines the absolute tolerance(s) that is to be used by the solver. - Can be set differently for each variable. - - Parameters:: - - atol - - Default '1.0e-6'. - - - Should be a positive float or a numpy vector - of floats. - - Example: - atol = [1.0e-4, 1.0e-6] - """ - return self.options["atol"] - - atol=property(_get_atol,_set_atol) - - def _set_rtol(self,rtol): - try: - self.options["rtol"] = float(rtol) - except (ValueError, TypeError): - raise ODEPACK_Exception('Relative tolerance must be a (scalar) float.') - if self.options["rtol"] <= 0.0: - raise ODEPACK_Exception('Relative tolerance must be a positive (scalar) float.') - - def _get_rtol(self): - """ - Defines the relative tolerance that is to be used by the solver. - - Parameters:: - - rtol - - Default '1.0e-6'. - - - Should be a positive float. - - Example: - rtol = 1.0e-4 - """ - return self.options["rtol"] - - rtol=property(_get_rtol,_set_rtol) - - def _get_maxsteps(self): - """ - The maximum number of steps allowed to be taken to reach the - final time. - - Parameters:: - - maxsteps - - Default 10000 - - - Should be a positive integer - """ - return self.options["maxsteps"] - - def _set_maxsteps(self, max_steps): - try: - max_steps = int(max_steps) - except (TypeError, ValueError): - raise ODEPACK_Exception("Maximum number of steps must be a positive integer.") - self.options["maxsteps"] = max_steps - - maxsteps = property(_get_maxsteps, _set_maxsteps) - def _get_hmax(self): - """ - The absolute value of the maximal stepsize for all methods - - Parameters:: - - hmax - - Default: 0. (no maximal step size) - - - Should be a positive float - """ - logging.warning("The option 'hmax' is deprecated and will be removed, please use 'maxh' instead.") - return self.options["maxh"] - def _set_hmax(self,hmax): - if not (isinstance(hmax,float) and hmax >= 0.): - raise ODEPACK_Exception("Maximal step size hmax should be a positive float") - logging.warning("The option 'hmax' is deprecated and will be removed, please use 'maxh' instead.") - self.options["maxh"]=hmax - hmax = property(_get_hmax, _set_hmax) - def _get_maxh(self): - """ - The absolute value of the maximal stepsize for all methods - - Parameters:: - - maxh - - Default: 0. (no maximal step size) - - - Should be a positive float - """ - return self.options["maxh"] - def _set_maxh(self,maxh): - if not (isinstance(maxh,float) and maxh >= 0.): - raise ODEPACK_Exception("Maximal step size maxh should be a positive float") - self.options["maxh"]=maxh - maxh = property(_get_maxh, _set_maxh) - def _get_maxordn(self): - """ - The maximum order used by the Adams-Moulton method (nonstiff case) - - Parameters:: - - maxordn - - Default 12 - - - Should be a positive integer - """ - return self.options["maxordn"] - - def _set_maxordn(self, maxordn): - try: - maxordn = int(maxordn) - except (TypeError, ValueError): - raise ODEPACK_Exception("Maximum order must be a positive integer.") - if maxordn > 12: - raise ODEPACK_Exception("Maximum order should not exceed 12.") - self.options["maxordn"] = maxordn - - maxordn = property(_get_maxordn, _set_maxordn) - def _get_maxords(self): - """ - The maximum order used by the BDF method (stiff case) - - Parameters:: - - maxords - - Default 5 - - - Should be a positive integer - """ - return self.options["maxords"] - - def _set_maxords(self, maxords): - try: - maxords = int(maxords) - except (TypeError, ValueError): - raise ODEPACK_Exception("Maximum order must be a positive integer.") - if maxords > 5: - raise ODEPACK_Exception("Maximum order should not exceed 5.") - self.options["maxords"] = maxords - - maxords = property(_get_maxords, _set_maxords) - def _get_rkstarter(self): - """ - This defines how LSODAR is started. - (classically or with a fourth order Runge-Kutta starter) - - Parameters:: - - rkstarter - - Default False starts LSODAR in the classical multistep way - - - Should be a Boolean - """ - return self.options["rkstarter"] - - def _set_rkstarter(self, rkstarter): - if not rkstarter in {1,2,3,4,5}: - raise ODEPACK_Exception("Must be a positive integer less than 6") - self.options["rkstarter"] = rkstarter - - rkstarter = property(_get_rkstarter, _set_rkstarter) - -class RKStarterNordsieck(object): - """ - A family of Runge-Kutta starters producing a - Nordsieck array to (re-)start a Nordsieck based multistep - method with a given order. - - See: Mohammadi (2013): https://lup.lub.lu.se/luur/download?func=downloadFile&recordOId=4196026&fileOId=4196027 - """ - # Gamma matrix of Gear's RK starter which produce Nordsieck vector at t0 - Gamma_0=[N.array([[1.,0.], # 1st order - [0.,1.]]), - N.array([[1.,0.,0.], # 2nd order - [0.,1.,-1.], - [0.,0.,1.]]), - N.array([[1.,0.,0.,0.], # 3rd order - [0.,1.,-5./3.,1.], - [0.,0.,3.,-2.], - [0.,0.,0.,1.], - [0.,0.,-4./3.,0.]]), - N.array([[1.,0.,0.,0.,0.], # 4th order - [0.,1.,-5./6.,4./9.,-1./9.], - [0.,0.,0.,0.,0.], - [0.,0.,1./2.,-4./9.,1./9.], - [0.,0.,7./3.,-19./9.,7./9.], - [0.,0.,-3.,10./3.,-4./3.], - [0.,0.,1.,-11./9.,5./9.]])] - - # A matrices of RK starter with equidistanced states - - A_s=[ N.array([1]), # 1st order - N.array([[0.,0],[1,0]]), # 2nd order - N.array([[0.,0.,0.,0.,0.],[1./2,0.,0.,0.,0.], # 3rd order - [0.,3./4,0.,0.,0.],[2./9,1./3,4./9,0.,0.], - [17./72,1./6,2./9,-1./8,0.]]), - N.array([[0.,0.,0.,0.,0.,0.,0.], # 4th order - [1./6.,0.,0.,0.,0.,0.,0.], - [0.,1./6.,0.,0.,0.,0.,0.], - [0.,0.,1./3.,0.,0.,0.,0.], - [1./18.,1./9.,1./9.,1./18.,0.,0.,0.], - [2.5,-3.,-3.,2.25,2.25,0.,0.], - [10./45.,-8./45.,-8./45.,-4./45.,13./15.,1./45.,0.]]), - N.array([[0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.], # 5th order - [1./20,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.], - [3./160,9./160,0.,0.,0.,0.,0., 0.,0.,0.,0.,0.,0.,0.], - [3./40,-9./40,6./20,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.], - [-11./216,5/8,-70/108,35./108,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.], - [1631/221184,175./2048,575./55296,44275./442368,253./16384,0.,0.,0.,0.,0.,0.,0.,0.,0.], - [37./1512,0.,250/2484,125./2376,0.,512/7084,0.,0.,0.,0.,0.,0.,0.,0.], - [32.2687682,-39.4956646,-20.7849921,22.2296308,45.4543383,51.9961815,-91.1682621,0.,0.,0.,0.,0.,0.,0.], - [-23.867154567764317,41+31195543/31462660,8+6080897/76520184,-23-7543649/15003372,-63-91662407/130833420,-55-27823937/32947321,117+23615874/27978401,0.,0.,0.,0.,0.,0.,0.], - [-0.5118403967750724,0.,2+379808/6250467,-2-866339/6430593,-1-151963/7810968,-18723895/33225736,8/3,0.,303385/143008499,0.,0.,0.,0.,0.], - [-118.50992840087292,4+1586290/42052551,7+76050433/143964928,11+61385198/111742353,16+54354964/63299173,15+84679766/214253769,15+71618817/71932238,24+14108046/149713813, - 2+12784603/81260661,21+38484710/59808171,0.,0.,0.,0.], - [10.183365237189525,0.,-36-34947657/185923229,38+50251318/55929787,18+114017528/325586295,10+78529445/98887874,-43.5,0.,-6714159/175827524,2.25,0.,0.,0.,0.], - [-0.1491588850008383,0.,19145766/113939551,8434687/36458574,2012005/39716421,8409989/57254530,10739409/94504714,0.,-3321/86525909,-30352753/150092385,0.,70257074/109630355, - 0.,0.], - [0.03877310906055409,0.,3021245/89251943,5956469/58978530,851373/32201684,11559106/149527791,11325471/112382620,0.,-12983/235976962,17692261/82454251,0., - 38892959/120069679,11804845./141497517,0.]], dtype=object)] - - co_ord_s=[[],[],[4],[4,6],[6,9,11]] - b_s=[ N.array([1]), - N.array([1./2,1./2]), - N.array([1./6,0.,0.,1./6,2./3]), - N.array([29./1062.,83./531.,83./531.,83./1062.,2./531.,56./531.,251./531.]), # b-vectors of of 1st to 5th order RK starter method - N.array([0.03877310906055409,0.,3021245/89251943,5956469/58978530,851373/32201684,11559106/149527791,11325471/112382620,0.,-12983/235976962,17692261/82454251,0., - 38892959/120069679,11804845./141497517,0.])] - - C_s=[ N.array([0.]), - N.array([0.]), - N.array([0.,1./2,3./4,1.,1./2,1.]), - N.array([0.,1./6,1./6,1./3,1./3,1.,1.,2./3,1.]), - N.array([0.,1./20,3./40,3./20,1./4,7./28,1./4,2./4,1.,2./4,3./4,3./4,1.,1.])] # C-values in Butcher tableau of 8-stages Runge-Kutta - - # A matrices of RK starter with non-equidistanced stages - A_n=[ N.array([1]), - N.array([[0.,0.],[0.,0.]]), - N.array([[0.,0.,0.,0.],[1./2,0.,0.,0.],[0.,1./2,0.,0.],[0.,0.,1.,0.]]), # 3rd order - N.array([[0.,0.,0.,0.,0.,0.],[2./5,0.,0.,0.,0.,0.],[-3./20,3./4,0.,0.,0.,0.], # 4th order - [19./44,-15./44,40./44,0.,0.,0.],[-31./64,185./192,5./64,-11./192,0.,0.],[11./72,25./72,25./72,11./72,0.,0.]])] - - b_n=[ N.array([0.]), - N.array([1./2.,1./2.]), - N.array([[5./24,1./6,1./6,-1/24],[1./6,1./3,1./3,1./6]]), - N.array([[802./5625,68./225,-67./225,-143./5625,144./625,6./125],[699./5000,81./200,-39./200,99./5000,144./625,0.],[11./72,25./72,25./72,11./72,0.,0.]])] - - C_n=[ N.array([0.]), - N.array([0.]), - N.array([0.,1./2,1./2,1.]), - N.array([0.,2./5,3./5,1.,1./2,1.])] - - #co_ord_n=[[],[],[1./2,1.],[2./5,3./5,1.]] - #A=N.array([[1.,0.,0.,0.], - # [1.,1./9.,1./27,1./81.], - # [1.,4./9.,8./27,16./81.], - # [1.,1.,1.,1.]]) - A=[N.array([0.]), # Convert the state values to Nordsieck vector - N.array([0.]), - N.array([1.]), - N.array([[1./4,1./8],[1.,1.]]), - N.array([[1./9,1./27,1./81.], - [4./9.,8./27,16./81], - [1.,1.,1.]]), - N.array([[1./16,1./64,1./256,1./1024], - [1./4,1./8,1./16,1./32], - [9./16,27./64,81./256,243./1024], - [1.,1.,1.,1.]])] - - scale=N.array([1, 1, 1/2., 1./6., 1./24., 1./120.]).reshape(-1,1) - - - def __init__(self, rhs, H, method='RKs_f', eval_at=0., number_of_steps=4): - """ - Initiates the Runge-Kutta Starter. - - Parameters:: - - rhs - - The problem's rhs function - - H - - starter step size - - eval_at - - Where to evaluate the Nordiseck vector - evaluation point is (eval_at)*H - Possible choices - eval_at=0. - eval_at=1. - - number_of_steps - - the number of steps :math:`(k)` to start the multistep method with - This will determine the initial order of the method, which is - :math:`k` for BDF methods and :math:`k+1` for Adams Moulton Methods . - """ - self.f = rhs - self.H = H - self.method=method - - if not 1 < number_of_steps < 6: - raise RKStarter_Exception('Step number larget than 4 not yet implemented') - self.number_of_steps = number_of_steps - self.eval_at = float(eval_at) - if not self.eval_at == 0.: - raise RKStarter_Exception("Parameter eval_at different from 0 not yet implemented.") - - def RKs_f(self,t0,y0,sw0): - - s=self.number_of_steps - A_s,C_s,b_s,co_ord_s=self.A_s,self.C_s,self.b_s,self.co_ord_s - A_s=A_s[s-1] - C_s=C_s[s-1] - b_s=b_s[s-1] - co_ord_s=co_ord_s[s-1] - H=(s-1)*self.H - K=N.zeros((N.size(A_s,0),len(y0))) - for i in range(N.size(A_s,0)): - K[i,:]=self.f(t0+C_s[i]*H,y0+H*N.dot(A_s[i,:],K),sw0) - y=N.zeros((s,len(y0))) - y[0,:]=y0 - for i in range(1,s): - if i==s-1: - y[i,:]=y0+H*N.dot(b_s,K) - else: - y[i,:]=y0+H*N.dot(A_s[co_ord_s[i-1],:],K) - return y - def RKn_f(self,t0,y0,sw0): - s=self.number_of_steps - H=(s-1)*self.H - A_n,C_n,b_n=self.A_n,self.C_n,self.b_n - A_n=A_n[s-1] - C_n=C_n[s-1] - b_n=b_n[s-1] - - K=N.zeros((N.size(A_n,0),len(y0))) - for i in range(N.size(A_n,0)): - K[i,:]=self.f(t0+C_n[i]*H,y0+H*N.dot(A_n[i,:],K),sw0) - y=N.zeros((s,len(y0))) - y[0,:]=y0 - for i in range(1,s): - y[i,:]=y0+H*N.dot(b_n[i-1],K) - return y - - - def rk_like4(self, t0, y0, sw0): - """ - rk_like computes Runge-Kutta stages - Note, the currently implementation is **only** correct for - autonomous systems. - """ - f = lambda y: self.f(t0, y, sw0) - h = self.H/4. - k1 = h*f(y0) - k2 = h*f(y0 + k1) - k3 = h*f(y0 + 2. * k2) - k4 = h*f(y0 + 3./4. * k1 + 9./4. * k3) - k5 = h*f(y0 + k1/2. + k2 + k3/2. + 2. * k4) - k6 = h*f(y0+k1/12.+2. * k2 + k3/4. + 2./3. * k4 + 2. * k5) - return N.array([y0,k1,k2,k3,k4,k5,k6]) - def rk_like3(self, t0, y0, sw0): - """ - rk_like computes Runge-Kutta stages - Note, the currently implementation is **only** correct for - autonomous systems. - - """ - f = lambda y: self.f(t0, y, sw0) - h = self.H/3. - k1 = h*f(y0) - k2 = h*f(y0 + k1) - k3 = h*f(y0 + k1+ k2) - k4 = h*f(y0 + 3./2. * k1) - return N.array([y0,k1,k2,k3,k4]) - def rk_like2(self, t0, y0, sw0): - """ - rk_like2 computes Runge-Kutta 2nd-stages - Note, the currently implementation is **only** correct for - autonomous systems. - """ - f=lambda y: self.f(t0, y, sw0) - h=self.H/2. - k1=h*f(y0) - k2=h*f(y0+k1) - return N.array([y0,k1,k2]) - def rk_like13(self, t0, y0, sw0): - """ - rk_like6 computes Runge-Kutta 8th-stages - """ - h = self.H - self.Gamma_2=self.Gamma_0[3] - f=lambda y: self.f(t0 , y , sw0) - K=N.zeros((6,len(y0))) - sol=N.zeros((3,len(y0))) - b=N.zeros((2,len(y0))) #remove the fifth stage value that is for error estimation - nord = N.zeros((4,len(y0))) #Nordsieck vector - for i in range(5): - K[i,:]= f(y0+h*N.dot(self.Gamma_2[i,:],K)) - c=0 - for i in range(3): - sol[i,:]=y0+h*N.dot(self.Gamma_2[i+3,:],K) - if i!=0: - b[c,:]=sol[i,:]-y0-(c+1)*h/2*K[0,:] - c+=1 - nord[0,:] = y0 - nord[1,:] = h*K[0,:] - nord[2:,:] = Sc.solve(self.A[self.number_of_steps],b) - return nord - def rk_like14(self, t0, y0, sw0): - """ - rk_like6 computes Runge-Kutta 8th-stages - """ - h = self.H - Gamma_2=self.Gamma_0[4] - f=lambda y: self.f(t0 , y , sw0) - K=N.zeros((8,len(y0))) - sol=N.zeros((4,len(y0))) - b=N.zeros((3,len(y0))) #remove the fifth stage value that is for error estimation - nord = N.zeros((5,len(y0))) #Nordsieck vector - for i in range(7): - K[i,:]= f(y0+h*N.dot(Gamma_2[i,:],K)) - c=0 - for i in range(4): - sol[i,:]=y0+h*N.dot(Gamma_2[i+4,:],K) - if i!=1: - b[c,:]=sol[i,:]-y0-(c+1)*h/3*K[0,:] - c+=1 - nord[0,:] = y0 - nord[1,:] = h*K[0,:] - nord[2:,:] = Sc.solve(self.A[self.number_of_steps],b) - return nord - def rk_like15(self, t0, y0, sw0): - """ - rk_like6 computes Runge-Kutta 5th-stages ****needs to be modified**** - """ - h = self.H - Gamma_2=self.Gamma_0[5] - f=lambda y: self.f(t0 , y , sw0) - K=N.zeros((14,len(y0))) - sol=N.zeros((8,len(y0))) - b=N.zeros((4,len(y0))) #remove the fifth stage value that is for error estimation - nord = N.zeros((6,len(y0))) #Nordsieck vector - for i in range(13): - K[i,:]= f(y0+h*N.dot(Gamma_2[i,:],K)) - c=0 - for i in range(8): - sol[i,:]=y0+h*N.dot(Gamma_2[i+6,:],K) - if (i!=1) and (i!=2) and (i!=4) and (i!=6): - b[c,:]=sol[i,:]-y0-(c+1)*h/4*K[0,:] - c+=1 - nord[0,:] = y0 - nord[1,:] = h*K[0,:] - nord[2:,:] = Sc.solve(self.A[self.number_of_steps],b) - return nord - def nordsieck(self,k): - """ - Nordsieck array computed at initial point - """ - nord=self.scale[:self.number_of_steps+1]*N.dot(self.Gamma_0[self.number_of_steps-1].T,k) - - return nord - def Nordsieck_RKn(self,t0,y,sw0): - s=self.number_of_steps - H=(s-1)*self.H - co_nord=[N.array([1./2,1.]),N.array([2./5,3./5,1.])] - l=size(y,0) - y0=y[0,:] - yf=self.f(t0,y0,sw0) - - if l==3: - co=N.array([co_nord[0]]) - nord_n=N.vander(co_nord[0],self.number_of_steps+1) - b=y[1:]-y0-co.T*yf - nord=Sc.solve(nord_n[0:2,0:2],b) - elif l==4: - co=N.array([co_nord[1]]) - nord_n=N.vander(co_nord[1],self.number_of_steps+1) - b=y[1:]-y0-H*co.T*yf - nord=Sc.solve(nord_n[0:3,0:3],b) - nord=N.vstack((y0,H*yf,nord[::-1])) - return nord - def Nordsieck_RKs(self,t0,y,sw0): - s=self.number_of_steps - H=(s-1)*self.H - co_nord=[N.array([1]),N.array([1./2,1]),N.array([1./3,2./3,1]), - N.array([1./4,2./4,3./4,1.])] - A=self.A - y0=y[0,:] - yf=self.f(t0,y0,sw0) - co=co_nord[s-2] - co=N.array([co]) - b=y[1:]-y0-H*co.T*yf - nord=Sc.solve(A[s],b) - nord=N.vstack((y0,H*yf,nord)) - return nord - - - - def __call__(self, t0 , y0, sw0=[]): - """ - Evaluates the Runge-Kutta starting values - - Parameters:: - - y0 - - starting value - """ - if self.method=='RK_G': - # We construct a call like: rk_like4(self, t0, y0, sw0) - k=self.__getattribute__('rk_like{}'.format(self.number_of_steps))(t0, y0, sw0) - t = t0+self.eval_at*self.H - #t= t0 + self.H - k=self.nordsieck(k) - - elif self.method=='RKs_f': - y=self.RKs_f(t0, y0, sw0) - t = t0+self.eval_at*self.H - k=self.Nordsieck_RKs(t0,y,sw0) - - elif self.method=='RKn_f': - y=self.RKn_f(t0,y0,sw0) - t=t0+self.eval_at*self.H - k=self.Nordsieck_RKn(t0,y,sw0) - return t,k - - - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010-2021 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import scipy.linalg as Sc +import scipy.sparse as sp +import sys +from assimulo.exception import * +from assimulo.ode import * +import logging + +from assimulo.explicit_ode import Explicit_ODE + +try: + from assimulo.lib.odepack import dlsodar, dcfode, dintdy + from assimulo.lib.odepack import set_lsod_common, get_lsod_common +except ImportError: + sys.stderr.write("Could not find ODEPACK functions.\n") + +# Real work array +class common_like(object): + def __call__(self): + return self.__dict__ + +def g_dummy(t,y): + return y + +def jac_dummy(t,y): + return N.zeros((len(y),len(y))) + +class LSODAR(Explicit_ODE): + """ + LOSDAR is a multistep method for solving explicit ordinary + differential equations on the form, + + .. math:: + + \dot{y} = f(t,y), \quad y(t_0) = y_0. + + LSODAR automatically switches between using an ADAMS method + or an BDF method and is also able to monitor events. + + LSODAR is part of ODEPACK, http://www.netlib.org/odepack/opkd-sum + """ + + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Implicit_Problem' class. + """ + Explicit_ODE.__init__(self, problem) #Calls the base class + + #Default values + self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + self.options["usejac"] = False + self.options["maxsteps"] = 100000 + self.options["rkstarter"] = 1 + self.options["maxordn"] = 12 + self.options["maxords"] = 5 + self.options["maxh"] = 0. + + self._leny = len(self.y) #Dimension of the problem + self._nordsieck_array = [] + self._nordsieck_order = 0 + self._nordsieck_time = 0.0 + self._nordsieck_h = 0.0 + self._update_nordsieck = False + + # Solver support + self.supports["state_events"] = True + self.supports["report_continuously"] = True + self.supports["interpolated_output"] = True + + self._RWORK = N.array([0.0]*(22 + self.problem_info["dim"] * + max(16,self.problem_info["dim"]+9) + + 3*self.problem_info["dimRoot"])) + self._IWORK = N.array([0]*(20 + self.problem_info["dim"])) + + + def initialize(self): + """ + Initializes the overall simulation process + (called before _simulate) + """ + #Reset statistics + self.statistics.reset() + + #self._tlist = [] + #self._ylist = [] + + #starts simulation with classical multistep starting procedure + # Runge-Kutta starter will be started if desired (see options) + # only after an event occured. + self._rkstarter_active = False + + def interpolate(self, t): + """ + Helper method to interpolate the solution at time t using the Nordsieck history + array. Wrapper to ODEPACK's subroutine DINTDY. + """ + if self._update_nordsieck: + #Nordsieck start index + nordsieck_start_index = 21+3*self.problem_info["dimRoot"] - 1 + + hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() + self._nordsieck_array = \ + self._RWORK[nordsieck_start_index:nordsieck_start_index+(nq+1)*nyh].reshape((nyh,-1),order='F') + self._nyh = nyh + self._update_nordsieck = False + + dky, iflag = dintdy(t, 0, self._nordsieck_array, self._nyh) + + if iflag!= 0 and iflag!=-2: + raise ODEPACK_Exception("DINTDY returned with iflag={} (see ODEPACK documentation).".format(iflag)) + elif iflag==-2: + dky=self.y.copy() + return dky + + def autostart(self,t,y,sw0=[]): + """ + autostart determines the initial stepsize for Runge--Kutta solvers + """ + RWORK=self._RWORK.copy() + IWORK=self._IWORK.copy() + pr=self.rkstarter+1 + tol=self.options["atol"] + tolscale=tol[0]**(1./pr) + normscale=1. + f=self.problem.rhs + + t0=t + tf=RWORK[0] + T=abs(tf-t0) + direction=N.sign(tf-t0) + + #Perturb initial condition and compute rough Lipschitz constant + cent=Sc.norm(y)/normscale/100. + v0=y+cent*N.random.rand(len(y),1) + u0prime=f(t,y,sw0) + v0prime=f(t,v0,sw0) + Lip=Sc.norm(u0prime-v0prime)/Sc.norm(y-v0) + h=direction*min(1e-3*T,max(1e-8*T,0.05/Lip)) + #step 1: fwd Euler step + u1=y+h*u0prime + t1=t+h + #step 2: fwd Euler step in reverse + u1prime=f(t1,u1,sw0) + u0comp=u1-h*u1prime + #step 3: estimate of local error + du=u0comp-y + dunorm=Sc.norm(du) + errnorm=dunorm/normscale + #step 4: new estimate of Lipschitz constant + u0comprime=f(t0,u0comp,sw0) + L=Sc.norm(u0comprime-u0prime)/dunorm + M=N.dot(du,u0comprime-u0prime)/dunorm**2 + #step 5: construct a refined starting stepsize + theta1=tolscale/N.sqrt(errnorm) + theta2=tolscale/abs(h*(L+M/2)) + h=h*(theta1+theta2)/2 + h=direction*min(3e-3*T,abs(h)) + return h + + + def integrate_start(self, t, y): + """ + Helper program for the initialization of LSODAR + """ + #print ' We have rkstarter {} and rkstarter_active {}'.format(self.rkstarter, self._rkstarter_active) + if not(self.rkstarter>1 and self._rkstarter_active): + # first call or classical restart after a discontinuity + ISTATE=1 + # reset work arrays + RWORK = 0.0*self._RWORK + IWORK = 0.0*self._IWORK + else: #self.rkstarter and self._rkstarter_active + # RK restart + RWORK=self._RWORK.copy() + IWORK=self._IWORK.copy() + ISTATE=2 # should be 2 + dls001=common_like() + dlsr01=common_like() + # invoke rkstarter + # a) get previous stepsize if any + hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() + #H = hu if hu != 0. else 1.e-4 # this needs some reflections + #H =(abs(RWORK[0]-t)*((self.options["rtol"])**(1/(self.rkstarter+1))))/(100*Sc.norm(self.problem.rhs(t,y,self.sw))+10)#if hu != 0. else 1.e-4 + H=1e-2 + #H=self.autostart(t,y) + #H=3*H + # b) compute the Nordsieck array and put it into RWORK + rkNordsieck = RKStarterNordsieck(self.problem.rhs,H,number_of_steps=self.rkstarter) + t,nordsieck = rkNordsieck(t,y,self.sw) + nordsieck=nordsieck.T + nordsieck_start_index = 21+3*self.problem_info["dimRoot"] - 1 + RWORK[nordsieck_start_index:nordsieck_start_index+nordsieck.size] = \ + nordsieck.flatten(order='F') + + # c) compute method coefficients and update the common blocks + dls001.init = 1 + #dls001.jstart = -1.0 #take the next step with a new value of H,n,meth,.. + mf = 20 + nq = self.rkstarter + dls001.meth = meth = mf // 10 + dls001.miter =mf % 10 + elco,tesco =dcfode(meth) # + dls001.maxord= 5 #max order + dls001.nq= self.rkstarter #Next step order + dls001.nqu=self.rkstarter #Method order last used (check if this is needed) + dls001.meo= meth #meth + dls001.nqnyh= nq*self.problem_info["dim"] #nqnyh + dls001.conit= 0.5/(nq+2) #conit + dls001.el= elco[:,nq-1] # el0 is set internally + dls001.hu=H # this sets also hold and h internally + dls001.jstart=1 + + # IWORK[...] = + #IWORK[13]=dls001.nqu + IWORK[14]=dls001.nq + #IWORK[18]=dls001.meth + #IWORK[7]=dlsa01.mxordn #max allowed order for Adams methods + #IWORK[8]=dlsa01.mxords #max allowed order for BDF + IWORK[19]=meth #the current method indicator + #RWORK[...] + dls001.tn=t + RWORK[12]=t + RWORK[10]=hu #step-size used successfully + RWORK[11]=H #step-size to be attempted for the next step + #RWORK[6]=dls001.hmin + #RWORK[5]=dls001.hmxi + + number_of_fevals=N.array([1,2,4,7,11]) + # d) Reset statistics + IWORK[9:13]=[0]*4 + dls001.nst=1 + dls001.nfe=number_of_fevals[self.rkstarter-1] # from the starter + dls001.nje=0 + dlsr01.nge=0 + # set common block + commonblocks={} + commonblocks.update(dls001()) + commonblocks.update(dlsr01()) + set_lsod_common(**commonblocks) + + + return ISTATE, RWORK, IWORK + + def _jacobian(self, t, y): + """ + Calculates the Jacobian, either by an approximation or by the user + defined (jac specified in the problem class). + """ + jac = self.problem.jac(t,y) + + if isinstance(jac, sp.csc_matrix): + jac = jac.toarray() + + return jac + + def integrate(self, t, y, tf, opts): + ITOL = 2 #Only atol is a vector + ITASK = 5 #For one step mode and hitting exactly tcrit, normally tf + IOPT = 1 #optional inputs are used + + # provide work arrays and set common blocks (if needed) + ISTATE, RWORK, IWORK = self.integrate_start( t, y) + + JT = 1 if self.usejac else 2#Jacobian type indicator + JROOT = N.array([0]*self.problem_info["dimRoot"]) + + #Setting work options + RWORK[0] = tf #Do not integrate past tf + RWORK[5] = self.options["maxh"] + + #Setting iwork options + IWORK[5] = self.maxsteps + + #Setting maxord to IWORK + IWORK[7] = self.maxordn + IWORK[8] = self.maxords + + #Dummy methods + if self.problem_info["state_events"]: + if self.problem_info["switches"]: + def state_events(t,y,sw): + return self.problem.state_events(t,y,sw) + g_fcn = state_events + else: + def state_events(t,y): + return self.problem.state_events(t,y) + g_fcn = state_events + else: + g_fcn = g_dummy + + #jac_dummy = (lambda t,y:N.zeros((len(y),len(y)))) if not self.usejac else self.problem.jac + jac_fcn = jac_dummy if not self.usejac else self._jacobian + + #Extra args to rhs and state_events + rhs_extra_args = (self.sw,) if self.problem_info["switches"] else () + g_extra_args = (self.sw,) if self.problem_info["switches"] else () + + #Store the opts + self._opts = opts + + #Outputs + tlist = [] + ylist = [] + + + + #Run in normal mode? + normal_mode = 1 if opts["output_list"] is not None else 0 + + #Tolerances: + atol = self.atol + rtol = self.rtol*N.ones(self.problem_info["dim"]) + rhs = self.problem.rhs + + #if normal_mode == 0: + if opts["report_continuously"] or opts["output_list"] is None: + + while (ISTATE == 2 or ISTATE == 1) and t < tf: + + y, t, ISTATE, RWORK, IWORK, roots = dlsodar(rhs, y.copy(), t, tf, ITOL, + rtol, atol, + ITASK, ISTATE, IOPT, RWORK, IWORK, jac_fcn, JT, g_fcn, JROOT, + f_extra_args = rhs_extra_args, g_extra_args = g_extra_args) + + self._update_nordsieck = True + self._IWORK = IWORK + self._RWORK = RWORK + #hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() + #self._nordsieck_array = \ + # RWORK[nordsieck_start_index:nordsieck_start_index+(nq+1)*nyh].reshape((nyh,-1),order='F') + #self._nyh = nyh + self._event_info = roots + + if opts["report_continuously"]: + flag_initialize = self.report_solution(t, y, opts) + if flag_initialize: + #If a step event has occured the integration has to be reinitialized + ISTATE = 3 + else: + #Store results + tlist.append(t) + ylist.append(y.copy()) + + #Checking return + if ISTATE == 2: + flag = ID_PY_COMPLETE + elif ISTATE == 3: + flag = ID_PY_EVENT + else: + raise ODEPACK_Exception("LSODAR failed with flag %d"%ISTATE) + + else: + + #Change the ITASK + ITASK = 4 #For computation of yout + + output_index = opts["output_index"] + output_list = opts["output_list"][output_index:] + + flag = ID_PY_COMPLETE + + for tout in output_list: + output_index += 1 + + y, t, ISTATE, RWORK, IWORK, roots = dlsodar(rhs, y.copy(), t, tout, ITOL, + rtol, atol, + ITASK, ISTATE, IOPT, RWORK, IWORK, jac_fcn, JT, g_fcn, JROOT, + f_extra_args = rhs_extra_args, g_extra_args = g_extra_args) + + #Store results + tlist.append(t) + ylist.append(y.copy()) + self._event_info = roots + + #Checking return + if ISTATE == 2 and t >= tf: + flag = ID_PY_COMPLETE + break + elif ISTATE == 3: + flag = ID_PY_EVENT + break + elif ISTATE < 0: + raise ODEPACK_Exception("LSODAR failed with flag %d"%ISTATE) + + opts["output_index"] = output_index + # deciding on restarting options + self._rkstarter_active = True if ISTATE == 3 and self.rkstarter > 1 else False + #print 'rkstarter_active set to {} and ISTATE={}'.format(self._rkstarter_active, ISTATE) + + #Retrieving statistics + self.statistics["nstatefcns"] += IWORK[9] + self.statistics["nsteps"] += IWORK[10] + self.statistics["nfcns"] += IWORK[11] + self.statistics["njacs"] += IWORK[12] + self.statistics["nstateevents"] += 1 if flag == ID_PY_EVENT else 0 + # save RWORK, IWORK for restarting feature + if self.rkstarter>1: + self._RWORK=RWORK + self._IWORK=IWORK + + return flag, tlist, ylist + + def get_algorithm_data(self): + """ + Returns the order and step size used in the last successful step. + """ + hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() + + return hu, nqu + + def state_event_info(self): + """ + Returns the state events indicator as a list where a one (1) + indicates that the event function have been activated and a (0) + if not. + """ + return self._event_info + + def print_statistics(self, verbose=NORMAL): + """ + Prints the run-time statistics for the problem. + """ + Explicit_ODE.print_statistics(self, verbose) #Calls the base class + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : LSODAR ', verbose) + self.log_message(' Absolute tolerances : {}'.format(self.options["atol"]), verbose) + self.log_message(' Relative tolerances : {}'.format(self.options["rtol"]), verbose) + if self.rkstarter==1: + self.log_message(' Starter : {}'.format('classical'), verbose) + else: + self.log_message(' Starter : Runge-Kutta order {}'.format(self.rkstarter), verbose) + if self.maxordn < 12 or self.maxords < 5: + self.log_message(' Maximal order Adams : {}'.format(self.maxordn), verbose) + self.log_message(' Maximal order BDF : {}'.format(self.maxords), verbose) + if self.maxh > 0. : + self.log_message(' Maximal stepsize maxh : {}'.format(self.maxh), verbose) + self.log_message('', verbose) + + def _set_usejac(self, jac): + self.options["usejac"] = bool(jac) + + def _get_usejac(self): + """ + This sets the option to use the user defined jacobian. If a + user provided jacobian is implemented into the problem the + default setting is to use that jacobian. If not, an + approximation is used. + + Parameters:: + + usejac + - True - use user defined jacobian + False - use an approximation + + - Should be a boolean. + + Example: + usejac = False + """ + return self.options["usejac"] + + usejac = property(_get_usejac,_set_usejac) + + def _set_atol(self,atol): + + self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) + + if len(self.options["atol"]) == 1: + self.options["atol"] = self.options["atol"]*N.ones(self._leny) + elif len(self.options["atol"]) != self._leny: + raise ODEPACK_Exception("atol must be of length one or same as the dimension of the problem.") + + def _get_atol(self): + """ + Defines the absolute tolerance(s) that is to be used by the solver. + Can be set differently for each variable. + + Parameters:: + + atol + - Default '1.0e-6'. + + - Should be a positive float or a numpy vector + of floats. + + Example: + atol = [1.0e-4, 1.0e-6] + """ + return self.options["atol"] + + atol=property(_get_atol,_set_atol) + + def _set_rtol(self,rtol): + try: + self.options["rtol"] = float(rtol) + except (ValueError, TypeError): + raise ODEPACK_Exception('Relative tolerance must be a (scalar) float.') + if self.options["rtol"] <= 0.0: + raise ODEPACK_Exception('Relative tolerance must be a positive (scalar) float.') + + def _get_rtol(self): + """ + Defines the relative tolerance that is to be used by the solver. + + Parameters:: + + rtol + - Default '1.0e-6'. + + - Should be a positive float. + + Example: + rtol = 1.0e-4 + """ + return self.options["rtol"] + + rtol=property(_get_rtol,_set_rtol) + + def _get_maxsteps(self): + """ + The maximum number of steps allowed to be taken to reach the + final time. + + Parameters:: + + maxsteps + - Default 10000 + + - Should be a positive integer + """ + return self.options["maxsteps"] + + def _set_maxsteps(self, max_steps): + try: + max_steps = int(max_steps) + except (TypeError, ValueError): + raise ODEPACK_Exception("Maximum number of steps must be a positive integer.") + self.options["maxsteps"] = max_steps + + maxsteps = property(_get_maxsteps, _set_maxsteps) + def _get_hmax(self): + """ + The absolute value of the maximal stepsize for all methods + + Parameters:: + + hmax + - Default: 0. (no maximal step size) + + - Should be a positive float + """ + logging.warning("The option 'hmax' is deprecated and will be removed, please use 'maxh' instead.") + return self.options["maxh"] + def _set_hmax(self,hmax): + if not (isinstance(hmax,float) and hmax >= 0.): + raise ODEPACK_Exception("Maximal step size hmax should be a positive float") + logging.warning("The option 'hmax' is deprecated and will be removed, please use 'maxh' instead.") + self.options["maxh"]=hmax + hmax = property(_get_hmax, _set_hmax) + def _get_maxh(self): + """ + The absolute value of the maximal stepsize for all methods + + Parameters:: + + maxh + - Default: 0. (no maximal step size) + + - Should be a positive float + """ + return self.options["maxh"] + def _set_maxh(self,maxh): + if not (isinstance(maxh,float) and maxh >= 0.): + raise ODEPACK_Exception("Maximal step size maxh should be a positive float") + self.options["maxh"]=maxh + maxh = property(_get_maxh, _set_maxh) + def _get_maxordn(self): + """ + The maximum order used by the Adams-Moulton method (nonstiff case) + + Parameters:: + + maxordn + - Default 12 + + - Should be a positive integer + """ + return self.options["maxordn"] + + def _set_maxordn(self, maxordn): + try: + maxordn = int(maxordn) + except (TypeError, ValueError): + raise ODEPACK_Exception("Maximum order must be a positive integer.") + if maxordn > 12: + raise ODEPACK_Exception("Maximum order should not exceed 12.") + self.options["maxordn"] = maxordn + + maxordn = property(_get_maxordn, _set_maxordn) + def _get_maxords(self): + """ + The maximum order used by the BDF method (stiff case) + + Parameters:: + + maxords + - Default 5 + + - Should be a positive integer + """ + return self.options["maxords"] + + def _set_maxords(self, maxords): + try: + maxords = int(maxords) + except (TypeError, ValueError): + raise ODEPACK_Exception("Maximum order must be a positive integer.") + if maxords > 5: + raise ODEPACK_Exception("Maximum order should not exceed 5.") + self.options["maxords"] = maxords + + maxords = property(_get_maxords, _set_maxords) + def _get_rkstarter(self): + """ + This defines how LSODAR is started. + (classically or with a fourth order Runge-Kutta starter) + + Parameters:: + + rkstarter + - Default False starts LSODAR in the classical multistep way + + - Should be a Boolean + """ + return self.options["rkstarter"] + + def _set_rkstarter(self, rkstarter): + if not rkstarter in {1,2,3,4,5}: + raise ODEPACK_Exception("Must be a positive integer less than 6") + self.options["rkstarter"] = rkstarter + + rkstarter = property(_get_rkstarter, _set_rkstarter) + +class RKStarterNordsieck(object): + """ + A family of Runge-Kutta starters producing a + Nordsieck array to (re-)start a Nordsieck based multistep + method with a given order. + + See: Mohammadi (2013): https://lup.lub.lu.se/luur/download?func=downloadFile&recordOId=4196026&fileOId=4196027 + """ + # Gamma matrix of Gear's RK starter which produce Nordsieck vector at t0 + Gamma_0=[N.array([[1.,0.], # 1st order + [0.,1.]]), + N.array([[1.,0.,0.], # 2nd order + [0.,1.,-1.], + [0.,0.,1.]]), + N.array([[1.,0.,0.,0.], # 3rd order + [0.,1.,-5./3.,1.], + [0.,0.,3.,-2.], + [0.,0.,0.,1.], + [0.,0.,-4./3.,0.]]), + N.array([[1.,0.,0.,0.,0.], # 4th order + [0.,1.,-5./6.,4./9.,-1./9.], + [0.,0.,0.,0.,0.], + [0.,0.,1./2.,-4./9.,1./9.], + [0.,0.,7./3.,-19./9.,7./9.], + [0.,0.,-3.,10./3.,-4./3.], + [0.,0.,1.,-11./9.,5./9.]])] + + # A matrices of RK starter with equidistanced states + + A_s=[ N.array([1]), # 1st order + N.array([[0.,0],[1,0]]), # 2nd order + N.array([[0.,0.,0.,0.,0.],[1./2,0.,0.,0.,0.], # 3rd order + [0.,3./4,0.,0.,0.],[2./9,1./3,4./9,0.,0.], + [17./72,1./6,2./9,-1./8,0.]]), + N.array([[0.,0.,0.,0.,0.,0.,0.], # 4th order + [1./6.,0.,0.,0.,0.,0.,0.], + [0.,1./6.,0.,0.,0.,0.,0.], + [0.,0.,1./3.,0.,0.,0.,0.], + [1./18.,1./9.,1./9.,1./18.,0.,0.,0.], + [2.5,-3.,-3.,2.25,2.25,0.,0.], + [10./45.,-8./45.,-8./45.,-4./45.,13./15.,1./45.,0.]]), + N.array([[0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.], # 5th order + [1./20,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.], + [3./160,9./160,0.,0.,0.,0.,0., 0.,0.,0.,0.,0.,0.,0.], + [3./40,-9./40,6./20,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.], + [-11./216,5/8,-70/108,35./108,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.], + [1631/221184,175./2048,575./55296,44275./442368,253./16384,0.,0.,0.,0.,0.,0.,0.,0.,0.], + [37./1512,0.,250/2484,125./2376,0.,512/7084,0.,0.,0.,0.,0.,0.,0.,0.], + [32.2687682,-39.4956646,-20.7849921,22.2296308,45.4543383,51.9961815,-91.1682621,0.,0.,0.,0.,0.,0.,0.], + [-23.867154567764317,41+31195543/31462660,8+6080897/76520184,-23-7543649/15003372,-63-91662407/130833420,-55-27823937/32947321,117+23615874/27978401,0.,0.,0.,0.,0.,0.,0.], + [-0.5118403967750724,0.,2+379808/6250467,-2-866339/6430593,-1-151963/7810968,-18723895/33225736,8/3,0.,303385/143008499,0.,0.,0.,0.,0.], + [-118.50992840087292,4+1586290/42052551,7+76050433/143964928,11+61385198/111742353,16+54354964/63299173,15+84679766/214253769,15+71618817/71932238,24+14108046/149713813, + 2+12784603/81260661,21+38484710/59808171,0.,0.,0.,0.], + [10.183365237189525,0.,-36-34947657/185923229,38+50251318/55929787,18+114017528/325586295,10+78529445/98887874,-43.5,0.,-6714159/175827524,2.25,0.,0.,0.,0.], + [-0.1491588850008383,0.,19145766/113939551,8434687/36458574,2012005/39716421,8409989/57254530,10739409/94504714,0.,-3321/86525909,-30352753/150092385,0.,70257074/109630355, + 0.,0.], + [0.03877310906055409,0.,3021245/89251943,5956469/58978530,851373/32201684,11559106/149527791,11325471/112382620,0.,-12983/235976962,17692261/82454251,0., + 38892959/120069679,11804845./141497517,0.]], dtype=object)] + + co_ord_s=[[],[],[4],[4,6],[6,9,11]] + b_s=[ N.array([1]), + N.array([1./2,1./2]), + N.array([1./6,0.,0.,1./6,2./3]), + N.array([29./1062.,83./531.,83./531.,83./1062.,2./531.,56./531.,251./531.]), # b-vectors of of 1st to 5th order RK starter method + N.array([0.03877310906055409,0.,3021245/89251943,5956469/58978530,851373/32201684,11559106/149527791,11325471/112382620,0.,-12983/235976962,17692261/82454251,0., + 38892959/120069679,11804845./141497517,0.])] + + C_s=[ N.array([0.]), + N.array([0.]), + N.array([0.,1./2,3./4,1.,1./2,1.]), + N.array([0.,1./6,1./6,1./3,1./3,1.,1.,2./3,1.]), + N.array([0.,1./20,3./40,3./20,1./4,7./28,1./4,2./4,1.,2./4,3./4,3./4,1.,1.])] # C-values in Butcher tableau of 8-stages Runge-Kutta + + # A matrices of RK starter with non-equidistanced stages + A_n=[ N.array([1]), + N.array([[0.,0.],[0.,0.]]), + N.array([[0.,0.,0.,0.],[1./2,0.,0.,0.],[0.,1./2,0.,0.],[0.,0.,1.,0.]]), # 3rd order + N.array([[0.,0.,0.,0.,0.,0.],[2./5,0.,0.,0.,0.,0.],[-3./20,3./4,0.,0.,0.,0.], # 4th order + [19./44,-15./44,40./44,0.,0.,0.],[-31./64,185./192,5./64,-11./192,0.,0.],[11./72,25./72,25./72,11./72,0.,0.]])] + + b_n=[ N.array([0.]), + N.array([1./2.,1./2.]), + N.array([[5./24,1./6,1./6,-1/24],[1./6,1./3,1./3,1./6]]), + N.array([[802./5625,68./225,-67./225,-143./5625,144./625,6./125],[699./5000,81./200,-39./200,99./5000,144./625,0.],[11./72,25./72,25./72,11./72,0.,0.]])] + + C_n=[ N.array([0.]), + N.array([0.]), + N.array([0.,1./2,1./2,1.]), + N.array([0.,2./5,3./5,1.,1./2,1.])] + + #co_ord_n=[[],[],[1./2,1.],[2./5,3./5,1.]] + #A=N.array([[1.,0.,0.,0.], + # [1.,1./9.,1./27,1./81.], + # [1.,4./9.,8./27,16./81.], + # [1.,1.,1.,1.]]) + A=[N.array([0.]), # Convert the state values to Nordsieck vector + N.array([0.]), + N.array([1.]), + N.array([[1./4,1./8],[1.,1.]]), + N.array([[1./9,1./27,1./81.], + [4./9.,8./27,16./81], + [1.,1.,1.]]), + N.array([[1./16,1./64,1./256,1./1024], + [1./4,1./8,1./16,1./32], + [9./16,27./64,81./256,243./1024], + [1.,1.,1.,1.]])] + + scale=N.array([1, 1, 1/2., 1./6., 1./24., 1./120.]).reshape(-1,1) + + + def __init__(self, rhs, H, method='RKs_f', eval_at=0., number_of_steps=4): + """ + Initiates the Runge-Kutta Starter. + + Parameters:: + + rhs + - The problem's rhs function + + H + - starter step size + + eval_at + - Where to evaluate the Nordiseck vector + evaluation point is (eval_at)*H + Possible choices + eval_at=0. + eval_at=1. + + number_of_steps + - the number of steps :math:`(k)` to start the multistep method with + This will determine the initial order of the method, which is + :math:`k` for BDF methods and :math:`k+1` for Adams Moulton Methods . + """ + self.f = rhs + self.H = H + self.method=method + + if not 1 < number_of_steps < 6: + raise RKStarter_Exception('Step number larget than 4 not yet implemented') + self.number_of_steps = number_of_steps + self.eval_at = float(eval_at) + if not self.eval_at == 0.: + raise RKStarter_Exception("Parameter eval_at different from 0 not yet implemented.") + + def RKs_f(self,t0,y0,sw0): + + s=self.number_of_steps + A_s,C_s,b_s,co_ord_s=self.A_s,self.C_s,self.b_s,self.co_ord_s + A_s=A_s[s-1] + C_s=C_s[s-1] + b_s=b_s[s-1] + co_ord_s=co_ord_s[s-1] + H=(s-1)*self.H + K=N.zeros((N.size(A_s,0),len(y0))) + for i in range(N.size(A_s,0)): + K[i,:]=self.f(t0+C_s[i]*H,y0+H*N.dot(A_s[i,:],K),sw0) + y=N.zeros((s,len(y0))) + y[0,:]=y0 + for i in range(1,s): + if i==s-1: + y[i,:]=y0+H*N.dot(b_s,K) + else: + y[i,:]=y0+H*N.dot(A_s[co_ord_s[i-1],:],K) + return y + def RKn_f(self,t0,y0,sw0): + s=self.number_of_steps + H=(s-1)*self.H + A_n,C_n,b_n=self.A_n,self.C_n,self.b_n + A_n=A_n[s-1] + C_n=C_n[s-1] + b_n=b_n[s-1] + + K=N.zeros((N.size(A_n,0),len(y0))) + for i in range(N.size(A_n,0)): + K[i,:]=self.f(t0+C_n[i]*H,y0+H*N.dot(A_n[i,:],K),sw0) + y=N.zeros((s,len(y0))) + y[0,:]=y0 + for i in range(1,s): + y[i,:]=y0+H*N.dot(b_n[i-1],K) + return y + + + def rk_like4(self, t0, y0, sw0): + """ + rk_like computes Runge-Kutta stages + Note, the currently implementation is **only** correct for + autonomous systems. + """ + f = lambda y: self.f(t0, y, sw0) + h = self.H/4. + k1 = h*f(y0) + k2 = h*f(y0 + k1) + k3 = h*f(y0 + 2. * k2) + k4 = h*f(y0 + 3./4. * k1 + 9./4. * k3) + k5 = h*f(y0 + k1/2. + k2 + k3/2. + 2. * k4) + k6 = h*f(y0+k1/12.+2. * k2 + k3/4. + 2./3. * k4 + 2. * k5) + return N.array([y0,k1,k2,k3,k4,k5,k6]) + def rk_like3(self, t0, y0, sw0): + """ + rk_like computes Runge-Kutta stages + Note, the currently implementation is **only** correct for + autonomous systems. + + """ + f = lambda y: self.f(t0, y, sw0) + h = self.H/3. + k1 = h*f(y0) + k2 = h*f(y0 + k1) + k3 = h*f(y0 + k1+ k2) + k4 = h*f(y0 + 3./2. * k1) + return N.array([y0,k1,k2,k3,k4]) + def rk_like2(self, t0, y0, sw0): + """ + rk_like2 computes Runge-Kutta 2nd-stages + Note, the currently implementation is **only** correct for + autonomous systems. + """ + f=lambda y: self.f(t0, y, sw0) + h=self.H/2. + k1=h*f(y0) + k2=h*f(y0+k1) + return N.array([y0,k1,k2]) + def rk_like13(self, t0, y0, sw0): + """ + rk_like6 computes Runge-Kutta 8th-stages + """ + h = self.H + self.Gamma_2=self.Gamma_0[3] + f=lambda y: self.f(t0 , y , sw0) + K=N.zeros((6,len(y0))) + sol=N.zeros((3,len(y0))) + b=N.zeros((2,len(y0))) #remove the fifth stage value that is for error estimation + nord = N.zeros((4,len(y0))) #Nordsieck vector + for i in range(5): + K[i,:]= f(y0+h*N.dot(self.Gamma_2[i,:],K)) + c=0 + for i in range(3): + sol[i,:]=y0+h*N.dot(self.Gamma_2[i+3,:],K) + if i!=0: + b[c,:]=sol[i,:]-y0-(c+1)*h/2*K[0,:] + c+=1 + nord[0,:] = y0 + nord[1,:] = h*K[0,:] + nord[2:,:] = Sc.solve(self.A[self.number_of_steps],b) + return nord + def rk_like14(self, t0, y0, sw0): + """ + rk_like6 computes Runge-Kutta 8th-stages + """ + h = self.H + Gamma_2=self.Gamma_0[4] + f=lambda y: self.f(t0 , y , sw0) + K=N.zeros((8,len(y0))) + sol=N.zeros((4,len(y0))) + b=N.zeros((3,len(y0))) #remove the fifth stage value that is for error estimation + nord = N.zeros((5,len(y0))) #Nordsieck vector + for i in range(7): + K[i,:]= f(y0+h*N.dot(Gamma_2[i,:],K)) + c=0 + for i in range(4): + sol[i,:]=y0+h*N.dot(Gamma_2[i+4,:],K) + if i!=1: + b[c,:]=sol[i,:]-y0-(c+1)*h/3*K[0,:] + c+=1 + nord[0,:] = y0 + nord[1,:] = h*K[0,:] + nord[2:,:] = Sc.solve(self.A[self.number_of_steps],b) + return nord + def rk_like15(self, t0, y0, sw0): + """ + rk_like6 computes Runge-Kutta 5th-stages ****needs to be modified**** + """ + h = self.H + Gamma_2=self.Gamma_0[5] + f=lambda y: self.f(t0 , y , sw0) + K=N.zeros((14,len(y0))) + sol=N.zeros((8,len(y0))) + b=N.zeros((4,len(y0))) #remove the fifth stage value that is for error estimation + nord = N.zeros((6,len(y0))) #Nordsieck vector + for i in range(13): + K[i,:]= f(y0+h*N.dot(Gamma_2[i,:],K)) + c=0 + for i in range(8): + sol[i,:]=y0+h*N.dot(Gamma_2[i+6,:],K) + if (i!=1) and (i!=2) and (i!=4) and (i!=6): + b[c,:]=sol[i,:]-y0-(c+1)*h/4*K[0,:] + c+=1 + nord[0,:] = y0 + nord[1,:] = h*K[0,:] + nord[2:,:] = Sc.solve(self.A[self.number_of_steps],b) + return nord + def nordsieck(self,k): + """ + Nordsieck array computed at initial point + """ + nord=self.scale[:self.number_of_steps+1]*N.dot(self.Gamma_0[self.number_of_steps-1].T,k) + + return nord + def Nordsieck_RKn(self,t0,y,sw0): + s=self.number_of_steps + H=(s-1)*self.H + co_nord=[N.array([1./2,1.]),N.array([2./5,3./5,1.])] + l=size(y,0) + y0=y[0,:] + yf=self.f(t0,y0,sw0) + + if l==3: + co=N.array([co_nord[0]]) + nord_n=N.vander(co_nord[0],self.number_of_steps+1) + b=y[1:]-y0-co.T*yf + nord=Sc.solve(nord_n[0:2,0:2],b) + elif l==4: + co=N.array([co_nord[1]]) + nord_n=N.vander(co_nord[1],self.number_of_steps+1) + b=y[1:]-y0-H*co.T*yf + nord=Sc.solve(nord_n[0:3,0:3],b) + nord=N.vstack((y0,H*yf,nord[::-1])) + return nord + def Nordsieck_RKs(self,t0,y,sw0): + s=self.number_of_steps + H=(s-1)*self.H + co_nord=[N.array([1]),N.array([1./2,1]),N.array([1./3,2./3,1]), + N.array([1./4,2./4,3./4,1.])] + A=self.A + y0=y[0,:] + yf=self.f(t0,y0,sw0) + co=co_nord[s-2] + co=N.array([co]) + b=y[1:]-y0-H*co.T*yf + nord=Sc.solve(A[s],b) + nord=N.vstack((y0,H*yf,nord)) + return nord + + + + def __call__(self, t0 , y0, sw0=[]): + """ + Evaluates the Runge-Kutta starting values + + Parameters:: + + y0 + - starting value + """ + if self.method=='RK_G': + # We construct a call like: rk_like4(self, t0, y0, sw0) + k=self.__getattribute__('rk_like{}'.format(self.number_of_steps))(t0, y0, sw0) + t = t0+self.eval_at*self.H + #t= t0 + self.H + k=self.nordsieck(k) + + elif self.method=='RKs_f': + y=self.RKs_f(t0, y0, sw0) + t = t0+self.eval_at*self.H + k=self.Nordsieck_RKs(t0,y,sw0) + + elif self.method=='RKn_f': + y=self.RKn_f(t0,y0,sw0) + t=t0+self.eval_at*self.H + k=self.Nordsieck_RKn(t0,y,sw0) + return t,k + + + diff --git a/src/solvers/radar5.py b/src/solvers/radar5.py index c7626fe0..ed939257 100644 --- a/src/solvers/radar5.py +++ b/src/solvers/radar5.py @@ -1,905 +1,905 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import scipy as S -import scipy.linalg as LIN -import copy -import sys - -from assimulo.exception import * -from assimulo.ode import * - -from assimulo.explicit_ode import Explicit_ODE -from assimulo.implicit_ode import Implicit_ODE - -try: - from assimulo.lib import radar5 -except ImportError: - sys.stderr.write("Could not find RADAR5\n") - -class Radar_Exception(Exception): - pass - -class Radar5ODE(Explicit_ODE): - """ - Radar5 - """ - - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Delay_Explicit_Problem' class. - """ - Explicit_ODE.__init__(self, problem) #Calls the base class - - #Default values - self.options["inith"] = 0.01 - self.options["newt"] = 7 #Maximum number of newton iterations - self.options["thet"] = 1.e-3 #Boundary for re-calculation of jac -# self.options["fnewt"] = 0.0 #Stopping critera for Newtons Method - self.options["fnewt"] = 0.03 #Stopping critera for Newtons Method - self.options["quot1"] = 1.0 #Parameters for changing step-size (lower bound) - self.options["quot2"] = 1.2 #Parameters for changing step-size (upper bound) - self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) - self.options["fac2"] = 8.0 #Parameters for step-size selection (upper bound) - self.options["maxh"] = N.inf #Maximum step-size. - self.options["safe"] = 0.9 #Safety factor - self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - self.options["usejac"] = True if self.problem_info["jac_fcn"] else False - self.options["maxsteps"] = 10000 - - self.options["alpha"] = 0.0 # Parameter to tune the error control of dense output (smaller = stronger control) - self.options["tckbp"] = 5.0 # Parameter for controlling the search for breaking points - self.options["ieflag"] = 0 # Switch between different modes of error control - self.options["mxst"] = 100 # The maximum number of stored dense output points - self.options["usejaclag"] = True if self.problem_info["jaclag_fcn"] else False - - SQ6 = N.sqrt(6.0) - C1 = (4.0-SQ6)/10.0 - C2 = (4.0+SQ6)/10.0 - self.C1M1 = C1-1.0 - self.C2M1 = C2-1.0 - - # - Statistic values - self.statistics["nsteps"] = 0 #Number of steps - self.statistics["nfcn"] = 0 #Number of function evaluations - self.statistics["njac"] = 0 #Number of Jacobian evaluations - self.statistics["njacfcn"] = 0 #Number of function evaluations when evaluating the jacobian - self.statistics["errfail"] = 0 #Number of step rejections - self.statistics["nlu"] = 0 #Number of LU decompositions - self.statistics["nstepstotal"] = 0 #Number of total computed steps (may NOT be equal to nsteps+nerrfail) - - #Internal values - self._leny = len(self.y) #Dimension of the problem - self._type = '(explicit)' - self._yDelayTemp = [] - self._ntimelags = len(self.problem.lagcompmap) - for i in range(self._ntimelags): - self._yDelayTemp.append(range(len(self.problem.lagcompmap[i]))) - flat_lagcompmap = [] - for comp in self.problem.lagcompmap: - flat_lagcompmap.extend(comp) - self._nrdens = len(N.unique(flat_lagcompmap)) - self._ipast = N.unique(flat_lagcompmap).tolist()+[0] - self._grid = N.array([]) - -# if hasattr(problem, 'pbar'): - - def initialize(self): - #Reset statistics - for k in self.statistics.keys(): - self.statistics[k] = 0 - - self._tlist = [] - self._ylist = [] - - def _solout(self,nr, told, t, hold, y, cont,irtrn): - """ - This method is called after every successful step taken by Radar5 - """ - #print "SOLOUT:", told, t, hold, y, cont -# print cont -# print told, t, told + hold - if self._opts["output_list"] is None: - self._tlist.append(t) - self._ylist.append(y.copy()) - else: - output_list = self._opts["output_list"] - output_index = self._opts["output_index"] - try: - while output_list[output_index] <= t: - self._tlist.append(output_list[output_index]) - - yval = N.empty(self._leny) - for i in range(self._leny): -# yval[i] = radar5.contr5(i+1,self.problem_info["dim"],output_list[output_index],t,hold) - yval[i] = radar5.contr5(i+1,self.problem_info["dim"],output_list[output_index],cont,t,hold) -# yval[i] = radar5.contr5(i+1,output_list[output_index], cont) - - self._ylist.append(yval) - - output_index = output_index+1 - except IndexError: - pass - self._opts["output_index"] = output_index - - return irtrn - - #def coutput(self,t): - #Nx = self.problem_info["dim"] - #y = N.zeros(Nx) - - #theta, pos = radar5.lagr5(10, t, None, self.arglag, self.past, self.problem.phi, self.problem.ipast) - #for i in range(1,Nx+1): - #y[i-1] = radar5.ylagr5(i, theta, pos, self.problem.phi, self.past, self.problem.ipast) - #return y - - def coutput(self, t, i = -1): - """ - Return the continous output solution at time t. - - t: time - i: solution component (default -1 gives the whole vector) - """ - Nx = self.problem_info["dim"] - y = N.zeros(Nx) - - - # t belongs to the interval (tk[ik], tk[ik+1]) - ik = N.searchsorted(self.tk, t) - 1 - - I = self.idif*ik - - H = self.past[I+self.idif-1] - theta = (t - (self.past[I] + H))/H - - - if i == -1: - # The line below this comment is what's effectively happening, - # but it is unfortunately extremely slow compared to the - # vectorized version below that doesn't use the cpoly function: - #return N.array([self.cpoly(i, I, theta) for i in range(self.problem_info["dim"])]) - nrds = self._nrdens - I = I + 1 - I2 = I + self.problem_info["dim"] - return self.past[I:I2] + theta*(self.past[nrds+I:nrds+I2] + (theta-self.C2M1)*(self.past[2*nrds+I:2*nrds+I2] + (theta-self.C1M1)*(self.past[3*nrds+I:3*nrds+I2]))) - elif i >= 0: - return self.cpoly(i, I, theta) - else: - raise ValueError('i has to be either -1 or a positive integer <= the problem dimension') - - def cpoly(self, i, I, theta): - """ - Evaluate the I:th dense output polynomial for component i at theta. - """ - nrds = self._nrdens - I = I + i + 1 - return self.past[I] + theta*(self.past[nrds+I] + (theta-self.C2M1)*(self.past[2*nrds+I] + (theta-self.C1M1)*(self.past[3*nrds+I]))) - - def arglag(self, i, t, y, past, ipast): - return self.problem.time_lags(t,y)[i-1] - - - def compute_ydelay(self, t, y, past, ipast): - ydelay = self._yDelayTemp - for i in range(1, self._ntimelags+1): - theta, pos = radar5.lagr5(i, t, y, self.arglag, past, self.problem.phi, ipast) - - for j, val in enumerate(self.problem.lagcompmap[i-1]): - ydelay[i-1][j] = radar5.ylagr5(val+1, theta, pos, self.problem.phi, past, ipast) - - return ydelay - - def F(self, t, y, past, ipast): - #print 'F:', t, y, past, ipast - - # First find the correct place in the past vector for each time-lag - # then evaluate all required solution components at that point - ydelay = self.compute_ydelay(t,y, past, ipast) - - # Now we can compute the right-hand-side - return self.problem.rhs(t, y, ydelay) - - def Fjac(self, t, y, past, ipast): - # First find the correct place in the past vector for each time-lag - # then evaluate all required solution components at that point - ydelay = self.compute_ydelay(t,y, None, None, past, ipast) - - # Now we can compute the right-hand-side - return self.problem.jac(t, y, ydelay) - - - def integrate(self, t, y, tf, opts): - ITOL = 1 #Both atol and rtol are vectors - IJAC = 0 if self.usejac else 0 #Switch for the jacobian, 0==NO JACOBIAN - MLJAC = self.problem_info["dim"] #The jacobian is full - MUJAC = self.problem_info["dim"] #See MLJAC - IMAS = 0 #The mass matrix is the identity - MLMAS = self.problem_info["dim"] #The mass matrix is full - MUMAS = self.problem_info["dim"] #See MLMAS - IOUT = 1 #solout is called after every step - WORK = N.array([0.0]*30) #Work (double) vector - IWORK = N.array([0]*30) #Work (integer) vector - - #Setting work options - WORK[0] = N.finfo(N.double).eps # Rounding unit - WORK[1] = self.safe - WORK[2] = self.thet - WORK[3] = self.fnewt - WORK[4] = self.quot1 - WORK[5] = self.quot2 - WORK[6] = self.maxh - WORK[7] = self.fac1 - WORK[8] = self.fac2 - WORK[9] = self.alpha - WORK[10] = self.tckbp - - #Setting iwork options - IWORK[1] = self.maxsteps - IWORK[2] = self.newt - IWORK[7] = 1 - IWORK[10] = self.ieflag - IWORK[11] = self.mxst - IWORK[12] = len(self.grid) - IWORK[13] = 1 - IWORK[14] = self._nrdens - - self.idif = 4*self._nrdens + 2 - lrpast = self.mxst*self.idif - past = N.zeros(lrpast) - - #past = N.zeros(self.mxst*(4*self.problem.nrdens+2)) -# print WORK -# print IWORK - - #Dummy methods - mas_dummy = lambda t:x - jac_dummy = (lambda t:x) if not self.usejac else self.Fjac - jaclag_dummy = (lambda t:x) if not self.usejaclag else self.problem.jaclag - nlags = 0 if not self.usejaclag else self.problem.nlags - njacl = 0 if not self.usejaclag else self.problem.njacl - - #Store the opts - self._opts = opts - #print "INIT", t,y,tf,self.inith, self.problem.ipast - #print "GRID", self.problem.grid, self.problem.ngrid - #t, y, h, iwork, flag, past = radar5.assimulo_radar5(self.F, \ - a = radar5.assimulo_radar5(self.F, \ - self.problem.phi, \ - self.arglag, \ - t, \ - y.copy(), \ - tf, \ - self.inith, \ - self.rtol*N.ones(self.problem_info["dim"]), \ - self.atol, \ - ITOL, \ - jac_dummy, \ - IJAC, \ - MLJAC, \ - MUJAC, \ - jaclag_dummy, \ - nlags, \ - njacl, \ - IMAS, \ - self._solout, \ - IOUT, \ - WORK, \ - IWORK, \ - self.grid.tolist()+[0.0], \ - self._ipast, \ - mas_dummy, \ - MLMAS, \ - MUMAS, - past), -# lrpast)#, \ - #past, \ - #IWORK[14]+1, \ -## IWORK[14], \ - #self.problem.ngrid, \ - #len(past) \ - #) - t, y, h, iwork, flag, past = a[0] - #print a[0] - #print len(a[0]) - #self.past = copy.deepcopy(past) - self.past = past - self.tk = N.trim_zeros(self.past[::self.idif], 'b') - self.hk = N.trim_zeros(self.past[self.idif-1:-1:self.idif], 'b') - - #Checking return - if flag == 1: - flag = ID_PY_COMPLETE - elif flag == 2: - flag = ID_PY_EVENT - else: - raise Exception("Radar5 failed with flag %d"%flag) - - #Retrieving statistics - self.statistics["nsteps"] += iwork[16] - self.statistics["nfcn"] += iwork[13] - self.statistics["njac"] += iwork[14] - self.statistics["nstepstotal"] += iwork[15] - self.statistics["errfail"] += iwork[17] - self.statistics["nlu"] += iwork[18] - - return flag, self._tlist, self._ylist - - def print_statistics(self, verbose=NORMAL): - """ - Prints the run-time statistics for the problem. - """ - self.log_message('Final Run Statistics: %s \n' % self.problem.name, verbose) - - self.log_message(' Number of steps : '+str(self.statistics["nsteps"]), verbose) - self.log_message(' Number of function evaluations : '+str(self.statistics["nfcn"]), verbose) - self.log_message(' Number of Jacobian evaluations : '+ str(self.statistics["njac"]), verbose) - self.log_message(' Number of error test failures : '+ str(self.statistics["errfail"]), verbose) - self.log_message(' Number of LU decompositions : '+ str(self.statistics["nlu"]), verbose) - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : Radar5 ' + self._type, verbose) - self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) - self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) - self.log_message('', verbose) - - def _get_h(self): - """ - Sets the stepsize. - """ - return self.__h - - def _set_h(self, h): - """ - Sets the stepsize. - """ - self.__h = h - - h = property(fget=_get_h,fset=_set_h) - - - def plot_stepsize(self): - """ - Plots the step-size. - """ - P.semilogy(N.diff(self.t),drawstyle='steps-post') - P.title(self.problem.name) - P.ylabel('Step length') - P.xlabel('Number of steps') - P.show() - - def _set_newt(self, newt): - """ - Maximal number of Newton iterations. - - Parameters:: - - newt - - Default '7'. - - - Should be an integer. - - Example: - newt = 10 - """ - try: - self.options["newt"] = int(newt) - except (ValueError, TypeError): - raise Radar_Exception('The newt must be an integer or float.') - - def _get_newt(self): - """ - Maximal number of Newton iterations. - - Parameters:: - - newt - - Default '7'. - - - Should be an integer. - - Example: - newt = 10 - """ - return self.options["newt"] - - newt = property(_get_newt,_set_newt) - - def _set_fnewt(self, fnewt): - try: - self.options["fnewt"] = float(fnewt) - except (ValueError, TypeError): - raise Radar_Exception('The fnewt must be an integer or float.') - - def _get_fnewt(self): - """ - Stopping criterion for Newton's method, usually chosen <1. - Smaller values of fnewt make the code slower, but safer. - - Parameters:: - - fnewt - - Default min(0.03,rtol**0.5) - - - Should be a float. - - Example: - fnewt = 0.05 - """ - return self.options["fnewt"] - - fnewt = property(_get_fnewt,_set_fnewt) - - def _set_safe(self, safe): - try: - self.options["safe"] = float(safe) - except (ValueError, TypeError): - raise Radar_Exception('The safe must be an integer or float.') - - def _get_safe(self): - """ - The safety factor in the step-size prediction. - - Parameters:: - - safe - - Default '0.9' - - - Should be float. - - Example: - safe = 0.8 - """ - return self.options["safe"] - - safe = property(_get_safe, _set_safe) - - def _set_thet(self, thet): - try: - self.options["thet"] = float(thet) - except (ValueError, TypeError): - raise Radar_Exception('The thet must be an integer or float.') - - def _get_thet(self): - """ - Value for determine if the Jacobian is to be recomputed or not. - Increasing thet makes the code compute new Jacobians more seldom. - Negative thet forces the code to compute the Jacobian after every accepted step. - - Parameters:: - - thet - - Default '0.003' - - - Should be float. - - Example: - thet = 0.01 - """ - return self.options["thet"] - - thet = property(_get_thet, _set_thet) - - def _set_max_h(self,max_h): - try: - self.options["maxh"] = float(max_h) - except (ValueError,TypeError): - raise Radar_Exception('Maximal stepsize must be a (scalar) float.') - if self.options["maxh"] < 0: - raise Radar_Exception('Maximal stepsize must be a positive (scalar) float.') - - def _get_max_h(self): - """ - Defines the maximal step-size that is to be used by the solver. - - Parameters:: - - maxh - - Default final time - current time. - - - Should be a float. - - Example: - maxh = 0.01 - - """ - return self.options["maxh"] - - maxh=property(_get_max_h,_set_max_h) - - def _set_initial_step(self, initstep): - try: - self.options["inith"] = float(initstep) - except (ValueError, TypeError): - raise Radar_Exception('The initial step must be an integer or float.') - - def _get_initial_step(self): - """ - This determines the initial step-size to be used in the integration. - - Parameters:: - - inith - - Default '0.01'. - - - Should be float. - - Example: - inith = 0.01 - """ - return self.options["inith"] - - inith = property(_get_initial_step,_set_initial_step) - - - def _set_quot1(self, quot1): - try: - self.options["quot1"] = float(quot1) - except (ValueError, TypeError): - raise Radar_Exception('The quot1 must be an integer or float.') - - def _get_quot1(self): - """ - If quot1 < current step-size / old step-size < quot2 the the step-size is not changed. - This saves LU-decompositions and computing time for large systems. - - Parameters:: - - quot1 - - Default 1.0 - - - Should be a float. - - Example: - quot1 = 0.9 - """ - return self.options["quot1"] - - quot1 = property(_get_quot1, _set_quot1) - - def _set_quot2(self, quot2): - try: - self.options["quot2"] = float(quot2) - except (ValueError, TypeError): - raise Radar_Exception('The quot2 must be an integer or float.') - - def _get_quot2(self): - """ - If quot1 < current step-size / old step-size < quot2 the the step-size is not changed. - This saves LU-decompositions and computing time for large systems. - - Parameters:: - - quot2 - - Default 1.2 - - - Should be a float. - - Example: - quot2 = 1.2 - """ - return self.options["quot2"] - - quot2 = property(_get_quot2, _set_quot2) - - def _set_fac1(self, fac1): - try: - self.options["fac1"] = float(fac1) - except (ValueError, TypeError): - raise Radar_Exception('The fac1 must be an integer or float.') - - def _get_fac1(self): - """ - Parameters for step-size selection. The new step-size is chosen - subject to the restriction fac1 <= current step-size / old step-size <= fac2. - - Parameters:: - - fac1 - - Default 0.2 - - - Should be a float. - - Example: - fac1 = 0.1 - """ - return self.options["fac1"] - - fac1 = property(_get_fac1, _set_fac1) - - def _set_fac2(self, fac2): - try: - self.options["fac2"] = float(fac2) - except (ValueError, TypeError): - raise Radar_Exception('The fac2 must be an integer or float.') - - def _get_fac2(self): - """ - Parameters for step-size selection. The new step-size is chosen - subject to the restriction fac1 <= current step-size / old step-size <= fac2. - - Parameters:: - - fac2 - - Default 8.0 - - - Should be a float. - - Example: - fac2 = 10.0 - """ - return self.options["fac2"] - - fac2 = property(_get_fac2, _set_fac2) - - def _set_usejac(self, jac): - self.options["usejac"] = bool(jac) - - def _get_usejac(self): - """ - This sets the option to use the user defined jacobian. If a - user provided jacobian is implemented into the problem the - default setting is to use that jacobian. If not, an - approximation is used. - - Parameters:: - - usejac - - True - use user defined jacobian - False - use an approximation - - - Should be a boolean. - - Example: - usejac = False - """ - return self.options["usejac"] - - usejac = property(_get_usejac,_set_usejac) - - def _set_usejaclag(self, jaclag): - self.options["usejaclag"] = bool(jaclag) - def _get_usejaclag(self, jaclag): - return self.options["usejaclag"] - usejaclag = property(_get_usejaclag,_set_usejaclag) - - def _set_atol(self,atol): - - self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) - - if len(self.options["atol"]) == 1: - self.options["atol"] = self.options["atol"]*N.ones(self._leny) - elif len(self.options["atol"]) != self._leny: - raise Radar_Exception("atol must be of length one or same as the dimension of the problem.") - - def _get_atol(self): - """ - Defines the absolute tolerance(s) that is to be used by the solver. - Can be set differently for each variable. - - Parameters:: - - atol - - Default '1.0e-6'. - - - Should be a positive float or a numpy vector - of floats. - - Example: - atol = [1.0e-4, 1.0e-6] - """ - return self.options["atol"] - - atol=property(_get_atol,_set_atol) - - def _set_rtol(self,rtol): - try: - self.options["rtol"] = float(rtol) - except (ValueError, TypeError): - raise Radar_Exception('Relative tolerance must be a (scalar) float.') - if self.options["rtol"] <= 0.0: - raise Radar_Exception('Relative tolerance must be a positive (scalar) float.') - - def _get_rtol(self): - """ - Defines the relative tolerance that is to be used by the solver. - - Parameters:: - - rtol - - Default '1.0e-6'. - - - Should be a positive float. - - Example: - rtol = 1.0e-4 - """ - return self.options["rtol"] - - rtol=property(_get_rtol,_set_rtol) - - def _get_grid(self): - """ - TODO - """ - return self._grid - def _set_grid(self, grid): - self._grid - grid=property(_get_grid,_set_grid) - - def _get_maxsteps(self): - """ - The maximum number of steps allowed to be taken to reach the - final time. - - Parameters:: - - maxsteps - - Default 10000 - - - Should be a positive integer - """ - return self.options["maxsteps"] - - def _set_maxsteps(self, max_steps): - try: - max_steps = int(max_steps) - except (TypeError, ValueError): - raise Radar_Exception("Maximum number of steps must be a positive integer.") - self.options["maxsteps"] = max_steps - - maxsteps = property(_get_maxsteps, _set_maxsteps) - - def _get_alpha(self): - """ - Used to tune the error control of dense output. A small value means - stricter control and should be used for problems with almost - discontinuous solutions while alpha = 1.0 can be used for problems - with fairly smooth solutions. - - - Parameters:: - - alpha - - Default 0 - - - Should be in [0.0, 1.0] - """ - return self.options["alpha"] - - def _set_alpha(self, alpha): - try: - self.options["alpha"] = float(alpha) - except (TypeError, ValueError): - raise Radar_Exception("alpha must be a float.") - if (self.options["alpha"] < 0.0) or (self.options["alpha"] > 1.0): - raise Radar_Exception("alpha must be in the interval [0,1].") - - alpha = property(_get_alpha, _set_alpha) - - - def _get_tckbp(self): - """ - Used to control the search for breaking points. If the error increases - by a factor larger than tckbp in one step the routine that searches for - breaking points is activated. - - - Parameters:: - - tckbp - - Default 5.0 - - - Should be a positive float - """ - return self.options["tckbp"] - - def _set_tckbp(self, tckbp): - try: - self.options["tckbp"] = float(tckbp) - except (TypeError, ValueError): - raise Radar_Exception("tckbp must be a float.") - if (self.options["tckbp"] < 0.0): - raise Radar_Exception("tckbp must be a positive float.") - - tckbp = property(_get_tckbp, _set_tckbp) - - - def _get_ieflag(self): - """ - Switch between different modes of error control. - -1: pure control of the dense output (makes use of a quadratic and a - linear polynomial interpolating the stage values) - 0: mixed control of dense output and discrete output - 1: simpler mixed control - 2: pure control of the discrete output (is provided by the routine ESTRAD) - - Parameters:: - - ieflag - - Default 0.0 - - - Should be either -1, 0, 1 or 2 - """ - return self.options["ieflag"] - - def _set_ieflag(self, ieflag): - try: - self.options["ieflag"] = int(ieflag) - except (TypeError, ValueError): - raise Radar_Exception("ieflag must be an integer.") - if (self.options["ieflag"] < -1) or (self.options["ieflag"] > 2): - raise Radar_Exception("ieflag must be either -1, 0, 1 or 2.") - - ieflag = property(_get_ieflag, _set_ieflag) - - - def _get_mxst(self): - """ - The maximum number of steps stored in the dense output array. - The dimension of this array will be nrdens*(4*self.nrdens+2). - - Parameters:: - - mxst - - Default 100 - - - Should be a positive integer - """ - return self.options["mxst"] - - def _set_mxst(self, mxst): - try: - self.options["mxst"] = int(mxst) - except (TypeError, ValueError): - raise Radar_Exception("mxst must be a positive integer.") - if (self.options["mxst"] < 1): - raise Radar_Exception("mxst must be a positive integer.") - - mxst = property(_get_mxst, _set_mxst) - - def _set_usejaclag(self, jaclag): - self.options["usejaclag"] = bool(jaclag) - - def _get_usejaclag(self): - """ - This sets the option to use the user defined lagged jacobian. If a - user provided lagged jacobian is implemented into the problem the - default setting is to use that lagged jacobian. If not, an - approximation is used. - - Parameters:: - - usejaclag - - True - use user defined lagged jacobian - False - use an approximation - - - Should be a boolean. - - Example: - usejaclag = False - """ - return self.options["usejaclag"] - - usejaclag = property(_get_usejaclag,_set_usejaclag) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import scipy as S +import scipy.linalg as LIN +import copy +import sys + +from assimulo.exception import * +from assimulo.ode import * + +from assimulo.explicit_ode import Explicit_ODE +from assimulo.implicit_ode import Implicit_ODE + +try: + from assimulo.lib import radar5 +except ImportError: + sys.stderr.write("Could not find RADAR5\n") + +class Radar_Exception(Exception): + pass + +class Radar5ODE(Explicit_ODE): + """ + Radar5 + """ + + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Delay_Explicit_Problem' class. + """ + Explicit_ODE.__init__(self, problem) #Calls the base class + + #Default values + self.options["inith"] = 0.01 + self.options["newt"] = 7 #Maximum number of newton iterations + self.options["thet"] = 1.e-3 #Boundary for re-calculation of jac +# self.options["fnewt"] = 0.0 #Stopping critera for Newtons Method + self.options["fnewt"] = 0.03 #Stopping critera for Newtons Method + self.options["quot1"] = 1.0 #Parameters for changing step-size (lower bound) + self.options["quot2"] = 1.2 #Parameters for changing step-size (upper bound) + self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) + self.options["fac2"] = 8.0 #Parameters for step-size selection (upper bound) + self.options["maxh"] = N.inf #Maximum step-size. + self.options["safe"] = 0.9 #Safety factor + self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + self.options["usejac"] = True if self.problem_info["jac_fcn"] else False + self.options["maxsteps"] = 10000 + + self.options["alpha"] = 0.0 # Parameter to tune the error control of dense output (smaller = stronger control) + self.options["tckbp"] = 5.0 # Parameter for controlling the search for breaking points + self.options["ieflag"] = 0 # Switch between different modes of error control + self.options["mxst"] = 100 # The maximum number of stored dense output points + self.options["usejaclag"] = True if self.problem_info["jaclag_fcn"] else False + + SQ6 = N.sqrt(6.0) + C1 = (4.0-SQ6)/10.0 + C2 = (4.0+SQ6)/10.0 + self.C1M1 = C1-1.0 + self.C2M1 = C2-1.0 + + # - Statistic values + self.statistics["nsteps"] = 0 #Number of steps + self.statistics["nfcn"] = 0 #Number of function evaluations + self.statistics["njac"] = 0 #Number of Jacobian evaluations + self.statistics["njacfcn"] = 0 #Number of function evaluations when evaluating the jacobian + self.statistics["errfail"] = 0 #Number of step rejections + self.statistics["nlu"] = 0 #Number of LU decompositions + self.statistics["nstepstotal"] = 0 #Number of total computed steps (may NOT be equal to nsteps+nerrfail) + + #Internal values + self._leny = len(self.y) #Dimension of the problem + self._type = '(explicit)' + self._yDelayTemp = [] + self._ntimelags = len(self.problem.lagcompmap) + for i in range(self._ntimelags): + self._yDelayTemp.append(range(len(self.problem.lagcompmap[i]))) + flat_lagcompmap = [] + for comp in self.problem.lagcompmap: + flat_lagcompmap.extend(comp) + self._nrdens = len(N.unique(flat_lagcompmap)) + self._ipast = N.unique(flat_lagcompmap).tolist()+[0] + self._grid = N.array([]) + +# if hasattr(problem, 'pbar'): + + def initialize(self): + #Reset statistics + for k in self.statistics.keys(): + self.statistics[k] = 0 + + self._tlist = [] + self._ylist = [] + + def _solout(self,nr, told, t, hold, y, cont,irtrn): + """ + This method is called after every successful step taken by Radar5 + """ + #print "SOLOUT:", told, t, hold, y, cont +# print cont +# print told, t, told + hold + if self._opts["output_list"] is None: + self._tlist.append(t) + self._ylist.append(y.copy()) + else: + output_list = self._opts["output_list"] + output_index = self._opts["output_index"] + try: + while output_list[output_index] <= t: + self._tlist.append(output_list[output_index]) + + yval = N.empty(self._leny) + for i in range(self._leny): +# yval[i] = radar5.contr5(i+1,self.problem_info["dim"],output_list[output_index],t,hold) + yval[i] = radar5.contr5(i+1,self.problem_info["dim"],output_list[output_index],cont,t,hold) +# yval[i] = radar5.contr5(i+1,output_list[output_index], cont) + + self._ylist.append(yval) + + output_index = output_index+1 + except IndexError: + pass + self._opts["output_index"] = output_index + + return irtrn + + #def coutput(self,t): + #Nx = self.problem_info["dim"] + #y = N.zeros(Nx) + + #theta, pos = radar5.lagr5(10, t, None, self.arglag, self.past, self.problem.phi, self.problem.ipast) + #for i in range(1,Nx+1): + #y[i-1] = radar5.ylagr5(i, theta, pos, self.problem.phi, self.past, self.problem.ipast) + #return y + + def coutput(self, t, i = -1): + """ + Return the continous output solution at time t. + + t: time + i: solution component (default -1 gives the whole vector) + """ + Nx = self.problem_info["dim"] + y = N.zeros(Nx) + + + # t belongs to the interval (tk[ik], tk[ik+1]) + ik = N.searchsorted(self.tk, t) - 1 + + I = self.idif*ik + + H = self.past[I+self.idif-1] + theta = (t - (self.past[I] + H))/H + + + if i == -1: + # The line below this comment is what's effectively happening, + # but it is unfortunately extremely slow compared to the + # vectorized version below that doesn't use the cpoly function: + #return N.array([self.cpoly(i, I, theta) for i in range(self.problem_info["dim"])]) + nrds = self._nrdens + I = I + 1 + I2 = I + self.problem_info["dim"] + return self.past[I:I2] + theta*(self.past[nrds+I:nrds+I2] + (theta-self.C2M1)*(self.past[2*nrds+I:2*nrds+I2] + (theta-self.C1M1)*(self.past[3*nrds+I:3*nrds+I2]))) + elif i >= 0: + return self.cpoly(i, I, theta) + else: + raise ValueError('i has to be either -1 or a positive integer <= the problem dimension') + + def cpoly(self, i, I, theta): + """ + Evaluate the I:th dense output polynomial for component i at theta. + """ + nrds = self._nrdens + I = I + i + 1 + return self.past[I] + theta*(self.past[nrds+I] + (theta-self.C2M1)*(self.past[2*nrds+I] + (theta-self.C1M1)*(self.past[3*nrds+I]))) + + def arglag(self, i, t, y, past, ipast): + return self.problem.time_lags(t,y)[i-1] + + + def compute_ydelay(self, t, y, past, ipast): + ydelay = self._yDelayTemp + for i in range(1, self._ntimelags+1): + theta, pos = radar5.lagr5(i, t, y, self.arglag, past, self.problem.phi, ipast) + + for j, val in enumerate(self.problem.lagcompmap[i-1]): + ydelay[i-1][j] = radar5.ylagr5(val+1, theta, pos, self.problem.phi, past, ipast) + + return ydelay + + def F(self, t, y, past, ipast): + #print 'F:', t, y, past, ipast + + # First find the correct place in the past vector for each time-lag + # then evaluate all required solution components at that point + ydelay = self.compute_ydelay(t,y, past, ipast) + + # Now we can compute the right-hand-side + return self.problem.rhs(t, y, ydelay) + + def Fjac(self, t, y, past, ipast): + # First find the correct place in the past vector for each time-lag + # then evaluate all required solution components at that point + ydelay = self.compute_ydelay(t,y, None, None, past, ipast) + + # Now we can compute the right-hand-side + return self.problem.jac(t, y, ydelay) + + + def integrate(self, t, y, tf, opts): + ITOL = 1 #Both atol and rtol are vectors + IJAC = 0 if self.usejac else 0 #Switch for the jacobian, 0==NO JACOBIAN + MLJAC = self.problem_info["dim"] #The jacobian is full + MUJAC = self.problem_info["dim"] #See MLJAC + IMAS = 0 #The mass matrix is the identity + MLMAS = self.problem_info["dim"] #The mass matrix is full + MUMAS = self.problem_info["dim"] #See MLMAS + IOUT = 1 #solout is called after every step + WORK = N.array([0.0]*30) #Work (double) vector + IWORK = N.array([0]*30) #Work (integer) vector + + #Setting work options + WORK[0] = N.finfo(N.double).eps # Rounding unit + WORK[1] = self.safe + WORK[2] = self.thet + WORK[3] = self.fnewt + WORK[4] = self.quot1 + WORK[5] = self.quot2 + WORK[6] = self.maxh + WORK[7] = self.fac1 + WORK[8] = self.fac2 + WORK[9] = self.alpha + WORK[10] = self.tckbp + + #Setting iwork options + IWORK[1] = self.maxsteps + IWORK[2] = self.newt + IWORK[7] = 1 + IWORK[10] = self.ieflag + IWORK[11] = self.mxst + IWORK[12] = len(self.grid) + IWORK[13] = 1 + IWORK[14] = self._nrdens + + self.idif = 4*self._nrdens + 2 + lrpast = self.mxst*self.idif + past = N.zeros(lrpast) + + #past = N.zeros(self.mxst*(4*self.problem.nrdens+2)) +# print WORK +# print IWORK + + #Dummy methods + mas_dummy = lambda t:x + jac_dummy = (lambda t:x) if not self.usejac else self.Fjac + jaclag_dummy = (lambda t:x) if not self.usejaclag else self.problem.jaclag + nlags = 0 if not self.usejaclag else self.problem.nlags + njacl = 0 if not self.usejaclag else self.problem.njacl + + #Store the opts + self._opts = opts + #print "INIT", t,y,tf,self.inith, self.problem.ipast + #print "GRID", self.problem.grid, self.problem.ngrid + #t, y, h, iwork, flag, past = radar5.assimulo_radar5(self.F, \ + a = radar5.assimulo_radar5(self.F, \ + self.problem.phi, \ + self.arglag, \ + t, \ + y.copy(), \ + tf, \ + self.inith, \ + self.rtol*N.ones(self.problem_info["dim"]), \ + self.atol, \ + ITOL, \ + jac_dummy, \ + IJAC, \ + MLJAC, \ + MUJAC, \ + jaclag_dummy, \ + nlags, \ + njacl, \ + IMAS, \ + self._solout, \ + IOUT, \ + WORK, \ + IWORK, \ + self.grid.tolist()+[0.0], \ + self._ipast, \ + mas_dummy, \ + MLMAS, \ + MUMAS, + past), +# lrpast)#, \ + #past, \ + #IWORK[14]+1, \ +## IWORK[14], \ + #self.problem.ngrid, \ + #len(past) \ + #) + t, y, h, iwork, flag, past = a[0] + #print a[0] + #print len(a[0]) + #self.past = copy.deepcopy(past) + self.past = past + self.tk = N.trim_zeros(self.past[::self.idif], 'b') + self.hk = N.trim_zeros(self.past[self.idif-1:-1:self.idif], 'b') + + #Checking return + if flag == 1: + flag = ID_PY_COMPLETE + elif flag == 2: + flag = ID_PY_EVENT + else: + raise Exception("Radar5 failed with flag %d"%flag) + + #Retrieving statistics + self.statistics["nsteps"] += iwork[16] + self.statistics["nfcn"] += iwork[13] + self.statistics["njac"] += iwork[14] + self.statistics["nstepstotal"] += iwork[15] + self.statistics["errfail"] += iwork[17] + self.statistics["nlu"] += iwork[18] + + return flag, self._tlist, self._ylist + + def print_statistics(self, verbose=NORMAL): + """ + Prints the run-time statistics for the problem. + """ + self.log_message('Final Run Statistics: %s \n' % self.problem.name, verbose) + + self.log_message(' Number of steps : '+str(self.statistics["nsteps"]), verbose) + self.log_message(' Number of function evaluations : '+str(self.statistics["nfcn"]), verbose) + self.log_message(' Number of Jacobian evaluations : '+ str(self.statistics["njac"]), verbose) + self.log_message(' Number of error test failures : '+ str(self.statistics["errfail"]), verbose) + self.log_message(' Number of LU decompositions : '+ str(self.statistics["nlu"]), verbose) + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : Radar5 ' + self._type, verbose) + self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) + self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) + self.log_message('', verbose) + + def _get_h(self): + """ + Sets the stepsize. + """ + return self.__h + + def _set_h(self, h): + """ + Sets the stepsize. + """ + self.__h = h + + h = property(fget=_get_h,fset=_set_h) + + + def plot_stepsize(self): + """ + Plots the step-size. + """ + P.semilogy(N.diff(self.t),drawstyle='steps-post') + P.title(self.problem.name) + P.ylabel('Step length') + P.xlabel('Number of steps') + P.show() + + def _set_newt(self, newt): + """ + Maximal number of Newton iterations. + + Parameters:: + + newt + - Default '7'. + + - Should be an integer. + + Example: + newt = 10 + """ + try: + self.options["newt"] = int(newt) + except (ValueError, TypeError): + raise Radar_Exception('The newt must be an integer or float.') + + def _get_newt(self): + """ + Maximal number of Newton iterations. + + Parameters:: + + newt + - Default '7'. + + - Should be an integer. + + Example: + newt = 10 + """ + return self.options["newt"] + + newt = property(_get_newt,_set_newt) + + def _set_fnewt(self, fnewt): + try: + self.options["fnewt"] = float(fnewt) + except (ValueError, TypeError): + raise Radar_Exception('The fnewt must be an integer or float.') + + def _get_fnewt(self): + """ + Stopping criterion for Newton's method, usually chosen <1. + Smaller values of fnewt make the code slower, but safer. + + Parameters:: + + fnewt + - Default min(0.03,rtol**0.5) + + - Should be a float. + + Example: + fnewt = 0.05 + """ + return self.options["fnewt"] + + fnewt = property(_get_fnewt,_set_fnewt) + + def _set_safe(self, safe): + try: + self.options["safe"] = float(safe) + except (ValueError, TypeError): + raise Radar_Exception('The safe must be an integer or float.') + + def _get_safe(self): + """ + The safety factor in the step-size prediction. + + Parameters:: + + safe + - Default '0.9' + + - Should be float. + + Example: + safe = 0.8 + """ + return self.options["safe"] + + safe = property(_get_safe, _set_safe) + + def _set_thet(self, thet): + try: + self.options["thet"] = float(thet) + except (ValueError, TypeError): + raise Radar_Exception('The thet must be an integer or float.') + + def _get_thet(self): + """ + Value for determine if the Jacobian is to be recomputed or not. + Increasing thet makes the code compute new Jacobians more seldom. + Negative thet forces the code to compute the Jacobian after every accepted step. + + Parameters:: + + thet + - Default '0.003' + + - Should be float. + + Example: + thet = 0.01 + """ + return self.options["thet"] + + thet = property(_get_thet, _set_thet) + + def _set_max_h(self,max_h): + try: + self.options["maxh"] = float(max_h) + except (ValueError,TypeError): + raise Radar_Exception('Maximal stepsize must be a (scalar) float.') + if self.options["maxh"] < 0: + raise Radar_Exception('Maximal stepsize must be a positive (scalar) float.') + + def _get_max_h(self): + """ + Defines the maximal step-size that is to be used by the solver. + + Parameters:: + + maxh + - Default final time - current time. + + - Should be a float. + + Example: + maxh = 0.01 + + """ + return self.options["maxh"] + + maxh=property(_get_max_h,_set_max_h) + + def _set_initial_step(self, initstep): + try: + self.options["inith"] = float(initstep) + except (ValueError, TypeError): + raise Radar_Exception('The initial step must be an integer or float.') + + def _get_initial_step(self): + """ + This determines the initial step-size to be used in the integration. + + Parameters:: + + inith + - Default '0.01'. + + - Should be float. + + Example: + inith = 0.01 + """ + return self.options["inith"] + + inith = property(_get_initial_step,_set_initial_step) + + + def _set_quot1(self, quot1): + try: + self.options["quot1"] = float(quot1) + except (ValueError, TypeError): + raise Radar_Exception('The quot1 must be an integer or float.') + + def _get_quot1(self): + """ + If quot1 < current step-size / old step-size < quot2 the the step-size is not changed. + This saves LU-decompositions and computing time for large systems. + + Parameters:: + + quot1 + - Default 1.0 + + - Should be a float. + + Example: + quot1 = 0.9 + """ + return self.options["quot1"] + + quot1 = property(_get_quot1, _set_quot1) + + def _set_quot2(self, quot2): + try: + self.options["quot2"] = float(quot2) + except (ValueError, TypeError): + raise Radar_Exception('The quot2 must be an integer or float.') + + def _get_quot2(self): + """ + If quot1 < current step-size / old step-size < quot2 the the step-size is not changed. + This saves LU-decompositions and computing time for large systems. + + Parameters:: + + quot2 + - Default 1.2 + + - Should be a float. + + Example: + quot2 = 1.2 + """ + return self.options["quot2"] + + quot2 = property(_get_quot2, _set_quot2) + + def _set_fac1(self, fac1): + try: + self.options["fac1"] = float(fac1) + except (ValueError, TypeError): + raise Radar_Exception('The fac1 must be an integer or float.') + + def _get_fac1(self): + """ + Parameters for step-size selection. The new step-size is chosen + subject to the restriction fac1 <= current step-size / old step-size <= fac2. + + Parameters:: + + fac1 + - Default 0.2 + + - Should be a float. + + Example: + fac1 = 0.1 + """ + return self.options["fac1"] + + fac1 = property(_get_fac1, _set_fac1) + + def _set_fac2(self, fac2): + try: + self.options["fac2"] = float(fac2) + except (ValueError, TypeError): + raise Radar_Exception('The fac2 must be an integer or float.') + + def _get_fac2(self): + """ + Parameters for step-size selection. The new step-size is chosen + subject to the restriction fac1 <= current step-size / old step-size <= fac2. + + Parameters:: + + fac2 + - Default 8.0 + + - Should be a float. + + Example: + fac2 = 10.0 + """ + return self.options["fac2"] + + fac2 = property(_get_fac2, _set_fac2) + + def _set_usejac(self, jac): + self.options["usejac"] = bool(jac) + + def _get_usejac(self): + """ + This sets the option to use the user defined jacobian. If a + user provided jacobian is implemented into the problem the + default setting is to use that jacobian. If not, an + approximation is used. + + Parameters:: + + usejac + - True - use user defined jacobian + False - use an approximation + + - Should be a boolean. + + Example: + usejac = False + """ + return self.options["usejac"] + + usejac = property(_get_usejac,_set_usejac) + + def _set_usejaclag(self, jaclag): + self.options["usejaclag"] = bool(jaclag) + def _get_usejaclag(self, jaclag): + return self.options["usejaclag"] + usejaclag = property(_get_usejaclag,_set_usejaclag) + + def _set_atol(self,atol): + + self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) + + if len(self.options["atol"]) == 1: + self.options["atol"] = self.options["atol"]*N.ones(self._leny) + elif len(self.options["atol"]) != self._leny: + raise Radar_Exception("atol must be of length one or same as the dimension of the problem.") + + def _get_atol(self): + """ + Defines the absolute tolerance(s) that is to be used by the solver. + Can be set differently for each variable. + + Parameters:: + + atol + - Default '1.0e-6'. + + - Should be a positive float or a numpy vector + of floats. + + Example: + atol = [1.0e-4, 1.0e-6] + """ + return self.options["atol"] + + atol=property(_get_atol,_set_atol) + + def _set_rtol(self,rtol): + try: + self.options["rtol"] = float(rtol) + except (ValueError, TypeError): + raise Radar_Exception('Relative tolerance must be a (scalar) float.') + if self.options["rtol"] <= 0.0: + raise Radar_Exception('Relative tolerance must be a positive (scalar) float.') + + def _get_rtol(self): + """ + Defines the relative tolerance that is to be used by the solver. + + Parameters:: + + rtol + - Default '1.0e-6'. + + - Should be a positive float. + + Example: + rtol = 1.0e-4 + """ + return self.options["rtol"] + + rtol=property(_get_rtol,_set_rtol) + + def _get_grid(self): + """ + TODO + """ + return self._grid + def _set_grid(self, grid): + self._grid + grid=property(_get_grid,_set_grid) + + def _get_maxsteps(self): + """ + The maximum number of steps allowed to be taken to reach the + final time. + + Parameters:: + + maxsteps + - Default 10000 + + - Should be a positive integer + """ + return self.options["maxsteps"] + + def _set_maxsteps(self, max_steps): + try: + max_steps = int(max_steps) + except (TypeError, ValueError): + raise Radar_Exception("Maximum number of steps must be a positive integer.") + self.options["maxsteps"] = max_steps + + maxsteps = property(_get_maxsteps, _set_maxsteps) + + def _get_alpha(self): + """ + Used to tune the error control of dense output. A small value means + stricter control and should be used for problems with almost + discontinuous solutions while alpha = 1.0 can be used for problems + with fairly smooth solutions. + + + Parameters:: + + alpha + - Default 0 + + - Should be in [0.0, 1.0] + """ + return self.options["alpha"] + + def _set_alpha(self, alpha): + try: + self.options["alpha"] = float(alpha) + except (TypeError, ValueError): + raise Radar_Exception("alpha must be a float.") + if (self.options["alpha"] < 0.0) or (self.options["alpha"] > 1.0): + raise Radar_Exception("alpha must be in the interval [0,1].") + + alpha = property(_get_alpha, _set_alpha) + + + def _get_tckbp(self): + """ + Used to control the search for breaking points. If the error increases + by a factor larger than tckbp in one step the routine that searches for + breaking points is activated. + + + Parameters:: + + tckbp + - Default 5.0 + + - Should be a positive float + """ + return self.options["tckbp"] + + def _set_tckbp(self, tckbp): + try: + self.options["tckbp"] = float(tckbp) + except (TypeError, ValueError): + raise Radar_Exception("tckbp must be a float.") + if (self.options["tckbp"] < 0.0): + raise Radar_Exception("tckbp must be a positive float.") + + tckbp = property(_get_tckbp, _set_tckbp) + + + def _get_ieflag(self): + """ + Switch between different modes of error control. + -1: pure control of the dense output (makes use of a quadratic and a + linear polynomial interpolating the stage values) + 0: mixed control of dense output and discrete output + 1: simpler mixed control + 2: pure control of the discrete output (is provided by the routine ESTRAD) + + Parameters:: + + ieflag + - Default 0.0 + + - Should be either -1, 0, 1 or 2 + """ + return self.options["ieflag"] + + def _set_ieflag(self, ieflag): + try: + self.options["ieflag"] = int(ieflag) + except (TypeError, ValueError): + raise Radar_Exception("ieflag must be an integer.") + if (self.options["ieflag"] < -1) or (self.options["ieflag"] > 2): + raise Radar_Exception("ieflag must be either -1, 0, 1 or 2.") + + ieflag = property(_get_ieflag, _set_ieflag) + + + def _get_mxst(self): + """ + The maximum number of steps stored in the dense output array. + The dimension of this array will be nrdens*(4*self.nrdens+2). + + Parameters:: + + mxst + - Default 100 + + - Should be a positive integer + """ + return self.options["mxst"] + + def _set_mxst(self, mxst): + try: + self.options["mxst"] = int(mxst) + except (TypeError, ValueError): + raise Radar_Exception("mxst must be a positive integer.") + if (self.options["mxst"] < 1): + raise Radar_Exception("mxst must be a positive integer.") + + mxst = property(_get_mxst, _set_mxst) + + def _set_usejaclag(self, jaclag): + self.options["usejaclag"] = bool(jaclag) + + def _get_usejaclag(self): + """ + This sets the option to use the user defined lagged jacobian. If a + user provided lagged jacobian is implemented into the problem the + default setting is to use that lagged jacobian. If not, an + approximation is used. + + Parameters:: + + usejaclag + - True - use user defined lagged jacobian + False - use an approximation + + - Should be a boolean. + + Example: + usejaclag = False + """ + return self.options["usejaclag"] + + usejaclag = property(_get_usejaclag,_set_usejaclag) diff --git a/src/solvers/radau5.py b/src/solvers/radau5.py index 568c1941..2b2d1209 100644 --- a/src/solvers/radau5.py +++ b/src/solvers/radau5.py @@ -1,1636 +1,1636 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import scipy as S -import scipy.linalg as LIN -import scipy.sparse as sp - -from assimulo.exception import * -from assimulo.ode import * - -from assimulo.explicit_ode import Explicit_ODE -from assimulo.implicit_ode import Implicit_ODE -from assimulo.lib.radau_core import Radau_Common - -from assimulo.lib import radau5 - -class Radau5Error(AssimuloException): - """ - Defines the Radau5Error and provides the textual error message. - """ - msg = { -1 : 'The input is not consistent.', - -2 : 'The solver took max internal steps but could not reach the next output time.', - -3 : 'The step size became too small.', - -4 : 'The matrix is repeatedly singular.', - -5 : 'Repeated unexpected step rejections.'} - - def __init__(self, value, t = 0.0): - self.value = value - self.t = t - - def __str__(self): - try: - return repr(self.msg[self.value]+' At time %f.'%self.t) - except KeyError: - return repr('Radau failed with flag %s. At time %f.'%(self.value, self.t)) - - -class Radau5ODE(Radau_Common,Explicit_ODE): - """ - Radau IIA fifth-order three-stages with step-size control and - continuous output. Based on the FORTRAN code RADAU5 by E.Hairer and - G.Wanner, which can be found here: - http://www.unige.ch/~hairer/software.html - - Details about the implementation (FORTRAN) can be found in the book,:: - - Solving Ordinary Differential Equations II, - Stiff and Differential-Algebraic Problems - - Authors: E. Hairer and G. Wanner - Springer-Verlag, ISBN: 3-540-60452-9 - - """ - - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Explicit_Problem' class. - """ - Explicit_ODE.__init__(self, problem) #Calls the base class - - #Default values - self.options["inith"] = 0.01 - self.options["newt"] = 7 #Maximum number of newton iterations - self.options["thet"] = 1.e-3 #Boundary for re-calculation of jac - self.options["fnewt"] = 0.0 #Stopping critera for Newtons Method - self.options["quot1"] = 1.0 #Parameters for changing step-size (lower bound) - self.options["quot2"] = 1.2 #Parameters for changing step-size (upper bound) - self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) - self.options["fac2"] = 8.0 #Parameters for step-size selection (upper bound) - self.options["maxh"] = N.inf #Maximum step-size. - self.options["safe"] = 0.9 #Safety factor - self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - self.options["usejac"] = True if self.problem_info["jac_fcn"] else False - self.options["maxsteps"] = 100000 - - #Solver support - self.supports["report_continuously"] = True - self.supports["interpolated_output"] = True - self.supports["state_events"] = True - - self._leny = len(self.y) #Dimension of the problem - self._type = '(explicit)' - self._event_info = None - self._werr = N.zeros(self._leny) - - def initialize(self): - #Reset statistics +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import scipy as S +import scipy.linalg as LIN +import scipy.sparse as sp + +from assimulo.exception import * +from assimulo.ode import * + +from assimulo.explicit_ode import Explicit_ODE +from assimulo.implicit_ode import Implicit_ODE +from assimulo.lib.radau_core import Radau_Common + +from assimulo.lib import radau5 + +class Radau5Error(AssimuloException): + """ + Defines the Radau5Error and provides the textual error message. + """ + msg = { -1 : 'The input is not consistent.', + -2 : 'The solver took max internal steps but could not reach the next output time.', + -3 : 'The step size became too small.', + -4 : 'The matrix is repeatedly singular.', + -5 : 'Repeated unexpected step rejections.'} + + def __init__(self, value, t = 0.0): + self.value = value + self.t = t + + def __str__(self): + try: + return repr(self.msg[self.value]+' At time %f.'%self.t) + except KeyError: + return repr('Radau failed with flag %s. At time %f.'%(self.value, self.t)) + + +class Radau5ODE(Radau_Common,Explicit_ODE): + """ + Radau IIA fifth-order three-stages with step-size control and + continuous output. Based on the FORTRAN code RADAU5 by E.Hairer and + G.Wanner, which can be found here: + http://www.unige.ch/~hairer/software.html + + Details about the implementation (FORTRAN) can be found in the book,:: + + Solving Ordinary Differential Equations II, + Stiff and Differential-Algebraic Problems + + Authors: E. Hairer and G. Wanner + Springer-Verlag, ISBN: 3-540-60452-9 + + """ + + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Explicit_Problem' class. + """ + Explicit_ODE.__init__(self, problem) #Calls the base class + + #Default values + self.options["inith"] = 0.01 + self.options["newt"] = 7 #Maximum number of newton iterations + self.options["thet"] = 1.e-3 #Boundary for re-calculation of jac + self.options["fnewt"] = 0.0 #Stopping critera for Newtons Method + self.options["quot1"] = 1.0 #Parameters for changing step-size (lower bound) + self.options["quot2"] = 1.2 #Parameters for changing step-size (upper bound) + self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) + self.options["fac2"] = 8.0 #Parameters for step-size selection (upper bound) + self.options["maxh"] = N.inf #Maximum step-size. + self.options["safe"] = 0.9 #Safety factor + self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + self.options["usejac"] = True if self.problem_info["jac_fcn"] else False + self.options["maxsteps"] = 100000 + + #Solver support + self.supports["report_continuously"] = True + self.supports["interpolated_output"] = True + self.supports["state_events"] = True + + self._leny = len(self.y) #Dimension of the problem + self._type = '(explicit)' + self._event_info = None + self._werr = N.zeros(self._leny) + + def initialize(self): + #Reset statistics self.statistics.reset() #for k in self.statistics.keys(): # self.statistics[k] = 0 - - def set_problem_data(self): - if self.problem_info["state_events"]: - def event_func(t, y): - return self.problem.state_events(t, y, self.sw) - def f(t, y): - ret = 0 - try: - rhs = self.problem.rhs(t, y, self.sw) - except(N.linalg.LinAlgError,ZeroDivisionError,AssimuloRecoverableError): - rhs = y.copy() - ret = -1 #Recoverable error - return rhs, [ret] - self.f = f - self.event_func = event_func - self._event_info = [0] * self.problem_info["dimRoot"] - self.g_old = N.array(self.event_func(self.t, self.y)).copy() - else: - def f(t, y): - ret = 0 - try: - rhs = self.problem.rhs(t, y) - except(N.linalg.LinAlgError,ZeroDivisionError,AssimuloRecoverableError): - rhs = y.copy() - ret = -1 #Recoverable error - return rhs, [ret] - self.f = f - - def interpolate(self, time): - y = N.empty(self._leny) - for i in range(self._leny): - y[i] = radau5.contr5(i+1, time, self.cont) - - return y - - def get_weighted_local_errors(self): - """ - Returns the vector of weighted estimated local errors at the current step. - """ - return N.abs(self._werr) - - def _solout(self, nrsol, told, t, y, cont, werr, lrc, irtrn): - """ - This method is called after every successful step taken by Radau5 - """ - self.cont = cont #Saved to be used by the interpolation function. - self._werr = werr - - if self.problem_info["state_events"]: - flag, t, y = self.event_locator(told, t, y) - #Convert to Fortram indicator. - if flag == ID_PY_EVENT: irtrn = -1 - - if self._opts["report_continuously"]: - initialize_flag = self.report_solution(t, y.copy(), self._opts) - if initialize_flag: irtrn = -1 - else: - if self._opts["output_list"] is None: - self._tlist.append(t) - self._ylist.append(y.copy()) - else: - output_list = self._opts["output_list"] - output_index = self._opts["output_index"] - try: - while output_list[output_index] <= t: - self._tlist.append(output_list[output_index]) - self._ylist.append(self.interpolate(output_list[output_index])) - - output_index += 1 - except IndexError: - pass - self._opts["output_index"] = output_index - - if self.problem_info["state_events"] and flag == ID_PY_EVENT and len(self._tlist) > 0 and self._tlist[-1] != t: - self._tlist.append(t) - self._ylist.append(y) - - return irtrn - - def _jacobian(self, t, y): - """ - Calculates the Jacobian, either by an approximation or by the user - defined (jac specified in the problem class). - """ - jac = self.problem.jac(t,y) - - if isinstance(jac, sp.csc_matrix): - jac = jac.toarray() - - return jac - - def integrate(self, t, y, tf, opts): - ITOL = 1 #Both atol and rtol are vectors - IJAC = 1 if self.usejac else 0 #Switch for the jacobian, 0==NO JACOBIAN - MLJAC = self.problem_info["dim"] #The jacobian is full - MUJAC = self.problem_info["dim"] #See MLJAC - IMAS = 0 #The mass matrix is the identity - MLMAS = self.problem_info["dim"] #The mass matrix is full - MUMAS = self.problem_info["dim"] #See MLMAS - IOUT = 1 #solout is called after every step - WORK = N.array([0.0]*(4*self.problem_info["dim"]**2+12*self.problem_info["dim"]+20)) #Work (double) vector - IWORK = N.array([0]*(3*self.problem_info["dim"]+20)) #Work (integer) vector - - #Setting work options - WORK[1] = self.safe - WORK[2] = self.thet - WORK[3] = self.fnewt - WORK[4] = self.quot1 - WORK[5] = self.quot2 - WORK[6] = self.maxh - WORK[7] = self.fac1 - WORK[8] = self.fac2 - - #Setting iwork options - IWORK[1] = self.maxsteps - IWORK[2] = self.newt - - #Dummy methods - mas_dummy = lambda t:x - jac_dummy = (lambda t:x) if not self.usejac else self._jacobian - - #Check for initialization - if opts["initialize"]: - self.set_problem_data() - self._tlist = [] - self._ylist = [] - - - #Store the opts - self._opts = opts - - t, y, h, iwork, flag = radau5.radau5(self.f, t, y.copy(), tf, self.inith, self.rtol*N.ones(self.problem_info["dim"]), self.atol, - ITOL, jac_dummy, IJAC, MLJAC, MUJAC, mas_dummy, IMAS, MLMAS, MUMAS, self._solout, IOUT, WORK, IWORK) - - #Checking return - if flag == 1: - flag = ID_PY_COMPLETE - elif flag == 2: - flag = ID_PY_EVENT - else: - raise Radau5Error(flag, t) - - #Retrieving statistics - self.statistics["nsteps"] += iwork[16] - self.statistics["nfcns"] += iwork[13] - self.statistics["njacs"] += iwork[14] - self.statistics["nfcnjacs"] += (iwork[14]*self.problem_info["dim"] if not self.usejac else 0) - #self.statistics["nstepstotal"] += iwork[15] - self.statistics["nerrfails"] += iwork[17] - self.statistics["nlus"] += iwork[18] - - return flag, self._tlist, self._ylist - - def state_event_info(self): - return self._event_info - - def set_event_info(self, event_info): - self._event_info = event_info - - def print_statistics(self, verbose=NORMAL): - """ - Prints the run-time statistics for the problem. - """ - Explicit_ODE.print_statistics(self, verbose) #Calls the base class - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : Radau5 ' + self._type, verbose) - self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) - self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) - self.log_message('', verbose) - - -class _Radau5ODE(Radau_Common,Explicit_ODE): - """ - Radau IIA fifth-order three-stages with step-size control and continuous output. - Based on the FORTRAN code by E.Hairer and G.Wanner, which can be found here: - http://www.unige.ch/~hairer/software.html - - Details about the implementation (FORTRAN) can be found in the book,:: - - Solving Ordinary Differential Equations II, - Stiff and Differential-Algebraic Problems - - Authors: E. Hairer and G. Wanner - Springer-Verlag, ISBN: 3-540-60452-9 - - This code is aimed at providing a Python implementation of the original code. - """ - - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Explicit_Problem' class. - """ - Explicit_ODE.__init__(self, problem) #Calls the base class - - #Default values - self.options["inith"] = 0.01 - self.options["newt"] = 7 #Maximum number of newton iterations - self.options["thet"] = 1.e-3 #Boundary for re-calculation of jac - self.options["fnewt"] = 0 #Stopping critera for Newtons Method - self.options["quot1"] = 1.0 #Parameters for changing step-size (lower bound) - self.options["quot2"] = 1.2 #Parameters for changing step-size (upper bound) - self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) - self.options["fac2"] = 8.0 #Parameters for step-size selection (upper bound) - self.options["maxh"] = N.inf #Maximum step-size. - self.options["safe"] = 0.9 #Safety factor - self.options["atol"] = 1.0e-6 #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - self.options["usejac"] = True if self.problem_info["jac_fcn"] else False - self.options["maxsteps"] = 10000 - - #Internal values - self._curjac = False #Current jacobian? - self._itfail = False #Iteration failed? - self._needjac = True #Need to update the jacobian? - self._needLU = True #Need new LU-factorisation? - self._first = True #First step? - self._rejected = True #Is the last step rejected? - self._leny = len(self.y) #Dimension of the problem - self._oldh = 0.0 #Old stepsize - self._olderr = 1.0 #Old error - self._eps = N.finfo('double').eps - self._col_poly = N.zeros(self._leny*3) - self._type = '(explicit)' - self._curiter = 0 #Number of current iterations - - #RHS-Function - self.f = problem.rhs_internal - - #Internal temporary result vector - self.Y1 = N.array([0.0]*len(self.y0)) - self.Y2 = N.array([0.0]*len(self.y0)) - self.Y3 = N.array([0.0]*len(self.y0)) - self._f0 = N.array([0.0]*len(self.y0)) - - #Solver support - self.supports["one_step_mode"] = True - self.supports["interpolated_output"] = True - - # - Retrieve the Radau5 parameters - self._load_parameters() #Set the Radau5 parameters - - def initialize(self): - #Reset statistics + + def set_problem_data(self): + if self.problem_info["state_events"]: + def event_func(t, y): + return self.problem.state_events(t, y, self.sw) + def f(t, y): + ret = 0 + try: + rhs = self.problem.rhs(t, y, self.sw) + except(N.linalg.LinAlgError,ZeroDivisionError,AssimuloRecoverableError): + rhs = y.copy() + ret = -1 #Recoverable error + return rhs, [ret] + self.f = f + self.event_func = event_func + self._event_info = [0] * self.problem_info["dimRoot"] + self.g_old = N.array(self.event_func(self.t, self.y)).copy() + else: + def f(t, y): + ret = 0 + try: + rhs = self.problem.rhs(t, y) + except(N.linalg.LinAlgError,ZeroDivisionError,AssimuloRecoverableError): + rhs = y.copy() + ret = -1 #Recoverable error + return rhs, [ret] + self.f = f + + def interpolate(self, time): + y = N.empty(self._leny) + for i in range(self._leny): + y[i] = radau5.contr5(i+1, time, self.cont) + + return y + + def get_weighted_local_errors(self): + """ + Returns the vector of weighted estimated local errors at the current step. + """ + return N.abs(self._werr) + + def _solout(self, nrsol, told, t, y, cont, werr, lrc, irtrn): + """ + This method is called after every successful step taken by Radau5 + """ + self.cont = cont #Saved to be used by the interpolation function. + self._werr = werr + + if self.problem_info["state_events"]: + flag, t, y = self.event_locator(told, t, y) + #Convert to Fortram indicator. + if flag == ID_PY_EVENT: irtrn = -1 + + if self._opts["report_continuously"]: + initialize_flag = self.report_solution(t, y.copy(), self._opts) + if initialize_flag: irtrn = -1 + else: + if self._opts["output_list"] is None: + self._tlist.append(t) + self._ylist.append(y.copy()) + else: + output_list = self._opts["output_list"] + output_index = self._opts["output_index"] + try: + while output_list[output_index] <= t: + self._tlist.append(output_list[output_index]) + self._ylist.append(self.interpolate(output_list[output_index])) + + output_index += 1 + except IndexError: + pass + self._opts["output_index"] = output_index + + if self.problem_info["state_events"] and flag == ID_PY_EVENT and len(self._tlist) > 0 and self._tlist[-1] != t: + self._tlist.append(t) + self._ylist.append(y) + + return irtrn + + def _jacobian(self, t, y): + """ + Calculates the Jacobian, either by an approximation or by the user + defined (jac specified in the problem class). + """ + jac = self.problem.jac(t,y) + + if isinstance(jac, sp.csc_matrix): + jac = jac.toarray() + + return jac + + def integrate(self, t, y, tf, opts): + ITOL = 1 #Both atol and rtol are vectors + IJAC = 1 if self.usejac else 0 #Switch for the jacobian, 0==NO JACOBIAN + MLJAC = self.problem_info["dim"] #The jacobian is full + MUJAC = self.problem_info["dim"] #See MLJAC + IMAS = 0 #The mass matrix is the identity + MLMAS = self.problem_info["dim"] #The mass matrix is full + MUMAS = self.problem_info["dim"] #See MLMAS + IOUT = 1 #solout is called after every step + WORK = N.array([0.0]*(4*self.problem_info["dim"]**2+12*self.problem_info["dim"]+20)) #Work (double) vector + IWORK = N.array([0]*(3*self.problem_info["dim"]+20)) #Work (integer) vector + + #Setting work options + WORK[1] = self.safe + WORK[2] = self.thet + WORK[3] = self.fnewt + WORK[4] = self.quot1 + WORK[5] = self.quot2 + WORK[6] = self.maxh + WORK[7] = self.fac1 + WORK[8] = self.fac2 + + #Setting iwork options + IWORK[1] = self.maxsteps + IWORK[2] = self.newt + + #Dummy methods + mas_dummy = lambda t:x + jac_dummy = (lambda t:x) if not self.usejac else self._jacobian + + #Check for initialization + if opts["initialize"]: + self.set_problem_data() + self._tlist = [] + self._ylist = [] + + + #Store the opts + self._opts = opts + + t, y, h, iwork, flag = radau5.radau5(self.f, t, y.copy(), tf, self.inith, self.rtol*N.ones(self.problem_info["dim"]), self.atol, + ITOL, jac_dummy, IJAC, MLJAC, MUJAC, mas_dummy, IMAS, MLMAS, MUMAS, self._solout, IOUT, WORK, IWORK) + + #Checking return + if flag == 1: + flag = ID_PY_COMPLETE + elif flag == 2: + flag = ID_PY_EVENT + else: + raise Radau5Error(flag, t) + + #Retrieving statistics + self.statistics["nsteps"] += iwork[16] + self.statistics["nfcns"] += iwork[13] + self.statistics["njacs"] += iwork[14] + self.statistics["nfcnjacs"] += (iwork[14]*self.problem_info["dim"] if not self.usejac else 0) + #self.statistics["nstepstotal"] += iwork[15] + self.statistics["nerrfails"] += iwork[17] + self.statistics["nlus"] += iwork[18] + + return flag, self._tlist, self._ylist + + def state_event_info(self): + return self._event_info + + def set_event_info(self, event_info): + self._event_info = event_info + + def print_statistics(self, verbose=NORMAL): + """ + Prints the run-time statistics for the problem. + """ + Explicit_ODE.print_statistics(self, verbose) #Calls the base class + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : Radau5 ' + self._type, verbose) + self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) + self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) + self.log_message('', verbose) + + +class _Radau5ODE(Radau_Common,Explicit_ODE): + """ + Radau IIA fifth-order three-stages with step-size control and continuous output. + Based on the FORTRAN code by E.Hairer and G.Wanner, which can be found here: + http://www.unige.ch/~hairer/software.html + + Details about the implementation (FORTRAN) can be found in the book,:: + + Solving Ordinary Differential Equations II, + Stiff and Differential-Algebraic Problems + + Authors: E. Hairer and G. Wanner + Springer-Verlag, ISBN: 3-540-60452-9 + + This code is aimed at providing a Python implementation of the original code. + """ + + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Explicit_Problem' class. + """ + Explicit_ODE.__init__(self, problem) #Calls the base class + + #Default values + self.options["inith"] = 0.01 + self.options["newt"] = 7 #Maximum number of newton iterations + self.options["thet"] = 1.e-3 #Boundary for re-calculation of jac + self.options["fnewt"] = 0 #Stopping critera for Newtons Method + self.options["quot1"] = 1.0 #Parameters for changing step-size (lower bound) + self.options["quot2"] = 1.2 #Parameters for changing step-size (upper bound) + self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) + self.options["fac2"] = 8.0 #Parameters for step-size selection (upper bound) + self.options["maxh"] = N.inf #Maximum step-size. + self.options["safe"] = 0.9 #Safety factor + self.options["atol"] = 1.0e-6 #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + self.options["usejac"] = True if self.problem_info["jac_fcn"] else False + self.options["maxsteps"] = 10000 + + #Internal values + self._curjac = False #Current jacobian? + self._itfail = False #Iteration failed? + self._needjac = True #Need to update the jacobian? + self._needLU = True #Need new LU-factorisation? + self._first = True #First step? + self._rejected = True #Is the last step rejected? + self._leny = len(self.y) #Dimension of the problem + self._oldh = 0.0 #Old stepsize + self._olderr = 1.0 #Old error + self._eps = N.finfo('double').eps + self._col_poly = N.zeros(self._leny*3) + self._type = '(explicit)' + self._curiter = 0 #Number of current iterations + + #RHS-Function + self.f = problem.rhs_internal + + #Internal temporary result vector + self.Y1 = N.array([0.0]*len(self.y0)) + self.Y2 = N.array([0.0]*len(self.y0)) + self.Y3 = N.array([0.0]*len(self.y0)) + self._f0 = N.array([0.0]*len(self.y0)) + + #Solver support + self.supports["one_step_mode"] = True + self.supports["interpolated_output"] = True + + # - Retrieve the Radau5 parameters + self._load_parameters() #Set the Radau5 parameters + + def initialize(self): + #Reset statistics self.statistics.reset() - - def step_generator(self, t, y, tf, opts): - - if opts["initialize"]: - self._oldh = self.inith - self.h = self.inith - self._fac_con = 1.0 - - if self.fnewt == 0: - self.fnewt = max(10.*self._eps/self.rtol,min(0.03,self.rtol**0.5)) - - self.f(self._f0,t,y) - self.statistics["nfcns"] +=1 - self._tc = t - self._yc = y - + + def step_generator(self, t, y, tf, opts): + + if opts["initialize"]: + self._oldh = self.inith + self.h = self.inith + self._fac_con = 1.0 + + if self.fnewt == 0: + self.fnewt = max(10.*self._eps/self.rtol,min(0.03,self.rtol**0.5)) + + self.f(self._f0,t,y) + self.statistics["nfcns"] +=1 + self._tc = t + self._yc = y + for i in range(self.maxsteps): - - if t < tf: - t, y = self._step(t, y) - self._tc = t - self._yc = y - - if self.h > N.abs(tf-t): - self.h = N.abs(tf-t) - - if t < tf: - yield ID_PY_OK, t, y - else: - yield ID_PY_COMPLETE, t, y - break - - self._first = False - else: - raise Explicit_ODE_Exception('Final time not reached within maximum number of steps') - - #t, y = self._step(t,y) - #yield ID_PY_COMPLETE, t, y - - def step(self, t, y, tf, opts): - if opts["initialize"]: - self._next_step = self.step_generator(t,y,tf,opts) + + if t < tf: + t, y = self._step(t, y) + self._tc = t + self._yc = y + + if self.h > N.abs(tf-t): + self.h = N.abs(tf-t) + + if t < tf: + yield ID_PY_OK, t, y + else: + yield ID_PY_COMPLETE, t, y + break + + self._first = False + else: + raise Explicit_ODE_Exception('Final time not reached within maximum number of steps') + + #t, y = self._step(t,y) + #yield ID_PY_COMPLETE, t, y + + def step(self, t, y, tf, opts): + if opts["initialize"]: + self._next_step = self.step_generator(t,y,tf,opts) return next(self._next_step) - - def integrate(self, t, y, tf, opts): - - if opts["output_list"] is not None: - - output_list = opts["output_list"] - output_index = opts["output_index"] - - next_step = self.step_generator(t,y,tf,opts) - - tlist,ylist = [], [] - res = [ID_PY_OK] - - while res[0] != ID_PY_COMPLETE: + + def integrate(self, t, y, tf, opts): + + if opts["output_list"] is not None: + + output_list = opts["output_list"] + output_index = opts["output_index"] + + next_step = self.step_generator(t,y,tf,opts) + + tlist,ylist = [], [] + res = [ID_PY_OK] + + while res[0] != ID_PY_COMPLETE: res = next(next_step) - try: - while output_list[output_index] <= res[1]: - tlist.append(output_list[output_index]) - ylist.append(self.interpolate(output_list[output_index])) - - output_index = output_index+1 - except IndexError: - pass - return res[0], tlist, ylist - else: + try: + while output_list[output_index] <= res[1]: + tlist.append(output_list[output_index]) + ylist.append(self.interpolate(output_list[output_index])) + + output_index = output_index+1 + except IndexError: + pass + return res[0], tlist, ylist + else: [flags, tlist, ylist] = list(zip(*list(self.step_generator(t, y, tf,opts)))) - - return flags[-1], tlist, ylist - - def _step(self, t, y): - """ - This calculates the next step in the integration. - """ - self._scaling = N.array(abs(y)*self.rtol + self.atol) #The scaling used. - - while True: #Loop for integrating one step. - - self.newton(t,y) - self._err = self.estimate_error() - - if self._err > 1.0: #Step was rejected. - self._rejected = True - self.statistics["nerrfails"] += 1 - ho = self.h - self.h = self.adjust_stepsize(self._err) - - self.log_message('Rejecting step at ' + str(t) + 'with old stepsize' + str(ho) + 'and new ' + str(self.h), SCREAM) - - if self._curjac or self._curiter == 1: - self._needjac = False - self._needLU = True - else: - self._needjac = True - self._needLU = True - else: - self.log_message('Accepting step at ' + str(t) + 'with stepsize ' + str(self.h),SCREAM) - - self.statistics["nsteps"] += 1 - - tn = t+self.h #Preform the step - yn = y+self._Z[2*self._leny:3*self._leny] - self.f(self._f0,tn,yn) - self.statistics["nfcns"] += 1 - - self._oldoldh = self._oldh #Store the old(old) step-size for use in the test below. - self._oldh = self.h #Store the old step-size - self._oldt = t #Store the old time-point - self._newt = tn #Store the new time-point - - #Adjust the new step-size - ht = self.adjust_stepsize(self._err, predict=True) - self.h = min(self.h,ht) if self._rejected else ht - - self._rejected = False - self._curjac = False - - if self._oldoldh == self.h and (self._theta <= self.thet):# or self._curiter==1): - self._needjac = False - self._needLU = False - else: - if self._theta <= self.thet: #or self._curiter == 1: - self._needjac = False - self._needLU = True - else: - self._needjac = True - self._needLU = True - if self.thet < 0: - self._needjac = True - self._needLU = True - - self._olderr = max(self._err,1.e-2) #Store the old error - break - - self._col_poly = self._collocation_pol(self._Z, self._col_poly, self._leny) #Calculate the new collocation polynomial - - return tn, yn #Return the step - - def _collocation_pol(self, Z, col_poly, leny): - - col_poly[2*leny:3*leny] = Z[:leny] / self.C[0,0] - col_poly[leny:2*leny] = ( Z[:leny] - Z[leny:2*leny] ) / (self.C[0,0]-self.C[1,0]) - col_poly[:leny] = ( Z[leny:2*leny] -Z[2*leny:3*leny] ) / (self.C[1,0]-1.) - col_poly[2*leny:3*leny] = ( col_poly[leny:2*leny] - col_poly[2*leny:3*leny] ) / self.C[1,0] - col_poly[leny:2*leny] = ( col_poly[leny:2*leny] - col_poly[:leny] ) / (self.C[0,0]-1.) - col_poly[2*leny:3*leny] = col_poly[leny:2*leny]-col_poly[2*leny:3*leny] - - return col_poly - - def _radau_F(self, Z, t, y): - - Z1 = Z[:self._leny] - Z2 = Z[self._leny:2*self._leny] - Z3 = Z[2*self._leny:3*self._leny] - - self.f(self.Y1,t+self.C[0]*self.h, y+Z1) - self.f(self.Y2,t+self.C[1]*self.h, y+Z2) - self.f(self.Y3,t+self.C[2]*self.h, y+Z3) - - self.statistics["nfcns"] += 3 - - return N.hstack((N.hstack((self.Y1,self.Y2)),self.Y3)) - - def calc_start_values(self): - """ - Calculate newton starting values. - """ - if self._first: - Z = N.zeros(self._leny*3) - W = N.zeros(self._leny*3) - else: - Z = self._Z - cq = self.C*self.h/self._oldh#self._oldoldh#self._oldh - newtval = self._col_poly - leny = self._leny - - Z[:leny] = cq[0,0]*(newtval[:leny]+(cq[0,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[0,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) - Z[leny:2*leny] = cq[1,0]*(newtval[:leny]+(cq[1,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[1,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) - Z[2*leny:3*leny]= cq[2,0]*(newtval[:leny]+(cq[2,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[2,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) - - W = N.dot(self.T2,Z) - - return Z, W - - def newton(self,t,y): - """ - The newton iteration. - """ - + + return flags[-1], tlist, ylist + + def _step(self, t, y): + """ + This calculates the next step in the integration. + """ + self._scaling = N.array(abs(y)*self.rtol + self.atol) #The scaling used. + + while True: #Loop for integrating one step. + + self.newton(t,y) + self._err = self.estimate_error() + + if self._err > 1.0: #Step was rejected. + self._rejected = True + self.statistics["nerrfails"] += 1 + ho = self.h + self.h = self.adjust_stepsize(self._err) + + self.log_message('Rejecting step at ' + str(t) + 'with old stepsize' + str(ho) + 'and new ' + str(self.h), SCREAM) + + if self._curjac or self._curiter == 1: + self._needjac = False + self._needLU = True + else: + self._needjac = True + self._needLU = True + else: + self.log_message('Accepting step at ' + str(t) + 'with stepsize ' + str(self.h),SCREAM) + + self.statistics["nsteps"] += 1 + + tn = t+self.h #Preform the step + yn = y+self._Z[2*self._leny:3*self._leny] + self.f(self._f0,tn,yn) + self.statistics["nfcns"] += 1 + + self._oldoldh = self._oldh #Store the old(old) step-size for use in the test below. + self._oldh = self.h #Store the old step-size + self._oldt = t #Store the old time-point + self._newt = tn #Store the new time-point + + #Adjust the new step-size + ht = self.adjust_stepsize(self._err, predict=True) + self.h = min(self.h,ht) if self._rejected else ht + + self._rejected = False + self._curjac = False + + if self._oldoldh == self.h and (self._theta <= self.thet):# or self._curiter==1): + self._needjac = False + self._needLU = False + else: + if self._theta <= self.thet: #or self._curiter == 1: + self._needjac = False + self._needLU = True + else: + self._needjac = True + self._needLU = True + if self.thet < 0: + self._needjac = True + self._needLU = True + + self._olderr = max(self._err,1.e-2) #Store the old error + break + + self._col_poly = self._collocation_pol(self._Z, self._col_poly, self._leny) #Calculate the new collocation polynomial + + return tn, yn #Return the step + + def _collocation_pol(self, Z, col_poly, leny): + + col_poly[2*leny:3*leny] = Z[:leny] / self.C[0,0] + col_poly[leny:2*leny] = ( Z[:leny] - Z[leny:2*leny] ) / (self.C[0,0]-self.C[1,0]) + col_poly[:leny] = ( Z[leny:2*leny] -Z[2*leny:3*leny] ) / (self.C[1,0]-1.) + col_poly[2*leny:3*leny] = ( col_poly[leny:2*leny] - col_poly[2*leny:3*leny] ) / self.C[1,0] + col_poly[leny:2*leny] = ( col_poly[leny:2*leny] - col_poly[:leny] ) / (self.C[0,0]-1.) + col_poly[2*leny:3*leny] = col_poly[leny:2*leny]-col_poly[2*leny:3*leny] + + return col_poly + + def _radau_F(self, Z, t, y): + + Z1 = Z[:self._leny] + Z2 = Z[self._leny:2*self._leny] + Z3 = Z[2*self._leny:3*self._leny] + + self.f(self.Y1,t+self.C[0]*self.h, y+Z1) + self.f(self.Y2,t+self.C[1]*self.h, y+Z2) + self.f(self.Y3,t+self.C[2]*self.h, y+Z3) + + self.statistics["nfcns"] += 3 + + return N.hstack((N.hstack((self.Y1,self.Y2)),self.Y3)) + + def calc_start_values(self): + """ + Calculate newton starting values. + """ + if self._first: + Z = N.zeros(self._leny*3) + W = N.zeros(self._leny*3) + else: + Z = self._Z + cq = self.C*self.h/self._oldh#self._oldoldh#self._oldh + newtval = self._col_poly + leny = self._leny + + Z[:leny] = cq[0,0]*(newtval[:leny]+(cq[0,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[0,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) + Z[leny:2*leny] = cq[1,0]*(newtval[:leny]+(cq[1,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[1,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) + Z[2*leny:3*leny]= cq[2,0]*(newtval[:leny]+(cq[2,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[2,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) + + W = N.dot(self.T2,Z) + + return Z, W + + def newton(self,t,y): + """ + The newton iteration. + """ + for k in range(20): - - self._curiter = 0 #Reset the iteration - self._fac_con = max(self._fac_con, self._eps)**0.8; - self._theta = abs(self.thet); - - if self._needjac: - self._jac = self.jacobian(t,y) - - if self._needLU: - self.statistics["nlus"] += 1 - self._a = self._alpha/self.h - self._b = self._beta/self.h - self._g = self._gamma/self.h - self._B = self._g*self.I - self._jac - - self._P1,self._L1,self._U1 = S.linalg.lu(self._B) #LU decomposition - self._P2,self._L2,self._U2 = S.linalg.lu(self._a*self.I-self._jac) - self._P3,self._L3,self._U3 = S.linalg.lu(self._b*self.I-self._jac) - - self._needLU = False - - if min(abs(N.diag(self._U1))) 0: - thq = newnrm/oldnrm - if i == 1: - self._theta = thq - else: - self._theta = N.sqrt(thq*thqold) - thqold = thq - - if self._theta < 0.99: #Convergence - self._fac_con = self._theta/(1.-self._theta) - dyth = self._fac_con*newnrm*self._theta**(self.newt-(i+1)-1)/self.fnewt - - if dyth >= 1.0: #Too slow convergence - qnewt = max(1.e-4,min(20.,dyth)) - self.h = 0.8*qnewt**(-1.0/(4.0+self.newt-(i+1)-1))*self.h - self._itfail = True - self._rejected = True - break - else: #Not convergence, abort - self._itfail = True - break - - oldnrm = max(newnrm,self._eps) #Store oldnorm - W = W+Z #Perform the iteration - - Z = N.dot(self.T3,W) #Calculate the new Z values - - if self._fac_con*newnrm <= self.fnewt: #Convergence? - self._itfail = False; - break - - else: #Iteration failed - self._itfail = True - - if not self._itfail: #Newton iteration converged - self._Z = Z.real - break - else: #Iteration failed - self.log_message('Iteration failed at time %e with step-size %e'%(t,self.h),SCREAM) - - self.statistics["nnfails"] += 1 - self._rejected = True #The step is rejected - - if self._theta >= 0.99: - self.h = self.h/2.0 - if self._curjac: - self._needjac = False - self._needLU = True - else: - self._needjac = True - self._needLU = True - else: - raise Explicit_ODE_Exception('Newton iteration failed at time %e with step-size %e'%(t,self.h)) - - def adjust_stepsize(self, err, predict=False): - - fac = min(self.safe, self.safe*(2.*self.newt+1.)/(2.*self.newt+self._curiter)) - quot = max(1./self.fac2,min(1./self.fac1,(err**0.25)/fac)) - hnormal = self.h/quot - - if predict: - if not self._first: - facgus = (self._hacc/self.h)*(err**2/self._olderr)**0.25/self.safe - facgus = max(1./self.fac2,min(1./self.fac1,facgus)) - quot = max(quot,facgus) - h = self.h/quot - else: - h = hnormal - self._hacc = self.h - else: - h = hnormal - - qt = h/self.h - - if (qt >= self.quot1) and (qt <= self.quot2): - h = self.h - - if self._first and err>=1.0: - h = self.h/10. - - if h < self._eps: - raise Explicit_ODE_Exception('Step-size to small at %e with h = %e'%(self._tc,self.h)) - - if h > self.maxh: - h = self.maxh - - return h - - def estimate_error(self): - - temp = 1./self.h*(self.E[0]*self._Z[:self._leny]+self.E[1]*self._Z[self._leny:2*self._leny]+self.E[2]*self._Z[2*self._leny:3*self._leny]) - - scal = self._scaling#/self.h - err_v = N.linalg.solve(self._U1,N.linalg.solve(self._L1,N.linalg.solve(self._P1,self._f0+temp))) - err = N.linalg.norm(err_v/scal) - err = max(err/N.sqrt(self._leny),1.e-10) - - if (self._rejected or self._first) and err >= 1.: #If the step was rejected, use the more expensive error estimation - self.statistics["nfcns"] += 1 - err_new = N.array([0.0]*self._leny) - self.f(err_new,self._tc,self._yc+err_v) - err_v = N.linalg.solve(self._U1,N.linalg.solve(self._L1,N.linalg.solve(self._P1,err_new+temp))) - err = N.linalg.norm(err_v/scal) - err = max(err/N.sqrt(self._leny),1.e-10) - - return err - - def jacobian(self, t, y): - """ - Calculates the Jacobian, either by an approximation or by the user - defined (jac specified in the problem class). - """ - self._curjac = True #The jacobian is up to date - self._needLU = True #A new LU-decomposition is needed - self._needjac = False #A new jacobian is not needed - - if self.usejac: #Retrieve the user-defined jacobian - cjac = self.problem.jac(t,y) - else: #Calculate a numeric jacobian - delt = N.array([(self._eps*max(abs(yi),1.e-5))**0.5 for yi in y])*N.identity(self._leny) #Calculate a disturbance - Fdelt = N.array([self.problem.rhs(t,y+e) for e in delt]) #Add the disturbance (row by row) - grad = ((Fdelt-self.problem.rhs(t,y)).T/delt.diagonal()).T - cjac = N.array(grad).T - - self.statistics["nfcnjacs"] += 1+self._leny #Add the number of function evaluations - - self.statistics["njacs"] += 1 #add the number of jacobian evaluation - return cjac - - def interpolate(self, t, k=0): - """ - Calculates the continuous output from Radau5. - """ - leny = self._leny - s = (t-self._newt)/self._oldh - Z = self._col_poly - - yout = self._yc+s*(Z[:leny]+(s-self.C[1,0]+1.)*(Z[leny:2*leny]+(s-self.C[0,0]+1.)*Z[2*leny:3*leny])) - return yout - - def _load_parameters(self): - - #Parameters - A = N.zeros([3,3]) - A[0,0] = (88.-7.*N.sqrt(6.))/360.0 - A[0,1] = (296.-169.*N.sqrt(6.))/1800.0 - A[0,2] = (-2.0+3.0*N.sqrt(6.))/225.0 - A[1,0] = (296.0+169.0*N.sqrt(6.))/1800.0 - A[1,1] = (88.+7.*N.sqrt(6.))/360.0 - A[1,2] = (-2.-3.*N.sqrt(6.))/225.0 - A[2,0] = (16.0-N.sqrt(6.))/36.0 - A[2,1] = (16.0+N.sqrt(6.))/36.0 - A[2,2] = (1.0/9.0) - - C = N.zeros([3,1]) - C[0,0]=(4.0-N.sqrt(6.0))/10.0 - C[1,0]=(4.0+N.sqrt(6.0))/10.0 - C[2,0]=1.0 - - B = N.zeros([1,3]) - B[0,0]=(16.0-N.sqrt(6.0))/36.0 - B[0,1]=(16.0+N.sqrt(6.0))/36.0 - B[0,2]=1.0/9.0 - - E = N.zeros(3) - E[0] = -13.0-7.*N.sqrt(6.) - E[1] = -13.0+7.0*N.sqrt(6.) - E[2] = -1.0 - E = 1.0/3.0*E - - Ainv = N.linalg.inv(A) - [eig, T] = N.linalg.eig(Ainv) - eig = N.array([eig[2],eig[0],eig[1]]) - J = N.diag(eig) - - self._alpha = eig[1] - self._beta = eig[2] - self._gamma = eig[0].real - - temp0 = T[:,0].copy() - temp1 = T[:,1].copy() - temp2 = T[:,2].copy() - T[:,0] = temp2 - T[:,1] = temp0 - T[:,2] = temp1 - Tinv = N.linalg.inv(T) - - I = N.eye(self._leny) - I3 = N.eye(3) - T1 = N.kron(J,I) - T2 = N.kron(Tinv,I) - T3 = N.kron(T,I) - - self.A = A - self.B = B - self.C = C - self.I = I - self.E = E - self.T1 = T1 - self.T2 = T2 - self.T3 = T3 - self.I3 = I3 - self.EIG = eig - -class Radau5DAE(Radau_Common,Implicit_ODE): - """ - Radau IIA fifth-order three-stages with step-size control and - continuous output. Based on the FORTRAN code RADAU5 by E.Hairer and - G.Wanner, which can be found here: - http://www.unige.ch/~hairer/software.html - - Details about the implementation (FORTRAN) can be found in the book,:: - - Solving Ordinary Differential Equations II, - Stiff and Differential-Algebraic Problems - - Authors: E. Hairer and G. Wanner - Springer-Verlag, ISBN: 3-540-60452-9 - - """ - - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Explicit_Problem' class. - """ - Implicit_ODE.__init__(self, problem) #Calls the base class - - #Default values - self.options["inith"] = 0.01 - self.options["newt"] = 7 #Maximum number of newton iterations - self.options["thet"] = 1.e-3 #Boundary for re-calculation of jac - self.options["fnewt"] = 0.0 #Stopping critera for Newtons Method - self.options["quot1"] = 1.0 #Parameters for changing step-size (lower bound) - self.options["quot2"] = 1.2 #Parameters for changing step-size (upper bound) - self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) - self.options["fac2"] = 8.0 #Parameters for step-size selection (upper bound) - self.options["maxh"] = N.inf #Maximum step-size. - self.options["safe"] = 0.9 #Safety factor - self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - self.options["usejac"] = True if self.problem_info["jac_fcn"] else False - self.options["maxsteps"] = 100000 - - #Solver support - self.supports["report_continuously"] = True - self.supports["interpolated_output"] = True - self.supports["state_events"] = True - - self._leny = len(self.y) #Dimension of the problem - self._type = '(implicit)' - self._event_info = None - - def initialize(self): - #Reset statistics + self._curiter += 1 #The current iteration + self.statistics["nniters"] += 1 #Adding one iteration + + #Solve the system + Z = N.dot(self.T2,self._radau_F(Z.real,t,y)) + + Z[:self._leny] =Z[:self._leny] -self._g*N.dot(self.I,W[:self._leny]) + Z[self._leny:2*self._leny] =Z[self._leny:2*self._leny] -self._a*N.dot(self.I,W[self._leny:2*self._leny]) #+self._b*N.dot(self.I,W[2*self._leny:3*self._leny]) + Z[2*self._leny:3*self._leny]=Z[2*self._leny:3*self._leny]-self._b*N.dot(self.I,W[2*self._leny:3*self._leny]) #-self._a*N.dot(self.I,W[2*self._leny:3*self._leny]) + + Z[:self._leny] =N.linalg.solve(self._U1,N.linalg.solve(self._L1,N.linalg.solve(self._P1,Z[:self._leny]))) + Z[self._leny:2*self._leny] =N.linalg.solve(self._U2,N.linalg.solve(self._L2,N.linalg.solve(self._P2,Z[self._leny:2*self._leny]))) + Z[2*self._leny:3*self._leny]=N.linalg.solve(self._U3,N.linalg.solve(self._L3,N.linalg.solve(self._P3,Z[2*self._leny:3*self._leny]))) + #---- + newnrm = N.linalg.norm(Z.reshape(-1,self._leny)/self._scaling,'fro')/N.sqrt(3.*self._leny) + + if i > 0: + thq = newnrm/oldnrm + if i == 1: + self._theta = thq + else: + self._theta = N.sqrt(thq*thqold) + thqold = thq + + if self._theta < 0.99: #Convergence + self._fac_con = self._theta/(1.-self._theta) + dyth = self._fac_con*newnrm*self._theta**(self.newt-(i+1)-1)/self.fnewt + + if dyth >= 1.0: #Too slow convergence + qnewt = max(1.e-4,min(20.,dyth)) + self.h = 0.8*qnewt**(-1.0/(4.0+self.newt-(i+1)-1))*self.h + self._itfail = True + self._rejected = True + break + else: #Not convergence, abort + self._itfail = True + break + + oldnrm = max(newnrm,self._eps) #Store oldnorm + W = W+Z #Perform the iteration + + Z = N.dot(self.T3,W) #Calculate the new Z values + + if self._fac_con*newnrm <= self.fnewt: #Convergence? + self._itfail = False; + break + + else: #Iteration failed + self._itfail = True + + if not self._itfail: #Newton iteration converged + self._Z = Z.real + break + else: #Iteration failed + self.log_message('Iteration failed at time %e with step-size %e'%(t,self.h),SCREAM) + + self.statistics["nnfails"] += 1 + self._rejected = True #The step is rejected + + if self._theta >= 0.99: + self.h = self.h/2.0 + if self._curjac: + self._needjac = False + self._needLU = True + else: + self._needjac = True + self._needLU = True + else: + raise Explicit_ODE_Exception('Newton iteration failed at time %e with step-size %e'%(t,self.h)) + + def adjust_stepsize(self, err, predict=False): + + fac = min(self.safe, self.safe*(2.*self.newt+1.)/(2.*self.newt+self._curiter)) + quot = max(1./self.fac2,min(1./self.fac1,(err**0.25)/fac)) + hnormal = self.h/quot + + if predict: + if not self._first: + facgus = (self._hacc/self.h)*(err**2/self._olderr)**0.25/self.safe + facgus = max(1./self.fac2,min(1./self.fac1,facgus)) + quot = max(quot,facgus) + h = self.h/quot + else: + h = hnormal + self._hacc = self.h + else: + h = hnormal + + qt = h/self.h + + if (qt >= self.quot1) and (qt <= self.quot2): + h = self.h + + if self._first and err>=1.0: + h = self.h/10. + + if h < self._eps: + raise Explicit_ODE_Exception('Step-size to small at %e with h = %e'%(self._tc,self.h)) + + if h > self.maxh: + h = self.maxh + + return h + + def estimate_error(self): + + temp = 1./self.h*(self.E[0]*self._Z[:self._leny]+self.E[1]*self._Z[self._leny:2*self._leny]+self.E[2]*self._Z[2*self._leny:3*self._leny]) + + scal = self._scaling#/self.h + err_v = N.linalg.solve(self._U1,N.linalg.solve(self._L1,N.linalg.solve(self._P1,self._f0+temp))) + err = N.linalg.norm(err_v/scal) + err = max(err/N.sqrt(self._leny),1.e-10) + + if (self._rejected or self._first) and err >= 1.: #If the step was rejected, use the more expensive error estimation + self.statistics["nfcns"] += 1 + err_new = N.array([0.0]*self._leny) + self.f(err_new,self._tc,self._yc+err_v) + err_v = N.linalg.solve(self._U1,N.linalg.solve(self._L1,N.linalg.solve(self._P1,err_new+temp))) + err = N.linalg.norm(err_v/scal) + err = max(err/N.sqrt(self._leny),1.e-10) + + return err + + def jacobian(self, t, y): + """ + Calculates the Jacobian, either by an approximation or by the user + defined (jac specified in the problem class). + """ + self._curjac = True #The jacobian is up to date + self._needLU = True #A new LU-decomposition is needed + self._needjac = False #A new jacobian is not needed + + if self.usejac: #Retrieve the user-defined jacobian + cjac = self.problem.jac(t,y) + else: #Calculate a numeric jacobian + delt = N.array([(self._eps*max(abs(yi),1.e-5))**0.5 for yi in y])*N.identity(self._leny) #Calculate a disturbance + Fdelt = N.array([self.problem.rhs(t,y+e) for e in delt]) #Add the disturbance (row by row) + grad = ((Fdelt-self.problem.rhs(t,y)).T/delt.diagonal()).T + cjac = N.array(grad).T + + self.statistics["nfcnjacs"] += 1+self._leny #Add the number of function evaluations + + self.statistics["njacs"] += 1 #add the number of jacobian evaluation + return cjac + + def interpolate(self, t, k=0): + """ + Calculates the continuous output from Radau5. + """ + leny = self._leny + s = (t-self._newt)/self._oldh + Z = self._col_poly + + yout = self._yc+s*(Z[:leny]+(s-self.C[1,0]+1.)*(Z[leny:2*leny]+(s-self.C[0,0]+1.)*Z[2*leny:3*leny])) + return yout + + def _load_parameters(self): + + #Parameters + A = N.zeros([3,3]) + A[0,0] = (88.-7.*N.sqrt(6.))/360.0 + A[0,1] = (296.-169.*N.sqrt(6.))/1800.0 + A[0,2] = (-2.0+3.0*N.sqrt(6.))/225.0 + A[1,0] = (296.0+169.0*N.sqrt(6.))/1800.0 + A[1,1] = (88.+7.*N.sqrt(6.))/360.0 + A[1,2] = (-2.-3.*N.sqrt(6.))/225.0 + A[2,0] = (16.0-N.sqrt(6.))/36.0 + A[2,1] = (16.0+N.sqrt(6.))/36.0 + A[2,2] = (1.0/9.0) + + C = N.zeros([3,1]) + C[0,0]=(4.0-N.sqrt(6.0))/10.0 + C[1,0]=(4.0+N.sqrt(6.0))/10.0 + C[2,0]=1.0 + + B = N.zeros([1,3]) + B[0,0]=(16.0-N.sqrt(6.0))/36.0 + B[0,1]=(16.0+N.sqrt(6.0))/36.0 + B[0,2]=1.0/9.0 + + E = N.zeros(3) + E[0] = -13.0-7.*N.sqrt(6.) + E[1] = -13.0+7.0*N.sqrt(6.) + E[2] = -1.0 + E = 1.0/3.0*E + + Ainv = N.linalg.inv(A) + [eig, T] = N.linalg.eig(Ainv) + eig = N.array([eig[2],eig[0],eig[1]]) + J = N.diag(eig) + + self._alpha = eig[1] + self._beta = eig[2] + self._gamma = eig[0].real + + temp0 = T[:,0].copy() + temp1 = T[:,1].copy() + temp2 = T[:,2].copy() + T[:,0] = temp2 + T[:,1] = temp0 + T[:,2] = temp1 + Tinv = N.linalg.inv(T) + + I = N.eye(self._leny) + I3 = N.eye(3) + T1 = N.kron(J,I) + T2 = N.kron(Tinv,I) + T3 = N.kron(T,I) + + self.A = A + self.B = B + self.C = C + self.I = I + self.E = E + self.T1 = T1 + self.T2 = T2 + self.T3 = T3 + self.I3 = I3 + self.EIG = eig + +class Radau5DAE(Radau_Common,Implicit_ODE): + """ + Radau IIA fifth-order three-stages with step-size control and + continuous output. Based on the FORTRAN code RADAU5 by E.Hairer and + G.Wanner, which can be found here: + http://www.unige.ch/~hairer/software.html + + Details about the implementation (FORTRAN) can be found in the book,:: + + Solving Ordinary Differential Equations II, + Stiff and Differential-Algebraic Problems + + Authors: E. Hairer and G. Wanner + Springer-Verlag, ISBN: 3-540-60452-9 + + """ + + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Explicit_Problem' class. + """ + Implicit_ODE.__init__(self, problem) #Calls the base class + + #Default values + self.options["inith"] = 0.01 + self.options["newt"] = 7 #Maximum number of newton iterations + self.options["thet"] = 1.e-3 #Boundary for re-calculation of jac + self.options["fnewt"] = 0.0 #Stopping critera for Newtons Method + self.options["quot1"] = 1.0 #Parameters for changing step-size (lower bound) + self.options["quot2"] = 1.2 #Parameters for changing step-size (upper bound) + self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) + self.options["fac2"] = 8.0 #Parameters for step-size selection (upper bound) + self.options["maxh"] = N.inf #Maximum step-size. + self.options["safe"] = 0.9 #Safety factor + self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + self.options["usejac"] = True if self.problem_info["jac_fcn"] else False + self.options["maxsteps"] = 100000 + + #Solver support + self.supports["report_continuously"] = True + self.supports["interpolated_output"] = True + self.supports["state_events"] = True + + self._leny = len(self.y) #Dimension of the problem + self._type = '(implicit)' + self._event_info = None + + def initialize(self): + #Reset statistics self.statistics.reset() #for k in self.statistics.keys(): # self.statistics[k] = 0 - - def set_problem_data(self): - if self.problem_info["state_events"]: - if self.problem_info["type"] == 1: - def event_func(t, y, yd): - return self.problem.state_events(t, y, yd, self.sw) - else: - def event_func(t, y, yd): - return self.problem.state_events(t, y, self.sw) - def f(t, y): - leny = self._leny - ret = 0 - res = self.problem.res(t, y[:leny], y[leny:2*leny], self.sw) - return N.append(y[leny:2*leny],res), [ret] - self._f = f - self.event_func = event_func - self._event_info = [0] * self.problem_info["dimRoot"] - self.g_old = self.event_func(self.t, self.y, self.yd) - else: - def f(t, y): - leny = self._leny - ret = 0 - res = self.problem.res(t, y[:leny], y[leny:2*leny]) - return N.append(y[leny:2*leny],res), [ret] - self._f = f - - def interpolate(self, time, k=0): - y = N.empty(self._leny*2) - for i in range(self._leny*2): - y[i] = radau5.contr5(i+1, time, self.cont) - if k == 0: - return y[:self._leny] - elif k == 1: - return y[self._leny:2*self._leny] - - def _solout(self, nrsol, told, t, y, cont, lrc, irtrn): - """ - This method is called after every successful step taken by Radau5 - """ - self.cont = cont #Saved to be used by the interpolation function. - - yd = y[self._leny:2*self._leny].copy() - y = y[:self._leny].copy() - if self.problem_info["state_events"]: - flag, t, y, yd = self.event_locator(told, t, y, yd) - #Convert to Fortram indicator. - if flag == ID_PY_EVENT: irtrn = -1 - - if self._opts["report_continuously"]: - initialize_flag = self.report_solution(t, y, yd, self._opts) - if initialize_flag: irtrn = -1 - else: - if self._opts["output_list"] is None: - self._tlist.append(t) - self._ylist.append(y) - self._ydlist.append(yd) - else: - output_list = self._opts["output_list"] - output_index = self._opts["output_index"] - try: - while output_list[output_index] <= t: - self._tlist.append(output_list[output_index]) - self._ylist.append(self.interpolate(output_list[output_index])) - self._ydlist.append(self.interpolate(output_list[output_index], 1)) - - output_index += 1 - except IndexError: - pass - self._opts["output_index"] = output_index - - if self.problem_info["state_events"] and flag == ID_PY_EVENT and len(self._tlist) > 0 and self._tlist[-1] != t: - self._tlist.append(t) - self._ylist.append(y) - self._ydlist.append(yd) - - return irtrn - - def _mas_f(self, am): - #return N.array([[1]*self._leny+[0]*self._leny]) - return self._mass_matrix - - def integrate(self, t, y, yd, tf, opts): - if self.usejac: - self.usejac=False - self.log_message("Jacobians are not currently supported, disabling.",NORMAL) - - ITOL = 1 #Both atol and rtol are vectors - IJAC = 1 if self.usejac else 0 #Switch for the jacobian, 0==NO JACOBIAN - MLJAC = self.problem_info["dim"]*2 #The jacobian is full - MUJAC = 0 #self.problem_info["dim"] #See MLJAC - IMAS = 1 #The mass matrix is supplied - MLMAS = 0 #The mass matrix is only defined on the diagonal - MUMAS = 0 #The mass matrix is only defined on the diagonal - IOUT = 1 #solout is called after every step - WORK = N.array([0.0]*(5*((self.problem_info["dim"]*2)**2+12)+20)) #Work (double) vector - IWORK = N.array([0]*(3*(self.problem_info["dim"]*2)+20)) #Work (integer) vector - - #Setting work options - WORK[1] = self.safe - WORK[2] = self.thet - WORK[3] = self.fnewt - WORK[4] = self.quot1 - WORK[5] = self.quot2 - WORK[6] = self.maxh - WORK[7] = self.fac1 - WORK[8] = self.fac2 - - #Setting iwork options - IWORK[1] = self.maxsteps - IWORK[2] = self.newt - IWORK[4] = self._leny #Number of index 1 variables - IWORK[5] = self._leny #Number of index 2 variables - IWORK[8] = self._leny #M1 - IWORK[9] = self._leny #M2 - - #Dummy methods - mas_dummy = lambda t:x - jac_dummy = (lambda t:x) if not self.usejac else self.problem.jac - - #Check for initialization - if opts["initialize"]: - self.set_problem_data() - self._tlist = [] - self._ylist = [] - self._ydlist = [] - - #Store the opts - self._opts = opts - - #Create y = [y, yd] - y = N.append(y,yd) - #Create mass matrix - #self._mass_matrix = N.array([[1]*self._leny+[0]*self._leny]) - self._mass_matrix = N.array([[0]*self._leny]) - - atol = N.append(self.atol, self.atol) - - t, y, h, iwork, flag = radau5.radau5(self._f, t, y.copy(), tf, self.inith, self.rtol*N.ones(self.problem_info["dim"]*2), atol, - ITOL, jac_dummy, IJAC, MLJAC, MUJAC, self._mas_f, IMAS, MLMAS, MUMAS, self._solout, IOUT, WORK, IWORK) - - #Checking return - if flag == 1: - flag = ID_PY_COMPLETE - elif flag == 2: - flag = ID_PY_EVENT - else: - raise Radau5Error(flag, t) - - #Retrieving statistics - self.statistics["nsteps"] += iwork[16] - self.statistics["nfcns"] += iwork[13] - self.statistics["njacs"] += iwork[14] - self.statistics["nfcnjacs"] += (iwork[14]*self.problem_info["dim"] if not self.usejac else 0) - #self.statistics["nstepstotal"] += iwork[15] - self.statistics["nerrfails"] += iwork[17] - self.statistics["nlus"] += iwork[18] - - return flag, self._tlist, self._ylist, self._ydlist - - def state_event_info(self): - return self._event_info - - def set_event_info(self, event_info): - self._event_info = event_info - - def print_statistics(self, verbose=NORMAL): - """ - Prints the run-time statistics for the problem. - """ - Implicit_ODE.print_statistics(self, verbose) #Calls the base class - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : Radau5 ' + self._type, verbose) - self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) - self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) - self.log_message('', verbose) - -class _Radau5DAE(Radau_Common,Implicit_ODE): - """ - Radau IIA fifth-order three-stages with step-size control and continuous output. - Based on the FORTRAN code by E.Hairer and G.Wanner, which can be found here: - http://www.unige.ch/~hairer/software.html - - Details about the implementation (FORTRAN) can be found in the book,:: - - Solving Ordinary Differential Equations II, - Stiff and Differential-Algebraic Problems - - Authors: E. Hairer and G. Wanner - Springer-Verlag, ISBN: 3-540-60452-9 - - This code is aimed at providing a Python implementation of the original code. - """ - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Implicit_Problem' class. - """ - Implicit_ODE.__init__(self, problem) #Calls the base class - - #Internal values - self._leny = len(self.y) #Dimension of the problem - self._2leny = 2*self._leny - - #Default values - self.options["inith"] = 0.01 - self.options["newt"] = 7 #Maximum number of newton iterations - self.options["thet"] = 1.e-3 #Boundary for re-calculation of jac - self.options["fnewt"] = 0 #Stopping critera for Newtons Method - self.options["quot1"] = 1.0 #Parameters for changing step-size (lower bound) - self.options["quot2"] = 1.2 #Parameters for changing step-size (upper bound) - self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) - self.options["fac2"] = 8.0 #Parameters for step-size selection (upper bound) - self.options["maxh"] = N.inf #Maximum step-size. - self.options["safe"] = 0.9 #Safety factor - self.options["atol"] = N.array([1.0e-6]*self._leny) #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - self.options["index"] = N.array([1]*self._leny+[2]*self._leny) - self.options["usejac"] = True if self.problem_info["jac_fcn"] else False - self.options["maxsteps"] = 10000 - - #Internal values - self._curjac = False #Current jacobian? - self._itfail = False #Iteration failed? - self._needjac = True #Need to update the jacobian? - self._needLU = True #Need new LU-factorisation? - self._first = True #First step? - self._rejected = True #Is the last step rejected? - self._oldh = 0.0 #Old stepsize - self._olderr = 1.0 #Old error - self._eps = N.finfo('double').eps - self._col_poly = N.zeros(self._2leny*3) - self._type = '(implicit)' - self._curiter = 0 #Number of current iterations - - #RES-Function - self.f = problem.res_internal - self.RES = N.array([0.0]*len(self.y0)) - - #Internal temporary result vector - self.Y1 = N.array([0.0]*len(self.y0)) - self.Y2 = N.array([0.0]*len(self.y0)) - self.Y3 = N.array([0.0]*len(self.y0)) - self._f0 = N.array([0.0]*len(self.y0)) - - - #Solver support - self.supports["one_step_mode"] = True - self.supports["interpolated_output"] = True - - # - Retrieve the Radau5 parameters - self._load_parameters() #Set the Radau5 parameters - - def _set_index(self, index): - """ - Sets the index of the variables in the problem which in turn - determine the error estimations. - - Parameters:: - - index - A list of integers, indicating the index - (1,2,3) of the variable. - - Example: - Radau5.index = [2,1] - - """ - if len(index) == self._2leny: - ind = N.array(index) - elif len(index) == self._leny: - ind = N.array(index+(N.array(index)+1).tolist()) - else: - raise Implicit_ODE_Exception('Wrong number of variables in the index vector.') - self.options["index"] = ind - - def _get_index(self): - """ - Sets the index of the variables in the problem which in turn - determine the error estimations. - - Parameters:: - - index - A list of integers, indicating the index - (1,2,3) of the variable. - - Example: - Radau5.index = [2,1] - - """ - return self.options["index"] - - index = property(_get_index,_set_index) - - def initialize(self): - #Reset statistics + + def set_problem_data(self): + if self.problem_info["state_events"]: + if self.problem_info["type"] == 1: + def event_func(t, y, yd): + return self.problem.state_events(t, y, yd, self.sw) + else: + def event_func(t, y, yd): + return self.problem.state_events(t, y, self.sw) + def f(t, y): + leny = self._leny + ret = 0 + res = self.problem.res(t, y[:leny], y[leny:2*leny], self.sw) + return N.append(y[leny:2*leny],res), [ret] + self._f = f + self.event_func = event_func + self._event_info = [0] * self.problem_info["dimRoot"] + self.g_old = self.event_func(self.t, self.y, self.yd) + else: + def f(t, y): + leny = self._leny + ret = 0 + res = self.problem.res(t, y[:leny], y[leny:2*leny]) + return N.append(y[leny:2*leny],res), [ret] + self._f = f + + def interpolate(self, time, k=0): + y = N.empty(self._leny*2) + for i in range(self._leny*2): + y[i] = radau5.contr5(i+1, time, self.cont) + if k == 0: + return y[:self._leny] + elif k == 1: + return y[self._leny:2*self._leny] + + def _solout(self, nrsol, told, t, y, cont, lrc, irtrn): + """ + This method is called after every successful step taken by Radau5 + """ + self.cont = cont #Saved to be used by the interpolation function. + + yd = y[self._leny:2*self._leny].copy() + y = y[:self._leny].copy() + if self.problem_info["state_events"]: + flag, t, y, yd = self.event_locator(told, t, y, yd) + #Convert to Fortram indicator. + if flag == ID_PY_EVENT: irtrn = -1 + + if self._opts["report_continuously"]: + initialize_flag = self.report_solution(t, y, yd, self._opts) + if initialize_flag: irtrn = -1 + else: + if self._opts["output_list"] is None: + self._tlist.append(t) + self._ylist.append(y) + self._ydlist.append(yd) + else: + output_list = self._opts["output_list"] + output_index = self._opts["output_index"] + try: + while output_list[output_index] <= t: + self._tlist.append(output_list[output_index]) + self._ylist.append(self.interpolate(output_list[output_index])) + self._ydlist.append(self.interpolate(output_list[output_index], 1)) + + output_index += 1 + except IndexError: + pass + self._opts["output_index"] = output_index + + if self.problem_info["state_events"] and flag == ID_PY_EVENT and len(self._tlist) > 0 and self._tlist[-1] != t: + self._tlist.append(t) + self._ylist.append(y) + self._ydlist.append(yd) + + return irtrn + + def _mas_f(self, am): + #return N.array([[1]*self._leny+[0]*self._leny]) + return self._mass_matrix + + def integrate(self, t, y, yd, tf, opts): + if self.usejac: + self.usejac=False + self.log_message("Jacobians are not currently supported, disabling.",NORMAL) + + ITOL = 1 #Both atol and rtol are vectors + IJAC = 1 if self.usejac else 0 #Switch for the jacobian, 0==NO JACOBIAN + MLJAC = self.problem_info["dim"]*2 #The jacobian is full + MUJAC = 0 #self.problem_info["dim"] #See MLJAC + IMAS = 1 #The mass matrix is supplied + MLMAS = 0 #The mass matrix is only defined on the diagonal + MUMAS = 0 #The mass matrix is only defined on the diagonal + IOUT = 1 #solout is called after every step + WORK = N.array([0.0]*(5*((self.problem_info["dim"]*2)**2+12)+20)) #Work (double) vector + IWORK = N.array([0]*(3*(self.problem_info["dim"]*2)+20)) #Work (integer) vector + + #Setting work options + WORK[1] = self.safe + WORK[2] = self.thet + WORK[3] = self.fnewt + WORK[4] = self.quot1 + WORK[5] = self.quot2 + WORK[6] = self.maxh + WORK[7] = self.fac1 + WORK[8] = self.fac2 + + #Setting iwork options + IWORK[1] = self.maxsteps + IWORK[2] = self.newt + IWORK[4] = self._leny #Number of index 1 variables + IWORK[5] = self._leny #Number of index 2 variables + IWORK[8] = self._leny #M1 + IWORK[9] = self._leny #M2 + + #Dummy methods + mas_dummy = lambda t:x + jac_dummy = (lambda t:x) if not self.usejac else self.problem.jac + + #Check for initialization + if opts["initialize"]: + self.set_problem_data() + self._tlist = [] + self._ylist = [] + self._ydlist = [] + + #Store the opts + self._opts = opts + + #Create y = [y, yd] + y = N.append(y,yd) + #Create mass matrix + #self._mass_matrix = N.array([[1]*self._leny+[0]*self._leny]) + self._mass_matrix = N.array([[0]*self._leny]) + + atol = N.append(self.atol, self.atol) + + t, y, h, iwork, flag = radau5.radau5(self._f, t, y.copy(), tf, self.inith, self.rtol*N.ones(self.problem_info["dim"]*2), atol, + ITOL, jac_dummy, IJAC, MLJAC, MUJAC, self._mas_f, IMAS, MLMAS, MUMAS, self._solout, IOUT, WORK, IWORK) + + #Checking return + if flag == 1: + flag = ID_PY_COMPLETE + elif flag == 2: + flag = ID_PY_EVENT + else: + raise Radau5Error(flag, t) + + #Retrieving statistics + self.statistics["nsteps"] += iwork[16] + self.statistics["nfcns"] += iwork[13] + self.statistics["njacs"] += iwork[14] + self.statistics["nfcnjacs"] += (iwork[14]*self.problem_info["dim"] if not self.usejac else 0) + #self.statistics["nstepstotal"] += iwork[15] + self.statistics["nerrfails"] += iwork[17] + self.statistics["nlus"] += iwork[18] + + return flag, self._tlist, self._ylist, self._ydlist + + def state_event_info(self): + return self._event_info + + def set_event_info(self, event_info): + self._event_info = event_info + + def print_statistics(self, verbose=NORMAL): + """ + Prints the run-time statistics for the problem. + """ + Implicit_ODE.print_statistics(self, verbose) #Calls the base class + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : Radau5 ' + self._type, verbose) + self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) + self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) + self.log_message('', verbose) + +class _Radau5DAE(Radau_Common,Implicit_ODE): + """ + Radau IIA fifth-order three-stages with step-size control and continuous output. + Based on the FORTRAN code by E.Hairer and G.Wanner, which can be found here: + http://www.unige.ch/~hairer/software.html + + Details about the implementation (FORTRAN) can be found in the book,:: + + Solving Ordinary Differential Equations II, + Stiff and Differential-Algebraic Problems + + Authors: E. Hairer and G. Wanner + Springer-Verlag, ISBN: 3-540-60452-9 + + This code is aimed at providing a Python implementation of the original code. + """ + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Implicit_Problem' class. + """ + Implicit_ODE.__init__(self, problem) #Calls the base class + + #Internal values + self._leny = len(self.y) #Dimension of the problem + self._2leny = 2*self._leny + + #Default values + self.options["inith"] = 0.01 + self.options["newt"] = 7 #Maximum number of newton iterations + self.options["thet"] = 1.e-3 #Boundary for re-calculation of jac + self.options["fnewt"] = 0 #Stopping critera for Newtons Method + self.options["quot1"] = 1.0 #Parameters for changing step-size (lower bound) + self.options["quot2"] = 1.2 #Parameters for changing step-size (upper bound) + self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) + self.options["fac2"] = 8.0 #Parameters for step-size selection (upper bound) + self.options["maxh"] = N.inf #Maximum step-size. + self.options["safe"] = 0.9 #Safety factor + self.options["atol"] = N.array([1.0e-6]*self._leny) #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + self.options["index"] = N.array([1]*self._leny+[2]*self._leny) + self.options["usejac"] = True if self.problem_info["jac_fcn"] else False + self.options["maxsteps"] = 10000 + + #Internal values + self._curjac = False #Current jacobian? + self._itfail = False #Iteration failed? + self._needjac = True #Need to update the jacobian? + self._needLU = True #Need new LU-factorisation? + self._first = True #First step? + self._rejected = True #Is the last step rejected? + self._oldh = 0.0 #Old stepsize + self._olderr = 1.0 #Old error + self._eps = N.finfo('double').eps + self._col_poly = N.zeros(self._2leny*3) + self._type = '(implicit)' + self._curiter = 0 #Number of current iterations + + #RES-Function + self.f = problem.res_internal + self.RES = N.array([0.0]*len(self.y0)) + + #Internal temporary result vector + self.Y1 = N.array([0.0]*len(self.y0)) + self.Y2 = N.array([0.0]*len(self.y0)) + self.Y3 = N.array([0.0]*len(self.y0)) + self._f0 = N.array([0.0]*len(self.y0)) + + + #Solver support + self.supports["one_step_mode"] = True + self.supports["interpolated_output"] = True + + # - Retrieve the Radau5 parameters + self._load_parameters() #Set the Radau5 parameters + + def _set_index(self, index): + """ + Sets the index of the variables in the problem which in turn + determine the error estimations. + + Parameters:: + + index - A list of integers, indicating the index + (1,2,3) of the variable. + + Example: + Radau5.index = [2,1] + + """ + if len(index) == self._2leny: + ind = N.array(index) + elif len(index) == self._leny: + ind = N.array(index+(N.array(index)+1).tolist()) + else: + raise Implicit_ODE_Exception('Wrong number of variables in the index vector.') + self.options["index"] = ind + + def _get_index(self): + """ + Sets the index of the variables in the problem which in turn + determine the error estimations. + + Parameters:: + + index - A list of integers, indicating the index + (1,2,3) of the variable. + + Example: + Radau5.index = [2,1] + + """ + return self.options["index"] + + index = property(_get_index,_set_index) + + def initialize(self): + #Reset statistics self.statistics.reset() - - def step_generator(self, t, y, yd, tf, opts): - - if opts["initialize"]: - self._oldh = self.inith - self.h = self.inith - self._fac_con = 1.0 - - if self.fnewt == 0: - self.fnewt = max(10.*self._eps/self.rtol,min(0.03,self.rtol**0.5)) - - self._f0 = self._ode_f(t,N.append(y,yd)) - self.statistics["nfcns"] +=1 - self._tc = t - self._yc = y - self._ydc = yd - + + def step_generator(self, t, y, yd, tf, opts): + + if opts["initialize"]: + self._oldh = self.inith + self.h = self.inith + self._fac_con = 1.0 + + if self.fnewt == 0: + self.fnewt = max(10.*self._eps/self.rtol,min(0.03,self.rtol**0.5)) + + self._f0 = self._ode_f(t,N.append(y,yd)) + self.statistics["nfcns"] +=1 + self._tc = t + self._yc = y + self._ydc = yd + for i in range(self.maxsteps): - - if t < tf: - t, y, yd = self._step(t, y, yd) - self._tc = t - self._yc = y - self._ydc = yd - - if self.h > N.abs(tf-t): - self.h = N.abs(tf-t) - - if t < tf: - yield ID_PY_OK, t,y,yd - else: - yield ID_PY_COMPLETE, t, y, yd - break - - self._first = False - else: - raise Implicit_ODE_Exception('Final time not reached within maximum number of steps') - - def step(self, t, y, yd, tf, opts): - - if opts["initialize"]: - self._next_step = self.step_generator(t,y,yd,tf,opts) + + if t < tf: + t, y, yd = self._step(t, y, yd) + self._tc = t + self._yc = y + self._ydc = yd + + if self.h > N.abs(tf-t): + self.h = N.abs(tf-t) + + if t < tf: + yield ID_PY_OK, t,y,yd + else: + yield ID_PY_COMPLETE, t, y, yd + break + + self._first = False + else: + raise Implicit_ODE_Exception('Final time not reached within maximum number of steps') + + def step(self, t, y, yd, tf, opts): + + if opts["initialize"]: + self._next_step = self.step_generator(t,y,yd,tf,opts) return next(self._next_step) - - def integrate(self, t, y, yd, tf, opts): - - if opts["output_list"] is not None: - - output_list = opts["output_list"] - output_index = opts["output_index"] - - next_step = self.step_generator(t,y,yd,tf,opts) - - tlist,ylist,ydlist = [], [], [] - res = [ID_PY_OK] - - while res[0] != ID_PY_COMPLETE: + + def integrate(self, t, y, yd, tf, opts): + + if opts["output_list"] is not None: + + output_list = opts["output_list"] + output_index = opts["output_index"] + + next_step = self.step_generator(t,y,yd,tf,opts) + + tlist,ylist,ydlist = [], [], [] + res = [ID_PY_OK] + + while res[0] != ID_PY_COMPLETE: res = next(next_step) - try: - while output_list[output_index] <= res[1]: - tlist.append(output_list[output_index]) - ylist.append(self.interpolate(output_list[output_index])) - ydlist.append(self.interpolate(output_list[output_index],k=1)) - - output_index = output_index+1 - except IndexError: - pass - return res[0], tlist, ylist, ydlist - else: + try: + while output_list[output_index] <= res[1]: + tlist.append(output_list[output_index]) + ylist.append(self.interpolate(output_list[output_index])) + ydlist.append(self.interpolate(output_list[output_index],k=1)) + + output_index = output_index+1 + except IndexError: + pass + return res[0], tlist, ylist, ydlist + else: [flags, tlist, ylist, ydlist] = list(zip(*list(self.step_generator(t, y, yd, tf,opts)))) - - return flags[-1], tlist, ylist, ydlist - - def _ode_f(self, t, y): - - #self.res_fcn(t,y[:self._leny],y[self._leny:]) - #return N.hstack((y[self._leny:],self.res_fcn(t,y[:self._leny],y[self._leny:]))) - - self.f(self.RES,t,y[:self._leny],y[self._leny:]) - return N.hstack((y[self._leny:],self.RES)) - - def _radau_F(self, Z, t, y, yd): - - Z1 = Z[:self._2leny] - Z2 = Z[self._2leny:2*self._2leny] - Z3 = Z[2*self._2leny:3*self._2leny] - - q = N.append(y,yd) - - sol1 = self._ode_f(t+self.C[0]*self.h, q+Z1) - sol2 = self._ode_f(t+self.C[1]*self.h, q+Z2) - sol3 = self._ode_f(t+self.C[2]*self.h, q+Z3) - - self.statistics["nfcns"] += 3 - - return N.hstack((N.hstack((sol1,sol2)),sol3)) - - def _step(self, t, y, yd): - """ - This calculates the next step in the integration. - """ - self._scaling = N.array(abs(N.append(y,yd))*self.rtol + self.atol.tolist()*2) #The scaling used. - - while True: #Loop for integrating one step. - - self.newton(t,y,yd) - self._err = self.estimate_error() - - if self._err > 1.0: #Step was rejected. - self._rejected = True - self.statistics["nerrfails"] += 1 - ho = self.h - self.h = self.adjust_stepsize(self._err) - - self.log_message('Rejecting step at ' + str(t) + 'with old stepsize' + str(ho) + 'and new ' + - str(self.h) + '. Error: ' + str(self._err),SCREAM) - - if self._curjac or self._curiter == 1: - self._needjac = False - self._needLU = True - else: - self._needjac = True - self._needLU = True - else: - - self.log_message("Accepting step at " + str(t) + ' with stepsize ' + str(self.h) + '. Error: ' + str(self._err),SCREAM) - self.statistics["nsteps"] += 1 - - tn = t+self.h #Preform the step - yn = y+self._Z[2*self._2leny:3*self._2leny][:self._leny] - ydn = yd+self._Z[2*self._2leny:3*self._2leny][self._leny:] - self._f0 = self._ode_f(t,N.append(yn,ydn)) - self.statistics["nfcns"] += 1 - - self._oldoldh = self._oldh #Store the old(old) step-size for use in the test below. - self._oldh = self.h #Store the old step-size - self._oldt = t #Store the old time-point - self._newt = tn #Store the new time-point - - #Adjust the new step-size - ht = self.adjust_stepsize(self._err, predict=True) - self.h = min(self.h,ht) if self._rejected else ht - - self._rejected = False - self._curjac = False - - if self._oldoldh == self.h and (self._theta <= self.thet or self._curiter==1): - self._needjac = False - self._needLU = False - else: - if self._theta <= self.thet or self._curiter == 1: - self._needjac = False - self._needLU = True - else: - self._needjac = True - self._needLU = True - if self.thet < 0: - self._needjac = True - self._needLU = True - - self._olderr = max(self._err,1.e-2) #Store the old error - break - - self._col_poly = self._collocation_pol(self._Z, self._col_poly, self._2leny) #Calculate the new collocation polynomial - - return tn, yn, ydn #Return the step - - def newton(self,t,y,yd): - """ - The newton iteration. - """ - + + return flags[-1], tlist, ylist, ydlist + + def _ode_f(self, t, y): + + #self.res_fcn(t,y[:self._leny],y[self._leny:]) + #return N.hstack((y[self._leny:],self.res_fcn(t,y[:self._leny],y[self._leny:]))) + + self.f(self.RES,t,y[:self._leny],y[self._leny:]) + return N.hstack((y[self._leny:],self.RES)) + + def _radau_F(self, Z, t, y, yd): + + Z1 = Z[:self._2leny] + Z2 = Z[self._2leny:2*self._2leny] + Z3 = Z[2*self._2leny:3*self._2leny] + + q = N.append(y,yd) + + sol1 = self._ode_f(t+self.C[0]*self.h, q+Z1) + sol2 = self._ode_f(t+self.C[1]*self.h, q+Z2) + sol3 = self._ode_f(t+self.C[2]*self.h, q+Z3) + + self.statistics["nfcns"] += 3 + + return N.hstack((N.hstack((sol1,sol2)),sol3)) + + def _step(self, t, y, yd): + """ + This calculates the next step in the integration. + """ + self._scaling = N.array(abs(N.append(y,yd))*self.rtol + self.atol.tolist()*2) #The scaling used. + + while True: #Loop for integrating one step. + + self.newton(t,y,yd) + self._err = self.estimate_error() + + if self._err > 1.0: #Step was rejected. + self._rejected = True + self.statistics["nerrfails"] += 1 + ho = self.h + self.h = self.adjust_stepsize(self._err) + + self.log_message('Rejecting step at ' + str(t) + 'with old stepsize' + str(ho) + 'and new ' + + str(self.h) + '. Error: ' + str(self._err),SCREAM) + + if self._curjac or self._curiter == 1: + self._needjac = False + self._needLU = True + else: + self._needjac = True + self._needLU = True + else: + + self.log_message("Accepting step at " + str(t) + ' with stepsize ' + str(self.h) + '. Error: ' + str(self._err),SCREAM) + self.statistics["nsteps"] += 1 + + tn = t+self.h #Preform the step + yn = y+self._Z[2*self._2leny:3*self._2leny][:self._leny] + ydn = yd+self._Z[2*self._2leny:3*self._2leny][self._leny:] + self._f0 = self._ode_f(t,N.append(yn,ydn)) + self.statistics["nfcns"] += 1 + + self._oldoldh = self._oldh #Store the old(old) step-size for use in the test below. + self._oldh = self.h #Store the old step-size + self._oldt = t #Store the old time-point + self._newt = tn #Store the new time-point + + #Adjust the new step-size + ht = self.adjust_stepsize(self._err, predict=True) + self.h = min(self.h,ht) if self._rejected else ht + + self._rejected = False + self._curjac = False + + if self._oldoldh == self.h and (self._theta <= self.thet or self._curiter==1): + self._needjac = False + self._needLU = False + else: + if self._theta <= self.thet or self._curiter == 1: + self._needjac = False + self._needLU = True + else: + self._needjac = True + self._needLU = True + if self.thet < 0: + self._needjac = True + self._needLU = True + + self._olderr = max(self._err,1.e-2) #Store the old error + break + + self._col_poly = self._collocation_pol(self._Z, self._col_poly, self._2leny) #Calculate the new collocation polynomial + + return tn, yn, ydn #Return the step + + def newton(self,t,y,yd): + """ + The newton iteration. + """ + for k in range(20): - - self._curiter = 0 #Reset the iteration - self._fac_con = max(self._fac_con, self._eps)**0.8; - self._theta = abs(self.thet); - - if self._needjac: - self._jac = self.jacobian(t,y,yd) - - if self._needLU: - self.statistics["nlus"] += 1 - self._a = self._alpha/self.h - self._b = self._beta/self.h - self._g = self._gamma/self.h - self._B = self._g*self.M - self._jac - - self._P1,self._L1,self._U1 = S.linalg.lu(self._B) #LU decomposition - self._P2,self._L2,self._U2 = S.linalg.lu(self._a*self.M-self._jac) - self._P3,self._L3,self._U3 = S.linalg.lu(self._b*self.M-self._jac) - - self._needLU = False - - if min(abs(N.diag(self._U1))) 0: - thq = newnrm/oldnrm - if i == 1: - self._theta = thq - else: - self._theta = N.sqrt(thq*thqold) - thqold = thq - - if self._theta < 0.99: #Convergence - self._fac_con = self._theta/(1.-self._theta) - dyth = self._fac_con*newnrm*self._theta**(self.newt-(i+1)-1)/self.fnewt - - if dyth >= 1.0: #Too slow convergence - qnewt = max(1.e-4,min(20.,dyth)) - self._hhfac = 0.8*qnewt**(-1.0/(4.0+self.newt-(i+1)-1)) - self.h = self._hhfac*self.h - self._itfail = True - self._rejected = True - break - else: #Not convergence, abort - self._itfail = True - break - - oldnrm = max(newnrm,self._eps) #Store oldnorm - W = W+Z #Perform the iteration - - Z = N.dot(self.T3,W) #Calculate the new Z values - - if self._fac_con*newnrm <= self.fnewt: #Convergence? - self._itfail = False; - break - - else: #Iteration failed - self._itfail = True - - if not self._itfail: #Newton iteration converged - self._Z = Z.real - break - else: #Iteration failed - self.log_message("Iteration failed at time %e with step-size %e"%(t,self.h),SCREAM) - self.statistics["nnfails"] += 1 - self._rejected = True #The step is rejected - - if self._theta >= 0.99: - self._hhfac = 0.5 - self.h = self.h*self._hhfac - if self._curjac: - self._needjac = False - self._needLU = True - else: - self._needjac = True - self._needLU = True - else: - raise Implicit_ODE_Exception('Newton iteration failed at time %e with step-size %e'%(t,self.h)) - - def estimate_error(self): - - temp = 1./self.h*(self.E[0]*self._Z[:self._2leny]+self.E[1]*self._Z[self._2leny:2*self._2leny]+self.E[2]*self._Z[2*self._2leny:3*self._2leny]) - temp = N.dot(self.M,temp) - - self._scaling = self._scaling/self.h**(self.index-1)#hfac - - scal = self._scaling#/self.h - err_v = N.linalg.solve(self._U1,N.linalg.solve(self._L1,N.linalg.solve(self._P1,self._f0+temp))) - err = N.linalg.norm(err_v/scal) - err = max(err/N.sqrt(self._2leny),1.e-10) - - if (self._rejected or self._first) and err >= 1.: #If the step was rejected, use the more expensive error estimation - self.statistics["nfcns"] += 1 - err_v = self._ode_f(self._tc,N.append(self._yc,self._ydc)+err_v) - err_v = N.linalg.solve(self._U1,N.linalg.solve(self._L1,N.linalg.solve(self._P1,err_v+temp))) - err = N.linalg.norm(err_v/scal) - err = max(err/N.sqrt(self._2leny),1.e-10) - - return err - - def interpolate(self, t, k=0): - """ - Calculates the continuous output from Radau5. - """ - leny = self._2leny - s = (t-self._newt)/self._oldh - Z = self._col_poly - - diff = s*(Z[:leny]+(s-self.C[1,0]+1.)*(Z[leny:2*leny]+(s-self.C[0,0]+1.)*Z[2*leny:3*leny])) - - yout = self._yc + diff[:self._leny] - ydout = self._ydc+ diff[self._leny:] - - if k==0: - return yout - elif k==1: - return ydout - else: - raise Implicit_ODE_Exception('Unknown value of k. Should be either 0 or 1') - - def jacobian(self, t, y, yd): - """ - Calculates the Jacobian, either by an approximation or by the user - defined (jac specified in the problem class). - """ - self._curjac = True #The jacobian is up to date - self._needLU = True #A new LU-decomposition is needed - self._needjac = False #A new jacobian is not needed - - q = N.append(y,yd) - - if self.usejac: #Retrieve the user-defined jacobian - cjac = self.problem.jac(t,y,yd) - else: #Calculate a numeric jacobian - delt = N.array([(self._eps*max(abs(yi),1.e-5))**0.5 for yi in q])*N.identity(self._2leny) #Calculate a disturbance - Fdelt = N.array([self._ode_f(t,q+e) for e in delt]) #Add the disturbance (row by row) - grad = ((Fdelt-self._ode_f(t,q)).T/delt.diagonal()).T - cjac = N.array(grad).T - self.statistics["nfcnjacs"] += 1+self._2leny #Add the number of function evaluations - - self.statistics["njacs"] += 1 #add the number of jacobian evaluation - return cjac - - def adjust_stepsize(self, err, predict=False): - - fac = min(self.safe, self.safe*(2.*self.newt+1.)/(2.*self.newt+self._curiter)) - quot = max(1./self.fac2,min(1./self.fac1,(err**0.25)/fac)) - hnormal = self.h/quot - - if predict: - if not self._first: - facgus = (self._hacc/self.h)*(err**2/self._olderr)**0.25/self.safe - facgus = max(1./self.fac2,min(1./self.fac1,facgus)) - quot = max(quot,facgus) - h = self.h/quot - else: - h = hnormal - self._hacc = self.h - else: - h = hnormal - - qt = h/self.h - - if (qt >= self.quot1) and (qt <= self.quot2): - h = self.h - - if h > self.maxh: - h = self.maxh - - if self._first and err>=1.0: - self._hhfac = 0.1 - h = self.h*self._hhfac - else: - self._hhfac = h/self.h - - if h < self._eps: - raise Implicit_ODE_Exception('Step-size to small at %e with h = %e'%(self._tc,self.h)) - - return h - - def _collocation_pol(self, Z, col_poly, leny): - - col_poly[2*leny:3*leny] = Z[:leny] / self.C[0,0] - col_poly[leny:2*leny] = ( Z[:leny] - Z[leny:2*leny] ) / (self.C[0,0]-self.C[1,0]) - col_poly[:leny] = ( Z[leny:2*leny] -Z[2*leny:3*leny] ) / (self.C[1,0]-1.) - col_poly[2*leny:3*leny] = ( col_poly[leny:2*leny] - col_poly[2*leny:3*leny] ) / self.C[1,0] - col_poly[leny:2*leny] = ( col_poly[leny:2*leny] - col_poly[:leny] ) / (self.C[0,0]-1.) - col_poly[2*leny:3*leny] = col_poly[leny:2*leny]-col_poly[2*leny:3*leny] - - return col_poly - - def calc_start_values(self): - """ - Calculate newton starting values. - """ - if self._first: - Z = N.zeros(self._2leny*3) - W = N.zeros(self._2leny*3) - else: - Z = self._Z - cq = self.C*self.h/self._oldh#self._oldoldh#self._oldh - newtval = self._col_poly - leny = self._2leny - - Z[:leny] = cq[0,0]*(newtval[:leny]+(cq[0,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[0,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) - Z[leny:2*leny] = cq[1,0]*(newtval[:leny]+(cq[1,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[1,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) - Z[2*leny:3*leny]= cq[2,0]*(newtval[:leny]+(cq[2,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[2,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) - - W = N.dot(self.T2,Z) - - return Z, W - - def _load_parameters(self): - - #Parameters - A = N.zeros([3,3]) - A[0,0] = (88.-7.*N.sqrt(6.))/360.0 - A[0,1] = (296.-169.*N.sqrt(6.))/1800.0 - A[0,2] = (-2.0+3.0*N.sqrt(6.))/225.0 - A[1,0] = (296.0+169.0*N.sqrt(6.))/1800.0 - A[1,1] = (88.+7.*N.sqrt(6.))/360.0 - A[1,2] = (-2.-3.*N.sqrt(6.))/225.0 - A[2,0] = (16.0-N.sqrt(6.))/36.0 - A[2,1] = (16.0+N.sqrt(6.))/36.0 - A[2,2] = (1.0/9.0) - - C = N.zeros([3,1]) - C[0,0]=(4.0-N.sqrt(6.0))/10.0 - C[1,0]=(4.0+N.sqrt(6.0))/10.0 - C[2,0]=1.0 - - B = N.zeros([1,3]) - B[0,0]=(16.0-N.sqrt(6.0))/36.0 - B[0,1]=(16.0+N.sqrt(6.0))/36.0 - B[0,2]=1.0/9.0 - - E = N.zeros(3) - E[0] = -13.0-7.*N.sqrt(6.) - E[1] = -13.0+7.0*N.sqrt(6.) - E[2] = -1.0 - E = 1.0/3.0*E - - M = N.array([[1.,0.],[0.,0.]]) - - Ainv = N.linalg.inv(A) - [eig, T] = N.linalg.eig(Ainv) - eig = N.array([eig[2],eig[0],eig[1]]) - J = N.diag(eig) - - self._alpha = eig[1] - self._beta = eig[2] - self._gamma = eig[0].real - - temp0 = T[:,0].copy() - temp1 = T[:,1].copy() - temp2 = T[:,2].copy() - T[:,0] = temp2 - T[:,1] = temp0 - T[:,2] = temp1 - Tinv = N.linalg.inv(T) - - I = N.eye(self._2leny) - M = N.kron(M,N.eye(self._leny)) - I3 = N.eye(3) - T1 = N.kron(J,M) - T2 = N.kron(Tinv,I) - T3 = N.kron(T,I) - - self.A = A - self.B = B - self.C = C - self.I = I - self.E = E - self.M = M - self.T1 = T1 - self.T2 = T2 - self.T3 = T3 - self.I3 = I3 - self.EIG = eig + self._curiter += 1 #The current iteration + self.statistics["nniters"] += 1 #Adding one iteration + + #Solve the system + Z = N.dot(self.T2,self._radau_F(Z.real,t,y,yd)) + + Z[:self._2leny] =Z[:self._2leny] -self._g*N.dot(self.M,W[:self._2leny]) + Z[self._2leny:2*self._2leny] =Z[self._2leny:2*self._2leny] -self._a*N.dot(self.M,W[self._2leny:2*self._2leny]) #+self._b*N.dot(self.I,W[2*self._leny:3*self._leny]) + Z[2*self._2leny:3*self._2leny]=Z[2*self._2leny:3*self._2leny]-self._b*N.dot(self.M,W[2*self._2leny:3*self._2leny]) #-self._a*N.dot(self.I,W[2*self._leny:3*self._leny]) + + Z[:self._2leny] =N.linalg.solve(self._U1,N.linalg.solve(self._L1,N.linalg.solve(self._P1,Z[:self._2leny]))) + Z[self._2leny:2*self._2leny] =N.linalg.solve(self._U2,N.linalg.solve(self._L2,N.linalg.solve(self._P2,Z[self._2leny:2*self._2leny]))) + Z[2*self._2leny:3*self._2leny]=N.linalg.solve(self._U3,N.linalg.solve(self._L3,N.linalg.solve(self._P3,Z[2*self._2leny:3*self._2leny]))) + #---- + + self._scaling = self._scaling/self.h**(self.index-1)#hfac + + newnrm = N.linalg.norm(Z.reshape(-1,self._2leny)/self._scaling,'fro')/N.sqrt(3.*self._2leny) + + if i > 0: + thq = newnrm/oldnrm + if i == 1: + self._theta = thq + else: + self._theta = N.sqrt(thq*thqold) + thqold = thq + + if self._theta < 0.99: #Convergence + self._fac_con = self._theta/(1.-self._theta) + dyth = self._fac_con*newnrm*self._theta**(self.newt-(i+1)-1)/self.fnewt + + if dyth >= 1.0: #Too slow convergence + qnewt = max(1.e-4,min(20.,dyth)) + self._hhfac = 0.8*qnewt**(-1.0/(4.0+self.newt-(i+1)-1)) + self.h = self._hhfac*self.h + self._itfail = True + self._rejected = True + break + else: #Not convergence, abort + self._itfail = True + break + + oldnrm = max(newnrm,self._eps) #Store oldnorm + W = W+Z #Perform the iteration + + Z = N.dot(self.T3,W) #Calculate the new Z values + + if self._fac_con*newnrm <= self.fnewt: #Convergence? + self._itfail = False; + break + + else: #Iteration failed + self._itfail = True + + if not self._itfail: #Newton iteration converged + self._Z = Z.real + break + else: #Iteration failed + self.log_message("Iteration failed at time %e with step-size %e"%(t,self.h),SCREAM) + self.statistics["nnfails"] += 1 + self._rejected = True #The step is rejected + + if self._theta >= 0.99: + self._hhfac = 0.5 + self.h = self.h*self._hhfac + if self._curjac: + self._needjac = False + self._needLU = True + else: + self._needjac = True + self._needLU = True + else: + raise Implicit_ODE_Exception('Newton iteration failed at time %e with step-size %e'%(t,self.h)) + + def estimate_error(self): + + temp = 1./self.h*(self.E[0]*self._Z[:self._2leny]+self.E[1]*self._Z[self._2leny:2*self._2leny]+self.E[2]*self._Z[2*self._2leny:3*self._2leny]) + temp = N.dot(self.M,temp) + + self._scaling = self._scaling/self.h**(self.index-1)#hfac + + scal = self._scaling#/self.h + err_v = N.linalg.solve(self._U1,N.linalg.solve(self._L1,N.linalg.solve(self._P1,self._f0+temp))) + err = N.linalg.norm(err_v/scal) + err = max(err/N.sqrt(self._2leny),1.e-10) + + if (self._rejected or self._first) and err >= 1.: #If the step was rejected, use the more expensive error estimation + self.statistics["nfcns"] += 1 + err_v = self._ode_f(self._tc,N.append(self._yc,self._ydc)+err_v) + err_v = N.linalg.solve(self._U1,N.linalg.solve(self._L1,N.linalg.solve(self._P1,err_v+temp))) + err = N.linalg.norm(err_v/scal) + err = max(err/N.sqrt(self._2leny),1.e-10) + + return err + + def interpolate(self, t, k=0): + """ + Calculates the continuous output from Radau5. + """ + leny = self._2leny + s = (t-self._newt)/self._oldh + Z = self._col_poly + + diff = s*(Z[:leny]+(s-self.C[1,0]+1.)*(Z[leny:2*leny]+(s-self.C[0,0]+1.)*Z[2*leny:3*leny])) + + yout = self._yc + diff[:self._leny] + ydout = self._ydc+ diff[self._leny:] + + if k==0: + return yout + elif k==1: + return ydout + else: + raise Implicit_ODE_Exception('Unknown value of k. Should be either 0 or 1') + + def jacobian(self, t, y, yd): + """ + Calculates the Jacobian, either by an approximation or by the user + defined (jac specified in the problem class). + """ + self._curjac = True #The jacobian is up to date + self._needLU = True #A new LU-decomposition is needed + self._needjac = False #A new jacobian is not needed + + q = N.append(y,yd) + + if self.usejac: #Retrieve the user-defined jacobian + cjac = self.problem.jac(t,y,yd) + else: #Calculate a numeric jacobian + delt = N.array([(self._eps*max(abs(yi),1.e-5))**0.5 for yi in q])*N.identity(self._2leny) #Calculate a disturbance + Fdelt = N.array([self._ode_f(t,q+e) for e in delt]) #Add the disturbance (row by row) + grad = ((Fdelt-self._ode_f(t,q)).T/delt.diagonal()).T + cjac = N.array(grad).T + self.statistics["nfcnjacs"] += 1+self._2leny #Add the number of function evaluations + + self.statistics["njacs"] += 1 #add the number of jacobian evaluation + return cjac + + def adjust_stepsize(self, err, predict=False): + + fac = min(self.safe, self.safe*(2.*self.newt+1.)/(2.*self.newt+self._curiter)) + quot = max(1./self.fac2,min(1./self.fac1,(err**0.25)/fac)) + hnormal = self.h/quot + + if predict: + if not self._first: + facgus = (self._hacc/self.h)*(err**2/self._olderr)**0.25/self.safe + facgus = max(1./self.fac2,min(1./self.fac1,facgus)) + quot = max(quot,facgus) + h = self.h/quot + else: + h = hnormal + self._hacc = self.h + else: + h = hnormal + + qt = h/self.h + + if (qt >= self.quot1) and (qt <= self.quot2): + h = self.h + + if h > self.maxh: + h = self.maxh + + if self._first and err>=1.0: + self._hhfac = 0.1 + h = self.h*self._hhfac + else: + self._hhfac = h/self.h + + if h < self._eps: + raise Implicit_ODE_Exception('Step-size to small at %e with h = %e'%(self._tc,self.h)) + + return h + + def _collocation_pol(self, Z, col_poly, leny): + + col_poly[2*leny:3*leny] = Z[:leny] / self.C[0,0] + col_poly[leny:2*leny] = ( Z[:leny] - Z[leny:2*leny] ) / (self.C[0,0]-self.C[1,0]) + col_poly[:leny] = ( Z[leny:2*leny] -Z[2*leny:3*leny] ) / (self.C[1,0]-1.) + col_poly[2*leny:3*leny] = ( col_poly[leny:2*leny] - col_poly[2*leny:3*leny] ) / self.C[1,0] + col_poly[leny:2*leny] = ( col_poly[leny:2*leny] - col_poly[:leny] ) / (self.C[0,0]-1.) + col_poly[2*leny:3*leny] = col_poly[leny:2*leny]-col_poly[2*leny:3*leny] + + return col_poly + + def calc_start_values(self): + """ + Calculate newton starting values. + """ + if self._first: + Z = N.zeros(self._2leny*3) + W = N.zeros(self._2leny*3) + else: + Z = self._Z + cq = self.C*self.h/self._oldh#self._oldoldh#self._oldh + newtval = self._col_poly + leny = self._2leny + + Z[:leny] = cq[0,0]*(newtval[:leny]+(cq[0,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[0,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) + Z[leny:2*leny] = cq[1,0]*(newtval[:leny]+(cq[1,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[1,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) + Z[2*leny:3*leny]= cq[2,0]*(newtval[:leny]+(cq[2,0]-self.C[1,0]+1.)*(newtval[leny:2*leny]+(cq[2,0]-self.C[0,0]+1.)*newtval[2*leny:3*leny])) + + W = N.dot(self.T2,Z) + + return Z, W + + def _load_parameters(self): + + #Parameters + A = N.zeros([3,3]) + A[0,0] = (88.-7.*N.sqrt(6.))/360.0 + A[0,1] = (296.-169.*N.sqrt(6.))/1800.0 + A[0,2] = (-2.0+3.0*N.sqrt(6.))/225.0 + A[1,0] = (296.0+169.0*N.sqrt(6.))/1800.0 + A[1,1] = (88.+7.*N.sqrt(6.))/360.0 + A[1,2] = (-2.-3.*N.sqrt(6.))/225.0 + A[2,0] = (16.0-N.sqrt(6.))/36.0 + A[2,1] = (16.0+N.sqrt(6.))/36.0 + A[2,2] = (1.0/9.0) + + C = N.zeros([3,1]) + C[0,0]=(4.0-N.sqrt(6.0))/10.0 + C[1,0]=(4.0+N.sqrt(6.0))/10.0 + C[2,0]=1.0 + + B = N.zeros([1,3]) + B[0,0]=(16.0-N.sqrt(6.0))/36.0 + B[0,1]=(16.0+N.sqrt(6.0))/36.0 + B[0,2]=1.0/9.0 + + E = N.zeros(3) + E[0] = -13.0-7.*N.sqrt(6.) + E[1] = -13.0+7.0*N.sqrt(6.) + E[2] = -1.0 + E = 1.0/3.0*E + + M = N.array([[1.,0.],[0.,0.]]) + + Ainv = N.linalg.inv(A) + [eig, T] = N.linalg.eig(Ainv) + eig = N.array([eig[2],eig[0],eig[1]]) + J = N.diag(eig) + + self._alpha = eig[1] + self._beta = eig[2] + self._gamma = eig[0].real + + temp0 = T[:,0].copy() + temp1 = T[:,1].copy() + temp2 = T[:,2].copy() + T[:,0] = temp2 + T[:,1] = temp0 + T[:,2] = temp1 + Tinv = N.linalg.inv(T) + + I = N.eye(self._2leny) + M = N.kron(M,N.eye(self._leny)) + I3 = N.eye(3) + T1 = N.kron(J,M) + T2 = N.kron(Tinv,I) + T3 = N.kron(T,I) + + self.A = A + self.B = B + self.C = C + self.I = I + self.E = E + self.M = M + self.T1 = T1 + self.T2 = T2 + self.T3 = T3 + self.I3 = I3 + self.EIG = eig diff --git a/src/solvers/rosenbrock.py b/src/solvers/rosenbrock.py index 586bb87a..57fba5ca 100644 --- a/src/solvers/rosenbrock.py +++ b/src/solvers/rosenbrock.py @@ -1,459 +1,459 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import scipy.sparse as sp - -from assimulo.ode import * -from assimulo.explicit_ode import Explicit_ODE - -from assimulo.exception import * -from assimulo.support import set_type_shape_array - -from assimulo.lib import rodas - -class Rodas_Common(object): - - def _set_atol(self,atol): - - self.options["atol"] = set_type_shape_array(atol) - - if len(self.options["atol"]) == 1: - self.options["atol"] = self.options["atol"]*N.ones(self._leny) - elif len(self.options["atol"]) != self._leny: - raise Rodas_Exception("atol must be of length one or same as the dimension of the problem.") - - def _get_atol(self): - """ - Defines the absolute tolerance(s) that is to be used by the solver. - Can be set differently for each variable. - - Parameters:: - - atol - - Default '1.0e-6'. - - - Should be a positive float or a numpy vector - of floats. - - Example: - atol = [1.0e-4, 1.0e-6] - """ - return self.options["atol"] - - atol=property(_get_atol,_set_atol) - - def _set_rtol(self,rtol): - try: - self.options["rtol"] = float(rtol) - except (ValueError, TypeError): - raise Rodas_Exception('Relative tolerance must be a (scalar) float.') - if self.options["rtol"] <= 0.0: - raise Rodas_Exception('Relative tolerance must be a positive (scalar) float.') - - def _get_rtol(self): - """ - Defines the relative tolerance that is to be used by the solver. - - Parameters:: - - rtol - - Default '1.0e-6'. - - - Should be a positive float. - - Example: - rtol = 1.0e-4 - """ - return self.options["rtol"] - - rtol=property(_get_rtol,_set_rtol) - - def _get_maxsteps(self): - """ - The maximum number of steps allowed to be taken to reach the - final time. - - Parameters:: - - maxsteps - - Default 10000 - - - Should be a positive integer - """ - return self.options["maxsteps"] - - def _set_maxsteps(self, max_steps): - try: - max_steps = int(max_steps) - except (TypeError, ValueError): - raise Rodas_Exception("Maximum number of steps must be a positive integer.") - self.options["maxsteps"] = max_steps - - maxsteps = property(_get_maxsteps, _set_maxsteps) - - def _set_fac1(self, fac1): - try: - self.options["fac1"] = float(fac1) - except (ValueError, TypeError): - raise Rodas_Exception('The fac1 must be an integer or float.') - - def _get_fac1(self): - """ - Parameters for step-size selection. The new step-size is chosen - subject to the restriction fac1 <= current step-size / old step-size <= fac2. - - Parameters:: - - fac1 - - Default 0.2 - - - Should be a float. - - Example: - fac1 = 0.1 - """ - return self.options["fac1"] - - fac1 = property(_get_fac1, _set_fac1) - - def _set_fac2(self, fac2): - try: - self.options["fac2"] = float(fac2) - except (ValueError, TypeError): - raise Rodas_Exception('The fac2 must be an integer or float.') - - def _get_fac2(self): - """ - Parameters for step-size selection. The new step-size is chosen - subject to the restriction fac1 <= current step-size / old step-size <= fac2. - - Parameters:: - - fac2 - - Default 8.0 - - - Should be a float. - - Example: - fac2 = 10.0 - """ - return self.options["fac2"] - - fac2 = property(_get_fac2, _set_fac2) - - def _set_safe(self, safe): - try: - self.options["safe"] = float(safe) - except (ValueError, TypeError): - raise Rodas_Exception('The safe must be an integer or float.') - - def _get_safe(self): - """ - The safety factor in the step-size prediction. - - Parameters:: - - safe - - Default '0.9' - - - Should be float. - - Example: - safe = 0.8 - """ - return self.options["safe"] - - safe = property(_get_safe, _set_safe) - - def _set_initial_step(self, initstep): - try: - self.options["inith"] = float(initstep) - except (ValueError, TypeError): - raise Rodas_Exception('The initial step must be an integer or float.') - - def _get_initial_step(self): - """ - This determines the initial step-size to be used in the integration. - - Parameters:: - - inith - - Default '0.01'. - - - Should be float. - - Example: - inith = 0.01 - """ - return self.options["inith"] - - inith = property(_get_initial_step,_set_initial_step) - - def _set_max_h(self,max_h): - try: - self.options["maxh"] = float(max_h) - except (ValueError,TypeError): - raise Rodas_Exception('Maximal stepsize must be a (scalar) float.') - if self.options["maxh"] < 0: - raise Rodas_Exception('Maximal stepsize must be a positiv (scalar) float.') - - def _get_max_h(self): - """ - Defines the maximal step-size that is to be used by the solver. - - Parameters:: - - maxh - - Default maxh = final time - current time. - - - Should be a float. - - Example: - maxh = 0.01 - - """ - return self.options["maxh"] - - maxh=property(_get_max_h,_set_max_h) - - def _set_usejac(self, jac): - self.options["usejac"] = bool(jac) - - def _get_usejac(self): - """ - This sets the option to use the user defined Jacobian. If a - user provided jacobian is implemented into the problem the - default setting is to use that Jacobian. If not, an - approximation is used. - - Parameters:: - - usejac - - True - use user defined Jacobian - False - use an approximation - - - Should be a Boolean. - - Example: - usejac = False - """ - return self.options["usejac"] - - usejac = property(_get_usejac,_set_usejac) - - -class RodasODE(Rodas_Common, Explicit_ODE): - """ - Rosenbrock method of order (3)4 with step-size control and - continuous output. - - Based on the FORTRAN code RODAS by E.Hairer and G.Wanner, which can - be found here: http://www.unige.ch/~hairer/software.html - - Details about the implementation (FORTRAN) can be found in the book,:: - - Solving Ordinary Differential Equations II, - Stiff and Differential-Algebraic Problems - - Authors: E. Hairer and G. Wanner - Springer-Verlag, ISBN: 3-540-60452-9 - - """ - - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Explicit_Problem' class. - """ - Explicit_ODE.__init__(self, problem) #Calls the base class - - #Default values - self.options["inith"] = 0.01 - self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) - self.options["fac2"] = 6.0 #Parameters for step-size selection (upper bound) - self.options["maxh"] = N.inf #Maximum step-size. - self.options["safe"] = 0.9 #Safety factor - self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - self.options["usejac"] = True if self.problem_info["jac_fcn"] else False - self.options["maxsteps"] = 10000 - - #Solver support - self.supports["report_continuously"] = True - self.supports["interpolated_output"] = True - self.supports["state_events"] = True - - #Internal - self._leny = len(self.y) #Dimension of the problem - - def initialize(self): - #Reset statistics - self.statistics.reset() - - def set_problem_data(self): - if self.problem_info["state_events"]: - def event_func(t, y): - return self.problem.state_events(t, y, self.sw) - def f(t, y): - return self.problem.rhs(t, y, self.sw) - self.f = f - self.event_func = event_func - self._event_info = [0] * self.problem_info["dimRoot"] - self.g_old = self.event_func(self.t, self.y) - else: - self.f = self.problem.rhs - - def interpolate(self, time): - y = N.empty(self._leny) - for i in range(self._leny): - y[i] = rodas.contro(i+1, time, self.cont) - - return y - - def _solout(self, nrsol, told, t, y, cont, lrc, irtrn): - """ - This method is called after every successful step taken by Rodas - """ - self.cont = cont #Saved to be used by the interpolation function. - - if self.problem_info["state_events"]: - flag, t, y = self.event_locator(told, t, y) - #Convert to Fortram indicator. - if flag == ID_PY_EVENT: irtrn = -1 - - if self._opts["report_continuously"]: - initialize_flag = self.report_solution(t, y, self._opts) - if initialize_flag: irtrn = -1 - else: - if self._opts["output_list"] is None: - self._tlist.append(t) - self._ylist.append(y.copy()) - else: - output_list = self._opts["output_list"] - output_index = self._opts["output_index"] - try: - while output_list[output_index] <= t: - self._tlist.append(output_list[output_index]) - self._ylist.append(self.interpolate(output_list[output_index])) - - output_index += 1 - except IndexError: - pass - self._opts["output_index"] = output_index - - if self.problem_info["state_events"] and flag == ID_PY_EVENT and len(self._tlist) > 0 and self._tlist[-1] != t: - self._tlist.append(t) - self._ylist.append(y) - - return irtrn - - def _jacobian(self, t, y): - """ - Calculates the Jacobian, either by an approximation or by the user - defined (jac specified in the problem class). - """ - jac = self.problem.jac(t,y) - - if isinstance(jac, sp.csc_matrix): - jac = jac.toarray() - - return jac - - def integrate(self, t, y, tf, opts): - IFCN = 1 #The function may depend on t - ITOL = 1 #Both rtol and atol are vectors - IJAC = 1 if self.usejac else 0 #Switch for the jacobian, 0==NO JACOBIAN - MLJAC = self.problem_info["dim"] #The jacobian is full - MUJAC = self.problem_info["dim"] #The jacobian is full - IDFX = 0 #df/dt is computed internally - IMAS = 0 #The mass matrix is the identity - MLMAS = self.problem_info["dim"] #The mass matrix is full - MUMAS = self.problem_info["dim"] #The mass matrix is full - IOUT = 1 #Solout is called after every accepted step - WORK = N.array([0.0]*(2*self.problem_info["dim"]**2+14*self.problem_info["dim"]+20)) - IWORK = N.array([0]*(self.problem_info["dim"]+20)) - - #Setting work options - WORK[1] = self.maxh - WORK[2] = self.fac1 - WORK[3] = self.fac2 - WORK[4] = self.safe - - #Setting iwork options - IWORK[0] = self.maxsteps - - #Dummy methods - mas_dummy = lambda t:x - jac_dummy = (lambda t:x) if not self.usejac else self._jacobian - dfx_dummy = lambda t:x - - #Check for initialization - if opts["initialize"]: - self.set_problem_data() - self._tlist = [] - self._ylist = [] - - #Store the opts - self._opts = opts - - t, y, h, iwork, flag = rodas.rodas(self.f, IFCN, t, y.copy(), tf, self.inith, self.rtol*N.ones(self.problem_info["dim"]), self.atol, - ITOL, jac_dummy, IJAC, MLJAC, MUJAC, dfx_dummy, IDFX, mas_dummy, IMAS, MLMAS, MUMAS, self._solout, IOUT, WORK, IWORK) - - #Checking return - if flag == 1: - flag = ID_PY_COMPLETE - elif flag == 2: - flag = ID_PY_EVENT - else: - raise Exception("Rodas failed with flag %d"%flag) - - #Retrieving statistics - self.statistics["nsteps"] += iwork[16] - self.statistics["nfcns"] += iwork[13] - self.statistics["njacs"] += iwork[14] - #self.statistics["nstepstotal"] += iwork[15] - self.statistics["nfcnjacs"] += (iwork[14]*self.problem_info["dim"] if not self.usejac else 0) - self.statistics["nerrfails"] += iwork[17] - self.statistics["nlus"] += iwork[18] - - return flag, self._tlist, self._ylist - - def state_event_info(self): - return self._event_info - - def set_event_info(self, event_info): - self._event_info = event_info - - def print_statistics(self, verbose=NORMAL): - """ - Prints the run-time statistics for the problem. - """ - Explicit_ODE.print_statistics(self, verbose) #Calls the base class - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : Rodas ', verbose) - self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) - self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) - self.log_message('', verbose) - - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import scipy.sparse as sp + +from assimulo.ode import * +from assimulo.explicit_ode import Explicit_ODE + +from assimulo.exception import * +from assimulo.support import set_type_shape_array + +from assimulo.lib import rodas + +class Rodas_Common(object): + + def _set_atol(self,atol): + + self.options["atol"] = set_type_shape_array(atol) + + if len(self.options["atol"]) == 1: + self.options["atol"] = self.options["atol"]*N.ones(self._leny) + elif len(self.options["atol"]) != self._leny: + raise Rodas_Exception("atol must be of length one or same as the dimension of the problem.") + + def _get_atol(self): + """ + Defines the absolute tolerance(s) that is to be used by the solver. + Can be set differently for each variable. + + Parameters:: + + atol + - Default '1.0e-6'. + + - Should be a positive float or a numpy vector + of floats. + + Example: + atol = [1.0e-4, 1.0e-6] + """ + return self.options["atol"] + + atol=property(_get_atol,_set_atol) + + def _set_rtol(self,rtol): + try: + self.options["rtol"] = float(rtol) + except (ValueError, TypeError): + raise Rodas_Exception('Relative tolerance must be a (scalar) float.') + if self.options["rtol"] <= 0.0: + raise Rodas_Exception('Relative tolerance must be a positive (scalar) float.') + + def _get_rtol(self): + """ + Defines the relative tolerance that is to be used by the solver. + + Parameters:: + + rtol + - Default '1.0e-6'. + + - Should be a positive float. + + Example: + rtol = 1.0e-4 + """ + return self.options["rtol"] + + rtol=property(_get_rtol,_set_rtol) + + def _get_maxsteps(self): + """ + The maximum number of steps allowed to be taken to reach the + final time. + + Parameters:: + + maxsteps + - Default 10000 + + - Should be a positive integer + """ + return self.options["maxsteps"] + + def _set_maxsteps(self, max_steps): + try: + max_steps = int(max_steps) + except (TypeError, ValueError): + raise Rodas_Exception("Maximum number of steps must be a positive integer.") + self.options["maxsteps"] = max_steps + + maxsteps = property(_get_maxsteps, _set_maxsteps) + + def _set_fac1(self, fac1): + try: + self.options["fac1"] = float(fac1) + except (ValueError, TypeError): + raise Rodas_Exception('The fac1 must be an integer or float.') + + def _get_fac1(self): + """ + Parameters for step-size selection. The new step-size is chosen + subject to the restriction fac1 <= current step-size / old step-size <= fac2. + + Parameters:: + + fac1 + - Default 0.2 + + - Should be a float. + + Example: + fac1 = 0.1 + """ + return self.options["fac1"] + + fac1 = property(_get_fac1, _set_fac1) + + def _set_fac2(self, fac2): + try: + self.options["fac2"] = float(fac2) + except (ValueError, TypeError): + raise Rodas_Exception('The fac2 must be an integer or float.') + + def _get_fac2(self): + """ + Parameters for step-size selection. The new step-size is chosen + subject to the restriction fac1 <= current step-size / old step-size <= fac2. + + Parameters:: + + fac2 + - Default 8.0 + + - Should be a float. + + Example: + fac2 = 10.0 + """ + return self.options["fac2"] + + fac2 = property(_get_fac2, _set_fac2) + + def _set_safe(self, safe): + try: + self.options["safe"] = float(safe) + except (ValueError, TypeError): + raise Rodas_Exception('The safe must be an integer or float.') + + def _get_safe(self): + """ + The safety factor in the step-size prediction. + + Parameters:: + + safe + - Default '0.9' + + - Should be float. + + Example: + safe = 0.8 + """ + return self.options["safe"] + + safe = property(_get_safe, _set_safe) + + def _set_initial_step(self, initstep): + try: + self.options["inith"] = float(initstep) + except (ValueError, TypeError): + raise Rodas_Exception('The initial step must be an integer or float.') + + def _get_initial_step(self): + """ + This determines the initial step-size to be used in the integration. + + Parameters:: + + inith + - Default '0.01'. + + - Should be float. + + Example: + inith = 0.01 + """ + return self.options["inith"] + + inith = property(_get_initial_step,_set_initial_step) + + def _set_max_h(self,max_h): + try: + self.options["maxh"] = float(max_h) + except (ValueError,TypeError): + raise Rodas_Exception('Maximal stepsize must be a (scalar) float.') + if self.options["maxh"] < 0: + raise Rodas_Exception('Maximal stepsize must be a positiv (scalar) float.') + + def _get_max_h(self): + """ + Defines the maximal step-size that is to be used by the solver. + + Parameters:: + + maxh + - Default maxh = final time - current time. + + - Should be a float. + + Example: + maxh = 0.01 + + """ + return self.options["maxh"] + + maxh=property(_get_max_h,_set_max_h) + + def _set_usejac(self, jac): + self.options["usejac"] = bool(jac) + + def _get_usejac(self): + """ + This sets the option to use the user defined Jacobian. If a + user provided jacobian is implemented into the problem the + default setting is to use that Jacobian. If not, an + approximation is used. + + Parameters:: + + usejac + - True - use user defined Jacobian + False - use an approximation + + - Should be a Boolean. + + Example: + usejac = False + """ + return self.options["usejac"] + + usejac = property(_get_usejac,_set_usejac) + + +class RodasODE(Rodas_Common, Explicit_ODE): + """ + Rosenbrock method of order (3)4 with step-size control and + continuous output. + + Based on the FORTRAN code RODAS by E.Hairer and G.Wanner, which can + be found here: http://www.unige.ch/~hairer/software.html + + Details about the implementation (FORTRAN) can be found in the book,:: + + Solving Ordinary Differential Equations II, + Stiff and Differential-Algebraic Problems + + Authors: E. Hairer and G. Wanner + Springer-Verlag, ISBN: 3-540-60452-9 + + """ + + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Explicit_Problem' class. + """ + Explicit_ODE.__init__(self, problem) #Calls the base class + + #Default values + self.options["inith"] = 0.01 + self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) + self.options["fac2"] = 6.0 #Parameters for step-size selection (upper bound) + self.options["maxh"] = N.inf #Maximum step-size. + self.options["safe"] = 0.9 #Safety factor + self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + self.options["usejac"] = True if self.problem_info["jac_fcn"] else False + self.options["maxsteps"] = 10000 + + #Solver support + self.supports["report_continuously"] = True + self.supports["interpolated_output"] = True + self.supports["state_events"] = True + + #Internal + self._leny = len(self.y) #Dimension of the problem + + def initialize(self): + #Reset statistics + self.statistics.reset() + + def set_problem_data(self): + if self.problem_info["state_events"]: + def event_func(t, y): + return self.problem.state_events(t, y, self.sw) + def f(t, y): + return self.problem.rhs(t, y, self.sw) + self.f = f + self.event_func = event_func + self._event_info = [0] * self.problem_info["dimRoot"] + self.g_old = self.event_func(self.t, self.y) + else: + self.f = self.problem.rhs + + def interpolate(self, time): + y = N.empty(self._leny) + for i in range(self._leny): + y[i] = rodas.contro(i+1, time, self.cont) + + return y + + def _solout(self, nrsol, told, t, y, cont, lrc, irtrn): + """ + This method is called after every successful step taken by Rodas + """ + self.cont = cont #Saved to be used by the interpolation function. + + if self.problem_info["state_events"]: + flag, t, y = self.event_locator(told, t, y) + #Convert to Fortram indicator. + if flag == ID_PY_EVENT: irtrn = -1 + + if self._opts["report_continuously"]: + initialize_flag = self.report_solution(t, y, self._opts) + if initialize_flag: irtrn = -1 + else: + if self._opts["output_list"] is None: + self._tlist.append(t) + self._ylist.append(y.copy()) + else: + output_list = self._opts["output_list"] + output_index = self._opts["output_index"] + try: + while output_list[output_index] <= t: + self._tlist.append(output_list[output_index]) + self._ylist.append(self.interpolate(output_list[output_index])) + + output_index += 1 + except IndexError: + pass + self._opts["output_index"] = output_index + + if self.problem_info["state_events"] and flag == ID_PY_EVENT and len(self._tlist) > 0 and self._tlist[-1] != t: + self._tlist.append(t) + self._ylist.append(y) + + return irtrn + + def _jacobian(self, t, y): + """ + Calculates the Jacobian, either by an approximation or by the user + defined (jac specified in the problem class). + """ + jac = self.problem.jac(t,y) + + if isinstance(jac, sp.csc_matrix): + jac = jac.toarray() + + return jac + + def integrate(self, t, y, tf, opts): + IFCN = 1 #The function may depend on t + ITOL = 1 #Both rtol and atol are vectors + IJAC = 1 if self.usejac else 0 #Switch for the jacobian, 0==NO JACOBIAN + MLJAC = self.problem_info["dim"] #The jacobian is full + MUJAC = self.problem_info["dim"] #The jacobian is full + IDFX = 0 #df/dt is computed internally + IMAS = 0 #The mass matrix is the identity + MLMAS = self.problem_info["dim"] #The mass matrix is full + MUMAS = self.problem_info["dim"] #The mass matrix is full + IOUT = 1 #Solout is called after every accepted step + WORK = N.array([0.0]*(2*self.problem_info["dim"]**2+14*self.problem_info["dim"]+20)) + IWORK = N.array([0]*(self.problem_info["dim"]+20)) + + #Setting work options + WORK[1] = self.maxh + WORK[2] = self.fac1 + WORK[3] = self.fac2 + WORK[4] = self.safe + + #Setting iwork options + IWORK[0] = self.maxsteps + + #Dummy methods + mas_dummy = lambda t:x + jac_dummy = (lambda t:x) if not self.usejac else self._jacobian + dfx_dummy = lambda t:x + + #Check for initialization + if opts["initialize"]: + self.set_problem_data() + self._tlist = [] + self._ylist = [] + + #Store the opts + self._opts = opts + + t, y, h, iwork, flag = rodas.rodas(self.f, IFCN, t, y.copy(), tf, self.inith, self.rtol*N.ones(self.problem_info["dim"]), self.atol, + ITOL, jac_dummy, IJAC, MLJAC, MUJAC, dfx_dummy, IDFX, mas_dummy, IMAS, MLMAS, MUMAS, self._solout, IOUT, WORK, IWORK) + + #Checking return + if flag == 1: + flag = ID_PY_COMPLETE + elif flag == 2: + flag = ID_PY_EVENT + else: + raise Exception("Rodas failed with flag %d"%flag) + + #Retrieving statistics + self.statistics["nsteps"] += iwork[16] + self.statistics["nfcns"] += iwork[13] + self.statistics["njacs"] += iwork[14] + #self.statistics["nstepstotal"] += iwork[15] + self.statistics["nfcnjacs"] += (iwork[14]*self.problem_info["dim"] if not self.usejac else 0) + self.statistics["nerrfails"] += iwork[17] + self.statistics["nlus"] += iwork[18] + + return flag, self._tlist, self._ylist + + def state_event_info(self): + return self._event_info + + def set_event_info(self, event_info): + self._event_info = event_info + + def print_statistics(self, verbose=NORMAL): + """ + Prints the run-time statistics for the problem. + """ + Explicit_ODE.print_statistics(self, verbose) #Calls the base class + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : Rodas ', verbose) + self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) + self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) + self.log_message('', verbose) + + diff --git a/src/solvers/runge_kutta.py b/src/solvers/runge_kutta.py index 2dbea521..8e8f666e 100644 --- a/src/solvers/runge_kutta.py +++ b/src/solvers/runge_kutta.py @@ -1,854 +1,854 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N - -from assimulo.ode import * -from assimulo.explicit_ode import Explicit_ODE - -from assimulo.exception import * - -from assimulo.lib import dopri5 - -class Dopri5(Explicit_ODE): - """ - Explicit Runge-Kutta method of order (4)5 with step-size control - and continuous output. Based on the method by Dormand and Prince. - - Based on the FORTRAN code DOPRI5 by E.Hairer and G.Wanner, which can - be found here: http://www.unige.ch/~hairer/software.html - - Details about the implementation (FORTRAN) can be found in the book,:: - - Solving Ordinary Differential Equations I, - Nonstiff Problems - - Authors: E. Hairer, S. P. Norsett and G. Wanner - Springer-Verlag, ISBN: 3-540-56670-8 - - """ - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Explicit_Problem' class. - """ - Explicit_ODE.__init__(self, problem) #Calls the base class - - #Default values - self.options["safe"] = 0.9 #Safety factor - self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) - self.options["fac2"] = 10.0 #Parameters for step-size selection (upper bound) - self.options["beta"] = 0.04 - self.options["maxh"] = N.inf #Maximum step-size. - self.options["inith"] = 0.0 - self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - self.options["maxsteps"] = 100000 - - #Solver support - self.supports["report_continuously"] = True - self.supports["interpolated_output"] = True - self.supports["state_events"] = True - - #Internal - self._leny = len(self.y) #Dimension of the problem - - def initialize(self): - #Reset statistics - self.statistics.reset() - - def set_problem_data(self): - if self.problem_info["state_events"]: - def event_func(t, y): - return self.problem.state_events(t, y, self.sw) - def f(t, y): - return self.problem.rhs(t, y, self.sw) - self.f = f - self.event_func = event_func - self._event_info = [0] * self.problem_info["dimRoot"] - self.g_old = self.event_func(self.t, self.y) - else: - self.f = self.problem.rhs - - def interpolate(self, time): - y = N.empty(self._leny) - for i in range(self._leny): - y[i] = dopri5.contd5(i+1, time, self.cont, self.lrc) - - return y - - def _solout(self, nrsol, told, t, y, cont, lrc, irtrn): - """ - This method is called after every successful step taken by Radau5 - """ - #Saved to be used by the interpolation function. - self.cont = cont - self.lrc = lrc - - if self.problem_info["state_events"]: - flag, t, y = self.event_locator(told, t, y) - #Convert to Fortram indicator. - if flag == ID_PY_EVENT: irtrn = -1 - - if self._opts["report_continuously"]: - initialize_flag = self.report_solution(t, y, self._opts) - if initialize_flag: irtrn = -1 - else: - if self._opts["output_list"] is None: - self._tlist.append(t) - self._ylist.append(y.copy()) - else: - output_list = self._opts["output_list"] - output_index = self._opts["output_index"] - try: - while output_list[output_index] <= t: - self._tlist.append(output_list[output_index]) - self._ylist.append(self.interpolate(output_list[output_index])) - - output_index += 1 - except IndexError: - pass - self._opts["output_index"] = output_index - - if self.problem_info["state_events"] and flag == ID_PY_EVENT and len(self._tlist) > 0 and self._tlist[-1] != t: - self._tlist.append(t) - self._ylist.append(y) - - return irtrn - - def integrate(self, t, y, tf, opts): - ITOL = 1 #Both atol and rtol are vectors - IOUT = 2 #Dense out in solout - WORK = N.array([0.0]*(8*self.problem_info["dim"]+5*self.problem_info["dim"]+21)) - IWORK = N.array([0]*(self.problem_info["dim"]+21)) - - #Setting work options - WORK[1] = self.safe - WORK[2] = self.fac1 - WORK[3] = self.fac2 - WORK[4] = self.beta - WORK[5] = self.maxh - WORK[6] = self.inith - - #Setting iwork options - IWORK[0] = self.maxsteps - IWORK[4] = self.problem_info["dim"] - - #Check for initialization - if opts["initialize"]: - self.set_problem_data() - self._tlist = [] - self._ylist = [] - - #Store the opts - self._opts = opts - - t, y, iwork, flag = dopri5.dopri5(self.f, t, y.copy(), tf, self.rtol*N.ones(self.problem_info["dim"]), self.atol, ITOL, self._solout, IOUT, WORK, IWORK) - - #Checking return - if flag == 1: - flag = ID_PY_COMPLETE - elif flag == 2: - flag = ID_PY_EVENT - else: - raise Exception("Dopri5 failed with flag %d"%flag) - - #Retrieving statistics - self.statistics["nsteps"] += iwork[18] - self.statistics["nfcns"] += iwork[16] - #self.statistics["nstepstotal"] += iwork[17] - self.statistics["nerrfails"] += iwork[19] - - return flag, self._tlist, self._ylist - - def state_event_info(self): - return self._event_info - - def set_event_info(self, event_info): - self._event_info = event_info - - def print_statistics(self, verbose=NORMAL): - """ - Prints the run-time statistics for the problem. - """ - Explicit_ODE.print_statistics(self, verbose) #Calls the base class - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : Dopri5 ', verbose) - self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) - self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) - self.log_message('', verbose) - - def _set_atol(self,atol): - - self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) - - if len(self.options["atol"]) == 1: - self.options["atol"] = self.options["atol"]*N.ones(self._leny) - elif len(self.options["atol"]) != self._leny: - raise Dopri5_Exception("atol must be of length one or same as the dimension of the problem.") - - def _get_atol(self): - """ - Defines the absolute tolerance(s) that is to be used by the solver. - Can be set differently for each variable. - - Parameters:: - - atol - - Default '1.0e-6'. - - - Should be a positive float or a numpy vector - of floats. - - Example: - atol = [1.0e-4, 1.0e-6] - """ - return self.options["atol"] - - atol=property(_get_atol,_set_atol) - - def _set_rtol(self,rtol): - try: - self.options["rtol"] = float(rtol) - except (ValueError, TypeError): - raise Dopri5_Exception('Relative tolerance must be a (scalar) float.') - if self.options["rtol"] <= 0.0: - raise Dopri5_Exception('Relative tolerance must be a positive (scalar) float.') - - def _get_rtol(self): - """ - Defines the relative tolerance that is to be used by the solver. - - Parameters:: - - rtol - - Default '1.0e-6'. - - - Should be a positive float. - - Example: - rtol = 1.0e-4 - """ - return self.options["rtol"] - - rtol=property(_get_rtol,_set_rtol) - - def _get_maxsteps(self): - """ - The maximum number of steps allowed to be taken to reach the - final time. - - Parameters:: - - maxsteps - - Default 10000 - - - Should be a positive integer - """ - return self.options["maxsteps"] - - def _set_maxsteps(self, max_steps): - try: - max_steps = int(max_steps) - except (TypeError, ValueError): - raise Dopri5_Exception("Maximum number of steps must be a positive integer.") - self.options["maxsteps"] = max_steps - - maxsteps = property(_get_maxsteps, _set_maxsteps) - - def _set_fac1(self, fac1): - try: - self.options["fac1"] = float(fac1) - except (ValueError, TypeError): - raise Dopri5_Exception('The fac1 must be an integer or float.') - - def _get_fac1(self): - """ - Parameters for step-size selection. The new step-size is chosen - subject to the restriction fac1 <= current step-size / old step-size <= fac2. - - Parameters:: - - fac1 - - Default 0.2 - - - Should be a float. - - Example: - fac1 = 0.1 - """ - return self.options["fac1"] - - fac1 = property(_get_fac1, _set_fac1) - - def _set_fac2(self, fac2): - try: - self.options["fac2"] = float(fac2) - except (ValueError, TypeError): - raise Dopri5_Exception('The fac2 must be an integer or float.') - - def _get_fac2(self): - """ - Parameters for step-size selection. The new step-size is chosen - subject to the restriction fac1 <= current step-size / old step-size <= fac2. - - Parameters:: - - fac2 - - Default 8.0 - - - Should be a float. - - Example: - fac2 = 10.0 - """ - return self.options["fac2"] - - fac2 = property(_get_fac2, _set_fac2) - - def _set_safe(self, safe): - try: - self.options["safe"] = float(safe) - except (ValueError, TypeError): - raise Dopri5_Exception('The safe must be an integer or float.') - - def _get_safe(self): - """ - The safety factor in the step-size prediction. - - Parameters:: - - safe - - Default '0.9' - - - Should be float. - - Example: - safe = 0.8 - """ - return self.options["safe"] - - safe = property(_get_safe, _set_safe) - - def _set_initial_step(self, initstep): - try: - self.options["inith"] = float(initstep) - except (ValueError, TypeError): - raise Dopri5_Exception('The initial step must be an integer or float.') - - def _get_initial_step(self): - """ - This determines the initial step-size to be used in the integration. - - Parameters:: - - inith - - Default '0.01'. - - - Should be float. - - Example: - inith = 0.01 - """ - return self.options["inith"] - - inith = property(_get_initial_step,_set_initial_step) - - def _set_max_h(self,max_h): - try: - self.options["maxh"] = float(max_h) - except (ValueError,TypeError): - raise Dopri5_Exception('Maximal stepsize must be a (scalar) float.') - if self.options["maxh"] < 0: - raise Dopri5_Exception('Maximal stepsize must be a positiv (scalar) float.') - - def _get_max_h(self): - """ - Defines the maximal step-size that is to be used by the solver. - - Parameters:: - - maxh - - Default final time - current time. - - - Should be a float. - - Example: - maxh = 0.01 - - """ - return self.options["maxh"] - - maxh=property(_get_max_h,_set_max_h) - - def _set_beta(self, beta): - self.options["beta"] = beta - - def _get_beta(self): - """ - Option for stabilized step-size control. - - Parameters:: - - beta - - Default 0.04 - - - Should be a float. - """ - return self.options["beta"] - - beta = property(_get_beta, _set_beta) - -class RungeKutta34(Explicit_ODE): - """ - Adaptive Runge-Kutta of order four. - - Obs. Step rejection not implemented. - """ - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Explicit_Problem' class. - """ - Explicit_ODE.__init__(self, problem) #Calls the base class - - #Solver options - self.options["atol"] = 1.0e-6 - self.options["rtol"] = 1.0e-6 - self.options["inith"] = 0.01 - self.options["maxsteps"] = 10000 - - #Internal temporary result vector - self.Y1 = N.array([0.0]*len(self.y0)) - self.Y2 = N.array([0.0]*len(self.y0)) - self.Y3 = N.array([0.0]*len(self.y0)) - self.Y4 = N.array([0.0]*len(self.y0)) - self.Z3 = N.array([0.0]*len(self.y0)) - - #Solver support - self.supports["report_continuously"] = True - self.supports["interpolated_output"] = True - self.supports["state_events"] = True - - def initialize(self): - #Reset statistics - self.statistics.reset() - - def set_problem_data(self): - if self.problem_info["state_events"]: - def event_func(t, y): - return self.problem.state_events(t, y, self.sw) - def f(dy ,t, y): - try: - dy[:] = self.problem.rhs(t, y, self.sw) - except: - return False - return True - self.f = f - self.event_func = event_func - self._event_info = [0] * self.problem_info["dimRoot"] - self.g_old = self.event_func(self.t, self.y) - else: - self.f = self.problem.rhs_internal - - def _set_initial_step(self, initstep): - try: - initstep = float(initstep) - except (ValueError, TypeError): - raise Explicit_ODE_Exception('The initial step must be an integer or float.') - - self.options["inith"] = initstep - - def _get_initial_step(self): - """ - This determines the initial step-size to be used in the integration. - - Parameters:: - - inith - - Default '0.01'. - - - Should be float. - - Example: - inith = 0.01 - """ - return self.options["inith"] - - inith = property(_get_initial_step,_set_initial_step) - - def _set_atol(self,atol): - - try: - atol_arr = N.array(atol, dtype=float) - if (atol_arr <= 0.0).any(): - raise Explicit_ODE_Exception('Absolute tolerance must be a positive float or a float vector.') - except (ValueError,TypeError): - raise Explicit_ODE_Exception('Absolute tolerance must be a positive float or a float vector.') - if atol_arr.size == 1: - self.options["atol"] = float(atol) - elif atol_arr.size == len(self.y): - self.options["atol"] = [float(x) for x in atol] - else: - raise Explicit_ODE_Exception('Absolute tolerance must be a float vector of same dimension as the problem or a scalar.') - - def _get_atol(self): - """ - Sets the absolute tolerance to be used in the integration. - - Parameters:: - - atol - - Default 1.0e-6. - - - Should be float or an array/list of len(y) - - Example: - atol=1.e5 - atol=[1.e-5,1.e-4] - """ - return self.options["atol"] - - atol = property(_get_atol,_set_atol) - - def _set_rtol(self, rtol): - try: - rtol = float(rtol) - except (TypeError,ValueError): - raise Explicit_ODE_Exception('Relative tolerance must be a float.') - if rtol <= 0.0: - raise Explicit_ODE_Exception('Relative tolerance must be a positive (scalar) float.') - self.options["rtol"] = rtol - - def _get_rtol(self): - """ - The relative tolerance to be used in the integration. - - Parameters:: - - rtol - - Default 1.0e-6 - - - Should be a float. - """ - return self.options["rtol"] - - rtol = property(_get_rtol, _set_rtol) - - def _get_maxsteps(self): - """ - The maximum number of steps allowed to be taken to reach the - final time. - - Parameters:: - - maxsteps - - Default 10000 - - - Should be a positive integer - """ - return self.options["maxsteps"] - - def _set_maxsteps(self, max_steps): - try: - max_steps = int(max_steps) - except (TypeError, ValueError): - raise Explicit_ODE_Exception("Maximum number of steps must be a positive integer.") - self.options["maxsteps"] = max_steps - - maxsteps = property(_get_maxsteps, _set_maxsteps) - - def step(self, t, y, tf, opts): - initialize = opts["initialize"] - - if initialize: - self.solver_iterator = self._iter(t,y,tf,opts) - - return self.solver_iterator.next() - - def integrate(self, t, y, tf, opts): - """ - Integrates (t,y) values until t > tf - """ - [flags, tlist, ylist] = zip(*list(self._iter(t, y, tf,opts))) - - return flags[-1], tlist, ylist - - def _iter(self,t,y,tf,opts): - if opts["initialize"]: - self.set_problem_data() - maxsteps = self.options["maxsteps"] - h = self.options["inith"] - h = min(h, N.abs(tf-t)) - self.f(self.Y1, t, y) - flag = ID_PY_OK - - for i in range(maxsteps): - if t+h < tf and flag == ID_PY_OK: - t, y, error = self._step(t, y, h) - self.statistics["nsteps"] += 1 - if self.problem_info["state_events"]: - flag, t, y = self.event_locator(t-h , t, y) - - if opts["report_continuously"]: - initialize_flag = self.report_solution(t, y, opts) - if initialize_flag: flag = ID_PY_EVENT - yield flag, t,y - elif opts["output_list"] is None: - yield flag, t, y - else: - output_list = opts["output_list"] - output_index = opts["output_index"] - try: - while output_list[output_index] <= t: - yield flag, output_list[output_index], self.interpolate(output_list[output_index]) - output_index = output_index + 1 - except IndexError: - pass - opts["output_index"] = output_index - h=self.adjust_stepsize(h,error) - h=min(h, N.abs(tf-t)) - else: - break - else: - raise Explicit_ODE_Exception('Final time not reached within maximum number of steps') - - #If no event has been detected, do the last step. - if flag == ID_PY_OK: - t, y, error = self._step(t, y, h) - self.statistics["nsteps"] += 1 - if self.problem_info["state_events"]: - flag, t, y = self.event_locator(t-h , t, y) - if flag == ID_PY_OK: flag = ID_PY_COMPLETE - if opts["report_continuously"]: - initialize_flag = self.report_solution(t, y, opts) - if initialize_flag: flag = ID_PY_EVENT - else: flag = ID_PY_COMPLETE - yield flag, t,y - elif opts["output_list"] is None: - yield flag, t,y - else: - output_list = opts["output_list"] - output_index = opts["output_index"] - try: - while output_list[output_index] <= t: - yield flag, output_list[output_index], self.interpolate(output_list[output_index]) - output_index = output_index + 1 - except IndexError: - pass - opts["output_index"] = output_index - - def adjust_stepsize(self, h, error): - """ - Adjusts the stepsize. - """ - if error == 0.0: - fac = 2.0 - else: - fac=min((1.0/error)**(1.0/4.0),2.) - h *= fac - - return h - - def _step(self, t, y, h): - """ - This calculates the next step in the integration. - """ - self.statistics["nfcns"] += 5 - - scaling = N.array(abs(y)*self.rtol + self.atol) # to normalize the error - f = self.f - - f(self.Y2, t + h/2., y + h*self.Y1/2.) - f(self.Y3, t + h/2., y + h*self.Y2/2.) - f(self.Z3, t + h, y - h*self.Y1 + 2.0*h*self.Y2) - f(self.Y4, t + h, y + h*self.Y3) - - error = N.linalg.norm(h/6.0*(2.0*self.Y2 + self.Z3 - 2.0*self.Y3 - self.Y4)/scaling) #normalized - t_next = t + h - y_next = y + h/6.0*(self.Y1 + 2.0*self.Y2 + 2.0*self.Y3 + self.Y4) - - f_low = self.Y1.copy() - f(self.Y1, t_next, y_next) - #Hermitian interpolation for the solution in [t, t_next] - def interpolate(time): - thetha = (time - t) / (t_next - t) - f_high = self.Y1 - return (1 - thetha) * y + thetha * y_next + thetha * \ - (thetha - 1) * ((1 - 2*thetha) * (y_next - y) + \ - (thetha - 1) * h * f_low + thetha * h * f_high) - self.interpolate = interpolate - - return t_next, y_next, error - - def state_event_info(self): - return self._event_info - - def set_event_info(self, event_info): - self._event_info = event_info - - def print_statistics(self, verbose): - """ - Should print the statistics. - """ - Explicit_ODE.print_statistics(self, verbose) #Calls the base class - - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : RungeKutta34', verbose) - self.log_message(' Solver type : Adaptive', verbose) - self.log_message(' Relative tolerance : ' + str(self.options["rtol"]), verbose) - self.log_message(' Absolute tolerance : ' + str(self._compact_atol()) + '\n', verbose) - - -class RungeKutta4(Explicit_ODE): - """ - This solver solves an explicit ordinary differential equation using - a Runge-Kutta method of order 4. - - We want to approximate the solution to the ordinary differential - equation of the form, - - .. math:: - - \dot{y} = f(t,y), \quad y(t_0) = y_0 . - - Using a Runge-Kutta method of order 4, the approximation is defined as - follow, - - .. math:: - - y_{n+1} = y_n + \\frac{1}{6}(k_1+2k_2+2k_3+k_4) - - where, - - .. math:: - - k_1 = hf(t_n,y_n) - - k_2 = hf(t_n+\\frac{1}{2}h,y_n+\\frac{1}{2}k_1) - - k_3 = hf(t_n+\\frac{1}{2}h,y_n+\\frac{1}{2}k_2) - - k_4 = hf(t_n+h,y_n+k_3) - - with :math:`h` being the step-size and :math:`y_n` the previous - solution to the equation. - """ - def __init__(self, problem): - Explicit_ODE.__init__(self, problem) #Calls the base class - - #Solver options - self.options["h"] = 0.01 - - #Internal temporary result vector - self.Y1 = N.array([0.0]*len(self.y0)) - self.Y2 = N.array([0.0]*len(self.y0)) - self.Y3 = N.array([0.0]*len(self.y0)) - self.Y4 = N.array([0.0]*len(self.y0)) - - #RHS-Function - self.f = problem.rhs_internal - - #Solver support - self.supports["one_step_mode"] = True - - def step(self, t, y, tf, opts): - initialize = opts["initialize"] - - if initialize: - self.solver_iterator = self._iter(t,y,tf) - - return self.solver_iterator.next() - - def integrate(self, t, y, tf, opts): - """ - Integrates (t,y) values until t > tf - """ - [flags, tlist, ylist] = zip(*list(self._iter(t, y, tf))) - - return flags[-1], tlist, ylist - - def _set_h(self,h): - try: - self.options["h"] = float(h) - except: - raise AssimuloException("Step-size must be a (scalar) float.") - - def _get_h(self): - """ - Defines the step-size that is to be used by the solver. - - Parameters:: - - maxh - - Default '0.01'. - - - Should be a float. - - Example: - maxh = 0.01 - - """ - return self.options["h"] - - h=property(_get_h,_set_h) - - def _iter(self,t,y,tf): - h = self.options["h"] - h = min(h, N.abs(tf-t)) - - while t+h < tf: - t, y = self._step(t, y, h) - yield ID_PY_OK, t,y - h=min(h, N.abs(tf-t)) - else: - t, y = self._step(t, y, h) - yield ID_PY_COMPLETE, t, y - - def _step(self, t, y, h): - """ - This calculates the next step in the integration. - """ - f = self.f - - f(self.Y1, t, y) - f(self.Y2, t + h/2., y + h*self.Y1/2.) - f(self.Y3, t + h/2., y + h*self.Y2/2.) - f(self.Y4, t + h, y + h*self.Y3) - - return t+h, y + h/6.*(self.Y1 + 2.*self.Y2 + 2.*self.Y3 + self.Y4) - - def print_statistics(self, verbose): - """ - Should print the statistics. - """ - self.log_message('Final Run Statistics: %s \n' % self.problem.name, verbose) - self.log_message(' Step-length : %s '%(self.options["h"]), verbose) - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : RungeKutta4', verbose) - self.log_message(' Solver type : Fixed step\n', verbose) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N + +from assimulo.ode import * +from assimulo.explicit_ode import Explicit_ODE + +from assimulo.exception import * + +from assimulo.lib import dopri5 + +class Dopri5(Explicit_ODE): + """ + Explicit Runge-Kutta method of order (4)5 with step-size control + and continuous output. Based on the method by Dormand and Prince. + + Based on the FORTRAN code DOPRI5 by E.Hairer and G.Wanner, which can + be found here: http://www.unige.ch/~hairer/software.html + + Details about the implementation (FORTRAN) can be found in the book,:: + + Solving Ordinary Differential Equations I, + Nonstiff Problems + + Authors: E. Hairer, S. P. Norsett and G. Wanner + Springer-Verlag, ISBN: 3-540-56670-8 + + """ + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Explicit_Problem' class. + """ + Explicit_ODE.__init__(self, problem) #Calls the base class + + #Default values + self.options["safe"] = 0.9 #Safety factor + self.options["fac1"] = 0.2 #Parameters for step-size selection (lower bound) + self.options["fac2"] = 10.0 #Parameters for step-size selection (upper bound) + self.options["beta"] = 0.04 + self.options["maxh"] = N.inf #Maximum step-size. + self.options["inith"] = 0.0 + self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + self.options["maxsteps"] = 100000 + + #Solver support + self.supports["report_continuously"] = True + self.supports["interpolated_output"] = True + self.supports["state_events"] = True + + #Internal + self._leny = len(self.y) #Dimension of the problem + + def initialize(self): + #Reset statistics + self.statistics.reset() + + def set_problem_data(self): + if self.problem_info["state_events"]: + def event_func(t, y): + return self.problem.state_events(t, y, self.sw) + def f(t, y): + return self.problem.rhs(t, y, self.sw) + self.f = f + self.event_func = event_func + self._event_info = [0] * self.problem_info["dimRoot"] + self.g_old = self.event_func(self.t, self.y) + else: + self.f = self.problem.rhs + + def interpolate(self, time): + y = N.empty(self._leny) + for i in range(self._leny): + y[i] = dopri5.contd5(i+1, time, self.cont, self.lrc) + + return y + + def _solout(self, nrsol, told, t, y, cont, lrc, irtrn): + """ + This method is called after every successful step taken by Radau5 + """ + #Saved to be used by the interpolation function. + self.cont = cont + self.lrc = lrc + + if self.problem_info["state_events"]: + flag, t, y = self.event_locator(told, t, y) + #Convert to Fortram indicator. + if flag == ID_PY_EVENT: irtrn = -1 + + if self._opts["report_continuously"]: + initialize_flag = self.report_solution(t, y, self._opts) + if initialize_flag: irtrn = -1 + else: + if self._opts["output_list"] is None: + self._tlist.append(t) + self._ylist.append(y.copy()) + else: + output_list = self._opts["output_list"] + output_index = self._opts["output_index"] + try: + while output_list[output_index] <= t: + self._tlist.append(output_list[output_index]) + self._ylist.append(self.interpolate(output_list[output_index])) + + output_index += 1 + except IndexError: + pass + self._opts["output_index"] = output_index + + if self.problem_info["state_events"] and flag == ID_PY_EVENT and len(self._tlist) > 0 and self._tlist[-1] != t: + self._tlist.append(t) + self._ylist.append(y) + + return irtrn + + def integrate(self, t, y, tf, opts): + ITOL = 1 #Both atol and rtol are vectors + IOUT = 2 #Dense out in solout + WORK = N.array([0.0]*(8*self.problem_info["dim"]+5*self.problem_info["dim"]+21)) + IWORK = N.array([0]*(self.problem_info["dim"]+21)) + + #Setting work options + WORK[1] = self.safe + WORK[2] = self.fac1 + WORK[3] = self.fac2 + WORK[4] = self.beta + WORK[5] = self.maxh + WORK[6] = self.inith + + #Setting iwork options + IWORK[0] = self.maxsteps + IWORK[4] = self.problem_info["dim"] + + #Check for initialization + if opts["initialize"]: + self.set_problem_data() + self._tlist = [] + self._ylist = [] + + #Store the opts + self._opts = opts + + t, y, iwork, flag = dopri5.dopri5(self.f, t, y.copy(), tf, self.rtol*N.ones(self.problem_info["dim"]), self.atol, ITOL, self._solout, IOUT, WORK, IWORK) + + #Checking return + if flag == 1: + flag = ID_PY_COMPLETE + elif flag == 2: + flag = ID_PY_EVENT + else: + raise Exception("Dopri5 failed with flag %d"%flag) + + #Retrieving statistics + self.statistics["nsteps"] += iwork[18] + self.statistics["nfcns"] += iwork[16] + #self.statistics["nstepstotal"] += iwork[17] + self.statistics["nerrfails"] += iwork[19] + + return flag, self._tlist, self._ylist + + def state_event_info(self): + return self._event_info + + def set_event_info(self, event_info): + self._event_info = event_info + + def print_statistics(self, verbose=NORMAL): + """ + Prints the run-time statistics for the problem. + """ + Explicit_ODE.print_statistics(self, verbose) #Calls the base class + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : Dopri5 ', verbose) + self.log_message(' Tolerances (absolute) : ' + str(self._compact_atol()), verbose) + self.log_message(' Tolerances (relative) : ' + str(self.options["rtol"]), verbose) + self.log_message('', verbose) + + def _set_atol(self,atol): + + self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) + + if len(self.options["atol"]) == 1: + self.options["atol"] = self.options["atol"]*N.ones(self._leny) + elif len(self.options["atol"]) != self._leny: + raise Dopri5_Exception("atol must be of length one or same as the dimension of the problem.") + + def _get_atol(self): + """ + Defines the absolute tolerance(s) that is to be used by the solver. + Can be set differently for each variable. + + Parameters:: + + atol + - Default '1.0e-6'. + + - Should be a positive float or a numpy vector + of floats. + + Example: + atol = [1.0e-4, 1.0e-6] + """ + return self.options["atol"] + + atol=property(_get_atol,_set_atol) + + def _set_rtol(self,rtol): + try: + self.options["rtol"] = float(rtol) + except (ValueError, TypeError): + raise Dopri5_Exception('Relative tolerance must be a (scalar) float.') + if self.options["rtol"] <= 0.0: + raise Dopri5_Exception('Relative tolerance must be a positive (scalar) float.') + + def _get_rtol(self): + """ + Defines the relative tolerance that is to be used by the solver. + + Parameters:: + + rtol + - Default '1.0e-6'. + + - Should be a positive float. + + Example: + rtol = 1.0e-4 + """ + return self.options["rtol"] + + rtol=property(_get_rtol,_set_rtol) + + def _get_maxsteps(self): + """ + The maximum number of steps allowed to be taken to reach the + final time. + + Parameters:: + + maxsteps + - Default 10000 + + - Should be a positive integer + """ + return self.options["maxsteps"] + + def _set_maxsteps(self, max_steps): + try: + max_steps = int(max_steps) + except (TypeError, ValueError): + raise Dopri5_Exception("Maximum number of steps must be a positive integer.") + self.options["maxsteps"] = max_steps + + maxsteps = property(_get_maxsteps, _set_maxsteps) + + def _set_fac1(self, fac1): + try: + self.options["fac1"] = float(fac1) + except (ValueError, TypeError): + raise Dopri5_Exception('The fac1 must be an integer or float.') + + def _get_fac1(self): + """ + Parameters for step-size selection. The new step-size is chosen + subject to the restriction fac1 <= current step-size / old step-size <= fac2. + + Parameters:: + + fac1 + - Default 0.2 + + - Should be a float. + + Example: + fac1 = 0.1 + """ + return self.options["fac1"] + + fac1 = property(_get_fac1, _set_fac1) + + def _set_fac2(self, fac2): + try: + self.options["fac2"] = float(fac2) + except (ValueError, TypeError): + raise Dopri5_Exception('The fac2 must be an integer or float.') + + def _get_fac2(self): + """ + Parameters for step-size selection. The new step-size is chosen + subject to the restriction fac1 <= current step-size / old step-size <= fac2. + + Parameters:: + + fac2 + - Default 8.0 + + - Should be a float. + + Example: + fac2 = 10.0 + """ + return self.options["fac2"] + + fac2 = property(_get_fac2, _set_fac2) + + def _set_safe(self, safe): + try: + self.options["safe"] = float(safe) + except (ValueError, TypeError): + raise Dopri5_Exception('The safe must be an integer or float.') + + def _get_safe(self): + """ + The safety factor in the step-size prediction. + + Parameters:: + + safe + - Default '0.9' + + - Should be float. + + Example: + safe = 0.8 + """ + return self.options["safe"] + + safe = property(_get_safe, _set_safe) + + def _set_initial_step(self, initstep): + try: + self.options["inith"] = float(initstep) + except (ValueError, TypeError): + raise Dopri5_Exception('The initial step must be an integer or float.') + + def _get_initial_step(self): + """ + This determines the initial step-size to be used in the integration. + + Parameters:: + + inith + - Default '0.01'. + + - Should be float. + + Example: + inith = 0.01 + """ + return self.options["inith"] + + inith = property(_get_initial_step,_set_initial_step) + + def _set_max_h(self,max_h): + try: + self.options["maxh"] = float(max_h) + except (ValueError,TypeError): + raise Dopri5_Exception('Maximal stepsize must be a (scalar) float.') + if self.options["maxh"] < 0: + raise Dopri5_Exception('Maximal stepsize must be a positiv (scalar) float.') + + def _get_max_h(self): + """ + Defines the maximal step-size that is to be used by the solver. + + Parameters:: + + maxh + - Default final time - current time. + + - Should be a float. + + Example: + maxh = 0.01 + + """ + return self.options["maxh"] + + maxh=property(_get_max_h,_set_max_h) + + def _set_beta(self, beta): + self.options["beta"] = beta + + def _get_beta(self): + """ + Option for stabilized step-size control. + + Parameters:: + + beta + - Default 0.04 + + - Should be a float. + """ + return self.options["beta"] + + beta = property(_get_beta, _set_beta) + +class RungeKutta34(Explicit_ODE): + """ + Adaptive Runge-Kutta of order four. + + Obs. Step rejection not implemented. + """ + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Explicit_Problem' class. + """ + Explicit_ODE.__init__(self, problem) #Calls the base class + + #Solver options + self.options["atol"] = 1.0e-6 + self.options["rtol"] = 1.0e-6 + self.options["inith"] = 0.01 + self.options["maxsteps"] = 10000 + + #Internal temporary result vector + self.Y1 = N.array([0.0]*len(self.y0)) + self.Y2 = N.array([0.0]*len(self.y0)) + self.Y3 = N.array([0.0]*len(self.y0)) + self.Y4 = N.array([0.0]*len(self.y0)) + self.Z3 = N.array([0.0]*len(self.y0)) + + #Solver support + self.supports["report_continuously"] = True + self.supports["interpolated_output"] = True + self.supports["state_events"] = True + + def initialize(self): + #Reset statistics + self.statistics.reset() + + def set_problem_data(self): + if self.problem_info["state_events"]: + def event_func(t, y): + return self.problem.state_events(t, y, self.sw) + def f(dy ,t, y): + try: + dy[:] = self.problem.rhs(t, y, self.sw) + except: + return False + return True + self.f = f + self.event_func = event_func + self._event_info = [0] * self.problem_info["dimRoot"] + self.g_old = self.event_func(self.t, self.y) + else: + self.f = self.problem.rhs_internal + + def _set_initial_step(self, initstep): + try: + initstep = float(initstep) + except (ValueError, TypeError): + raise Explicit_ODE_Exception('The initial step must be an integer or float.') + + self.options["inith"] = initstep + + def _get_initial_step(self): + """ + This determines the initial step-size to be used in the integration. + + Parameters:: + + inith + - Default '0.01'. + + - Should be float. + + Example: + inith = 0.01 + """ + return self.options["inith"] + + inith = property(_get_initial_step,_set_initial_step) + + def _set_atol(self,atol): + + try: + atol_arr = N.array(atol, dtype=float) + if (atol_arr <= 0.0).any(): + raise Explicit_ODE_Exception('Absolute tolerance must be a positive float or a float vector.') + except (ValueError,TypeError): + raise Explicit_ODE_Exception('Absolute tolerance must be a positive float or a float vector.') + if atol_arr.size == 1: + self.options["atol"] = float(atol) + elif atol_arr.size == len(self.y): + self.options["atol"] = [float(x) for x in atol] + else: + raise Explicit_ODE_Exception('Absolute tolerance must be a float vector of same dimension as the problem or a scalar.') + + def _get_atol(self): + """ + Sets the absolute tolerance to be used in the integration. + + Parameters:: + + atol + - Default 1.0e-6. + + - Should be float or an array/list of len(y) + + Example: + atol=1.e5 + atol=[1.e-5,1.e-4] + """ + return self.options["atol"] + + atol = property(_get_atol,_set_atol) + + def _set_rtol(self, rtol): + try: + rtol = float(rtol) + except (TypeError,ValueError): + raise Explicit_ODE_Exception('Relative tolerance must be a float.') + if rtol <= 0.0: + raise Explicit_ODE_Exception('Relative tolerance must be a positive (scalar) float.') + self.options["rtol"] = rtol + + def _get_rtol(self): + """ + The relative tolerance to be used in the integration. + + Parameters:: + + rtol + - Default 1.0e-6 + + - Should be a float. + """ + return self.options["rtol"] + + rtol = property(_get_rtol, _set_rtol) + + def _get_maxsteps(self): + """ + The maximum number of steps allowed to be taken to reach the + final time. + + Parameters:: + + maxsteps + - Default 10000 + + - Should be a positive integer + """ + return self.options["maxsteps"] + + def _set_maxsteps(self, max_steps): + try: + max_steps = int(max_steps) + except (TypeError, ValueError): + raise Explicit_ODE_Exception("Maximum number of steps must be a positive integer.") + self.options["maxsteps"] = max_steps + + maxsteps = property(_get_maxsteps, _set_maxsteps) + + def step(self, t, y, tf, opts): + initialize = opts["initialize"] + + if initialize: + self.solver_iterator = self._iter(t,y,tf,opts) + + return self.solver_iterator.next() + + def integrate(self, t, y, tf, opts): + """ + Integrates (t,y) values until t > tf + """ + [flags, tlist, ylist] = zip(*list(self._iter(t, y, tf,opts))) + + return flags[-1], tlist, ylist + + def _iter(self,t,y,tf,opts): + if opts["initialize"]: + self.set_problem_data() + maxsteps = self.options["maxsteps"] + h = self.options["inith"] + h = min(h, N.abs(tf-t)) + self.f(self.Y1, t, y) + flag = ID_PY_OK + + for i in range(maxsteps): + if t+h < tf and flag == ID_PY_OK: + t, y, error = self._step(t, y, h) + self.statistics["nsteps"] += 1 + if self.problem_info["state_events"]: + flag, t, y = self.event_locator(t-h , t, y) + + if opts["report_continuously"]: + initialize_flag = self.report_solution(t, y, opts) + if initialize_flag: flag = ID_PY_EVENT + yield flag, t,y + elif opts["output_list"] is None: + yield flag, t, y + else: + output_list = opts["output_list"] + output_index = opts["output_index"] + try: + while output_list[output_index] <= t: + yield flag, output_list[output_index], self.interpolate(output_list[output_index]) + output_index = output_index + 1 + except IndexError: + pass + opts["output_index"] = output_index + h=self.adjust_stepsize(h,error) + h=min(h, N.abs(tf-t)) + else: + break + else: + raise Explicit_ODE_Exception('Final time not reached within maximum number of steps') + + #If no event has been detected, do the last step. + if flag == ID_PY_OK: + t, y, error = self._step(t, y, h) + self.statistics["nsteps"] += 1 + if self.problem_info["state_events"]: + flag, t, y = self.event_locator(t-h , t, y) + if flag == ID_PY_OK: flag = ID_PY_COMPLETE + if opts["report_continuously"]: + initialize_flag = self.report_solution(t, y, opts) + if initialize_flag: flag = ID_PY_EVENT + else: flag = ID_PY_COMPLETE + yield flag, t,y + elif opts["output_list"] is None: + yield flag, t,y + else: + output_list = opts["output_list"] + output_index = opts["output_index"] + try: + while output_list[output_index] <= t: + yield flag, output_list[output_index], self.interpolate(output_list[output_index]) + output_index = output_index + 1 + except IndexError: + pass + opts["output_index"] = output_index + + def adjust_stepsize(self, h, error): + """ + Adjusts the stepsize. + """ + if error == 0.0: + fac = 2.0 + else: + fac=min((1.0/error)**(1.0/4.0),2.) + h *= fac + + return h + + def _step(self, t, y, h): + """ + This calculates the next step in the integration. + """ + self.statistics["nfcns"] += 5 + + scaling = N.array(abs(y)*self.rtol + self.atol) # to normalize the error + f = self.f + + f(self.Y2, t + h/2., y + h*self.Y1/2.) + f(self.Y3, t + h/2., y + h*self.Y2/2.) + f(self.Z3, t + h, y - h*self.Y1 + 2.0*h*self.Y2) + f(self.Y4, t + h, y + h*self.Y3) + + error = N.linalg.norm(h/6.0*(2.0*self.Y2 + self.Z3 - 2.0*self.Y3 - self.Y4)/scaling) #normalized + t_next = t + h + y_next = y + h/6.0*(self.Y1 + 2.0*self.Y2 + 2.0*self.Y3 + self.Y4) + + f_low = self.Y1.copy() + f(self.Y1, t_next, y_next) + #Hermitian interpolation for the solution in [t, t_next] + def interpolate(time): + thetha = (time - t) / (t_next - t) + f_high = self.Y1 + return (1 - thetha) * y + thetha * y_next + thetha * \ + (thetha - 1) * ((1 - 2*thetha) * (y_next - y) + \ + (thetha - 1) * h * f_low + thetha * h * f_high) + self.interpolate = interpolate + + return t_next, y_next, error + + def state_event_info(self): + return self._event_info + + def set_event_info(self, event_info): + self._event_info = event_info + + def print_statistics(self, verbose): + """ + Should print the statistics. + """ + Explicit_ODE.print_statistics(self, verbose) #Calls the base class + + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : RungeKutta34', verbose) + self.log_message(' Solver type : Adaptive', verbose) + self.log_message(' Relative tolerance : ' + str(self.options["rtol"]), verbose) + self.log_message(' Absolute tolerance : ' + str(self._compact_atol()) + '\n', verbose) + + +class RungeKutta4(Explicit_ODE): + """ + This solver solves an explicit ordinary differential equation using + a Runge-Kutta method of order 4. + + We want to approximate the solution to the ordinary differential + equation of the form, + + .. math:: + + \dot{y} = f(t,y), \quad y(t_0) = y_0 . + + Using a Runge-Kutta method of order 4, the approximation is defined as + follow, + + .. math:: + + y_{n+1} = y_n + \\frac{1}{6}(k_1+2k_2+2k_3+k_4) + + where, + + .. math:: + + k_1 = hf(t_n,y_n) + + k_2 = hf(t_n+\\frac{1}{2}h,y_n+\\frac{1}{2}k_1) + + k_3 = hf(t_n+\\frac{1}{2}h,y_n+\\frac{1}{2}k_2) + + k_4 = hf(t_n+h,y_n+k_3) + + with :math:`h` being the step-size and :math:`y_n` the previous + solution to the equation. + """ + def __init__(self, problem): + Explicit_ODE.__init__(self, problem) #Calls the base class + + #Solver options + self.options["h"] = 0.01 + + #Internal temporary result vector + self.Y1 = N.array([0.0]*len(self.y0)) + self.Y2 = N.array([0.0]*len(self.y0)) + self.Y3 = N.array([0.0]*len(self.y0)) + self.Y4 = N.array([0.0]*len(self.y0)) + + #RHS-Function + self.f = problem.rhs_internal + + #Solver support + self.supports["one_step_mode"] = True + + def step(self, t, y, tf, opts): + initialize = opts["initialize"] + + if initialize: + self.solver_iterator = self._iter(t,y,tf) + + return self.solver_iterator.next() + + def integrate(self, t, y, tf, opts): + """ + Integrates (t,y) values until t > tf + """ + [flags, tlist, ylist] = zip(*list(self._iter(t, y, tf))) + + return flags[-1], tlist, ylist + + def _set_h(self,h): + try: + self.options["h"] = float(h) + except: + raise AssimuloException("Step-size must be a (scalar) float.") + + def _get_h(self): + """ + Defines the step-size that is to be used by the solver. + + Parameters:: + + maxh + - Default '0.01'. + + - Should be a float. + + Example: + maxh = 0.01 + + """ + return self.options["h"] + + h=property(_get_h,_set_h) + + def _iter(self,t,y,tf): + h = self.options["h"] + h = min(h, N.abs(tf-t)) + + while t+h < tf: + t, y = self._step(t, y, h) + yield ID_PY_OK, t,y + h=min(h, N.abs(tf-t)) + else: + t, y = self._step(t, y, h) + yield ID_PY_COMPLETE, t, y + + def _step(self, t, y, h): + """ + This calculates the next step in the integration. + """ + f = self.f + + f(self.Y1, t, y) + f(self.Y2, t + h/2., y + h*self.Y1/2.) + f(self.Y3, t + h/2., y + h*self.Y2/2.) + f(self.Y4, t + h, y + h*self.Y3) + + return t+h, y + h/6.*(self.Y1 + 2.*self.Y2 + 2.*self.Y3 + self.Y4) + + def print_statistics(self, verbose): + """ + Should print the statistics. + """ + self.log_message('Final Run Statistics: %s \n' % self.problem.name, verbose) + self.log_message(' Step-length : %s '%(self.options["h"]), verbose) + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : RungeKutta4', verbose) + self.log_message(' Solver type : Fixed step\n', verbose) diff --git a/src/solvers/sdirk_dae.pyx b/src/solvers/sdirk_dae.pyx index 92e2de99..70ac01ef 100644 --- a/src/solvers/sdirk_dae.pyx +++ b/src/solvers/sdirk_dae.pyx @@ -1,578 +1,578 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import numpy as N -import sys - -from assimulo.exception import * -from assimulo.ode import * - -from assimulo.explicit_ode import Implicit_ODE - -try: - from assimulo.lib.odepack import dlsodar, dcfode, dintdy - from assimulo.lib.odepack import set_lsod_common, get_lsod_common -except ImportError: - sys.stderr.write("Could not find ODEPACK functions\n") - -class SDIRK_DAE(Implicit_ODE): - """ - SDIRK_DAE is a special implicit Runge-Kutta methodstep for solving implicit ordinary - differential equations of index index on the form, - - .. math:: - - F(t, y, \dot{y}) = 0, \quad y(t_0) = y_0. - - - - The core integrator SDIRK_DAE is the original code by Anne Kvaernoe, - http://www.math.ntnu.no/~anne/ - """ - - def __init__(self, problem): - """ - Initiates the solver. - - Parameters:: - - problem - - The problem to be solved. Should be an instance - of the 'Implicit_Problem' class. - """ - Implicit_ODE.__init__(self, problem) #Calls the base class - - #Default values - self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance - self.options["rtol"] = 1.0e-6 #Relative tolerance - self.options["usejac"] = False - self.options["hmax"] = 0. - - # - Statistic values - self.statistics["nsteps"] = 0 #Number of steps - self.statistics["nfcn"] = 0 #Number of function evaluations - self.statistics["njac"] = 0 #Number of Jacobian evaluations - - self._leny = len(self.y) #Dimension of the problem - - # Solver support - self.supports["state_events"] = False - self.supports["report_continuously"] = True - self.supports["interpolated_output"] = False - - def initialize(self): - """ - Initializes the overall simulation process - (called before _simulate) - """ - #Reset statistics - for k in self.statistics.keys(): - self.statistics[k] = 0 - - #self._tlist = [] - #self._ylist = [] - - - def integrate_start(self, t, y): - - # first call or classical restart after a discontinuity - ISTATE=1 - RWORK = N.array([0.0]*(22 + self.problem_info["dim"] * - max(16,self.problem_info["dim"]+9) + - 3*self.problem_info["dimRoot"])) - # Integer work array - IWORK = N.array([0]*(20 + self.problem_info["dim"])) - - - return ISTATE, RWORK, IWORK - - - def integrate(self, t, y, tf, opts): - ITOL = 2 #Only atol is a vector - ITASK = 5 #For one step mode and hitting exactly tcrit, normally tf - IOPT = 1 #optional inputs are used - - # provide work arrays and set common blocks (if needed) - ISTATE, RWORK, IWORK = self.integrate_start( t, y) - - JT = 1 if self.usejac else 2#Jacobian type indicator - JROOT = N.array([0]*self.problem_info["dimRoot"]) - - #Setting work options - RWORK[0] = tf #Do not integrate past tf - RWORK[5] = self.options["hmax"] - - #Setting iwork options - IWORK[5] = self.maxsteps - - #Setting maxord to IWORK - IWORK[7] = self.maxordn - IWORK[8] = self.maxords - - - #Dummy methods - g_dummy = (lambda t:x) if not self.problem_info["state_events"] else self.problem.state_events - jac_dummy = (lambda t,y:N.zeros((len(y),len(y)))) if not self.usejac else self.problem.jac - - #Extra args to rhs and state_events - rhs_extra_args = (self.sw,) if self.problem_info["switches"] else () - g_extra_args = (self.sw,) if self.problem_info["switches"] else () - - #Store the opts - self._opts = opts - - #Outputs - tlist = [] - ylist = [] - - #Nordsieck start index - nordsieck_start_index = 21+3*self.problem_info["dimRoot"] - 1 - - #Run in normal mode? - normal_mode = 1 if opts["output_list"] != None else 0 - #if normal_mode == 0: - if opts["report_continuously"] or opts["output_list"] == None: - while (ISTATE == 2 or ISTATE == 1) and t < tf: - - y, t, ISTATE, RWORK, IWORK, roots = dlsodar(self.problem.rhs, y.copy(), t, tf, ITOL, - self.rtol*N.ones(self.problem_info["dim"]), self.atol, - ITASK, ISTATE, IOPT, RWORK, IWORK, jac_dummy, JT, g_dummy, JROOT, - f_extra_args = rhs_extra_args, g_extra_args = g_extra_args) - - hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() - #print 't= {}, tN={}, y={}, ns={}, hu={}'.format(t , RWORK[12], y, RWORK[nordsieck_start_index],hu) - self._nordsieck_array = \ - RWORK[nordsieck_start_index:nordsieck_start_index+(nq+1)*nyh].reshape((nyh,-1),order='F') - self._nyh = nyh - self._event_info = roots - - if opts["report_continuously"]: - flag_initialize = self.report_solution(t, y, opts) - if flag_initialize: - #If a step event has occured the integration has to be reinitialized - ISTATE = 3 - else: - #Store results - tlist.append(t) - ylist.append(y.copy()) - - #Checking return - if ISTATE == 2: - flag = ID_PY_COMPLETE - elif ISTATE == 3: - flag = ID_PY_EVENT - else: - raise ODEPACK_Exception("LSODAR failed with flag %d"%ISTATE) - - else: - - #Change the ITASK - ITASK = 4 #For computation of yout - - output_index = opts["output_index"] - output_list = opts["output_list"][output_index:] - - for tout in output_list: - output_index += 1 - - y, t, ISTATE, RWORK, IWORK, roots = dlsodar(self.problem.rhs, y.copy(), t, tout, ITOL, - self.rtol*N.ones(self.problem_info["dim"]), self.atol, - ITASK, ISTATE, IOPT, RWORK, IWORK, jac_dummy, JT, g_dummy, JROOT, - f_extra_args = rhs_extra_args, g_extra_args = g_extra_args) - - #Store results - tlist.append(t) - ylist.append(y.copy()) - self._event_info = roots - - #Checking return - if ISTATE == 2 and t >= tf: - flag = ID_PY_COMPLETE - break - elif ISTATE == 3: - flag = ID_PY_EVENT - break - elif ISTATE < 0: - raise ODEPACK_Exception("LSODAR failed with flag %d"%ISTATE) - - opts["output_index"] = output_index - # deciding on restarting options - self._rkstarter_active = True if ISTATE == 3 and self.rkstarter > 1 else False - #print 'rkstarter_active set to {} and ISTATE={}'.format(self._rkstarter_active, ISTATE) - - #Retrieving statistics - self.statistics["ng"] += IWORK[9] - self.statistics["nsteps"] += IWORK[10] - self.statistics["nfcn"] += IWORK[11] - self.statistics["njac"] += IWORK[12] - self.statistics["nevents"] += 1 if flag == ID_PY_EVENT else 0 - # save RWORK, IWORK for restarting feature - if self.rkstarter>1: - self._RWORK=RWORK - self._IWORK=IWORK - - return flag, tlist, ylist - - def get_algorithm_data(self): - """ - Returns the order and step size used in the last successful step. - """ - hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() - - return hu, nqu - - def state_event_info(self): - """ - Returns the state events indicator as a list where a one (1) - indicates that the event function have been activated and a (0) - if not. - """ - return self._event_info - - def print_statistics(self, verbose=NORMAL): - """ - Prints the run-time statistics for the problem. - """ - self.log_message('\nSolver options:\n', verbose) - self.log_message(' Solver : LSODAR ', verbose) - self.log_message(' Absolute tolerances : {}'.format(self.options["atol"]), verbose) - self.log_message(' Relative tolerances : {}'.format(self.options["rtol"]), verbose) - if self.rkstarter==1: - self.log_message(' Starter : {}'.format('classical'), verbose) - else: - self.log_message(' Starter : Runge-Kutta order {}'.format(self.rkstarter), verbose) - if self.maxordn < 12 or self.maxords < 5: - self.log_message(' Maximal order Adams : {}'.format(self.maxordn), verbose) - self.log_message(' Maximal order BDF : {}'.format(self.maxords), verbose) - if self.hmax > 0. : - self.log_message(' Maximal stepsize hmax : {}'.format(self.hmax), verbose) - self.log_message('', verbose) - - self.log_message('Final Run Statistics: %s \n' % self.problem.name, verbose) - - self.log_message(' Number of steps : {}'.format(self.statistics["nsteps"]), verbose) - self.log_message(' Number of function evaluations : {}'.format(self.statistics["nfcn"]), verbose) - self.log_message(' Number of Jacobian evaluations : {}'.format(self.statistics["njac"]), verbose) - self.log_message(' Number of event function evaluations : {}'.format(self.statistics["ng"]), verbose) - self.log_message(' Number of detected state events : {}'.format(self.statistics["nevents"]), verbose) - - - def _set_usejac(self, jac): - self.options["usejac"] = bool(jac) - - def _get_usejac(self): - """ - This sets the option to use the user defined jacobian. If a - user provided jacobian is implemented into the problem the - default setting is to use that jacobian. If not, an - approximation is used. - - Parameters:: - - usejac - - True - use user defined jacobian - False - use an approximation - - - Should be a boolean. - - Example: - usejac = False - """ - return self.options["usejac"] - - usejac = property(_get_usejac,_set_usejac) - - def _set_atol(self,atol): - - self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) - - if len(self.options["atol"]) == 1: - self.options["atol"] = self.options["atol"]*N.ones(self._leny) - elif len(self.options["atol"]) != self._leny: - raise ODEPACK_Exception("atol must be of length one or same as the dimension of the problem.") - - def _get_atol(self): - """ - Defines the absolute tolerance(s) that is to be used by the solver. - Can be set differently for each variable. - - Parameters:: - - atol - - Default '1.0e-6'. - - - Should be a positive float or a numpy vector - of floats. - - Example: - atol = [1.0e-4, 1.0e-6] - """ - return self.options["atol"] - - atol=property(_get_atol,_set_atol) - - def _set_rtol(self,rtol): - try: - self.options["rtol"] = float(rtol) - except (ValueError, TypeError): - raise ODEPACK_Exception('Relative tolerance must be a (scalar) float.') - if self.options["rtol"] <= 0.0: - raise ODEPACK_Exception('Relative tolerance must be a positive (scalar) float.') - - def _get_rtol(self): - """ - Defines the relative tolerance that is to be used by the solver. - - Parameters:: - - rtol - - Default '1.0e-6'. - - - Should be a positive float. - - Example: - rtol = 1.0e-4 - """ - return self.options["rtol"] - - rtol=property(_get_rtol,_set_rtol) - - def _get_maxsteps(self): - """ - The maximum number of steps allowed to be taken to reach the - final time. - - Parameters:: - - maxsteps - - Default 10000 - - - Should be a positive integer - """ - return self.options["maxsteps"] - - def _set_maxsteps(self, max_steps): - try: - max_steps = int(max_steps) - except (TypeError, ValueError): - raise ODEPACK_Exception("Maximum number of steps must be a positive integer.") - self.options["maxsteps"] = max_steps - - maxsteps = property(_get_maxsteps, _set_maxsteps) - def _get_hmax(self): - """ - The absolute value of the maximal stepsize for all methods - - Parameters:: - - hmax - - Default: 0. (no maximal step size) - - - Should be a positive float - """ - return self.options["hmax"] - def _set_hmax(self,hmax): - if not (isinstance(hmax,float) and hmax >= 0.): - raise ODEPACK_Exception("Maximal step size hmax should be a positive float") - self.options["hmax"]=hmax - hmax = property(_get_hmax, _set_hmax) - def _get_maxordn(self): - """ - The maximum order used by the Adams-Moulton method (nonstiff case) - - Parameters:: - - maxordn - - Default 12 - - - Should be a positive integer - """ - return self.options["maxordn"] - - def _set_maxordn(self, maxordn): - try: - maxordn = int(maxordn) - except (TypeError, ValueError): - raise ODEPACK_Exception("Maximum order must be a positive integer.") - if maxordn > 12: - raise ODEPACK_Exception("Maximum order should not exceed 12.") - self.options["maxordn"] = maxordn - - maxordn = property(_get_maxordn, _set_maxordn) - def _get_maxords(self): - """ - The maximum order used by the BDF method (stiff case) - - Parameters:: - - maxords - - Default 5 - - - Should be a positive integer - """ - return self.options["maxords"] - - def _set_maxords(self, maxords): - try: - maxords = int(maxords) - except (TypeError, ValueError): - raise ODEPACK_Exception("Maximum order must be a positive integer.") - if maxords > 5: - raise ODEPACK_Exception("Maximum order should not exceed 5.") - self.options["maxords"] = maxords - - maxords = property(_get_maxords, _set_maxords) - def _get_rkstarter(self): - """ - This defines how LSODAR is started. - (classically or with a fourth order Runge-Kutta starter) - - Parameters:: - - rkstarter - - Default False starts LSODAR in the classical multistep way - - - Should be a Boolean - """ - return self.options["rkstarter"] - - def _set_rkstarter(self, rkstarter): - if not rkstarter in {1,2,3,4,5}: - raise ODEPACK_Exception("Must be a positive integer less than 6") - self.options["rkstarter"] = rkstarter - - rkstarter = property(_get_rkstarter, _set_rkstarter) -class RKStarterNordsieck(object): - """ - A family of Runge-Kutta starters producing a - Nordsieck array to (re-)start a Nordsieck based multistep - method with a given order. - - See: Mohammadi (2013): https://lup.lub.lu.se/luur/download?func=downloadFile&recordOId=4196026&fileOId=4196027 - """ - Gamma_0=[N.array([[1.,0.], # 1st order - [0.,1.]]), - N.array([[1.,0.,0.], # 2nd order - [0.,1.,-1.], - [0.,0.,1.]]), - N.array([[1.,0.,0.,0.], # 3rd order - [0.,1.,-5./3.,1.], - [0.,0.,3.,-2.], - [0.,0.,0.,1.], - [0.,0.,-4./3.,0.]]), - N.array([[1.,0.,0.,0.,0.], # 4th order - [0.,1.,-5./6.,4./9.,-1./9.], - [0.,0.,0.,0.,0.], - [0.,0.,1./2.,-4./9.,1./9.], - [0.,0.,7./3.,-19./9.,7./9.], - [0.,0.,-3.,10./3.,-4./3.], - [0.,0.,1.,-11./9.,5./9.]])] - - scale=N.array([1, 1, 1/2., 1./6., 1./24., 1./120.]).reshape(-1,1) - - def __init__(self, rhs, H, eval_at=0.,number_of_steps=4): - """ - Initiates the Runge-Kutta Starter. - - Parameters:: - - rhs - - The problem's rhs function - - H - - starter step size - - eval_at - - Where to evaluate the Nordiseck vector - evaluation point is (eval_at)*H - Possible choices - eval_at=0. - eval_at=1. - - number_of_steps - - the number of steps :math:`(k)` to start the multistep method with - This will determine the initial order of the method, which is - :math:`k` for BDF methods and :math:`k+1` for Adams Moulton Methods . - """ - self.f = rhs - self.H = H - - if not 1 < number_of_steps < 5: - raise RKStarter_Exception('Step number larget than 4 not yet implemented') - self.number_of_steps = number_of_steps - self.eval_at = float(eval_at) - if not self.eval_at == 0.: - raise RKStarter_Exception("Parameter eval_at different from 0 not yet implemented.") - def rk_like4(self, t0, y0, sw0): - """ - rk_like computes Runge-Kutta stages - Note, the currently implementation is **only** correct for - autonomous systems. - """ - f = lambda y: self.f(t0, y, sw0) - h = self.H/4. - k1 = h*f(y0) - k2 = h*f(y0 + k1) - k3 = h*f(y0 + 2. * k2) - k4 = h*f(y0 + 3./4. * k1 + 9./4. * k3) - k5 = h*f(y0 + k1/2. + k2 + k3/2. + 2. * k4) - k6 = h*f(y0+k1/12.+2. * k2 + k3/4. + 2./3. * k4 + 2. * k5) - return N.array([y0,k1,k2,k3,k4,k5,k6]) - def rk_like3(self, t0, y0, sw0): - """ - rk_like computes Runge-Kutta stages - Note, the currently implementation is **only** correct for - autonomous systems. - - """ - f = lambda y: self.f(t0, y, sw0) - h = self.H/3. - k1 = h*f(y0) - k2 = h*f(y0 + k1) - k3 = h*f(y0 + k1+ k2) - k4 = h*f(y0 + 3./2. * k1) - return N.array([y0,k1,k2,k3,k4]) - def rk_like2(self, t0, y0, sw0): - """ - rk_like2 computes Runge-Kutta 2nd-stages - Note, the currently implementation is **only** correct for - autonomous systems. - """ - f=lambda y: self.f(t0, y, sw0) - h=self.H/2 - k1=h*f(y0) - k2=h*f(y0+k1) - return N.array([y0,k1,k2]) - def nordsieck(self,k): - """ - Nordsieck array computed at initial point - """ - nord=self.scale[:self.number_of_steps+1]*N.dot(self.Gamma_0[self.number_of_steps-1].T,k) - return nord - def __call__(self, t0 , y0, sw0=[]): - """ - Evaluates the Runge-Kutta starting values - - Parameters:: - - y0 - - starting value - """ - # We construct a call like: rk_like4(self, t0, y0, sw0) - k=self.__getattribute__('rk_like{}'.format(self.number_of_steps))(t0, y0, sw0) - t = t0+self.eval_at*self.H - return t, self.nordsieck(k) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import numpy as N +import sys + +from assimulo.exception import * +from assimulo.ode import * + +from assimulo.explicit_ode import Implicit_ODE + +try: + from assimulo.lib.odepack import dlsodar, dcfode, dintdy + from assimulo.lib.odepack import set_lsod_common, get_lsod_common +except ImportError: + sys.stderr.write("Could not find ODEPACK functions\n") + +class SDIRK_DAE(Implicit_ODE): + """ + SDIRK_DAE is a special implicit Runge-Kutta methodstep for solving implicit ordinary + differential equations of index index on the form, + + .. math:: + + F(t, y, \dot{y}) = 0, \quad y(t_0) = y_0. + + + + The core integrator SDIRK_DAE is the original code by Anne Kvaernoe, + http://www.math.ntnu.no/~anne/ + """ + + def __init__(self, problem): + """ + Initiates the solver. + + Parameters:: + + problem + - The problem to be solved. Should be an instance + of the 'Implicit_Problem' class. + """ + Implicit_ODE.__init__(self, problem) #Calls the base class + + #Default values + self.options["atol"] = 1.0e-6*N.ones(self.problem_info["dim"]) #Absolute tolerance + self.options["rtol"] = 1.0e-6 #Relative tolerance + self.options["usejac"] = False + self.options["hmax"] = 0. + + # - Statistic values + self.statistics["nsteps"] = 0 #Number of steps + self.statistics["nfcn"] = 0 #Number of function evaluations + self.statistics["njac"] = 0 #Number of Jacobian evaluations + + self._leny = len(self.y) #Dimension of the problem + + # Solver support + self.supports["state_events"] = False + self.supports["report_continuously"] = True + self.supports["interpolated_output"] = False + + def initialize(self): + """ + Initializes the overall simulation process + (called before _simulate) + """ + #Reset statistics + for k in self.statistics.keys(): + self.statistics[k] = 0 + + #self._tlist = [] + #self._ylist = [] + + + def integrate_start(self, t, y): + + # first call or classical restart after a discontinuity + ISTATE=1 + RWORK = N.array([0.0]*(22 + self.problem_info["dim"] * + max(16,self.problem_info["dim"]+9) + + 3*self.problem_info["dimRoot"])) + # Integer work array + IWORK = N.array([0]*(20 + self.problem_info["dim"])) + + + return ISTATE, RWORK, IWORK + + + def integrate(self, t, y, tf, opts): + ITOL = 2 #Only atol is a vector + ITASK = 5 #For one step mode and hitting exactly tcrit, normally tf + IOPT = 1 #optional inputs are used + + # provide work arrays and set common blocks (if needed) + ISTATE, RWORK, IWORK = self.integrate_start( t, y) + + JT = 1 if self.usejac else 2#Jacobian type indicator + JROOT = N.array([0]*self.problem_info["dimRoot"]) + + #Setting work options + RWORK[0] = tf #Do not integrate past tf + RWORK[5] = self.options["hmax"] + + #Setting iwork options + IWORK[5] = self.maxsteps + + #Setting maxord to IWORK + IWORK[7] = self.maxordn + IWORK[8] = self.maxords + + + #Dummy methods + g_dummy = (lambda t:x) if not self.problem_info["state_events"] else self.problem.state_events + jac_dummy = (lambda t,y:N.zeros((len(y),len(y)))) if not self.usejac else self.problem.jac + + #Extra args to rhs and state_events + rhs_extra_args = (self.sw,) if self.problem_info["switches"] else () + g_extra_args = (self.sw,) if self.problem_info["switches"] else () + + #Store the opts + self._opts = opts + + #Outputs + tlist = [] + ylist = [] + + #Nordsieck start index + nordsieck_start_index = 21+3*self.problem_info["dimRoot"] - 1 + + #Run in normal mode? + normal_mode = 1 if opts["output_list"] != None else 0 + #if normal_mode == 0: + if opts["report_continuously"] or opts["output_list"] == None: + while (ISTATE == 2 or ISTATE == 1) and t < tf: + + y, t, ISTATE, RWORK, IWORK, roots = dlsodar(self.problem.rhs, y.copy(), t, tf, ITOL, + self.rtol*N.ones(self.problem_info["dim"]), self.atol, + ITASK, ISTATE, IOPT, RWORK, IWORK, jac_dummy, JT, g_dummy, JROOT, + f_extra_args = rhs_extra_args, g_extra_args = g_extra_args) + + hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() + #print 't= {}, tN={}, y={}, ns={}, hu={}'.format(t , RWORK[12], y, RWORK[nordsieck_start_index],hu) + self._nordsieck_array = \ + RWORK[nordsieck_start_index:nordsieck_start_index+(nq+1)*nyh].reshape((nyh,-1),order='F') + self._nyh = nyh + self._event_info = roots + + if opts["report_continuously"]: + flag_initialize = self.report_solution(t, y, opts) + if flag_initialize: + #If a step event has occured the integration has to be reinitialized + ISTATE = 3 + else: + #Store results + tlist.append(t) + ylist.append(y.copy()) + + #Checking return + if ISTATE == 2: + flag = ID_PY_COMPLETE + elif ISTATE == 3: + flag = ID_PY_EVENT + else: + raise ODEPACK_Exception("LSODAR failed with flag %d"%ISTATE) + + else: + + #Change the ITASK + ITASK = 4 #For computation of yout + + output_index = opts["output_index"] + output_list = opts["output_list"][output_index:] + + for tout in output_list: + output_index += 1 + + y, t, ISTATE, RWORK, IWORK, roots = dlsodar(self.problem.rhs, y.copy(), t, tout, ITOL, + self.rtol*N.ones(self.problem_info["dim"]), self.atol, + ITASK, ISTATE, IOPT, RWORK, IWORK, jac_dummy, JT, g_dummy, JROOT, + f_extra_args = rhs_extra_args, g_extra_args = g_extra_args) + + #Store results + tlist.append(t) + ylist.append(y.copy()) + self._event_info = roots + + #Checking return + if ISTATE == 2 and t >= tf: + flag = ID_PY_COMPLETE + break + elif ISTATE == 3: + flag = ID_PY_EVENT + break + elif ISTATE < 0: + raise ODEPACK_Exception("LSODAR failed with flag %d"%ISTATE) + + opts["output_index"] = output_index + # deciding on restarting options + self._rkstarter_active = True if ISTATE == 3 and self.rkstarter > 1 else False + #print 'rkstarter_active set to {} and ISTATE={}'.format(self._rkstarter_active, ISTATE) + + #Retrieving statistics + self.statistics["ng"] += IWORK[9] + self.statistics["nsteps"] += IWORK[10] + self.statistics["nfcn"] += IWORK[11] + self.statistics["njac"] += IWORK[12] + self.statistics["nevents"] += 1 if flag == ID_PY_EVENT else 0 + # save RWORK, IWORK for restarting feature + if self.rkstarter>1: + self._RWORK=RWORK + self._IWORK=IWORK + + return flag, tlist, ylist + + def get_algorithm_data(self): + """ + Returns the order and step size used in the last successful step. + """ + hu, nqu ,nq ,nyh, nqnyh = get_lsod_common() + + return hu, nqu + + def state_event_info(self): + """ + Returns the state events indicator as a list where a one (1) + indicates that the event function have been activated and a (0) + if not. + """ + return self._event_info + + def print_statistics(self, verbose=NORMAL): + """ + Prints the run-time statistics for the problem. + """ + self.log_message('\nSolver options:\n', verbose) + self.log_message(' Solver : LSODAR ', verbose) + self.log_message(' Absolute tolerances : {}'.format(self.options["atol"]), verbose) + self.log_message(' Relative tolerances : {}'.format(self.options["rtol"]), verbose) + if self.rkstarter==1: + self.log_message(' Starter : {}'.format('classical'), verbose) + else: + self.log_message(' Starter : Runge-Kutta order {}'.format(self.rkstarter), verbose) + if self.maxordn < 12 or self.maxords < 5: + self.log_message(' Maximal order Adams : {}'.format(self.maxordn), verbose) + self.log_message(' Maximal order BDF : {}'.format(self.maxords), verbose) + if self.hmax > 0. : + self.log_message(' Maximal stepsize hmax : {}'.format(self.hmax), verbose) + self.log_message('', verbose) + + self.log_message('Final Run Statistics: %s \n' % self.problem.name, verbose) + + self.log_message(' Number of steps : {}'.format(self.statistics["nsteps"]), verbose) + self.log_message(' Number of function evaluations : {}'.format(self.statistics["nfcn"]), verbose) + self.log_message(' Number of Jacobian evaluations : {}'.format(self.statistics["njac"]), verbose) + self.log_message(' Number of event function evaluations : {}'.format(self.statistics["ng"]), verbose) + self.log_message(' Number of detected state events : {}'.format(self.statistics["nevents"]), verbose) + + + def _set_usejac(self, jac): + self.options["usejac"] = bool(jac) + + def _get_usejac(self): + """ + This sets the option to use the user defined jacobian. If a + user provided jacobian is implemented into the problem the + default setting is to use that jacobian. If not, an + approximation is used. + + Parameters:: + + usejac + - True - use user defined jacobian + False - use an approximation + + - Should be a boolean. + + Example: + usejac = False + """ + return self.options["usejac"] + + usejac = property(_get_usejac,_set_usejac) + + def _set_atol(self,atol): + + self.options["atol"] = N.array(atol,dtype=float) if len(N.array(atol,dtype=float).shape)>0 else N.array([atol],dtype=float) + + if len(self.options["atol"]) == 1: + self.options["atol"] = self.options["atol"]*N.ones(self._leny) + elif len(self.options["atol"]) != self._leny: + raise ODEPACK_Exception("atol must be of length one or same as the dimension of the problem.") + + def _get_atol(self): + """ + Defines the absolute tolerance(s) that is to be used by the solver. + Can be set differently for each variable. + + Parameters:: + + atol + - Default '1.0e-6'. + + - Should be a positive float or a numpy vector + of floats. + + Example: + atol = [1.0e-4, 1.0e-6] + """ + return self.options["atol"] + + atol=property(_get_atol,_set_atol) + + def _set_rtol(self,rtol): + try: + self.options["rtol"] = float(rtol) + except (ValueError, TypeError): + raise ODEPACK_Exception('Relative tolerance must be a (scalar) float.') + if self.options["rtol"] <= 0.0: + raise ODEPACK_Exception('Relative tolerance must be a positive (scalar) float.') + + def _get_rtol(self): + """ + Defines the relative tolerance that is to be used by the solver. + + Parameters:: + + rtol + - Default '1.0e-6'. + + - Should be a positive float. + + Example: + rtol = 1.0e-4 + """ + return self.options["rtol"] + + rtol=property(_get_rtol,_set_rtol) + + def _get_maxsteps(self): + """ + The maximum number of steps allowed to be taken to reach the + final time. + + Parameters:: + + maxsteps + - Default 10000 + + - Should be a positive integer + """ + return self.options["maxsteps"] + + def _set_maxsteps(self, max_steps): + try: + max_steps = int(max_steps) + except (TypeError, ValueError): + raise ODEPACK_Exception("Maximum number of steps must be a positive integer.") + self.options["maxsteps"] = max_steps + + maxsteps = property(_get_maxsteps, _set_maxsteps) + def _get_hmax(self): + """ + The absolute value of the maximal stepsize for all methods + + Parameters:: + + hmax + - Default: 0. (no maximal step size) + + - Should be a positive float + """ + return self.options["hmax"] + def _set_hmax(self,hmax): + if not (isinstance(hmax,float) and hmax >= 0.): + raise ODEPACK_Exception("Maximal step size hmax should be a positive float") + self.options["hmax"]=hmax + hmax = property(_get_hmax, _set_hmax) + def _get_maxordn(self): + """ + The maximum order used by the Adams-Moulton method (nonstiff case) + + Parameters:: + + maxordn + - Default 12 + + - Should be a positive integer + """ + return self.options["maxordn"] + + def _set_maxordn(self, maxordn): + try: + maxordn = int(maxordn) + except (TypeError, ValueError): + raise ODEPACK_Exception("Maximum order must be a positive integer.") + if maxordn > 12: + raise ODEPACK_Exception("Maximum order should not exceed 12.") + self.options["maxordn"] = maxordn + + maxordn = property(_get_maxordn, _set_maxordn) + def _get_maxords(self): + """ + The maximum order used by the BDF method (stiff case) + + Parameters:: + + maxords + - Default 5 + + - Should be a positive integer + """ + return self.options["maxords"] + + def _set_maxords(self, maxords): + try: + maxords = int(maxords) + except (TypeError, ValueError): + raise ODEPACK_Exception("Maximum order must be a positive integer.") + if maxords > 5: + raise ODEPACK_Exception("Maximum order should not exceed 5.") + self.options["maxords"] = maxords + + maxords = property(_get_maxords, _set_maxords) + def _get_rkstarter(self): + """ + This defines how LSODAR is started. + (classically or with a fourth order Runge-Kutta starter) + + Parameters:: + + rkstarter + - Default False starts LSODAR in the classical multistep way + + - Should be a Boolean + """ + return self.options["rkstarter"] + + def _set_rkstarter(self, rkstarter): + if not rkstarter in {1,2,3,4,5}: + raise ODEPACK_Exception("Must be a positive integer less than 6") + self.options["rkstarter"] = rkstarter + + rkstarter = property(_get_rkstarter, _set_rkstarter) +class RKStarterNordsieck(object): + """ + A family of Runge-Kutta starters producing a + Nordsieck array to (re-)start a Nordsieck based multistep + method with a given order. + + See: Mohammadi (2013): https://lup.lub.lu.se/luur/download?func=downloadFile&recordOId=4196026&fileOId=4196027 + """ + Gamma_0=[N.array([[1.,0.], # 1st order + [0.,1.]]), + N.array([[1.,0.,0.], # 2nd order + [0.,1.,-1.], + [0.,0.,1.]]), + N.array([[1.,0.,0.,0.], # 3rd order + [0.,1.,-5./3.,1.], + [0.,0.,3.,-2.], + [0.,0.,0.,1.], + [0.,0.,-4./3.,0.]]), + N.array([[1.,0.,0.,0.,0.], # 4th order + [0.,1.,-5./6.,4./9.,-1./9.], + [0.,0.,0.,0.,0.], + [0.,0.,1./2.,-4./9.,1./9.], + [0.,0.,7./3.,-19./9.,7./9.], + [0.,0.,-3.,10./3.,-4./3.], + [0.,0.,1.,-11./9.,5./9.]])] + + scale=N.array([1, 1, 1/2., 1./6., 1./24., 1./120.]).reshape(-1,1) + + def __init__(self, rhs, H, eval_at=0.,number_of_steps=4): + """ + Initiates the Runge-Kutta Starter. + + Parameters:: + + rhs + - The problem's rhs function + + H + - starter step size + + eval_at + - Where to evaluate the Nordiseck vector + evaluation point is (eval_at)*H + Possible choices + eval_at=0. + eval_at=1. + + number_of_steps + - the number of steps :math:`(k)` to start the multistep method with + This will determine the initial order of the method, which is + :math:`k` for BDF methods and :math:`k+1` for Adams Moulton Methods . + """ + self.f = rhs + self.H = H + + if not 1 < number_of_steps < 5: + raise RKStarter_Exception('Step number larget than 4 not yet implemented') + self.number_of_steps = number_of_steps + self.eval_at = float(eval_at) + if not self.eval_at == 0.: + raise RKStarter_Exception("Parameter eval_at different from 0 not yet implemented.") + def rk_like4(self, t0, y0, sw0): + """ + rk_like computes Runge-Kutta stages + Note, the currently implementation is **only** correct for + autonomous systems. + """ + f = lambda y: self.f(t0, y, sw0) + h = self.H/4. + k1 = h*f(y0) + k2 = h*f(y0 + k1) + k3 = h*f(y0 + 2. * k2) + k4 = h*f(y0 + 3./4. * k1 + 9./4. * k3) + k5 = h*f(y0 + k1/2. + k2 + k3/2. + 2. * k4) + k6 = h*f(y0+k1/12.+2. * k2 + k3/4. + 2./3. * k4 + 2. * k5) + return N.array([y0,k1,k2,k3,k4,k5,k6]) + def rk_like3(self, t0, y0, sw0): + """ + rk_like computes Runge-Kutta stages + Note, the currently implementation is **only** correct for + autonomous systems. + + """ + f = lambda y: self.f(t0, y, sw0) + h = self.H/3. + k1 = h*f(y0) + k2 = h*f(y0 + k1) + k3 = h*f(y0 + k1+ k2) + k4 = h*f(y0 + 3./2. * k1) + return N.array([y0,k1,k2,k3,k4]) + def rk_like2(self, t0, y0, sw0): + """ + rk_like2 computes Runge-Kutta 2nd-stages + Note, the currently implementation is **only** correct for + autonomous systems. + """ + f=lambda y: self.f(t0, y, sw0) + h=self.H/2 + k1=h*f(y0) + k2=h*f(y0+k1) + return N.array([y0,k1,k2]) + def nordsieck(self,k): + """ + Nordsieck array computed at initial point + """ + nord=self.scale[:self.number_of_steps+1]*N.dot(self.Gamma_0[self.number_of_steps-1].T,k) + return nord + def __call__(self, t0 , y0, sw0=[]): + """ + Evaluates the Runge-Kutta starting values + + Parameters:: + + y0 + - starting value + """ + # We construct a call like: rk_like4(self, t0, y0, sw0) + k=self.__getattribute__('rk_like{}'.format(self.number_of_steps))(t0, y0, sw0) + t = t0+self.eval_at*self.H + return t, self.nordsieck(k) diff --git a/src/solvers/sundials.pyx b/src/solvers/sundials.pyx index efb9987d..ff89f605 100644 --- a/src/solvers/sundials.pyx +++ b/src/solvers/sundials.pyx @@ -35,7 +35,9 @@ cimport sundials_includes as SUNDIALS #Various C includes transfered to namespace from sundials_includes cimport N_Vector, realtype, N_VectorContent_Serial, DENSE_COL, sunindextype -from sundials_includes cimport memcpy, N_VNew_Serial, DlsMat, SlsMat, SUNMatrix, SUNMatrixContent_Dense, SUNMatrixContent_Sparse +from sundials_includes cimport memcpy, N_VNew_Serial, DlsMat, SUNMatrix, SUNMatrixContent_Dense, SUNMatrixContent_Sparse +IF SUNDIALS_VERSION < (5,0,0): + from sundials_includes cimport SlsMat from sundials_includes cimport malloc, free, N_VCloneVectorArray_Serial from sundials_includes cimport N_VConst_Serial, N_VDestroy_Serial diff --git a/tests/solvers/__init__.py b/tests/solvers/__init__.py index f6219b5e..96eea2c5 100644 --- a/tests/solvers/__init__.py +++ b/tests/solvers/__init__.py @@ -1,16 +1,16 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . diff --git a/tests/solvers/test_euler.py b/tests/solvers/test_euler.py index feac8df6..2148d424 100644 --- a/tests/solvers/test_euler.py +++ b/tests/solvers/test_euler.py @@ -1,393 +1,393 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import nose -from assimulo import testattr -from assimulo.solvers.euler import * -from assimulo.problem import Explicit_Problem -from assimulo.exception import * -import scipy.sparse as sp - -class Extended_Problem(Explicit_Problem): - - #Sets the initial conditons directly into the problem - y0 = [0.0, -1.0, 0.0] - sw0 = [False,True,True] - event_array = N.array([0.0,0.0,0.0]) - rhs_array = N.array([0.0,0.0,0.0]) - - #The right-hand-side function (rhs) - def rhs(self,t,y,sw): - """ - This is our function we are trying to simulate. During simulation - the parameter sw should be fixed so that our function is continuous - over the interval. The parameters sw should only be changed when the - integrator has stopped. - """ - self.rhs_array[0] = (1.0 if sw[0] else -1.0) - self.rhs_array[1] = 0.0 - self.rhs_array[2] = 0.0 - - return self.rhs_array - - #Sets a name to our function - name = 'ODE with discontinuities and a function with consistency problem' - - #The event function - def state_events(self,t,y,sw): - """ - This is our function that keeps track of our events. When the sign - of any of the events has changed, we have an event. - """ - self.event_array[0] = y[1] - 1.0 - self.event_array[1] = -y[2] + 1.0 - self.event_array[2] = -t + 1.0 - - return self.event_array - - #Responsible for handling the events. - def handle_event(self, solver, event_info): - """ - Event handling. This functions is called when Assimulo finds an event as - specified by the event functions. - """ - event_info = event_info[0] #We only look at the state events information. - while True: #Event Iteration - self.event_switch(solver, event_info) #Turns the switches - - b_mode = self.state_events(solver.t, solver.y, solver.sw).copy() - self.init_mode(solver) #Pass in the solver to the problem specified init_mode - a_mode = self.state_events(solver.t, solver.y, solver.sw).copy() - - event_info = self.check_eIter(b_mode, a_mode) - - if not True in event_info: #Breaks the iteration loop - break - - #Helper function for handle_event - def event_switch(self, solver, event_info): - """ - Turns the switches. - """ - for i in range(len(event_info)): #Loop across all event functions - if event_info[i] != 0: - solver.sw[i] = not solver.sw[i] #Turn the switch - - #Helper function for handle_event - def check_eIter(self, before, after): - """ - Helper function for handle_event to determine if we have event - iteration. - - Input: Values of the event indicator functions (state_events) - before and after we have changed mode of operations. - """ - - eIter = [False]*len(before) - - for i in range(len(before)): - if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): - eIter[i] = True - - return eIter - - def init_mode(self, solver): - """ - Initialize the DAE with the new conditions. - """ - solver.y[1] = (-1.0 if solver.sw[1] else 3.0) - solver.y[2] = (0.0 if solver.sw[2] else 2.0) - - -class Test_Explicit_Euler: - - def setUp(self): - """ - This function sets up the test case. - """ - f = lambda t,y: 1.0 - y0 = 1.0 - - self.problem = Explicit_Problem(f, y0) - self.simulator = ExplicitEuler(self.problem) - - @testattr(stddist = True) - def test_event_localizer(self): - exp_mod = Extended_Problem() #Create the problem - - exp_sim = ExplicitEuler(exp_mod) #Create the solver - - exp_sim.verbosity = 0 - exp_sim.report_continuously = True - - #Simulate - t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points - - #Basic test - nose.tools.assert_almost_equal(y[-1][0],8.0) - nose.tools.assert_almost_equal(y[-1][1],3.0) - nose.tools.assert_almost_equal(y[-1][2],2.0) - - @testattr(stddist = True) - def test_h(self): - - nose.tools.assert_almost_equal(self.simulator.h, 0.01) - self.simulator.h = 1.0 - nose.tools.assert_almost_equal(self.simulator.h, 1.0) - nose.tools.assert_raises(AssimuloException, self.simulator._set_h, [1]) - - @testattr(stddist = True) - def test_time_event(self): - f = lambda t,y: N.array(1.0) - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Explicit_Problem(f,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = ExplicitEuler(exp_mod) - exp_sim(5.,100) - - assert nevent == 5 - - @testattr(stddist = True) - def test_integrator(self): - """ - This tests the functionality of using the normal mode. - """ - values = self.simulator.simulate(1) - - nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) - nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) - - @testattr(stddist = True) - def test_step(self): - """ - This tests the functionality of using one step mode. - """ - self.simulator.report_continuously = True - - self.simulator.h = 0.1 - self.simulator.simulate(1) - - nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) - nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) - - @testattr(stddist = True) - def test_exception(self): - """ - This tests that exceptions are no caught when evaluating the RHS in ExpEuler. - """ - def f(t,y): - raise NotImplementedError - - prob = Explicit_Problem(f,0.0) - sim = ExplicitEuler(prob) - - nose.tools.assert_raises(NotImplementedError, sim.simulate, 1.0) - - @testattr(stddist = True) - def test_switches(self): - """ - This tests that the switches are actually turned when override. - """ - f = lambda t,x,sw: N.array([1.0]) - state_events = lambda t,x,sw: N.array([x[0]-1.]) - def handle_event(solver, event_info): - solver.sw = [False] #Override the switches to point to another instance - - mod = Explicit_Problem(f,[0.0]) - mod.sw0 = [True] - - mod.state_events = state_events - mod.handle_event = handle_event - - sim = ExplicitEuler(mod) - assert sim.sw[0] == True - sim.simulate(3) - assert sim.sw[0] == False - -class Test_Implicit_Euler: - - def setUp(self): - """ - This function sets up the test case. - """ - f = lambda t,y: 1.0 - y0 = 1.0 - - self.problem = Explicit_Problem(f, y0) - self.simulator = ImplicitEuler(self.problem) - - @testattr(stddist = True) - def test_reset_statistics(self): - assert self.simulator.statistics["nsteps"] == 0 - - self.simulator.simulate(5) - nsteps = self.simulator.statistics["nsteps"] - self.simulator.simulate(6) - - assert self.simulator.statistics["nsteps"] < nsteps - - @testattr(stddist = True) - def test_usejac_csc_matrix(self): - """ - This tests the functionality of the property usejac. - """ - f = lambda t,x: N.array([x[1], -9.82]) #Defines the rhs - jac = lambda t,x: sp.csc_matrix(N.array([[0.,1.],[0.,0.]])) #Defines the jacobian - - exp_mod = Explicit_Problem(f, [1.0,0.0]) - exp_mod.jac = jac - - exp_sim = ImplicitEuler(exp_mod) - exp_sim.simulate(5.,100) - - assert exp_sim.statistics["nfcnjacs"] == 0 - nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.995500, 4) - - exp_sim.reset() - exp_sim.usejac=False - exp_sim.simulate(5.,100) - - nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.995500, 4) - assert exp_sim.statistics["nfcnjacs"] > 0 - - @testattr(stddist = True) - def test_h(self): - - nose.tools.assert_almost_equal(self.simulator.h, 0.01) - self.simulator.h = 1.0 - nose.tools.assert_almost_equal(self.simulator.h, 1.0) - nose.tools.assert_raises(AssimuloException, self.simulator._set_h, [1]) - - @testattr(stddist = True) - def test_time_event(self): - f = lambda t,y: N.array(1.0) - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Explicit_Problem(f,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = ImplicitEuler(exp_mod) - exp_sim(5.,100) - - assert nevent == 5 - - @testattr(stddist = True) - def test_integrator(self): - """ - This tests the functionality of using the normal mode. - """ - values = self.simulator.simulate(1) - - nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) - nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) - - @testattr(stddist = True) - def test_step(self): - """ - This tests the functionality of using one step mode. - """ - self.simulator.report_continuously = True - - self.simulator.h = 0.1 - self.simulator.simulate(1) - - nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) - nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) - - @testattr(stddist = True) - def test_stiff_problem(self): - f = lambda t,y: -15.0*y - y0 = 1.0 - - problem = Explicit_Problem(f, y0) - simulator = ImplicitEuler(problem) - - t,y = simulator.simulate(1) - - y_correct = lambda t: N.exp(-15*t) - - abs_err = N.abs(y[:,0]-y_correct(N.array(t))) - assert N.max(abs_err) < 0.1 - - @testattr(stddist = True) - def test_switches(self): - """ - This tests that the switches are actually turned when override. - """ - f = lambda t,x,sw: N.array([1.0]) - state_events = lambda t,x,sw: N.array([x[0]-1.]) - def handle_event(solver, event_info): - solver.sw = [False] #Override the switches to point to another instance - - mod = Explicit_Problem(f,[0.0]) - mod.sw0 = [True] - - mod.state_events = state_events - mod.handle_event = handle_event - - sim = ImplicitEuler(mod) - assert sim.sw[0] == True - sim.simulate(3) - assert sim.sw[0] == False +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import nose +from assimulo import testattr +from assimulo.solvers.euler import * +from assimulo.problem import Explicit_Problem +from assimulo.exception import * +import scipy.sparse as sp + +class Extended_Problem(Explicit_Problem): + + #Sets the initial conditons directly into the problem + y0 = [0.0, -1.0, 0.0] + sw0 = [False,True,True] + event_array = N.array([0.0,0.0,0.0]) + rhs_array = N.array([0.0,0.0,0.0]) + + #The right-hand-side function (rhs) + def rhs(self,t,y,sw): + """ + This is our function we are trying to simulate. During simulation + the parameter sw should be fixed so that our function is continuous + over the interval. The parameters sw should only be changed when the + integrator has stopped. + """ + self.rhs_array[0] = (1.0 if sw[0] else -1.0) + self.rhs_array[1] = 0.0 + self.rhs_array[2] = 0.0 + + return self.rhs_array + + #Sets a name to our function + name = 'ODE with discontinuities and a function with consistency problem' + + #The event function + def state_events(self,t,y,sw): + """ + This is our function that keeps track of our events. When the sign + of any of the events has changed, we have an event. + """ + self.event_array[0] = y[1] - 1.0 + self.event_array[1] = -y[2] + 1.0 + self.event_array[2] = -t + 1.0 + + return self.event_array + + #Responsible for handling the events. + def handle_event(self, solver, event_info): + """ + Event handling. This functions is called when Assimulo finds an event as + specified by the event functions. + """ + event_info = event_info[0] #We only look at the state events information. + while True: #Event Iteration + self.event_switch(solver, event_info) #Turns the switches + + b_mode = self.state_events(solver.t, solver.y, solver.sw).copy() + self.init_mode(solver) #Pass in the solver to the problem specified init_mode + a_mode = self.state_events(solver.t, solver.y, solver.sw).copy() + + event_info = self.check_eIter(b_mode, a_mode) + + if not True in event_info: #Breaks the iteration loop + break + + #Helper function for handle_event + def event_switch(self, solver, event_info): + """ + Turns the switches. + """ + for i in range(len(event_info)): #Loop across all event functions + if event_info[i] != 0: + solver.sw[i] = not solver.sw[i] #Turn the switch + + #Helper function for handle_event + def check_eIter(self, before, after): + """ + Helper function for handle_event to determine if we have event + iteration. + + Input: Values of the event indicator functions (state_events) + before and after we have changed mode of operations. + """ + + eIter = [False]*len(before) + + for i in range(len(before)): + if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): + eIter[i] = True + + return eIter + + def init_mode(self, solver): + """ + Initialize the DAE with the new conditions. + """ + solver.y[1] = (-1.0 if solver.sw[1] else 3.0) + solver.y[2] = (0.0 if solver.sw[2] else 2.0) + + +class Test_Explicit_Euler: + + def setUp(self): + """ + This function sets up the test case. + """ + f = lambda t,y: 1.0 + y0 = 1.0 + + self.problem = Explicit_Problem(f, y0) + self.simulator = ExplicitEuler(self.problem) + + @testattr(stddist = True) + def test_event_localizer(self): + exp_mod = Extended_Problem() #Create the problem + + exp_sim = ExplicitEuler(exp_mod) #Create the solver + + exp_sim.verbosity = 0 + exp_sim.report_continuously = True + + #Simulate + t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points + + #Basic test + nose.tools.assert_almost_equal(y[-1][0],8.0) + nose.tools.assert_almost_equal(y[-1][1],3.0) + nose.tools.assert_almost_equal(y[-1][2],2.0) + + @testattr(stddist = True) + def test_h(self): + + nose.tools.assert_almost_equal(self.simulator.h, 0.01) + self.simulator.h = 1.0 + nose.tools.assert_almost_equal(self.simulator.h, 1.0) + nose.tools.assert_raises(AssimuloException, self.simulator._set_h, [1]) + + @testattr(stddist = True) + def test_time_event(self): + f = lambda t,y: N.array(1.0) + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Explicit_Problem(f,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = ExplicitEuler(exp_mod) + exp_sim(5.,100) + + assert nevent == 5 + + @testattr(stddist = True) + def test_integrator(self): + """ + This tests the functionality of using the normal mode. + """ + values = self.simulator.simulate(1) + + nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) + nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) + + @testattr(stddist = True) + def test_step(self): + """ + This tests the functionality of using one step mode. + """ + self.simulator.report_continuously = True + + self.simulator.h = 0.1 + self.simulator.simulate(1) + + nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) + nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) + + @testattr(stddist = True) + def test_exception(self): + """ + This tests that exceptions are no caught when evaluating the RHS in ExpEuler. + """ + def f(t,y): + raise NotImplementedError + + prob = Explicit_Problem(f,0.0) + sim = ExplicitEuler(prob) + + nose.tools.assert_raises(NotImplementedError, sim.simulate, 1.0) + + @testattr(stddist = True) + def test_switches(self): + """ + This tests that the switches are actually turned when override. + """ + f = lambda t,x,sw: N.array([1.0]) + state_events = lambda t,x,sw: N.array([x[0]-1.]) + def handle_event(solver, event_info): + solver.sw = [False] #Override the switches to point to another instance + + mod = Explicit_Problem(f,[0.0]) + mod.sw0 = [True] + + mod.state_events = state_events + mod.handle_event = handle_event + + sim = ExplicitEuler(mod) + assert sim.sw[0] == True + sim.simulate(3) + assert sim.sw[0] == False + +class Test_Implicit_Euler: + + def setUp(self): + """ + This function sets up the test case. + """ + f = lambda t,y: 1.0 + y0 = 1.0 + + self.problem = Explicit_Problem(f, y0) + self.simulator = ImplicitEuler(self.problem) + + @testattr(stddist = True) + def test_reset_statistics(self): + assert self.simulator.statistics["nsteps"] == 0 + + self.simulator.simulate(5) + nsteps = self.simulator.statistics["nsteps"] + self.simulator.simulate(6) + + assert self.simulator.statistics["nsteps"] < nsteps + + @testattr(stddist = True) + def test_usejac_csc_matrix(self): + """ + This tests the functionality of the property usejac. + """ + f = lambda t,x: N.array([x[1], -9.82]) #Defines the rhs + jac = lambda t,x: sp.csc_matrix(N.array([[0.,1.],[0.,0.]])) #Defines the jacobian + + exp_mod = Explicit_Problem(f, [1.0,0.0]) + exp_mod.jac = jac + + exp_sim = ImplicitEuler(exp_mod) + exp_sim.simulate(5.,100) + + assert exp_sim.statistics["nfcnjacs"] == 0 + nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.995500, 4) + + exp_sim.reset() + exp_sim.usejac=False + exp_sim.simulate(5.,100) + + nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.995500, 4) + assert exp_sim.statistics["nfcnjacs"] > 0 + + @testattr(stddist = True) + def test_h(self): + + nose.tools.assert_almost_equal(self.simulator.h, 0.01) + self.simulator.h = 1.0 + nose.tools.assert_almost_equal(self.simulator.h, 1.0) + nose.tools.assert_raises(AssimuloException, self.simulator._set_h, [1]) + + @testattr(stddist = True) + def test_time_event(self): + f = lambda t,y: N.array(1.0) + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Explicit_Problem(f,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = ImplicitEuler(exp_mod) + exp_sim(5.,100) + + assert nevent == 5 + + @testattr(stddist = True) + def test_integrator(self): + """ + This tests the functionality of using the normal mode. + """ + values = self.simulator.simulate(1) + + nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) + nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) + + @testattr(stddist = True) + def test_step(self): + """ + This tests the functionality of using one step mode. + """ + self.simulator.report_continuously = True + + self.simulator.h = 0.1 + self.simulator.simulate(1) + + nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) + nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) + + @testattr(stddist = True) + def test_stiff_problem(self): + f = lambda t,y: -15.0*y + y0 = 1.0 + + problem = Explicit_Problem(f, y0) + simulator = ImplicitEuler(problem) + + t,y = simulator.simulate(1) + + y_correct = lambda t: N.exp(-15*t) + + abs_err = N.abs(y[:,0]-y_correct(N.array(t))) + assert N.max(abs_err) < 0.1 + + @testattr(stddist = True) + def test_switches(self): + """ + This tests that the switches are actually turned when override. + """ + f = lambda t,x,sw: N.array([1.0]) + state_events = lambda t,x,sw: N.array([x[0]-1.]) + def handle_event(solver, event_info): + solver.sw = [False] #Override the switches to point to another instance + + mod = Explicit_Problem(f,[0.0]) + mod.sw0 = [True] + + mod.state_events = state_events + mod.handle_event = handle_event + + sim = ImplicitEuler(mod) + assert sim.sw[0] == True + sim.simulate(3) + assert sim.sw[0] == False diff --git a/tests/solvers/test_glimda.py b/tests/solvers/test_glimda.py index cf7f3b6a..1974f16c 100644 --- a/tests/solvers/test_glimda.py +++ b/tests/solvers/test_glimda.py @@ -1,196 +1,196 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import nose -from assimulo import testattr -from assimulo.solvers import GLIMDA -from assimulo.problem import Implicit_Problem, Explicit_Problem -from assimulo.exception import * - -import numpy as N - -class Test_GLIMDA: - """ - Tests the GLIMDA solver. - """ - def setUp(self): - """ - This sets up the test case. - """ - #Define the residual - def f(t,y,yd): - eps = 1.e-6 - my = 1./eps - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - res_0 = yd[0]-yd_0 - res_1 = yd[1]-yd_1 - - return N.array([res_0,res_1]) - - y0 = [2.0,-0.6] #Initial conditions - yd0 = [-.6,-200000.] - - #Define an Assimulo problem - self.mod = Implicit_Problem(f,y0,yd0) - self.mod_t0 = Implicit_Problem(f,y0,yd0,1.0) - - #Define an explicit solver - self.sim = GLIMDA(self.mod) #Create a Radau5 solve - self.sim_t0 = GLIMDA(self.mod_t0) - - #Sets the parameters - self.sim.atol = 1e-4 #Default 1e-6 - self.sim.rtol = 1e-4 #Default 1e-6 - self.sim.inith = 1.e-4 #Initial step-size - - @testattr(stddist = True) - def test_simulate_explicit(self): - """ - Test a simulation of an explicit problem using GLIMDA. - """ - f = lambda t,y:N.array(-y) - y0 = [1.0] - - problem = Explicit_Problem(f,y0) - simulator = GLIMDA(problem) - - assert simulator.yd0[0] == -simulator.y0[0] - - t,y = simulator.simulate(1.0) - - nose.tools.assert_almost_equal(float(y[-1]), float(N.exp(-1.0)),4) - - @testattr(stddist = True) - def test_maxord(self): - """ - Tests the maximum order of GLIMDA. - """ - assert self.sim.maxord == 3 #Default - assert self.sim.options["maxord"] == 3 - - self.sim.maxord = 2 - - assert self.sim.maxord == 2 #Default - assert self.sim.options["maxord"] == 2 - - nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_maxord, 4) - nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_maxord, 0) - - @testattr(stddist = True) - def test_minord(self): - """ - Tests the minimum order of GLIMDA. - """ - assert self.sim.minord == 1 #Default - assert self.sim.options["minord"] == 1 - - self.sim.minord = 2 - - assert self.sim.minord == 2 #Default - assert self.sim.options["minord"] == 2 - - nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_minord, 4) - nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_minord, 0) - - @testattr(stddist = True) - def test_maxsteps(self): - """ - Tests the maximum allowed steps of GLIMDA - """ - assert self.sim.maxsteps == 100000 - assert self.sim.options["maxsteps"] == 100000 - - self.sim.maxsteps = 100 - - assert self.sim.maxsteps == 100 - assert self.sim.options["maxsteps"] == 100 - - nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_maxsteps, -1) - - @testattr(stddist = True) - def test_newt(self): - """ - Tests the maximum allowed number of Newton iterations GLIMDA - """ - assert self.sim.newt == 5 - assert self.sim.options["newt"] == 5 - - self.sim.newt = 3 - - assert self.sim.newt == 3 - assert self.sim.options["newt"] == 3 - - nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_newt, -1) - - @testattr(stddist = True) - def test_minh(self): - """ - Tests the minimum stepsize of GLIMDA. - """ - assert self.sim.minh == N.finfo(N.double).eps - assert self.sim.options["minh"] == N.finfo(N.double).eps - - self.sim.minh = 1e-5 - - assert self.sim.minh == 1e-5 - assert self.sim.options["minh"] == 1e-5 - - @testattr(stddist = True) - def test_order(self): - """ - Tests the order of GLIMDA. - """ - assert self.sim.order == 0 - assert self.sim.options["order"] == 0 - - self.sim.order = 1 - - assert self.sim.order == 1 - assert self.sim.options["order"] == 1 - - nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_order, -1) - - @testattr(stddist = True) - def test_maxh(self): - """ - Tests the maximum stepsize of GLIMDA. - """ - assert self.sim.maxh == N.inf - assert self.sim.options["maxh"] == N.inf - - self.sim.maxh = 1e5 - - assert self.sim.maxh == 1e5 - assert self.sim.options["maxh"] == 1e5 - - @testattr(stddist = True) - def test_maxretry(self): - """ - Tests the maximum number of retries of GLIMDA. - """ - assert self.sim.maxretry == 15 - assert self.sim.options["maxretry"] == 15 - - self.sim.maxretry = 10 - - assert self.sim.maxretry == 10 - assert self.sim.options["maxretry"] == 10 - - nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_maxretry, -1) - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import nose +from assimulo import testattr +from assimulo.solvers import GLIMDA +from assimulo.problem import Implicit_Problem, Explicit_Problem +from assimulo.exception import * + +import numpy as N + +class Test_GLIMDA: + """ + Tests the GLIMDA solver. + """ + def setUp(self): + """ + This sets up the test case. + """ + #Define the residual + def f(t,y,yd): + eps = 1.e-6 + my = 1./eps + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + res_0 = yd[0]-yd_0 + res_1 = yd[1]-yd_1 + + return N.array([res_0,res_1]) + + y0 = [2.0,-0.6] #Initial conditions + yd0 = [-.6,-200000.] + + #Define an Assimulo problem + self.mod = Implicit_Problem(f,y0,yd0) + self.mod_t0 = Implicit_Problem(f,y0,yd0,1.0) + + #Define an explicit solver + self.sim = GLIMDA(self.mod) #Create a Radau5 solve + self.sim_t0 = GLIMDA(self.mod_t0) + + #Sets the parameters + self.sim.atol = 1e-4 #Default 1e-6 + self.sim.rtol = 1e-4 #Default 1e-6 + self.sim.inith = 1.e-4 #Initial step-size + + @testattr(stddist = True) + def test_simulate_explicit(self): + """ + Test a simulation of an explicit problem using GLIMDA. + """ + f = lambda t,y:N.array(-y) + y0 = [1.0] + + problem = Explicit_Problem(f,y0) + simulator = GLIMDA(problem) + + assert simulator.yd0[0] == -simulator.y0[0] + + t,y = simulator.simulate(1.0) + + nose.tools.assert_almost_equal(float(y[-1]), float(N.exp(-1.0)),4) + + @testattr(stddist = True) + def test_maxord(self): + """ + Tests the maximum order of GLIMDA. + """ + assert self.sim.maxord == 3 #Default + assert self.sim.options["maxord"] == 3 + + self.sim.maxord = 2 + + assert self.sim.maxord == 2 #Default + assert self.sim.options["maxord"] == 2 + + nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_maxord, 4) + nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_maxord, 0) + + @testattr(stddist = True) + def test_minord(self): + """ + Tests the minimum order of GLIMDA. + """ + assert self.sim.minord == 1 #Default + assert self.sim.options["minord"] == 1 + + self.sim.minord = 2 + + assert self.sim.minord == 2 #Default + assert self.sim.options["minord"] == 2 + + nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_minord, 4) + nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_minord, 0) + + @testattr(stddist = True) + def test_maxsteps(self): + """ + Tests the maximum allowed steps of GLIMDA + """ + assert self.sim.maxsteps == 100000 + assert self.sim.options["maxsteps"] == 100000 + + self.sim.maxsteps = 100 + + assert self.sim.maxsteps == 100 + assert self.sim.options["maxsteps"] == 100 + + nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_maxsteps, -1) + + @testattr(stddist = True) + def test_newt(self): + """ + Tests the maximum allowed number of Newton iterations GLIMDA + """ + assert self.sim.newt == 5 + assert self.sim.options["newt"] == 5 + + self.sim.newt = 3 + + assert self.sim.newt == 3 + assert self.sim.options["newt"] == 3 + + nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_newt, -1) + + @testattr(stddist = True) + def test_minh(self): + """ + Tests the minimum stepsize of GLIMDA. + """ + assert self.sim.minh == N.finfo(N.double).eps + assert self.sim.options["minh"] == N.finfo(N.double).eps + + self.sim.minh = 1e-5 + + assert self.sim.minh == 1e-5 + assert self.sim.options["minh"] == 1e-5 + + @testattr(stddist = True) + def test_order(self): + """ + Tests the order of GLIMDA. + """ + assert self.sim.order == 0 + assert self.sim.options["order"] == 0 + + self.sim.order = 1 + + assert self.sim.order == 1 + assert self.sim.options["order"] == 1 + + nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_order, -1) + + @testattr(stddist = True) + def test_maxh(self): + """ + Tests the maximum stepsize of GLIMDA. + """ + assert self.sim.maxh == N.inf + assert self.sim.options["maxh"] == N.inf + + self.sim.maxh = 1e5 + + assert self.sim.maxh == 1e5 + assert self.sim.options["maxh"] == 1e5 + + @testattr(stddist = True) + def test_maxretry(self): + """ + Tests the maximum number of retries of GLIMDA. + """ + assert self.sim.maxretry == 15 + assert self.sim.options["maxretry"] == 15 + + self.sim.maxretry = 10 + + assert self.sim.maxretry == 10 + assert self.sim.options["maxretry"] == 10 + + nose.tools.assert_raises(GLIMDA_Exception, self.sim._set_maxretry, -1) + diff --git a/tests/solvers/test_odepack.py b/tests/solvers/test_odepack.py index 91b6063a..f758c396 100644 --- a/tests/solvers/test_odepack.py +++ b/tests/solvers/test_odepack.py @@ -1,334 +1,334 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import nose -import numpy.testing -from assimulo import testattr -from assimulo.lib.odepack import dsrcar, dcfode -from assimulo.solvers import LSODAR, odepack -from assimulo.problem import Explicit_Problem -from assimulo.exception import * - -import numpy as N -import scipy.sparse as sp - -class Extended_Problem(Explicit_Problem): - - #Sets the initial conditons directly into the problem - y0 = [0.0, -1.0, 0.0] - sw0 = [False,True,True] - event_array = N.array([0.0,0.0,0.0]) - rhs_array = N.array([0.0,0.0,0.0]) - - #The right-hand-side function (rhs) - def rhs(self,t,y,sw): - """ - This is our function we are trying to simulate. During simulation - the parameter sw should be fixed so that our function is continuous - over the interval. The parameters sw should only be changed when the - integrator has stopped. - """ - self.rhs_array[0] = (1.0 if sw[0] else -1.0) - self.rhs_array[1] = 0.0 - self.rhs_array[2] = 0.0 - - return self.rhs_array - - #Sets a name to our function - name = 'ODE with discontinuities and a function with consistency problem' - - #The event function - def state_events(self,t,y,sw): - """ - This is our function that keeps track of our events. When the sign - of any of the events has changed, we have an event. - """ - self.event_array[0] = y[1] - 1.0 - self.event_array[1] = -y[2] + 1.0 - self.event_array[2] = -t + 1.0 - - return self.event_array - - #Responsible for handling the events. - def handle_event(self, solver, event_info): - """ - Event handling. This functions is called when Assimulo finds an event as - specified by the event functions. - """ - event_info = event_info[0] #We only look at the state events information. - while True: #Event Iteration - self.event_switch(solver, event_info) #Turns the switches - - b_mode = self.state_events(solver.t, solver.y, solver.sw).copy() - self.init_mode(solver) #Pass in the solver to the problem specified init_mode - a_mode = self.state_events(solver.t, solver.y, solver.sw).copy() - - event_info = self.check_eIter(b_mode, a_mode) - - if not True in event_info: #Breaks the iteration loop - break - - #Helper function for handle_event - def event_switch(self, solver, event_info): - """ - Turns the switches. - """ - for i in range(len(event_info)): #Loop across all event functions - if event_info[i] != 0: - solver.sw[i] = not solver.sw[i] #Turn the switch - - #Helper function for handle_event - def check_eIter(self, before, after): - """ - Helper function for handle_event to determine if we have event - iteration. - - Input: Values of the event indicator functions (state_events) - before and after we have changed mode of operations. - """ - - eIter = [False]*len(before) - - for i in range(len(before)): - if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): - eIter[i] = True - - return eIter - - def init_mode(self, solver): - """ - Initialize the DAE with the new conditions. - """ - solver.y[1] = (-1.0 if solver.sw[1] else 3.0) - solver.y[2] = (0.0 if solver.sw[2] else 2.0) - - -class Test_LSODAR: - """ - Tests the LSODAR solver. - """ - def setUp(self): - """ - This sets up the test case. - """ - def f(t,y): - eps = 1.e-6 - my = 1./eps - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - return N.array([yd_0,yd_1]) - - def jac(t,y): - eps = 1.e-6 - my = 1./eps - J = N.zeros([2,2]) - - J[0,0]=0. - J[0,1]=1. - J[1,0]=my*(-2.*y[0]*y[1]-1.) - J[1,1]=my*(1.-y[0]**2) - - return J - - def jac_sparse(t,y): - eps = 1.e-6 - my = 1./eps - J = N.zeros([2,2]) - - J[0,0]=0. - J[0,1]=1. - J[1,0]=my*(-2.*y[0]*y[1]-1.) - J[1,1]=my*(1.-y[0]**2) - - return sp.csc_matrix(J) - - #Define an Assimulo problem - y0 = [2.0,-0.6] #Initial conditions - - exp_mod = Explicit_Problem(f,y0) - exp_mod_t0 = Explicit_Problem(f,y0,1.0) - exp_mod_sp = Explicit_Problem(f,y0) - - exp_mod.jac = jac - exp_mod_sp.jac = jac_sparse - self.mod = exp_mod - - #Define an explicit solver - self.sim = LSODAR(exp_mod) #Create a LSODAR solve - self.sim_sp = LSODAR(exp_mod_sp) - - #Sets the parameters - self.sim.atol = 1e-6 #Default 1e-6 - self.sim.rtol = 1e-6 #Default 1e-6 - self.sim.usejac = False - - @testattr(stddist = True) - def test_event_localizer(self): - exp_mod = Extended_Problem() #Create the problem - - exp_sim = LSODAR(exp_mod) #Create the solver - - exp_sim.verbosity = 0 - exp_sim.report_continuously = True - - #Simulate - t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points - - #Basic test - nose.tools.assert_almost_equal(y[-1][0],8.0) - nose.tools.assert_almost_equal(y[-1][1],3.0) - nose.tools.assert_almost_equal(y[-1][2],2.0) - - @testattr(stddist = True) - def test_simulation(self): - """ - This tests the LSODAR with a simulation of the van der pol problem. - """ - self.sim.simulate(1.) #Simulate 2 seconds - - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) - @testattr(stddist = True) - def test_setcoefficients(self): - elco,tesco=dcfode(1) - nose.tools.assert_almost_equal(elco[0,2],5./12.,9) # AM-2 - nose.tools.assert_almost_equal(tesco[0,2],2.,9) # AM-2 error coeff - @testattr(stddist = True) - def test_readcommon(self): - """ - This tests the LSODAR's subroutine dsrcar (read functionality). - """ - self.sim.simulate(1.) #Simulate 2 seconds - r=N.ones((245,),'d') - i=N.ones((55,),'i') - dsrcar(r,i,1) - nose.tools.assert_almost_equal(r[217], 2.22044605e-16, 20) - nose.tools.assert_equal(i[36], 3) - - @testattr(stddist = True) - def test_writereadcommon(self): - """ - This tests the LSODAR's subroutine dsrcar (write and read functionality). - """ - r=N.ones((245,),'d') - i=N.ones((55,),'i') - dsrcar(r,i,2) - r[0]=100. - i[0]=10 - dsrcar(r,i,1) - nose.tools.assert_almost_equal(r[0], 1., 4) - nose.tools.assert_equal(i[0], 1) - def test_rkstarter(self): - """ - This test checks the correctness of the Nordsieck array generated - from a RK starter - """ - pass - """ - A=N.array([[0.,1.],[-4.,0.]]) - def f(t,x,sw0): - return N.dot(A,N.array(x)) - H = 1.e-8 - # Compute the exact solution at h=0,H/4,H/2,3H/4,H - T=N.array([0.,H/4.,H/2.,3./4.*H,H]) - y0=N.array([1.,0.]) - from scipy.linalg import expm - exact=N.array([N.dot(expm(A*t),y0) for t in T]) - #polynomial interpolation - from scipy import polyfit - coeff = polyfit(T,exact,4) - d1coeff=N.array([4,3,2,1]).reshape(-1,1)*coeff[:-1,:] - d2coeff=N.array([3,2,1]).reshape(-1,1)*d1coeff[:-1,:] - d3coeff=N.array([2,1]).reshape(-1,1)*d2coeff[:-1,:] - d4coeff=N.array([1]).reshape(-1,1)*d3coeff[:-1,:] - h=H/4. - nordsieck_at_0=N.array([coeff[-1,:],h*d1coeff[-1,:],h**2/2.*d2coeff[-1,:], - h**3/6.*d3coeff[-1,:],h**4/24.*d4coeff[-1,:]]) - rkNordsieck=odepack.RKStarterNordsieck(f,H) - computed=rkNordsieck(0,y0) - numpy.testing.assert_allclose(computed[1], nordsieck_at_0, atol=H/100., verbose=True) - """ - - @testattr(stddist = True) - def test_interpol(self): - # a with interpolation and report_continuously - self.sim.report_continuously=True - t_sol,y_sol=self.sim.simulate(1.,ncp_list=[0.5]) - self.sim.reset() - t_sol1,y_sol1=self.sim.simulate(0.5) - ind05=N.nonzero(N.array(t_sol)==0.5)[0][0] - #print y_sol[ind05],y_sol1[-1] - nose.tools.assert_almost_equal(y_sol[ind05,0],y_sol1[-1,0],6) - - - def test_simulation_with_jac(self): - """ - This tests the LSODAR with a simulation of the van der pol problem. - """ - self.sim.usejac = True - self.sim.simulate(1.) #Simulate 2 seconds - - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) - - @testattr(stddist = True) - def test_simulation_ncp(self): - self.sim.simulate(1.,100) #Simulate 2 seconds - - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) - - @testattr(stddist = True) - def test_usejac_csc_matrix(self): - self.sim_sp.usejac = True - - self.sim_sp.simulate(2.) #Simulate 2 seconds - - assert self.sim_sp.statistics["nfcnjacs"] == 0 - - nose.tools.assert_almost_equal(self.sim_sp.y_sol[-1][0], 1.7061680350, 4) - - @testattr(stddist = True) - def test_simulation_ncp_list(self): - self.sim.simulate(1.,ncp_list=[0.5]) #Simulate 2 seconds - - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) - - @testattr(stddist = True) - def test_maxh(self): - - self.sim.hmax = 1.0 - assert self.sim.options["maxh"] == 1.0 - assert self.sim.maxh == 1.0 - - self.sim.maxh = 2.0 - assert self.sim.options["maxh"] == 2.0 - assert self.sim.maxh == 2.0 - - @testattr(stddist = True) - def test_simulation_ncp_list_2(self): - self.sim.simulate(1.,ncp_list=[0.5,4]) #Simulate 2 seconds - - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) - - @testattr(stddist = True) - def test_simulation_ncp_with_jac(self): - """ - Test a simulation with ncp. - """ - self.sim.usejac= True - self.sim.simulate(1.,100) #Simulate 2 seconds - - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import nose +import numpy.testing +from assimulo import testattr +from assimulo.lib.odepack import dsrcar, dcfode +from assimulo.solvers import LSODAR, odepack +from assimulo.problem import Explicit_Problem +from assimulo.exception import * + +import numpy as N +import scipy.sparse as sp + +class Extended_Problem(Explicit_Problem): + + #Sets the initial conditons directly into the problem + y0 = [0.0, -1.0, 0.0] + sw0 = [False,True,True] + event_array = N.array([0.0,0.0,0.0]) + rhs_array = N.array([0.0,0.0,0.0]) + + #The right-hand-side function (rhs) + def rhs(self,t,y,sw): + """ + This is our function we are trying to simulate. During simulation + the parameter sw should be fixed so that our function is continuous + over the interval. The parameters sw should only be changed when the + integrator has stopped. + """ + self.rhs_array[0] = (1.0 if sw[0] else -1.0) + self.rhs_array[1] = 0.0 + self.rhs_array[2] = 0.0 + + return self.rhs_array + + #Sets a name to our function + name = 'ODE with discontinuities and a function with consistency problem' + + #The event function + def state_events(self,t,y,sw): + """ + This is our function that keeps track of our events. When the sign + of any of the events has changed, we have an event. + """ + self.event_array[0] = y[1] - 1.0 + self.event_array[1] = -y[2] + 1.0 + self.event_array[2] = -t + 1.0 + + return self.event_array + + #Responsible for handling the events. + def handle_event(self, solver, event_info): + """ + Event handling. This functions is called when Assimulo finds an event as + specified by the event functions. + """ + event_info = event_info[0] #We only look at the state events information. + while True: #Event Iteration + self.event_switch(solver, event_info) #Turns the switches + + b_mode = self.state_events(solver.t, solver.y, solver.sw).copy() + self.init_mode(solver) #Pass in the solver to the problem specified init_mode + a_mode = self.state_events(solver.t, solver.y, solver.sw).copy() + + event_info = self.check_eIter(b_mode, a_mode) + + if not True in event_info: #Breaks the iteration loop + break + + #Helper function for handle_event + def event_switch(self, solver, event_info): + """ + Turns the switches. + """ + for i in range(len(event_info)): #Loop across all event functions + if event_info[i] != 0: + solver.sw[i] = not solver.sw[i] #Turn the switch + + #Helper function for handle_event + def check_eIter(self, before, after): + """ + Helper function for handle_event to determine if we have event + iteration. + + Input: Values of the event indicator functions (state_events) + before and after we have changed mode of operations. + """ + + eIter = [False]*len(before) + + for i in range(len(before)): + if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): + eIter[i] = True + + return eIter + + def init_mode(self, solver): + """ + Initialize the DAE with the new conditions. + """ + solver.y[1] = (-1.0 if solver.sw[1] else 3.0) + solver.y[2] = (0.0 if solver.sw[2] else 2.0) + + +class Test_LSODAR: + """ + Tests the LSODAR solver. + """ + def setUp(self): + """ + This sets up the test case. + """ + def f(t,y): + eps = 1.e-6 + my = 1./eps + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + return N.array([yd_0,yd_1]) + + def jac(t,y): + eps = 1.e-6 + my = 1./eps + J = N.zeros([2,2]) + + J[0,0]=0. + J[0,1]=1. + J[1,0]=my*(-2.*y[0]*y[1]-1.) + J[1,1]=my*(1.-y[0]**2) + + return J + + def jac_sparse(t,y): + eps = 1.e-6 + my = 1./eps + J = N.zeros([2,2]) + + J[0,0]=0. + J[0,1]=1. + J[1,0]=my*(-2.*y[0]*y[1]-1.) + J[1,1]=my*(1.-y[0]**2) + + return sp.csc_matrix(J) + + #Define an Assimulo problem + y0 = [2.0,-0.6] #Initial conditions + + exp_mod = Explicit_Problem(f,y0) + exp_mod_t0 = Explicit_Problem(f,y0,1.0) + exp_mod_sp = Explicit_Problem(f,y0) + + exp_mod.jac = jac + exp_mod_sp.jac = jac_sparse + self.mod = exp_mod + + #Define an explicit solver + self.sim = LSODAR(exp_mod) #Create a LSODAR solve + self.sim_sp = LSODAR(exp_mod_sp) + + #Sets the parameters + self.sim.atol = 1e-6 #Default 1e-6 + self.sim.rtol = 1e-6 #Default 1e-6 + self.sim.usejac = False + + @testattr(stddist = True) + def test_event_localizer(self): + exp_mod = Extended_Problem() #Create the problem + + exp_sim = LSODAR(exp_mod) #Create the solver + + exp_sim.verbosity = 0 + exp_sim.report_continuously = True + + #Simulate + t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points + + #Basic test + nose.tools.assert_almost_equal(y[-1][0],8.0) + nose.tools.assert_almost_equal(y[-1][1],3.0) + nose.tools.assert_almost_equal(y[-1][2],2.0) + + @testattr(stddist = True) + def test_simulation(self): + """ + This tests the LSODAR with a simulation of the van der pol problem. + """ + self.sim.simulate(1.) #Simulate 2 seconds + + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) + @testattr(stddist = True) + def test_setcoefficients(self): + elco,tesco=dcfode(1) + nose.tools.assert_almost_equal(elco[0,2],5./12.,9) # AM-2 + nose.tools.assert_almost_equal(tesco[0,2],2.,9) # AM-2 error coeff + @testattr(stddist = True) + def test_readcommon(self): + """ + This tests the LSODAR's subroutine dsrcar (read functionality). + """ + self.sim.simulate(1.) #Simulate 2 seconds + r=N.ones((245,),'d') + i=N.ones((55,),'i') + dsrcar(r,i,1) + nose.tools.assert_almost_equal(r[217], 2.22044605e-16, 20) + nose.tools.assert_equal(i[36], 3) + + @testattr(stddist = True) + def test_writereadcommon(self): + """ + This tests the LSODAR's subroutine dsrcar (write and read functionality). + """ + r=N.ones((245,),'d') + i=N.ones((55,),'i') + dsrcar(r,i,2) + r[0]=100. + i[0]=10 + dsrcar(r,i,1) + nose.tools.assert_almost_equal(r[0], 1., 4) + nose.tools.assert_equal(i[0], 1) + def test_rkstarter(self): + """ + This test checks the correctness of the Nordsieck array generated + from a RK starter + """ + pass + """ + A=N.array([[0.,1.],[-4.,0.]]) + def f(t,x,sw0): + return N.dot(A,N.array(x)) + H = 1.e-8 + # Compute the exact solution at h=0,H/4,H/2,3H/4,H + T=N.array([0.,H/4.,H/2.,3./4.*H,H]) + y0=N.array([1.,0.]) + from scipy.linalg import expm + exact=N.array([N.dot(expm(A*t),y0) for t in T]) + #polynomial interpolation + from scipy import polyfit + coeff = polyfit(T,exact,4) + d1coeff=N.array([4,3,2,1]).reshape(-1,1)*coeff[:-1,:] + d2coeff=N.array([3,2,1]).reshape(-1,1)*d1coeff[:-1,:] + d3coeff=N.array([2,1]).reshape(-1,1)*d2coeff[:-1,:] + d4coeff=N.array([1]).reshape(-1,1)*d3coeff[:-1,:] + h=H/4. + nordsieck_at_0=N.array([coeff[-1,:],h*d1coeff[-1,:],h**2/2.*d2coeff[-1,:], + h**3/6.*d3coeff[-1,:],h**4/24.*d4coeff[-1,:]]) + rkNordsieck=odepack.RKStarterNordsieck(f,H) + computed=rkNordsieck(0,y0) + numpy.testing.assert_allclose(computed[1], nordsieck_at_0, atol=H/100., verbose=True) + """ + + @testattr(stddist = True) + def test_interpol(self): + # a with interpolation and report_continuously + self.sim.report_continuously=True + t_sol,y_sol=self.sim.simulate(1.,ncp_list=[0.5]) + self.sim.reset() + t_sol1,y_sol1=self.sim.simulate(0.5) + ind05=N.nonzero(N.array(t_sol)==0.5)[0][0] + #print y_sol[ind05],y_sol1[-1] + nose.tools.assert_almost_equal(y_sol[ind05,0],y_sol1[-1,0],6) + + + def test_simulation_with_jac(self): + """ + This tests the LSODAR with a simulation of the van der pol problem. + """ + self.sim.usejac = True + self.sim.simulate(1.) #Simulate 2 seconds + + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) + + @testattr(stddist = True) + def test_simulation_ncp(self): + self.sim.simulate(1.,100) #Simulate 2 seconds + + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) + + @testattr(stddist = True) + def test_usejac_csc_matrix(self): + self.sim_sp.usejac = True + + self.sim_sp.simulate(2.) #Simulate 2 seconds + + assert self.sim_sp.statistics["nfcnjacs"] == 0 + + nose.tools.assert_almost_equal(self.sim_sp.y_sol[-1][0], 1.7061680350, 4) + + @testattr(stddist = True) + def test_simulation_ncp_list(self): + self.sim.simulate(1.,ncp_list=[0.5]) #Simulate 2 seconds + + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) + + @testattr(stddist = True) + def test_maxh(self): + + self.sim.hmax = 1.0 + assert self.sim.options["maxh"] == 1.0 + assert self.sim.maxh == 1.0 + + self.sim.maxh = 2.0 + assert self.sim.options["maxh"] == 2.0 + assert self.sim.maxh == 2.0 + + @testattr(stddist = True) + def test_simulation_ncp_list_2(self): + self.sim.simulate(1.,ncp_list=[0.5,4]) #Simulate 2 seconds + + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) + + @testattr(stddist = True) + def test_simulation_ncp_with_jac(self): + """ + Test a simulation with ncp. + """ + self.sim.usejac= True + self.sim.simulate(1.,100) #Simulate 2 seconds + + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], -1.863646028, 4) diff --git a/tests/solvers/test_radau5.py b/tests/solvers/test_radau5.py index 7a15a2d2..2cf841cf 100644 --- a/tests/solvers/test_radau5.py +++ b/tests/solvers/test_radau5.py @@ -1,1185 +1,1185 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import nose -from assimulo import testattr -from assimulo.solvers.radau5 import * -from assimulo.solvers.radau5 import Radau5DAE, _Radau5DAE -from assimulo.solvers.radau5 import Radau5ODE, _Radau5ODE -from assimulo.problem import Explicit_Problem -from assimulo.problem import Implicit_Problem -from assimulo.exception import * -from assimulo.lib.radau_core import Radau_Exception, Radau_Common -import scipy.sparse as sp - -class Extended_Problem(Explicit_Problem): - - #Sets the initial conditons directly into the problem - y0 = [0.0, -1.0, 0.0] - sw0 = [False,True,True] - event_array = N.array([0.0,0.0,0.0]) - rhs_array = N.array([0.0,0.0,0.0]) - - #The right-hand-side function (rhs) - def rhs(self,t,y,sw): - """ - This is our function we are trying to simulate. During simulation - the parameter sw should be fixed so that our function is continuous - over the interval. The parameters sw should only be changed when the - integrator has stopped. - """ - self.rhs_array[0] = (1.0 if sw[0] else -1.0) - self.rhs_array[1] = 0.0 - self.rhs_array[2] = 0.0 - - return self.rhs_array - - #Sets a name to our function - name = 'ODE with discontinuities and a function with consistency problem' - - #The event function - def state_events(self,t,y,sw): - """ - This is our function that keeps track of our events. When the sign - of any of the events has changed, we have an event. - """ - self.event_array[0] = y[1] - 1.0 - self.event_array[1] = -y[2] + 1.0 - self.event_array[2] = -t + 1.0 - - return self.event_array - - #Responsible for handling the events. - def handle_event(self, solver, event_info): - """ - Event handling. This functions is called when Assimulo finds an event as - specified by the event functions. - """ - event_info = event_info[0] #We only look at the state events information. - while True: #Event Iteration - self.event_switch(solver, event_info) #Turns the switches - - b_mode = self.state_events(solver.t, solver.y, solver.sw).copy() - self.init_mode(solver) #Pass in the solver to the problem specified init_mode - a_mode = self.state_events(solver.t, solver.y, solver.sw).copy() - - event_info = self.check_eIter(b_mode, a_mode) - - if not True in event_info: #Breaks the iteration loop - break - - #Helper function for handle_event - def event_switch(self, solver, event_info): - """ - Turns the switches. - """ - for i in range(len(event_info)): #Loop across all event functions - if event_info[i] != 0: - solver.sw[i] = not solver.sw[i] #Turn the switch - - #Helper function for handle_event - def check_eIter(self, before, after): - """ - Helper function for handle_event to determine if we have event - iteration. - - Input: Values of the event indicator functions (state_events) - before and after we have changed mode of operations. - """ - - eIter = [False]*len(before) - - for i in range(len(before)): - if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): - eIter[i] = True - - return eIter - - def init_mode(self, solver): - """ - Initialize the DAE with the new conditions. - """ - solver.y[1] = (-1.0 if solver.sw[1] else 3.0) - solver.y[2] = (0.0 if solver.sw[2] else 2.0) - -class Test_Explicit_Radau5: - """ - Tests the explicit Radau solver. - """ - def setUp(self): - """ - This sets up the test case. - """ - def f(t,y): - eps = 1.e-6 - my = 1./eps - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - return N.array([yd_0,yd_1]) - - def jac(t,y): - eps = 1.e-6 - my = 1./eps - J = N.zeros([2,2]) - - J[0,0]=0. - J[0,1]=1. - J[1,0]=my*(-2.*y[0]*y[1]-1.) - J[1,1]=my*(1.-y[0]**2) - - return J - - #Define an Assimulo problem - y0 = [2.0,-0.6] #Initial conditions - - exp_mod = Explicit_Problem(f,y0) - exp_mod_t0 = Explicit_Problem(f,y0,1.0) - - exp_mod.jac = jac - self.mod = exp_mod - - #Define an explicit solver - self.sim = _Radau5ODE(exp_mod) #Create a Radau5 solve - self.sim_t0 = _Radau5ODE(exp_mod_t0) - - #Sets the parameters - self.sim.atol = 1e-4 #Default 1e-6 - self.sim.rtol = 1e-4 #Default 1e-6 - self.sim.inith = 1.e-4 #Initial step-size - self.sim.usejac = False - - @testattr(stddist = True) - def test_event_localizer(self): - exp_mod = Extended_Problem() #Create the problem - - exp_sim = Radau5ODE(exp_mod) #Create the solver - - exp_sim.verbosity = 0 - exp_sim.report_continuously = True - - #Simulate - t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points - - #Basic test - nose.tools.assert_almost_equal(y[-1][0],8.0) - nose.tools.assert_almost_equal(y[-1][1],3.0) - nose.tools.assert_almost_equal(y[-1][2],2.0) - - @testattr(stddist = True) - def test_time_event(self): - f = lambda t,y: [1.0] - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Explicit_Problem(f,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = _Radau5ODE(exp_mod) - exp_sim(5.,100) - - assert nevent == 5 - - @testattr(stddist = True) - def test_init(self): - - #Test both y0 in problem and not. - sim = _Radau5ODE(self.mod) - - assert sim._leny == 2 - - @testattr(stddist = True) - def test_collocation_polynomial(self): - """ - This tests the functionality of the collocation polynomial (communication points) - """ - self.sim.report_continuously = False - - self.sim.simulate(2.,200) #Simulate 2 seconds - - assert self.sim.statistics["nsteps"] < 300 - - #nose.tools.assert_almost_equal(self.sim.y[-2][0], 1.71505001, 4) - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) - - self.sim.report_continuously = True - self.sim.reset() - self.sim.simulate(2.,200) #Simulate 2 seconds - - assert self.sim.statistics["nsteps"] < 300 - - #nose.tools.assert_almost_equal(self.sim.y[-2][0], 1.71505001, 4) - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) - - self.sim_t0.simulate(3.) - nose.tools.assert_almost_equal(self.sim_t0.t_sol[0], 1.0000000, 4) - nose.tools.assert_almost_equal(self.sim_t0.t_sol[-1], 3.0000000, 4) - nose.tools.assert_almost_equal(self.sim_t0.y_sol[-1][0], 1.7061680350, 4) - - @testattr(stddist = True) - def test_simulation(self): - """ - This tests the Radau5 with a simulation of the van der Pol problem. - """ - self.sim.simulate(2.) #Simulate 2 seconds - - assert self.sim.statistics["nsteps"] < 300 - - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) - - @testattr(stddist = True) - def test_simulation_ncp(self): - """ - Test a simulation with ncp. - """ - self.sim.report_continuously = True - - self.sim.simulate(1.0, 200) #Simulate 1 second - assert len(self.sim.t_sol) == 201 - - self.sim.reset() - self.sim.report_continuously = False - - self.sim.simulate(1.0, 200) #Simulate 1 second - assert len(self.sim.t_sol) == 201 - - @testattr(stddist = True) - def test_usejac(self): - """ - This tests the usejac property. - """ - self.sim.usejac = True - - self.sim.simulate(2.) #Simulate 2 seconds - - assert self.sim.statistics["nfcnjacs"] == 0 - - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) - - @testattr(stddist = True) - def test_thet(self): - """ - This tests a negative value of thet. - """ - self.sim.thet = -1 - self.sim.simulate(2.) #Simulate 2 seconds - - assert self.sim.statistics["nsteps"] == self.sim.statistics["njacs"] - - @testattr(stddist = True) - def test_maxh(self): - """ - This tests the maximum step length. - """ - self.sim.maxh = 0.01 - self.sim.simulate(0.5) - assert max(N.diff(self.sim.t_sol))-N.finfo('double').eps <= 0.01 - - @testattr(stddist = True) - def test_newt(self): - """ - This tests the maximum number of newton iterations. - """ - self.sim.newt = 10 - self.sim.simulate(1.0) - - assert self.sim.statistics["nnfails"] == 1 - - @testattr(stddist = True) - def test_safe(self): - """ - This tests the safety factor in the step-size prediction. - """ - self.sim.safe = 0.99 - self.sim.simulate(1.0) - assert self.sim.statistics["nsteps"] < 150 - - @testattr(stddist = True) - def test_reset_statistics(self): - """ - Tests that the statistics are reset. - """ - self.sim.simulate(1.0) - steps = self.sim.statistics["nsteps"] - - self.sim.reset() - self.sim.simulate(1.0) - - assert self.sim.statistics["nsteps"] < steps*1.5 - - @testattr(stddist = True) - def test_atol(self): - """ - This test the absolute tolerance. - """ - self.sim.simulate(1.0) - - steps = self.sim.statistics["nsteps"] - - self.sim.reset() - - self.sim.rtol = 1e-8 - self.sim.atol = 1e-8 - - self.sim.simulate(1.0) - steps2 = self.sim.statistics["nsteps"] - - assert steps2 > steps - - self.sim.reset() - self.sim.atol = [1e-8, 1e-8] - - steps3 = self.sim.statistics["nsteps"] - - assert steps3==steps2 - - nose.tools.assert_raises(Radau_Exception, self.sim._set_atol, [1e-6,1e-6,1e-6]) - -class Test_Explicit_Fortran_Radau5: - """ - Tests the explicit Radau solver. - """ - def setUp(self): - """ - This sets up the test case. - """ - def f(t,y): - eps = 1.e-6 - my = 1./eps - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - return N.array([yd_0,yd_1]) - - def jac(t,y): - eps = 1.e-6 - my = 1./eps - J = N.zeros([2,2]) - - J[0,0]=0. - J[0,1]=1. - J[1,0]=my*(-2.*y[0]*y[1]-1.) - J[1,1]=my*(1.-y[0]**2) - - return J - - def jac_sparse(t,y): - eps = 1.e-6 - my = 1./eps - J = N.zeros([2,2]) - - J[0,0]=0. - J[0,1]=1. - J[1,0]=my*(-2.*y[0]*y[1]-1.) - J[1,1]=my*(1.-y[0]**2) - - return sp.csc_matrix(J) - - #Define an Assimulo problem - y0 = [2.0,-0.6] #Initial conditions - - exp_mod = Explicit_Problem(f,y0) - exp_mod_t0 = Explicit_Problem(f,y0,1.0) - exp_mod_sp = Explicit_Problem(f,y0) - - exp_mod.jac = jac - exp_mod_sp.jac = jac_sparse - self.mod = exp_mod - - #Define an explicit solver - self.sim = Radau5ODE(exp_mod) #Create a Radau5 solve - self.sim_t0 = Radau5ODE(exp_mod_t0) - self.sim_sp = Radau5ODE(exp_mod_sp) - - #Sets the parameters - self.sim.atol = 1e-4 #Default 1e-6 - self.sim.rtol = 1e-4 #Default 1e-6 - self.sim.inith = 1.e-4 #Initial step-size - self.sim.usejac = False - - @testattr(stddist = True) - def test_nbr_fcn_evals_due_to_jac(self): - sim = Radau5ODE(self.mod) - - sim.usejac = False - sim.simulate(1) - - assert sim.statistics["nfcnjacs"] > 0 - - sim = Radau5ODE(self.mod) - sim.simulate(1) - - assert sim.statistics["nfcnjacs"] == 0 - - @testattr(stddist = True) - def test_time_event(self): - f = lambda t,y: [1.0] - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Explicit_Problem(f,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = Radau5ODE(exp_mod) - exp_sim(5.,100) - - assert nevent == 5 - - @testattr(stddist = True) - def test_init(self): - - #Test both y0 in problem and not. - sim = Radau5ODE(self.mod) - - assert sim._leny == 2 - - @testattr(stddist = True) - def test_collocation_polynomial(self): - """ - This tests the functionality of the collocation polynomial (communication points) - """ - self.sim.report_continuously = False - - self.sim.simulate(2.,200) #Simulate 2 seconds - - assert self.sim.statistics["nsteps"] < 300 - - #nose.tools.assert_almost_equal(self.sim.y[-2][0], 1.71505001, 4) - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) - - self.sim.report_continuously = True - self.sim.reset() - self.sim.simulate(2.,200) #Simulate 2 seconds - - assert self.sim.statistics["nsteps"] < 300 - - #nose.tools.assert_almost_equal(self.sim.y[-2][0], 1.71505001, 4) - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) - - self.sim_t0.simulate(3.) - nose.tools.assert_almost_equal(self.sim_t0.t_sol[0], 1.0000000, 4) - nose.tools.assert_almost_equal(self.sim_t0.t_sol[-1], 3.0000000, 4) - nose.tools.assert_almost_equal(self.sim_t0.y_sol[-1][0], 1.7061680350, 4) - - @testattr(stddist = True) - def test_simulation(self): - """ - This tests the Radau5 with a simulation of the van der pol problem. - """ - self.sim.simulate(2.) #Simulate 2 seconds - - assert self.sim.statistics["nsteps"] < 300 - - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) - - @testattr(stddist = True) - def test_simulation_ncp(self): - """ - Test a simulation with ncp. - """ - self.sim.report_continuously = True - - self.sim.simulate(1.0, 200) #Simulate 1 second - assert len(self.sim.t_sol) == 201 - - self.sim.reset() - self.sim.report_continuously = False - - self.sim.simulate(1.0, 200) #Simulate 1 second - assert len(self.sim.t_sol) == 201 - - @testattr(stddist = True) - def test_usejac(self): - """ - This tests the usejac property. - """ - self.sim.usejac = True - - self.sim.simulate(2.) #Simulate 2 seconds - - assert self.sim.statistics["nfcnjacs"] == 0 - - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) - - @testattr(stddist = True) - def test_usejac_csc_matrix(self): - """ - This tests the functionality of the property usejac. - """ - self.sim_sp.usejac = True - - self.sim_sp.simulate(2.) #Simulate 2 seconds - - assert self.sim_sp.statistics["nfcnjacs"] == 0 - - nose.tools.assert_almost_equal(self.sim_sp.y_sol[-1][0], 1.7061680350, 4) - - @testattr(stddist = True) - def test_thet(self): - """ - This tests a negative value of thet. - """ - self.sim.thet = -1 - self.sim.simulate(2.) #Simulate 2 seconds - - assert self.sim.statistics["nsteps"] == self.sim.statistics["njacs"] - - @testattr(stddist = True) - def test_maxh(self): - """ - This tests the maximum step length. - """ - self.sim.maxh = 0.01 - self.sim.simulate(0.5) - assert max(N.diff(self.sim.t_sol))-N.finfo('double').eps <= 0.01 - - @testattr(stddist = True) - def test_newt(self): - """ - This tests the maximum number of newton iterations. - """ - pass - #self.sim.simulate(1.0) - #self.sim.reset() - #self.sim.newt = 10 - #self.sim.simulate(1.0) - - #assert self.sim.statistics["nniterfail"] == 1 - - @testattr(stddist = True) - def test_safe(self): - """ - This tests the safety factor in the step-size prediction. - """ - self.sim.safe = 0.99 - self.sim.simulate(1.0) - assert self.sim.statistics["nsteps"] < 150 - - @testattr(stddist = True) - def test_reset_statistics(self): - """ - Tests that the statistics are reset. - """ - self.sim.simulate(1.0) - steps = self.sim.statistics["nsteps"] - - self.sim.reset() - self.sim.simulate(1.0) - - assert self.sim.statistics["nsteps"] < steps*1.5 - - @testattr(stddist = True) - def test_weighted_error(self): - - def handle_result(solver, t, y): - err = solver.get_weighted_local_errors() - assert len(err) == len(y) - - self.mod.handle_result = handle_result - - #Define an explicit solver - sim = Radau5ODE(self.mod) #Create a Radau5 solve - - sim.get_weighted_local_errors() - - sim.simulate(1) - - - @testattr(stddist = True) - def test_atol(self): - """ - This test the absolute tolerance. - """ - self.sim.simulate(1.0) - - steps = self.sim.statistics["nsteps"] - - self.sim.reset() - - self.sim.rtol = 1e-8 - self.sim.atol = 1e-8 - - self.sim.simulate(1.0) - steps2 = self.sim.statistics["nsteps"] - - assert steps2 > steps - - self.sim.reset() - self.sim.atol = [1e-8, 1e-8] - - steps3 = self.sim.statistics["nsteps"] - - assert steps3==steps2 - - nose.tools.assert_raises(Radau_Exception, self.sim._set_atol, [1e-6,1e-6,1e-6]) - - @testattr(stddist = True) - def test_switches(self): - """ - This tests that the switches are actually turned when override. - """ - f = lambda t,x,sw: N.array([1.0]) - state_events = lambda t,x,sw: N.array([x[0]-1.]) - def handle_event(solver, event_info): - solver.sw = [False] #Override the switches to point to another instance - - mod = Explicit_Problem(f,[0.0]) - mod.sw0 = [True] - - mod.state_events = state_events - mod.handle_event = handle_event - - sim = Radau5ODE(mod) - assert sim.sw[0] == True - sim.simulate(3) - assert sim.sw[0] == False - - -class Test_Implicit_Fortran_Radau5: - """ - Tests the implicit Radau solver. - """ - def setUp(self): - """ - This sets up the test case. - """ - #Define the residual - def f(t,y,yd): - eps = 1.e-6 - my = 1./eps - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - res_0 = yd[0]-yd_0 - res_1 = yd[1]-yd_1 - - return N.array([res_0,res_1]) - - y0 = [2.0,-0.6] #Initial conditions - yd0 = [-.6,-200000.] - - #Define an Assimulo problem - self.mod = Implicit_Problem(f,y0,yd0) - self.mod_t0 = Implicit_Problem(f,y0,yd0,1.0) - - #Define an explicit solver - self.sim = Radau5DAE(self.mod) #Create a Radau5 solve - self.sim_t0 = Radau5DAE(self.mod_t0) - - #Sets the parameters - self.sim.atol = 1e-4 #Default 1e-6 - self.sim.rtol = 1e-4 #Default 1e-6 - self.sim.inith = 1.e-4 #Initial step-size - - @testattr(stddist = True) - def test_nbr_fcn_evals_due_to_jac(self): - sim = Radau5DAE(self.mod) - - sim.usejac = False - sim.simulate(1) - - assert sim.statistics["nfcnjacs"] > 0 - - @testattr(stddist = True) - def test_simulate_explicit(self): - """ - Test a simulation of an explicit problem using Radau5DAE. - """ - f = lambda t,y:N.array(-y) - y0 = [1.0] - - problem = Explicit_Problem(f,y0) - simulator = Radau5DAE(problem) - - assert simulator.yd0[0] == -simulator.y0[0] - - t,y = simulator.simulate(1.0) - - nose.tools.assert_almost_equal(float(y[-1]), float(N.exp(-1.0)),4) - - @testattr(stddist = True) - def test_time_event(self): - f = lambda t,y,yd: y-yd - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,yd,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - #solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Implicit_Problem(f,0.0,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = Radau5DAE(exp_mod) - exp_sim.verbosity = 0 - exp_sim(5.,100) - - assert nevent == 5 - - @testattr(stddist = True) - def test_init(self): - """ - This tests the functionality of Radau5 Implicit Init. - """ - #Test both y0 in problem and not. - - sim = Radau5DAE(self.mod) - - assert sim._leny == 2 - - @testattr(stddist = True) - def test_thet(self): - """ - This tests a negative value of thet. - """ - self.sim.thet = -1 - self.sim.simulate(.5) #Simulate 2 seconds - - assert self.sim.statistics["nsteps"] == self.sim.statistics["njacs"] - - @testattr(stddist = True) - def test_simulation(self): - """ - Test a simulation of the van der Pol equations (1). - """ - #Simulate - self.sim.simulate(2.) #Simulate 2 seconds - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.706272, 3) - - self.sim.reset() - - self.sim.report_continuously = True - - #Simulate - self.sim.simulate(2.) #Simulate 2 seconds - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.706166, 3) - - self.sim_t0.simulate(3.) - nose.tools.assert_almost_equal(self.sim_t0.t_sol[0], 1.0000000, 4) - nose.tools.assert_almost_equal(self.sim_t0.t_sol[-1], 3.0000000, 4) - nose.tools.assert_almost_equal(self.sim_t0.y_sol[-1][0], 1.7061680350, 4) - - @testattr(stddist = True) - def test_simulation_ncp(self): - """ - Test a simulation with ncp. - """ - self.sim.report_continuously = True - - self.sim.simulate(1.0, 200) #Simulate 1 second - assert len(self.sim.t_sol) == 201 - - self.sim.reset() - self.sim.report_continuously = False - - self.sim.simulate(1.0, 200) #Simulate 1 second - assert len(self.sim.t_sol) == 201 - - @testattr(stddist = True) - def test_maxh(self): - """ - Tests implicit radau with maxh. - """ - self.sim.maxh = 0.01 - self.sim.simulate(0.5) - assert max(N.diff(self.sim.t_sol))-N.finfo('double').eps <= 0.01 - - - @testattr(stddist = True) - def test_switches(self): - """ - This tests that the switches are actually turned when override. - """ - res = lambda t,x,xd,sw: N.array([1.0 - xd]) - state_events = lambda t,x,xd,sw: N.array([x[0]-1.]) - def handle_event(solver, event_info): - solver.sw = [False] #Override the switches to point to another instance - - mod = Implicit_Problem(res,[0.0], [1.0]) - mod.sw0 = [True] - - mod.state_events = state_events - mod.handle_event = handle_event - - sim = Radau5DAE(mod) - assert sim.sw[0] == True - sim.simulate(3) - assert sim.sw[0] == False - - -class Test_Implicit_Radau5: - """ - Tests the implicit Radau solver. - """ - def setUp(self): - """ - This sets up the test case. - """ - #Define the residual - def f(t,y,yd): - eps = 1.e-6 - my = 1./eps - yd_0 = y[1] - yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) - - res_0 = yd[0]-yd_0 - res_1 = yd[1]-yd_1 - - return N.array([res_0,res_1]) - - y0 = [2.0,-0.6] #Initial conditions - yd0 = [-.6,-200000.] - - #Define an Assimulo problem - self.mod = Implicit_Problem(f,y0,yd0) - self.mod_t0 = Implicit_Problem(f,y0,yd0,1.0) - - #Define an explicit solver - self.sim = _Radau5DAE(self.mod) #Create a Radau5 solve - self.sim_t0 = _Radau5DAE(self.mod_t0) - - #Sets the parameters - self.sim.atol = 1e-4 #Default 1e-6 - self.sim.rtol = 1e-4 #Default 1e-6 - self.sim.inith = 1.e-4 #Initial step-size - - @testattr(stddist = True) - def test_time_event(self): - f = lambda t,y,yd: y-yd - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,yd,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - #solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Implicit_Problem(f,0.0,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = _Radau5DAE(exp_mod) - exp_sim.verbosity = 0 - exp_sim(5.,100) - - assert nevent == 5 - - @testattr(stddist = True) - def test_init(self): - """ - This tests the functionality of Radau5 Implicit Init. - """ - #Test both y0 in problem and not. - - sim = _Radau5DAE(self.mod) - - assert sim._leny == 2 - - @testattr(stddist = True) - def test_thet(self): - """ - This tests a negative value of thet. - """ - self.sim.thet = -1 - self.sim.simulate(.5) #Simulate 2 seconds - - assert self.sim.statistics["nsteps"] == self.sim.statistics["njacs"] - - @testattr(stddist = True) - def test_simulation(self): - """ - Test a simulation of the van der Pol equations (2). - """ - #Simulate - self.sim.simulate(2.) #Simulate 2 seconds - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.706272, 3) - - self.sim.reset() - - self.sim.report_continuously = True - - #Simulate - self.sim.simulate(2.) #Simulate 2 seconds - nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.706947, 2) - - self.sim_t0.simulate(3.) - nose.tools.assert_almost_equal(self.sim_t0.t_sol[0], 1.0000000, 4) - nose.tools.assert_almost_equal(self.sim_t0.t_sol[-1], 3.0000000, 4) - nose.tools.assert_almost_equal(self.sim_t0.y_sol[-1][0], 1.7061680350, 4) - - @testattr(stddist = True) - def test_simulation_ncp(self): - """ - Test a simulation with ncp. - """ - self.sim.report_continuously = True - - self.sim.simulate(1.0, 200) #Simulate 1 second - assert len(self.sim.t_sol) == 201 - - self.sim.reset() - self.sim.report_continuously = False - - self.sim.simulate(1.0, 200) #Simulate 1 second - assert len(self.sim.t_sol) == 201 - - @testattr(stddist = True) - def test_maxh(self): - """ - Tests implicit radau with maxh. - """ - self.sim.maxh = 0.01 - self.sim.simulate(0.5) - assert max(N.diff(self.sim.t_sol))-N.finfo('double').eps <= 0.01 - -class Test_Radau_Common: - """ - Tests the common attributes of the Radau solvers. - """ - def setUp(self): - """ - This sets up the test case. - """ - - f = lambda t,y:[1.0,2.0] - - #Define an Assimulo problem - y0 = [2.0,-0.6] #Initial conditions - exp_mod = Explicit_Problem(f,y0) - self.sim = Radau5ODE(exp_mod) - - @testattr(stddist = True) - def test_fac1(self): - """ - This tests the functionality of the property fac1. - """ - self.sim.fac1 = 0.01 - assert self.sim.fac1 == 0.01 - self.sim.fac1 = 0.001 - assert self.sim.fac1 == 0.001 - - nose.tools.assert_raises(Radau_Exception, self.sim._set_fac1, 'Test') - nose.tools.assert_raises(Radau_Exception, self.sim._set_fac1, [-1.0]) - - @testattr(stddist = True) - def test_fac2(self): - """ - This tests the functionality of the property fac2. - """ - self.sim.fac2 = 0.01 - assert self.sim.fac2 == 0.01 - self.sim.fac2 = 0.001 - assert self.sim.fac2 == 0.001 - - nose.tools.assert_raises(Radau_Exception, self.sim._set_fac2, 'Test') - nose.tools.assert_raises(Radau_Exception, self.sim._set_fac2, [-1.0]) - - @testattr(stddist = True) - def test_fnewt(self): - """ - This tests the functionality of the property fnewt. - """ - self.sim.fnewt = 0.01 - assert self.sim.fnewt == 0.01 - self.sim.fnewt = 0.001 - assert self.sim.fnewt == 0.001 - - nose.tools.assert_raises(Radau_Exception, self.sim._set_fnewt, 'Test') - nose.tools.assert_raises(Radau_Exception, self.sim._set_fnewt, [-1.0]) - - @testattr(stddist = True) - def test_h(self): - """ - This tests the functionality of the property h. - """ - self.sim.h = 0.01 - assert self.sim.h == 0.01 - self.sim.h = 0.001 - assert self.sim.h == 0.001 - - @testattr(stddist = True) - def test_initial_step(self): - """ - This tests the functionality of the property initial step. - """ - self.sim.inith = 0.01 - assert self.sim.inith == 0.01 - self.sim.inith = 0.001 - assert self.sim.inith == 0.001 - - nose.tools.assert_raises(Radau_Exception, self.sim._set_initial_step, 'Test') - nose.tools.assert_raises(Radau_Exception, self.sim._set_initial_step, [-1.0]) - - @testattr(stddist = True) - def test_newt(self): - """ - This tests the functionality of the property newt. - """ - self.sim.newt = 1 - assert self.sim.newt == 1 - self.sim.newt = 10 - assert self.sim.newt == 10 - self.sim.newt = 9.8 - assert self.sim.newt == 9 - - nose.tools.assert_raises(Radau_Exception, self.sim._set_newt, 'Test') - nose.tools.assert_raises(Radau_Exception, self.sim._set_newt, [-1.0]) - - @testattr(stddist = True) - def test_quot1(self): - """ - This tests the functionality of the property quot1. - """ - self.sim.quot1 = 0.01 - assert self.sim.quot1 == 0.01 - self.sim.quot1 = 0.001 - assert self.sim.quot1 == 0.001 - - nose.tools.assert_raises(Radau_Exception, self.sim._set_quot1, 'Test') - nose.tools.assert_raises(Radau_Exception, self.sim._set_quot1, [-1.0]) - - @testattr(stddist = True) - def test_quot2(self): - """ - This tests the functionality of the property quot2. - """ - self.sim.quot2 = 0.01 - assert self.sim.quot2 == 0.01 - self.sim.quot2 = 0.001 - assert self.sim.quot2 == 0.001 - - nose.tools.assert_raises(Radau_Exception, self.sim._set_quot2, 'Test') - nose.tools.assert_raises(Radau_Exception, self.sim._set_quot2, [-1.0]) - - @testattr(stddist = True) - def test_safe(self): - """ - This tests the functionality of the property safe. - """ - self.sim.safe = 0.01 - assert self.sim.safe == 0.01 - self.sim.safe = 0.001 - assert self.sim.safe == 0.001 - - nose.tools.assert_raises(Radau_Exception, self.sim._set_safe, 'Test') - nose.tools.assert_raises(Radau_Exception, self.sim._set_safe, [-1.0]) - - @testattr(stddist = True) - def test_thet(self): - """ - This tests the functionality of the property thet. - """ - self.sim.thet = 0.01 - assert self.sim.thet == 0.01 - self.sim.thet = 0.001 - assert self.sim.thet == 0.001 - - nose.tools.assert_raises(Radau_Exception, self.sim._set_thet, 'Test') - nose.tools.assert_raises(Radau_Exception, self.sim._set_thet, [-1.0]) - - @testattr(stddist = True) - def test_usejac(self): - """ - This tests the functionality of the property usejac. - """ - self.sim.usejac = True - assert self.sim.usejac == True - self.sim.usejac = False - assert self.sim.usejac == False - self.sim.usejac = 1 - assert self.sim.usejac == True - self.sim.usejac = [] - assert self.sim.usejac == False - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import nose +from assimulo import testattr +from assimulo.solvers.radau5 import * +from assimulo.solvers.radau5 import Radau5DAE, _Radau5DAE +from assimulo.solvers.radau5 import Radau5ODE, _Radau5ODE +from assimulo.problem import Explicit_Problem +from assimulo.problem import Implicit_Problem +from assimulo.exception import * +from assimulo.lib.radau_core import Radau_Exception, Radau_Common +import scipy.sparse as sp + +class Extended_Problem(Explicit_Problem): + + #Sets the initial conditons directly into the problem + y0 = [0.0, -1.0, 0.0] + sw0 = [False,True,True] + event_array = N.array([0.0,0.0,0.0]) + rhs_array = N.array([0.0,0.0,0.0]) + + #The right-hand-side function (rhs) + def rhs(self,t,y,sw): + """ + This is our function we are trying to simulate. During simulation + the parameter sw should be fixed so that our function is continuous + over the interval. The parameters sw should only be changed when the + integrator has stopped. + """ + self.rhs_array[0] = (1.0 if sw[0] else -1.0) + self.rhs_array[1] = 0.0 + self.rhs_array[2] = 0.0 + + return self.rhs_array + + #Sets a name to our function + name = 'ODE with discontinuities and a function with consistency problem' + + #The event function + def state_events(self,t,y,sw): + """ + This is our function that keeps track of our events. When the sign + of any of the events has changed, we have an event. + """ + self.event_array[0] = y[1] - 1.0 + self.event_array[1] = -y[2] + 1.0 + self.event_array[2] = -t + 1.0 + + return self.event_array + + #Responsible for handling the events. + def handle_event(self, solver, event_info): + """ + Event handling. This functions is called when Assimulo finds an event as + specified by the event functions. + """ + event_info = event_info[0] #We only look at the state events information. + while True: #Event Iteration + self.event_switch(solver, event_info) #Turns the switches + + b_mode = self.state_events(solver.t, solver.y, solver.sw).copy() + self.init_mode(solver) #Pass in the solver to the problem specified init_mode + a_mode = self.state_events(solver.t, solver.y, solver.sw).copy() + + event_info = self.check_eIter(b_mode, a_mode) + + if not True in event_info: #Breaks the iteration loop + break + + #Helper function for handle_event + def event_switch(self, solver, event_info): + """ + Turns the switches. + """ + for i in range(len(event_info)): #Loop across all event functions + if event_info[i] != 0: + solver.sw[i] = not solver.sw[i] #Turn the switch + + #Helper function for handle_event + def check_eIter(self, before, after): + """ + Helper function for handle_event to determine if we have event + iteration. + + Input: Values of the event indicator functions (state_events) + before and after we have changed mode of operations. + """ + + eIter = [False]*len(before) + + for i in range(len(before)): + if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): + eIter[i] = True + + return eIter + + def init_mode(self, solver): + """ + Initialize the DAE with the new conditions. + """ + solver.y[1] = (-1.0 if solver.sw[1] else 3.0) + solver.y[2] = (0.0 if solver.sw[2] else 2.0) + +class Test_Explicit_Radau5: + """ + Tests the explicit Radau solver. + """ + def setUp(self): + """ + This sets up the test case. + """ + def f(t,y): + eps = 1.e-6 + my = 1./eps + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + return N.array([yd_0,yd_1]) + + def jac(t,y): + eps = 1.e-6 + my = 1./eps + J = N.zeros([2,2]) + + J[0,0]=0. + J[0,1]=1. + J[1,0]=my*(-2.*y[0]*y[1]-1.) + J[1,1]=my*(1.-y[0]**2) + + return J + + #Define an Assimulo problem + y0 = [2.0,-0.6] #Initial conditions + + exp_mod = Explicit_Problem(f,y0) + exp_mod_t0 = Explicit_Problem(f,y0,1.0) + + exp_mod.jac = jac + self.mod = exp_mod + + #Define an explicit solver + self.sim = _Radau5ODE(exp_mod) #Create a Radau5 solve + self.sim_t0 = _Radau5ODE(exp_mod_t0) + + #Sets the parameters + self.sim.atol = 1e-4 #Default 1e-6 + self.sim.rtol = 1e-4 #Default 1e-6 + self.sim.inith = 1.e-4 #Initial step-size + self.sim.usejac = False + + @testattr(stddist = True) + def test_event_localizer(self): + exp_mod = Extended_Problem() #Create the problem + + exp_sim = Radau5ODE(exp_mod) #Create the solver + + exp_sim.verbosity = 0 + exp_sim.report_continuously = True + + #Simulate + t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points + + #Basic test + nose.tools.assert_almost_equal(y[-1][0],8.0) + nose.tools.assert_almost_equal(y[-1][1],3.0) + nose.tools.assert_almost_equal(y[-1][2],2.0) + + @testattr(stddist = True) + def test_time_event(self): + f = lambda t,y: [1.0] + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Explicit_Problem(f,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = _Radau5ODE(exp_mod) + exp_sim(5.,100) + + assert nevent == 5 + + @testattr(stddist = True) + def test_init(self): + + #Test both y0 in problem and not. + sim = _Radau5ODE(self.mod) + + assert sim._leny == 2 + + @testattr(stddist = True) + def test_collocation_polynomial(self): + """ + This tests the functionality of the collocation polynomial (communication points) + """ + self.sim.report_continuously = False + + self.sim.simulate(2.,200) #Simulate 2 seconds + + assert self.sim.statistics["nsteps"] < 300 + + #nose.tools.assert_almost_equal(self.sim.y[-2][0], 1.71505001, 4) + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) + + self.sim.report_continuously = True + self.sim.reset() + self.sim.simulate(2.,200) #Simulate 2 seconds + + assert self.sim.statistics["nsteps"] < 300 + + #nose.tools.assert_almost_equal(self.sim.y[-2][0], 1.71505001, 4) + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) + + self.sim_t0.simulate(3.) + nose.tools.assert_almost_equal(self.sim_t0.t_sol[0], 1.0000000, 4) + nose.tools.assert_almost_equal(self.sim_t0.t_sol[-1], 3.0000000, 4) + nose.tools.assert_almost_equal(self.sim_t0.y_sol[-1][0], 1.7061680350, 4) + + @testattr(stddist = True) + def test_simulation(self): + """ + This tests the Radau5 with a simulation of the van der Pol problem. + """ + self.sim.simulate(2.) #Simulate 2 seconds + + assert self.sim.statistics["nsteps"] < 300 + + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) + + @testattr(stddist = True) + def test_simulation_ncp(self): + """ + Test a simulation with ncp. + """ + self.sim.report_continuously = True + + self.sim.simulate(1.0, 200) #Simulate 1 second + assert len(self.sim.t_sol) == 201 + + self.sim.reset() + self.sim.report_continuously = False + + self.sim.simulate(1.0, 200) #Simulate 1 second + assert len(self.sim.t_sol) == 201 + + @testattr(stddist = True) + def test_usejac(self): + """ + This tests the usejac property. + """ + self.sim.usejac = True + + self.sim.simulate(2.) #Simulate 2 seconds + + assert self.sim.statistics["nfcnjacs"] == 0 + + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) + + @testattr(stddist = True) + def test_thet(self): + """ + This tests a negative value of thet. + """ + self.sim.thet = -1 + self.sim.simulate(2.) #Simulate 2 seconds + + assert self.sim.statistics["nsteps"] == self.sim.statistics["njacs"] + + @testattr(stddist = True) + def test_maxh(self): + """ + This tests the maximum step length. + """ + self.sim.maxh = 0.01 + self.sim.simulate(0.5) + assert max(N.diff(self.sim.t_sol))-N.finfo('double').eps <= 0.01 + + @testattr(stddist = True) + def test_newt(self): + """ + This tests the maximum number of newton iterations. + """ + self.sim.newt = 10 + self.sim.simulate(1.0) + + assert self.sim.statistics["nnfails"] == 1 + + @testattr(stddist = True) + def test_safe(self): + """ + This tests the safety factor in the step-size prediction. + """ + self.sim.safe = 0.99 + self.sim.simulate(1.0) + assert self.sim.statistics["nsteps"] < 150 + + @testattr(stddist = True) + def test_reset_statistics(self): + """ + Tests that the statistics are reset. + """ + self.sim.simulate(1.0) + steps = self.sim.statistics["nsteps"] + + self.sim.reset() + self.sim.simulate(1.0) + + assert self.sim.statistics["nsteps"] < steps*1.5 + + @testattr(stddist = True) + def test_atol(self): + """ + This test the absolute tolerance. + """ + self.sim.simulate(1.0) + + steps = self.sim.statistics["nsteps"] + + self.sim.reset() + + self.sim.rtol = 1e-8 + self.sim.atol = 1e-8 + + self.sim.simulate(1.0) + steps2 = self.sim.statistics["nsteps"] + + assert steps2 > steps + + self.sim.reset() + self.sim.atol = [1e-8, 1e-8] + + steps3 = self.sim.statistics["nsteps"] + + assert steps3==steps2 + + nose.tools.assert_raises(Radau_Exception, self.sim._set_atol, [1e-6,1e-6,1e-6]) + +class Test_Explicit_Fortran_Radau5: + """ + Tests the explicit Radau solver. + """ + def setUp(self): + """ + This sets up the test case. + """ + def f(t,y): + eps = 1.e-6 + my = 1./eps + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + return N.array([yd_0,yd_1]) + + def jac(t,y): + eps = 1.e-6 + my = 1./eps + J = N.zeros([2,2]) + + J[0,0]=0. + J[0,1]=1. + J[1,0]=my*(-2.*y[0]*y[1]-1.) + J[1,1]=my*(1.-y[0]**2) + + return J + + def jac_sparse(t,y): + eps = 1.e-6 + my = 1./eps + J = N.zeros([2,2]) + + J[0,0]=0. + J[0,1]=1. + J[1,0]=my*(-2.*y[0]*y[1]-1.) + J[1,1]=my*(1.-y[0]**2) + + return sp.csc_matrix(J) + + #Define an Assimulo problem + y0 = [2.0,-0.6] #Initial conditions + + exp_mod = Explicit_Problem(f,y0) + exp_mod_t0 = Explicit_Problem(f,y0,1.0) + exp_mod_sp = Explicit_Problem(f,y0) + + exp_mod.jac = jac + exp_mod_sp.jac = jac_sparse + self.mod = exp_mod + + #Define an explicit solver + self.sim = Radau5ODE(exp_mod) #Create a Radau5 solve + self.sim_t0 = Radau5ODE(exp_mod_t0) + self.sim_sp = Radau5ODE(exp_mod_sp) + + #Sets the parameters + self.sim.atol = 1e-4 #Default 1e-6 + self.sim.rtol = 1e-4 #Default 1e-6 + self.sim.inith = 1.e-4 #Initial step-size + self.sim.usejac = False + + @testattr(stddist = True) + def test_nbr_fcn_evals_due_to_jac(self): + sim = Radau5ODE(self.mod) + + sim.usejac = False + sim.simulate(1) + + assert sim.statistics["nfcnjacs"] > 0 + + sim = Radau5ODE(self.mod) + sim.simulate(1) + + assert sim.statistics["nfcnjacs"] == 0 + + @testattr(stddist = True) + def test_time_event(self): + f = lambda t,y: [1.0] + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Explicit_Problem(f,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = Radau5ODE(exp_mod) + exp_sim(5.,100) + + assert nevent == 5 + + @testattr(stddist = True) + def test_init(self): + + #Test both y0 in problem and not. + sim = Radau5ODE(self.mod) + + assert sim._leny == 2 + + @testattr(stddist = True) + def test_collocation_polynomial(self): + """ + This tests the functionality of the collocation polynomial (communication points) + """ + self.sim.report_continuously = False + + self.sim.simulate(2.,200) #Simulate 2 seconds + + assert self.sim.statistics["nsteps"] < 300 + + #nose.tools.assert_almost_equal(self.sim.y[-2][0], 1.71505001, 4) + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) + + self.sim.report_continuously = True + self.sim.reset() + self.sim.simulate(2.,200) #Simulate 2 seconds + + assert self.sim.statistics["nsteps"] < 300 + + #nose.tools.assert_almost_equal(self.sim.y[-2][0], 1.71505001, 4) + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) + + self.sim_t0.simulate(3.) + nose.tools.assert_almost_equal(self.sim_t0.t_sol[0], 1.0000000, 4) + nose.tools.assert_almost_equal(self.sim_t0.t_sol[-1], 3.0000000, 4) + nose.tools.assert_almost_equal(self.sim_t0.y_sol[-1][0], 1.7061680350, 4) + + @testattr(stddist = True) + def test_simulation(self): + """ + This tests the Radau5 with a simulation of the van der pol problem. + """ + self.sim.simulate(2.) #Simulate 2 seconds + + assert self.sim.statistics["nsteps"] < 300 + + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) + + @testattr(stddist = True) + def test_simulation_ncp(self): + """ + Test a simulation with ncp. + """ + self.sim.report_continuously = True + + self.sim.simulate(1.0, 200) #Simulate 1 second + assert len(self.sim.t_sol) == 201 + + self.sim.reset() + self.sim.report_continuously = False + + self.sim.simulate(1.0, 200) #Simulate 1 second + assert len(self.sim.t_sol) == 201 + + @testattr(stddist = True) + def test_usejac(self): + """ + This tests the usejac property. + """ + self.sim.usejac = True + + self.sim.simulate(2.) #Simulate 2 seconds + + assert self.sim.statistics["nfcnjacs"] == 0 + + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.7061680350, 4) + + @testattr(stddist = True) + def test_usejac_csc_matrix(self): + """ + This tests the functionality of the property usejac. + """ + self.sim_sp.usejac = True + + self.sim_sp.simulate(2.) #Simulate 2 seconds + + assert self.sim_sp.statistics["nfcnjacs"] == 0 + + nose.tools.assert_almost_equal(self.sim_sp.y_sol[-1][0], 1.7061680350, 4) + + @testattr(stddist = True) + def test_thet(self): + """ + This tests a negative value of thet. + """ + self.sim.thet = -1 + self.sim.simulate(2.) #Simulate 2 seconds + + assert self.sim.statistics["nsteps"] == self.sim.statistics["njacs"] + + @testattr(stddist = True) + def test_maxh(self): + """ + This tests the maximum step length. + """ + self.sim.maxh = 0.01 + self.sim.simulate(0.5) + assert max(N.diff(self.sim.t_sol))-N.finfo('double').eps <= 0.01 + + @testattr(stddist = True) + def test_newt(self): + """ + This tests the maximum number of newton iterations. + """ + pass + #self.sim.simulate(1.0) + #self.sim.reset() + #self.sim.newt = 10 + #self.sim.simulate(1.0) + + #assert self.sim.statistics["nniterfail"] == 1 + + @testattr(stddist = True) + def test_safe(self): + """ + This tests the safety factor in the step-size prediction. + """ + self.sim.safe = 0.99 + self.sim.simulate(1.0) + assert self.sim.statistics["nsteps"] < 150 + + @testattr(stddist = True) + def test_reset_statistics(self): + """ + Tests that the statistics are reset. + """ + self.sim.simulate(1.0) + steps = self.sim.statistics["nsteps"] + + self.sim.reset() + self.sim.simulate(1.0) + + assert self.sim.statistics["nsteps"] < steps*1.5 + + @testattr(stddist = True) + def test_weighted_error(self): + + def handle_result(solver, t, y): + err = solver.get_weighted_local_errors() + assert len(err) == len(y) + + self.mod.handle_result = handle_result + + #Define an explicit solver + sim = Radau5ODE(self.mod) #Create a Radau5 solve + + sim.get_weighted_local_errors() + + sim.simulate(1) + + + @testattr(stddist = True) + def test_atol(self): + """ + This test the absolute tolerance. + """ + self.sim.simulate(1.0) + + steps = self.sim.statistics["nsteps"] + + self.sim.reset() + + self.sim.rtol = 1e-8 + self.sim.atol = 1e-8 + + self.sim.simulate(1.0) + steps2 = self.sim.statistics["nsteps"] + + assert steps2 > steps + + self.sim.reset() + self.sim.atol = [1e-8, 1e-8] + + steps3 = self.sim.statistics["nsteps"] + + assert steps3==steps2 + + nose.tools.assert_raises(Radau_Exception, self.sim._set_atol, [1e-6,1e-6,1e-6]) + + @testattr(stddist = True) + def test_switches(self): + """ + This tests that the switches are actually turned when override. + """ + f = lambda t,x,sw: N.array([1.0]) + state_events = lambda t,x,sw: N.array([x[0]-1.]) + def handle_event(solver, event_info): + solver.sw = [False] #Override the switches to point to another instance + + mod = Explicit_Problem(f,[0.0]) + mod.sw0 = [True] + + mod.state_events = state_events + mod.handle_event = handle_event + + sim = Radau5ODE(mod) + assert sim.sw[0] == True + sim.simulate(3) + assert sim.sw[0] == False + + +class Test_Implicit_Fortran_Radau5: + """ + Tests the implicit Radau solver. + """ + def setUp(self): + """ + This sets up the test case. + """ + #Define the residual + def f(t,y,yd): + eps = 1.e-6 + my = 1./eps + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + res_0 = yd[0]-yd_0 + res_1 = yd[1]-yd_1 + + return N.array([res_0,res_1]) + + y0 = [2.0,-0.6] #Initial conditions + yd0 = [-.6,-200000.] + + #Define an Assimulo problem + self.mod = Implicit_Problem(f,y0,yd0) + self.mod_t0 = Implicit_Problem(f,y0,yd0,1.0) + + #Define an explicit solver + self.sim = Radau5DAE(self.mod) #Create a Radau5 solve + self.sim_t0 = Radau5DAE(self.mod_t0) + + #Sets the parameters + self.sim.atol = 1e-4 #Default 1e-6 + self.sim.rtol = 1e-4 #Default 1e-6 + self.sim.inith = 1.e-4 #Initial step-size + + @testattr(stddist = True) + def test_nbr_fcn_evals_due_to_jac(self): + sim = Radau5DAE(self.mod) + + sim.usejac = False + sim.simulate(1) + + assert sim.statistics["nfcnjacs"] > 0 + + @testattr(stddist = True) + def test_simulate_explicit(self): + """ + Test a simulation of an explicit problem using Radau5DAE. + """ + f = lambda t,y:N.array(-y) + y0 = [1.0] + + problem = Explicit_Problem(f,y0) + simulator = Radau5DAE(problem) + + assert simulator.yd0[0] == -simulator.y0[0] + + t,y = simulator.simulate(1.0) + + nose.tools.assert_almost_equal(float(y[-1]), float(N.exp(-1.0)),4) + + @testattr(stddist = True) + def test_time_event(self): + f = lambda t,y,yd: y-yd + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,yd,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + #solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Implicit_Problem(f,0.0,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = Radau5DAE(exp_mod) + exp_sim.verbosity = 0 + exp_sim(5.,100) + + assert nevent == 5 + + @testattr(stddist = True) + def test_init(self): + """ + This tests the functionality of Radau5 Implicit Init. + """ + #Test both y0 in problem and not. + + sim = Radau5DAE(self.mod) + + assert sim._leny == 2 + + @testattr(stddist = True) + def test_thet(self): + """ + This tests a negative value of thet. + """ + self.sim.thet = -1 + self.sim.simulate(.5) #Simulate 2 seconds + + assert self.sim.statistics["nsteps"] == self.sim.statistics["njacs"] + + @testattr(stddist = True) + def test_simulation(self): + """ + Test a simulation of the van der Pol equations (1). + """ + #Simulate + self.sim.simulate(2.) #Simulate 2 seconds + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.706272, 3) + + self.sim.reset() + + self.sim.report_continuously = True + + #Simulate + self.sim.simulate(2.) #Simulate 2 seconds + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.706166, 3) + + self.sim_t0.simulate(3.) + nose.tools.assert_almost_equal(self.sim_t0.t_sol[0], 1.0000000, 4) + nose.tools.assert_almost_equal(self.sim_t0.t_sol[-1], 3.0000000, 4) + nose.tools.assert_almost_equal(self.sim_t0.y_sol[-1][0], 1.7061680350, 4) + + @testattr(stddist = True) + def test_simulation_ncp(self): + """ + Test a simulation with ncp. + """ + self.sim.report_continuously = True + + self.sim.simulate(1.0, 200) #Simulate 1 second + assert len(self.sim.t_sol) == 201 + + self.sim.reset() + self.sim.report_continuously = False + + self.sim.simulate(1.0, 200) #Simulate 1 second + assert len(self.sim.t_sol) == 201 + + @testattr(stddist = True) + def test_maxh(self): + """ + Tests implicit radau with maxh. + """ + self.sim.maxh = 0.01 + self.sim.simulate(0.5) + assert max(N.diff(self.sim.t_sol))-N.finfo('double').eps <= 0.01 + + + @testattr(stddist = True) + def test_switches(self): + """ + This tests that the switches are actually turned when override. + """ + res = lambda t,x,xd,sw: N.array([1.0 - xd]) + state_events = lambda t,x,xd,sw: N.array([x[0]-1.]) + def handle_event(solver, event_info): + solver.sw = [False] #Override the switches to point to another instance + + mod = Implicit_Problem(res,[0.0], [1.0]) + mod.sw0 = [True] + + mod.state_events = state_events + mod.handle_event = handle_event + + sim = Radau5DAE(mod) + assert sim.sw[0] == True + sim.simulate(3) + assert sim.sw[0] == False + + +class Test_Implicit_Radau5: + """ + Tests the implicit Radau solver. + """ + def setUp(self): + """ + This sets up the test case. + """ + #Define the residual + def f(t,y,yd): + eps = 1.e-6 + my = 1./eps + yd_0 = y[1] + yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) + + res_0 = yd[0]-yd_0 + res_1 = yd[1]-yd_1 + + return N.array([res_0,res_1]) + + y0 = [2.0,-0.6] #Initial conditions + yd0 = [-.6,-200000.] + + #Define an Assimulo problem + self.mod = Implicit_Problem(f,y0,yd0) + self.mod_t0 = Implicit_Problem(f,y0,yd0,1.0) + + #Define an explicit solver + self.sim = _Radau5DAE(self.mod) #Create a Radau5 solve + self.sim_t0 = _Radau5DAE(self.mod_t0) + + #Sets the parameters + self.sim.atol = 1e-4 #Default 1e-6 + self.sim.rtol = 1e-4 #Default 1e-6 + self.sim.inith = 1.e-4 #Initial step-size + + @testattr(stddist = True) + def test_time_event(self): + f = lambda t,y,yd: y-yd + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,yd,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + #solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Implicit_Problem(f,0.0,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = _Radau5DAE(exp_mod) + exp_sim.verbosity = 0 + exp_sim(5.,100) + + assert nevent == 5 + + @testattr(stddist = True) + def test_init(self): + """ + This tests the functionality of Radau5 Implicit Init. + """ + #Test both y0 in problem and not. + + sim = _Radau5DAE(self.mod) + + assert sim._leny == 2 + + @testattr(stddist = True) + def test_thet(self): + """ + This tests a negative value of thet. + """ + self.sim.thet = -1 + self.sim.simulate(.5) #Simulate 2 seconds + + assert self.sim.statistics["nsteps"] == self.sim.statistics["njacs"] + + @testattr(stddist = True) + def test_simulation(self): + """ + Test a simulation of the van der Pol equations (2). + """ + #Simulate + self.sim.simulate(2.) #Simulate 2 seconds + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.706272, 3) + + self.sim.reset() + + self.sim.report_continuously = True + + #Simulate + self.sim.simulate(2.) #Simulate 2 seconds + nose.tools.assert_almost_equal(self.sim.y_sol[-1][0], 1.706947, 2) + + self.sim_t0.simulate(3.) + nose.tools.assert_almost_equal(self.sim_t0.t_sol[0], 1.0000000, 4) + nose.tools.assert_almost_equal(self.sim_t0.t_sol[-1], 3.0000000, 4) + nose.tools.assert_almost_equal(self.sim_t0.y_sol[-1][0], 1.7061680350, 4) + + @testattr(stddist = True) + def test_simulation_ncp(self): + """ + Test a simulation with ncp. + """ + self.sim.report_continuously = True + + self.sim.simulate(1.0, 200) #Simulate 1 second + assert len(self.sim.t_sol) == 201 + + self.sim.reset() + self.sim.report_continuously = False + + self.sim.simulate(1.0, 200) #Simulate 1 second + assert len(self.sim.t_sol) == 201 + + @testattr(stddist = True) + def test_maxh(self): + """ + Tests implicit radau with maxh. + """ + self.sim.maxh = 0.01 + self.sim.simulate(0.5) + assert max(N.diff(self.sim.t_sol))-N.finfo('double').eps <= 0.01 + +class Test_Radau_Common: + """ + Tests the common attributes of the Radau solvers. + """ + def setUp(self): + """ + This sets up the test case. + """ + + f = lambda t,y:[1.0,2.0] + + #Define an Assimulo problem + y0 = [2.0,-0.6] #Initial conditions + exp_mod = Explicit_Problem(f,y0) + self.sim = Radau5ODE(exp_mod) + + @testattr(stddist = True) + def test_fac1(self): + """ + This tests the functionality of the property fac1. + """ + self.sim.fac1 = 0.01 + assert self.sim.fac1 == 0.01 + self.sim.fac1 = 0.001 + assert self.sim.fac1 == 0.001 + + nose.tools.assert_raises(Radau_Exception, self.sim._set_fac1, 'Test') + nose.tools.assert_raises(Radau_Exception, self.sim._set_fac1, [-1.0]) + + @testattr(stddist = True) + def test_fac2(self): + """ + This tests the functionality of the property fac2. + """ + self.sim.fac2 = 0.01 + assert self.sim.fac2 == 0.01 + self.sim.fac2 = 0.001 + assert self.sim.fac2 == 0.001 + + nose.tools.assert_raises(Radau_Exception, self.sim._set_fac2, 'Test') + nose.tools.assert_raises(Radau_Exception, self.sim._set_fac2, [-1.0]) + + @testattr(stddist = True) + def test_fnewt(self): + """ + This tests the functionality of the property fnewt. + """ + self.sim.fnewt = 0.01 + assert self.sim.fnewt == 0.01 + self.sim.fnewt = 0.001 + assert self.sim.fnewt == 0.001 + + nose.tools.assert_raises(Radau_Exception, self.sim._set_fnewt, 'Test') + nose.tools.assert_raises(Radau_Exception, self.sim._set_fnewt, [-1.0]) + + @testattr(stddist = True) + def test_h(self): + """ + This tests the functionality of the property h. + """ + self.sim.h = 0.01 + assert self.sim.h == 0.01 + self.sim.h = 0.001 + assert self.sim.h == 0.001 + + @testattr(stddist = True) + def test_initial_step(self): + """ + This tests the functionality of the property initial step. + """ + self.sim.inith = 0.01 + assert self.sim.inith == 0.01 + self.sim.inith = 0.001 + assert self.sim.inith == 0.001 + + nose.tools.assert_raises(Radau_Exception, self.sim._set_initial_step, 'Test') + nose.tools.assert_raises(Radau_Exception, self.sim._set_initial_step, [-1.0]) + + @testattr(stddist = True) + def test_newt(self): + """ + This tests the functionality of the property newt. + """ + self.sim.newt = 1 + assert self.sim.newt == 1 + self.sim.newt = 10 + assert self.sim.newt == 10 + self.sim.newt = 9.8 + assert self.sim.newt == 9 + + nose.tools.assert_raises(Radau_Exception, self.sim._set_newt, 'Test') + nose.tools.assert_raises(Radau_Exception, self.sim._set_newt, [-1.0]) + + @testattr(stddist = True) + def test_quot1(self): + """ + This tests the functionality of the property quot1. + """ + self.sim.quot1 = 0.01 + assert self.sim.quot1 == 0.01 + self.sim.quot1 = 0.001 + assert self.sim.quot1 == 0.001 + + nose.tools.assert_raises(Radau_Exception, self.sim._set_quot1, 'Test') + nose.tools.assert_raises(Radau_Exception, self.sim._set_quot1, [-1.0]) + + @testattr(stddist = True) + def test_quot2(self): + """ + This tests the functionality of the property quot2. + """ + self.sim.quot2 = 0.01 + assert self.sim.quot2 == 0.01 + self.sim.quot2 = 0.001 + assert self.sim.quot2 == 0.001 + + nose.tools.assert_raises(Radau_Exception, self.sim._set_quot2, 'Test') + nose.tools.assert_raises(Radau_Exception, self.sim._set_quot2, [-1.0]) + + @testattr(stddist = True) + def test_safe(self): + """ + This tests the functionality of the property safe. + """ + self.sim.safe = 0.01 + assert self.sim.safe == 0.01 + self.sim.safe = 0.001 + assert self.sim.safe == 0.001 + + nose.tools.assert_raises(Radau_Exception, self.sim._set_safe, 'Test') + nose.tools.assert_raises(Radau_Exception, self.sim._set_safe, [-1.0]) + + @testattr(stddist = True) + def test_thet(self): + """ + This tests the functionality of the property thet. + """ + self.sim.thet = 0.01 + assert self.sim.thet == 0.01 + self.sim.thet = 0.001 + assert self.sim.thet == 0.001 + + nose.tools.assert_raises(Radau_Exception, self.sim._set_thet, 'Test') + nose.tools.assert_raises(Radau_Exception, self.sim._set_thet, [-1.0]) + + @testattr(stddist = True) + def test_usejac(self): + """ + This tests the functionality of the property usejac. + """ + self.sim.usejac = True + assert self.sim.usejac == True + self.sim.usejac = False + assert self.sim.usejac == False + self.sim.usejac = 1 + assert self.sim.usejac == True + self.sim.usejac = [] + assert self.sim.usejac == False + diff --git a/tests/solvers/test_rungekutta.py b/tests/solvers/test_rungekutta.py index 2e9aef39..45649bea 100644 --- a/tests/solvers/test_rungekutta.py +++ b/tests/solvers/test_rungekutta.py @@ -1,280 +1,280 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import nose -from assimulo import testattr -from assimulo.solvers.runge_kutta import * -from assimulo.problem import Explicit_Problem -from assimulo.exception import * - -class Test_Dopri5: - - def setUp(self): - """ - This function sets up the test case. - """ - f = lambda t,y:1.0 - y0 = 1 - - self.problem = Explicit_Problem(f,y0) - self.simulator = Dopri5(self.problem) - - @testattr(stddist = True) - def test_integrator(self): - """ - This tests the functionality of the method integrate. - """ - values = self.simulator.simulate(1) - - nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) - nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) - @testattr(stddist = True) - - @testattr(stddist = True) - def test_time_event(self): - f = lambda t,y: [1.0] - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Explicit_Problem(f,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = Dopri5(exp_mod) - exp_sim(5.,100) - - assert nevent == 5 - - def test_switches(self): - """ - This tests that the switches are actually turned when override. - """ - f = lambda t,x,sw: N.array([1.0]) - state_events = lambda t,x,sw: N.array([x[0]-1.]) - def handle_event(solver, event_info): - solver.sw = [False] #Override the switches to point to another instance - - mod = Explicit_Problem(f,[0.0]) - mod.sw0 = [True] - - mod.state_events = state_events - mod.handle_event = handle_event - - sim = Dopri5(mod) - assert sim.sw[0] == True - sim.simulate(3) - assert sim.sw[0] == False - - -class Test_RungeKutta34: - - def setUp(self): - """ - This function sets up the test case. - """ - f = lambda t,y:1.0 - y0 = 1 - - self.problem = Explicit_Problem(f,y0) - self.simulator = RungeKutta34(self.problem) - - @testattr(stddist = True) - def test_integrator(self): - """ - This tests the functionality of the method integrate. - """ - values = self.simulator.simulate(1) - - nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) - nose.tools.assert_almost_equal(self.simulator.y_sol[-1], 2.0) - - @testattr(stddist = True) - def test_step(self): - """ - This tests the functionality of the method step. - """ - self.simulator.report_continuously = True - - self.simulator.h = 0.1 - self.simulator.simulate(1) - - nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) - nose.tools.assert_almost_equal(self.simulator.y_sol[-1], 2.0) - - @testattr(stddist = True) - def test_time_event(self): - f = lambda t,y: [1.0] - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Explicit_Problem(f,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = RungeKutta34(exp_mod) - exp_sim(5.,100) - - assert nevent == 5 - - @testattr(stddist = True) - def test_tolerance(self): - """ - This tests the functionality of the tolerances. - """ - nose.tools.assert_raises(Explicit_ODE_Exception, self.simulator._set_rtol, 'hej') - nose.tools.assert_raises(Explicit_ODE_Exception, self.simulator._set_atol, 'hej') - nose.tools.assert_raises(Explicit_ODE_Exception, self.simulator._set_rtol, -1) - - self.simulator.rtol = 1.0 - assert self.simulator._get_rtol() == 1.0 - self.simulator.rtol = 1 - assert self.simulator._get_rtol() == 1 - - self.simulator.atol = 1.0 - assert self.simulator.atol == 1.0 - - nose.tools.assert_raises(Explicit_ODE_Exception, self.simulator._set_atol, [1.0,1.0]) - - - @testattr(stddist = True) - def test_switches(self): - """ - This tests that the switches are actually turned when override. - """ - f = lambda t,x,sw: N.array([1.0]) - state_events = lambda t,x,sw: N.array([x[0]-1.]) - def handle_event(solver, event_info): - solver.sw = [False] #Override the switches to point to another instance - - mod = Explicit_Problem(f,[0.0]) - mod.sw0 = [True] - - mod.state_events = state_events - mod.handle_event = handle_event - - sim = RungeKutta34(mod) - assert sim.sw[0] == True - sim.simulate(3) - assert sim.sw[0] == False - - -class Test_RungeKutta4: - - def setUp(self): - """ - This function sets up the test case. - """ - f = lambda t,y:1.0 - y0 = 1 - - self.problem = Explicit_Problem(f,y0) - self.simulator = RungeKutta4(self.problem) - - @testattr(stddist = True) - def test_time_event(self): - f = lambda t,y: [1.0] - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Explicit_Problem(f,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = RungeKutta4(exp_mod) - exp_sim(5.,100) - - assert nevent == 5 - - @testattr(stddist = True) - def test_integrate(self): - values = self.simulator.simulate(1) - - nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) - nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) - - @testattr(stddist = True) - def test_step(self): - self.simulator.report_continuously = True - - self.simulator.h = 0.1 - self.simulator.simulate(1) - - nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) - nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import nose +from assimulo import testattr +from assimulo.solvers.runge_kutta import * +from assimulo.problem import Explicit_Problem +from assimulo.exception import * + +class Test_Dopri5: + + def setUp(self): + """ + This function sets up the test case. + """ + f = lambda t,y:1.0 + y0 = 1 + + self.problem = Explicit_Problem(f,y0) + self.simulator = Dopri5(self.problem) + + @testattr(stddist = True) + def test_integrator(self): + """ + This tests the functionality of the method integrate. + """ + values = self.simulator.simulate(1) + + nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) + nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) + @testattr(stddist = True) + + @testattr(stddist = True) + def test_time_event(self): + f = lambda t,y: [1.0] + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Explicit_Problem(f,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = Dopri5(exp_mod) + exp_sim(5.,100) + + assert nevent == 5 + + def test_switches(self): + """ + This tests that the switches are actually turned when override. + """ + f = lambda t,x,sw: N.array([1.0]) + state_events = lambda t,x,sw: N.array([x[0]-1.]) + def handle_event(solver, event_info): + solver.sw = [False] #Override the switches to point to another instance + + mod = Explicit_Problem(f,[0.0]) + mod.sw0 = [True] + + mod.state_events = state_events + mod.handle_event = handle_event + + sim = Dopri5(mod) + assert sim.sw[0] == True + sim.simulate(3) + assert sim.sw[0] == False + + +class Test_RungeKutta34: + + def setUp(self): + """ + This function sets up the test case. + """ + f = lambda t,y:1.0 + y0 = 1 + + self.problem = Explicit_Problem(f,y0) + self.simulator = RungeKutta34(self.problem) + + @testattr(stddist = True) + def test_integrator(self): + """ + This tests the functionality of the method integrate. + """ + values = self.simulator.simulate(1) + + nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) + nose.tools.assert_almost_equal(self.simulator.y_sol[-1], 2.0) + + @testattr(stddist = True) + def test_step(self): + """ + This tests the functionality of the method step. + """ + self.simulator.report_continuously = True + + self.simulator.h = 0.1 + self.simulator.simulate(1) + + nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) + nose.tools.assert_almost_equal(self.simulator.y_sol[-1], 2.0) + + @testattr(stddist = True) + def test_time_event(self): + f = lambda t,y: [1.0] + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Explicit_Problem(f,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = RungeKutta34(exp_mod) + exp_sim(5.,100) + + assert nevent == 5 + + @testattr(stddist = True) + def test_tolerance(self): + """ + This tests the functionality of the tolerances. + """ + nose.tools.assert_raises(Explicit_ODE_Exception, self.simulator._set_rtol, 'hej') + nose.tools.assert_raises(Explicit_ODE_Exception, self.simulator._set_atol, 'hej') + nose.tools.assert_raises(Explicit_ODE_Exception, self.simulator._set_rtol, -1) + + self.simulator.rtol = 1.0 + assert self.simulator._get_rtol() == 1.0 + self.simulator.rtol = 1 + assert self.simulator._get_rtol() == 1 + + self.simulator.atol = 1.0 + assert self.simulator.atol == 1.0 + + nose.tools.assert_raises(Explicit_ODE_Exception, self.simulator._set_atol, [1.0,1.0]) + + + @testattr(stddist = True) + def test_switches(self): + """ + This tests that the switches are actually turned when override. + """ + f = lambda t,x,sw: N.array([1.0]) + state_events = lambda t,x,sw: N.array([x[0]-1.]) + def handle_event(solver, event_info): + solver.sw = [False] #Override the switches to point to another instance + + mod = Explicit_Problem(f,[0.0]) + mod.sw0 = [True] + + mod.state_events = state_events + mod.handle_event = handle_event + + sim = RungeKutta34(mod) + assert sim.sw[0] == True + sim.simulate(3) + assert sim.sw[0] == False + + +class Test_RungeKutta4: + + def setUp(self): + """ + This function sets up the test case. + """ + f = lambda t,y:1.0 + y0 = 1 + + self.problem = Explicit_Problem(f,y0) + self.simulator = RungeKutta4(self.problem) + + @testattr(stddist = True) + def test_time_event(self): + f = lambda t,y: [1.0] + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Explicit_Problem(f,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = RungeKutta4(exp_mod) + exp_sim(5.,100) + + assert nevent == 5 + + @testattr(stddist = True) + def test_integrate(self): + values = self.simulator.simulate(1) + + nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) + nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) + + @testattr(stddist = True) + def test_step(self): + self.simulator.report_continuously = True + + self.simulator.h = 0.1 + self.simulator.simulate(1) + + nose.tools.assert_almost_equal(self.simulator.t_sol[-1], 1.0) + nose.tools.assert_almost_equal(float(self.simulator.y_sol[-1]), 2.0) diff --git a/tests/solvers/test_sundials.py b/tests/solvers/test_sundials.py index 8d714bd5..e8392de6 100644 --- a/tests/solvers/test_sundials.py +++ b/tests/solvers/test_sundials.py @@ -1,1450 +1,1450 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import nose -from assimulo import testattr -from assimulo.solvers.sundials import * -from assimulo.problem import Explicit_Problem -from assimulo.problem import Implicit_Problem -from assimulo.exception import * -import numpy as np -import scipy.sparse as sp - -class Extended_Problem(Explicit_Problem): - - #Sets the initial conditons directly into the problem - y0 = [0.0, -1.0, 0.0] - sw0 = [False,True,True] - event_array = N.array([0.0,0.0,0.0]) - rhs_array = N.array([0.0,0.0,0.0]) - - #The right-hand-side function (rhs) - def rhs(self,t,y,sw): - """ - This is our function we are trying to simulate. During simulation - the parameter sw should be fixed so that our function is continuous - over the interval. The parameters sw should only be changed when the - integrator has stopped. - """ - self.rhs_array[0] = (1.0 if sw[0] else -1.0) - self.rhs_array[1] = 0.0 - self.rhs_array[2] = 0.0 - - return self.rhs_array - - #Sets a name to our function - name = 'ODE with discontinuities and a function with consistency problem' - - #The event function - def state_events(self,t,y,sw): - """ - This is our function that keeps track of our events. When the sign - of any of the events has changed, we have an event. - """ - self.event_array[0] = y[1] - 1.0 - self.event_array[1] = -y[2] + 1.0 - self.event_array[2] = -t + 1.0 - - return self.event_array - - #Responsible for handling the events. - def handle_event(self, solver, event_info): - """ - Event handling. This functions is called when Assimulo finds an event as - specified by the event functions. - """ - event_info = event_info[0] #We only look at the state events information. - while True: #Event Iteration - self.event_switch(solver, event_info) #Turns the switches - - b_mode = self.state_events(solver.t, solver.y, solver.sw).copy() - self.init_mode(solver) #Pass in the solver to the problem specified init_mode - a_mode = self.state_events(solver.t, solver.y, solver.sw).copy() - - event_info = self.check_eIter(b_mode, a_mode) - - if not True in event_info: #Breaks the iteration loop - break - - #Helper function for handle_event - def event_switch(self, solver, event_info): - """ - Turns the switches. - """ - for i in range(len(event_info)): #Loop across all event functions - if event_info[i] != 0: - solver.sw[i] = not solver.sw[i] #Turn the switch - - #Helper function for handle_event - def check_eIter(self, before, after): - """ - Helper function for handle_event to determine if we have event - iteration. - - Input: Values of the event indicator functions (state_events) - before and after we have changed mode of operations. - """ - - eIter = [False]*len(before) - - for i in range(len(before)): - if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): - eIter[i] = True - - return eIter - - def init_mode(self, solver): - """ - Initialize the DAE with the new conditions. - """ - solver.y[1] = (-1.0 if solver.sw[1] else 3.0) - solver.y[2] = (0.0 if solver.sw[2] else 2.0) - -class Test_CVode: - - def setUp(self): - """ - This function sets up the test case. - """ - f = lambda t,y:N.array(y) - y0 = [1.0] - - self.problem = Explicit_Problem(f,y0) - self.simulator = CVode(self.problem) - self.simulator.verbosity = 0 - - @testattr(stddist = True) - def test_backward_integration(self): - def f(t, y): - x, v = y - return [x, -x - 0.1*v] - - mod = Explicit_Problem(f, y0=[1, 0], t0=10) - sim = CVode(mod) - sim.backward = True - t, y = sim.simulate(0, ncp_list=np.arange(1, 10)) - - assert np.all(t == np.arange(0,11)[::-1]) - - mod = Explicit_Problem(f, y0=[1, 0], t0=10) - sim = CVode(mod) - sim.backward = True - t, y = sim.simulate(0, ncp_list=np.arange(1, 10)[::-1]) - - assert np.all(t == np.arange(0,11)[::-1]) - - @testattr(stddist = True) - def test_event_localizer(self): - exp_mod = Extended_Problem() #Create the problem - - exp_sim = CVode(exp_mod) #Create the solver - - exp_sim.verbosity = 0 - exp_sim.report_continuously = True - - #Simulate - t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points - - #Basic test - nose.tools.assert_almost_equal(y[-1][0],8.0) - nose.tools.assert_almost_equal(y[-1][1],3.0) - nose.tools.assert_almost_equal(y[-1][2],2.0) - - @testattr(stddist = True) - def test_get_error_weights(self): - nose.tools.assert_raises(CVodeError, self.simulator.get_error_weights) - - self.simulator.simulate(1.0) - - weights = self.simulator.get_error_weights() - assert weights[0] < 1e6 - - @testattr(stddist = True) - def test_get_used_initial_step(self): - self.simulator.simulate(1.0) - - step = self.simulator.get_used_initial_step() - nose.tools.assert_almost_equal(step, 0.001, 3) - - self.simulator.reset() - - self.simulator.inith = 1e-8 - self.simulator.simulate(1.0) - - step = self.simulator.get_used_initial_step() - assert N.abs(step-1e-8) < 1e-2 - - - @testattr(stddist = True) - def test_get_local_errors(self): - nose.tools.assert_raises(CVodeError, self.simulator.get_local_errors) - - self.simulator.simulate(1.0) - - err = self.simulator.get_local_errors() - assert err[0] < 1e-5 - - @testattr(stddist = True) - def test_get_last_order(self): - nose.tools.assert_raises(CVodeError, self.simulator.get_last_order) - - self.simulator.simulate(1.0) - - qlast = self.simulator.get_last_order() - assert qlast == 4 - - @testattr(stddist = True) - def test_max_convergence_failures(self): - assert self.simulator.maxncf == self.simulator.options["maxncf"] - self.simulator.maxncf = 15 - assert self.simulator.maxncf == 15 - - nose.tools.assert_raises(AssimuloException, self.simulator._set_max_conv_fails, -1) - - @testattr(stddist = True) - def test_max_error_tests_failures(self): - assert self.simulator.maxnef == self.simulator.options["maxnef"] - self.simulator.maxnef = 15 - assert self.simulator.maxnef == 15 - assert self.simulator.options["maxnef"] == 15 - - nose.tools.assert_raises(AssimuloException, self.simulator._set_max_err_fails, -1) - - @testattr(stddist = True) - def test_max_nonlinear_iterations(self): - assert self.simulator.maxcor == self.simulator.options["maxcor"] - self.simulator.maxcor = 15 - assert self.simulator.maxcor == 15 - assert self.simulator.options["maxcor"] == 15 - - #nose.tools.assert_raises(AssimuloException, self.simulator._set_max_err_fails, -1) - - @testattr(stddist = True) - def test_get_current_order(self): - - nose.tools.assert_raises(CVodeError, self.simulator.get_current_order) - - self.simulator.simulate(1.0) - - qcur = self.simulator.get_current_order() - assert qcur == 4 - - - - @testattr(stddist = True) - def test_init(self): - """ - This tests the functionality of the method __init__. - """ - #assert self.simulator.f == 'Test function' - assert self.simulator.y == 1.0 - assert self.simulator.discr == 'BDF' - assert self.simulator.iter == 'Newton' - assert self.simulator.maxord == 5 - - self.simulator.discr = 'Adams' - assert self.simulator.discr == 'Adams' - assert self.simulator.maxord == 12 - - @testattr(stddist = True) - def test_time_event(self): - f = lambda t,y: [1.0] - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Explicit_Problem(f,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = CVode(exp_mod) - exp_sim(5.,100) - - assert nevent == 5 - - @testattr(stddist = True) - def test_clear_event_log(self): - f = lambda t,y: [1.0] - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Explicit_Problem(f,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = CVode(exp_mod) - exp_sim.verbosity = 10 - exp_sim(5.,100) - - assert len(exp_sim.event_data) == 4 - - tnext = 0.0 - nevent = 0 - - exp_sim.reset() - assert len(exp_sim.event_data) == 0 - - exp_sim(5.,100) - assert len(exp_sim.event_data) == 4 - - @testattr(stddist = True) - def test_time_limit(self): - f = lambda t,y: -y - - exp_mod = Explicit_Problem(f,1.0) - exp_sim = CVode(exp_mod) - - exp_sim.maxh = 1e-8 - exp_sim.time_limit = 1 #One second - exp_sim.report_continuously = True - - nose.tools.assert_raises(TimeLimitExceeded, exp_sim.simulate, 1) - - @testattr(stddist = True) - def test_discr_method(self): - """ - This tests the functionality of the property discr. - """ - - nose.tools.assert_raises(Exception, self.simulator._set_discr_method, 'Test') - nose.tools.assert_raises(Exception, self.simulator._set_discr_method, 1) - nose.tools.assert_raises(Exception, self.simulator._set_discr_method, [1.0, 1]) - nose.tools.assert_raises(Exception, self.simulator._set_discr_method, {'Test':'case'}) - nose.tools.assert_raises(Exception, self.simulator._set_discr_method, 5.1) - nose.tools.assert_raises(Exception, self.simulator._set_discr_method, ['Test']) - - self.simulator.discr = 'BDF' - assert self.simulator.discr == 'BDF' - self.simulator.discr = 'Adams' - assert self.simulator.discr == 'Adams' - - @testattr(stddist = True) - def test_change_discr(self): - """ - This tests that the change from Functional to Newton works - """ - f = lambda t,y: N.array([1.0]) - y0 = 4.0 #Initial conditions - - exp_mod = Explicit_Problem(f,y0) - exp_sim = CVode(exp_mod) #Create a CVode solver - - exp_sim.iter = "FixedPoint" - exp_sim.simulate(1) - assert exp_sim.statistics["njacs"] == 0 - exp_sim.iter = "Newton" - exp_sim.simulate(2) - assert exp_sim.statistics["njacs"] > 0 - - @testattr(stddist = True) - def test_change_norm(self): - - assert self.simulator.options["norm"] == "WRMS" - self.simulator.norm = 'WRMS' - assert self.simulator.norm == 'WRMS' - self.simulator.norm = 'EUCLIDEAN' - assert self.simulator.options["norm"] == "EUCLIDEAN" - assert self.simulator.norm == 'EUCLIDEAN' - - f = lambda t,y: N.array([1.0]) - y0 = 4.0 #Initial conditions - - exp_mod = Explicit_Problem(f,y0) - exp_sim = CVode(exp_mod) #Create a CVode solver - - exp_sim.norm = "WRMS" - exp_sim.simulate(1) - - exp_mod = Explicit_Problem(f,y0) - exp_sim = CVode(exp_mod) #Create a CVode solver - - exp_sim.norm = "EUCLIDEAN" - exp_sim.simulate(1) - - @testattr(stddist = True) - def test_usejac(self): - """ - This tests the functionality of the property usejac. - """ - f = lambda t,x: N.array([x[1], -9.82]) #Defines the rhs - jac = lambda t,x: N.array([[0.,1.],[0.,0.]]) #Defines the jacobian - - exp_mod = Explicit_Problem(f, [1.0,0.0]) - exp_mod.jac = jac - - exp_sim = CVode(exp_mod) - exp_sim.discr='BDF' - exp_sim.iter='Newton' - exp_sim.simulate(5.,100) - - assert exp_sim.statistics["nfcnjacs"] == 0 - nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4) - - exp_sim.reset() - exp_sim.usejac=False - exp_sim.simulate(5.,100) - - nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4) - assert exp_sim.statistics["nfcnjacs"] > 0 - - @testattr(stddist = True) - def test_usejac_csc_matrix(self): - """ - This tests the functionality of the property usejac. - """ - f = lambda t,x: N.array([x[1], -9.82]) #Defines the rhs - jac = lambda t,x: sp.csc_matrix(N.array([[0.,1.],[0.,0.]])) #Defines the jacobian - - exp_mod = Explicit_Problem(f, [1.0,0.0]) - exp_mod.jac = jac - - exp_sim = CVode(exp_mod) - exp_sim.discr='BDF' - exp_sim.iter='Newton' - exp_sim.simulate(5.,100) - - assert exp_sim.statistics["nfcnjacs"] == 0 - nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4) - - exp_sim.reset() - exp_sim.usejac=False - exp_sim.simulate(5.,100) - - nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4) - assert exp_sim.statistics["nfcnjacs"] > 0 - - @testattr(stddist = True) - def test_switches(self): - """ - This tests that the switches are actually turned when override. - """ - f = lambda t,x,sw: N.array([1.0]) - state_events = lambda t,x,sw: N.array([x[0]-1.]) - def handle_event(solver, event_info): - solver.sw = [False] #Override the switches to point to another instance - - mod = Explicit_Problem(f,[0.0]) - mod.sw0 = [True] - - mod.state_events = state_events - mod.handle_event = handle_event - - sim = CVode(mod) - assert sim.sw[0] == True - sim.simulate(3) - assert sim.sw[0] == False - - @testattr(stddist = True) - def test_iter_method(self): - """ - This tests the functionality of the property iter. - """ - - nose.tools.assert_raises(Exception, self.simulator._set_iter_method, 'Test') - nose.tools.assert_raises(Exception, self.simulator._set_iter_method, 1) - nose.tools.assert_raises(Exception, self.simulator._set_iter_method, 0) - nose.tools.assert_raises(Exception, self.simulator._set_iter_method, ['Test']) - nose.tools.assert_raises(Exception, self.simulator._set_iter_method, [1.0, 1]) - nose.tools.assert_raises(Exception, self.simulator._set_iter_method, 11.1) - - self.simulator.iter = 'Newton' - assert self.simulator.iter == 'Newton' - self.simulator.iter = 'FixedPoint' - assert self.simulator.iter == 'FixedPoint' - - @testattr(stddist = True) - def test_initial_step(self): - """ - This tests the functionality of the property initstep. - """ - - nose.tools.assert_raises(Exception, self.simulator._set_initial_step, 'Test') - nose.tools.assert_raises(Exception, self.simulator._set_initial_step, ['Test']) - - assert self.simulator.inith == 0.0 - self.simulator.inith = 10.0 - assert self.simulator.inith == 10.0 - self.simulator.inith = 1 - assert self.simulator.inith == 1.0 - - @testattr(stddist = True) - def test_interpolate(self): - """ - This tests the functionality of the method interpolate. - """ - f = lambda t,x: N.array(x**0.25) - - prob = Explicit_Problem(f,[1.0]) - - sim = CVode(prob) - sim.simulate(10., 100) - y100 = sim.y_sol - t100 = sim.t_sol - sim.reset() - sim.simulate(10.) - nose.tools.assert_almost_equal(float(y100[-2]), float(sim.interpolate(9.9,0)),5) - - @testattr(stddist = True) - def test_ncp_list(self): - f = lambda t,y:N.array(-y) - y0 = [4.0] - - prob = Explicit_Problem(f,y0) - sim = CVode(prob) - - t, y = sim.simulate(7, ncp_list=N.arange(0, 7, 0.1)) #Simulate 5 seconds - - nose.tools.assert_almost_equal(float(y[-1]), 0.00364832, 4) - - @testattr(stddist = True) - def test_handle_result(self): - """ - This function tests the handle result. - """ - f = lambda t,x: x**0.25 - def handle_result(solver,t,y): - assert solver.t == t - - prob = Explicit_Problem(f, [1.0]) - prob.handle_result = handle_result - - sim = CVode(prob) - sim.report_continuously = True - sim.simulate(10.) - - @testattr(stddist = True) - def test_max_order(self): - """ - This tests the functionality of the property maxord. - """ - self.simulator.discr='Adams' - - nose.tools.assert_raises(Exception, self.simulator._set_max_ord, "Test") - nose.tools.assert_raises(Exception, self.simulator._set_max_ord, [1,1]) - - self.simulator.maxord = -1 - assert self.simulator.maxord == 1 - self.simulator.maxord = 2 - assert self.simulator.maxord == 2 - self.simulator.maxord = 13 - assert self.simulator.maxord == 12 - - self.simulator.discr='BDF' - - nose.tools.assert_raises(Exception, self.simulator._set_max_ord, "Test") - nose.tools.assert_raises(Exception, self.simulator._set_max_ord, [1,1]) - - self.simulator.maxord = -1 - assert self.simulator.maxord == 1 - self.simulator.maxord = 2 - assert self.simulator.maxord == 2 - self.simulator.maxord = 6 - assert self.simulator.maxord == 5 - - @testattr(stddist = True) - def test_spgmr(self): - f = lambda t,y: N.array([y[1], -9.82]) - fsw = lambda t,y,sw: N.array([y[1], -9.82]) - fp = lambda t,y,p: N.array([y[1], -9.82]) - fswp = lambda t,y,sw,p: N.array([y[1], -9.82]) - jacv = lambda t,y,fy,v: N.dot(N.array([[0,1.],[0,0]]),v) - jacvsw = lambda t,y,fy,v,sw: N.dot(N.array([[0,1.],[0,0]]),v) - jacvp = lambda t,y,fy,v,p: N.dot(N.array([[0,1.],[0,0]]),v) - jacvswp = lambda t,y,fy,v,sw,p: N.dot(N.array([[0,1.],[0,0]]),v) - y0 = [1.0,0.0] #Initial conditions - - def run_sim(exp_mod): - exp_sim = CVode(exp_mod) #Create a CVode solver - exp_sim.linear_solver = 'SPGMR' #Change linear solver - - #Simulate - t, y = exp_sim.simulate(5, 1000) #Simulate 5 seconds with 1000 communication points - - #Basic tests - nose.tools.assert_almost_equal(y[-1][0],-121.75000000,4) - nose.tools.assert_almost_equal(y[-1][1],-49.100000000) - - exp_mod = Explicit_Problem(f,y0) - exp_mod.jacv = jacv #Sets the jacobian - run_sim(exp_mod) - - #Need someway of suppressing error messages from deep down in the Cython wrapper - #See http://stackoverflow.com/questions/1218933/can-i-redirect-the-stdout-in-python-into-some-sort-of-string-buffer - try: - from cStringIO import StringIO - except ImportError: - from io import StringIO - import sys - stderr = sys.stderr - sys.stderr = StringIO() - - exp_mod = Explicit_Problem(f,y0) - exp_mod.jacv = jacvsw #Sets the jacobian - nose.tools.assert_raises(CVodeError,run_sim,exp_mod) - - exp_mod = Explicit_Problem(fswp,y0,sw0=[True],p0=1.0) - exp_mod.jacv = jacvsw #Sets the jacobian - nose.tools.assert_raises(CVodeError,run_sim,exp_mod) - - #Restore standard error - sys.stderr = stderr - - exp_mod = Explicit_Problem(fp,y0,p0=1.0) - exp_mod.jacv = jacvp #Sets the jacobian - run_sim(exp_mod) - - exp_mod = Explicit_Problem(fsw,y0,sw0=[True]) - exp_mod.jacv = jacvsw #Sets the jacobian - run_sim(exp_mod) - - exp_mod = Explicit_Problem(fswp,y0,sw0=[True],p0=1.0) - exp_mod.jacv = jacvswp #Sets the jacobian - run_sim(exp_mod) - - @testattr(stddist = True) - def test_max_order_discr(self): - """ - This tests the maximum order when the discretization is changed. - """ - self.simulator.discr = "Adams" - self.simulator.maxord = 7 - assert self.simulator.maxord == 7 - - self.simulator.discr = 'Adams' - assert self.simulator.maxord == 12 - self.simulator.discr = 'BDF' - assert self.simulator.maxord == 5 - self.simulator.discr = 'Adams' - assert self.simulator.maxord == 12 - self.simulator.maxord = 4 - self.simulator.discr = 'BDF' - assert self.simulator.maxord == 5 - self.simulator.discr = 'Adams' - assert self.simulator.maxord == 12 - - @testattr(stddist = True) - def test_pretype(self): - """ - This tests the precondition option. - """ - assert self.simulator.precond == 'PREC_NONE' - self.simulator.precond = 'prec_none' - assert self.simulator.precond == 'PREC_NONE' - - nose.tools.assert_raises(Exception, self.simulator._set_pre_cond, -1.0) - nose.tools.assert_raises(Exception, self.simulator._set_pre_cond, 'PREC_BOTH1') - - @testattr(stddist = True) - def test_maxkrylov(self): - """ - This test the maximum number of krylov subspaces. - """ - assert self.simulator.maxkrylov == 5 - self.simulator.maxkrylov = 3 - assert self.simulator.maxkrylov == 3 - self.simulator.maxkrylov = 4.5 - assert self.simulator.maxkrylov == 4 - - nose.tools.assert_raises(Exception, self.simulator._set_max_krylov, 'Test') - - @testattr(stddist = True) - def test_stablimit(self): - assert self.simulator.stablimit == False - self.simulator.stablimit = True - assert self.simulator.stablimit == True - assert self.simulator.options["stablimit"] == True - - @testattr(stddist = True) - def test_linearsolver(self): - """ - This test the choice of the linear solver. - """ - assert self.simulator.linear_solver == 'DENSE' - self.simulator.linear_solver = 'dense' - assert self.simulator.linear_solver == 'DENSE' - self.simulator.linear_solver = 'spgmr' - assert self.simulator.linear_solver == 'SPGMR' - - nose.tools.assert_raises(Exception, self.simulator._set_linear_solver, 'Test') - - @testattr(stddist = True) - def test_terminate_simulation(self): - """ - This tests the functionality of raising TerminateSimulation exception in handle_result. - """ - class Extended_Problem(Explicit_Problem): - def __init__(self): - pass - def handle_event(self, solver, event_info): - if solver.t > 1.5: - raise TerminateSimulation - rhs = lambda self,t,y,sw: N.array([1.0]) - y0 = [1.0] - sw0 = [False,True] - state_events = lambda self,t,y,sw: N.array([t-1.0, t-2.0]) - - exp_mod = Extended_Problem() - simulator = CVode(exp_mod) - simulator(3.) - - nose.tools.assert_almost_equal(simulator.t, 2.000000, 4) - - @testattr(stddist = True) - def test_completed_step(self): - """ - This tests the functionality of the method completed_step. - """ - global nsteps - nsteps = 0 - f = lambda t,x: x**0.25 - def completed_step(solver): - global nsteps - nsteps += 1 - mod = Explicit_Problem(f, 1.0) - mod.step_events = completed_step - - sim = CVode(mod) - - sim.simulate(2., 100) - assert len(sim.t_sol) == 101 - assert nsteps == sim.statistics["nsteps"] - - sim = CVode(mod) - nsteps = 0 - sim.simulate(2.) - assert len(sim.t_sol) == sim.statistics["nsteps"]+1 - assert nsteps == sim.statistics["nsteps"] - -class Test_IDA: - - def setUp(self): - """ - This function sets up the test case. - """ - f = lambda t,y,yd: y - y0 = [1.0] - yd0 = [1.0] - - self.problem = Implicit_Problem(f,y0,yd0) - self.simulator = IDA(self.problem) - - @testattr(stddist = True) - def test_time_limit(self): - f = lambda t,y,yd: yd-y - - exp_mod = Implicit_Problem(f,1.0,1.0) - exp_sim = IDA(exp_mod) - - exp_sim.maxh = 1e-8 - exp_sim.time_limit = 1 #One second - exp_sim.report_continuously = True - - nose.tools.assert_raises(TimeLimitExceeded, exp_sim.simulate, 1) - - @testattr(stddist = True) - def test_simulate_explicit(self): - """ - Test a simulation of an explicit problem using IDA. - """ - f = lambda t,y:N.array(-y) - y0 = [1.0] - - problem = Explicit_Problem(f,y0) - simulator = IDA(problem) - - assert simulator.yd0[0] == -simulator.y0[0] - - t,y = simulator.simulate(1.0) - - nose.tools.assert_almost_equal(float(y[-1]), float(N.exp(-1.0)),4) - - @testattr(stddist = True) - def test_init(self): - """ - This tests the functionality of the method __init__. - """ - assert self.simulator.suppress_alg == False - assert self.simulator.algvar[0] == 1.0 - assert self.simulator.sw == None - assert self.simulator.maxsteps == 10000 - assert self.simulator.y[0] == 1.0 - - @testattr(stddist = True) - def test_interpolate(self): - """ - This tests the functionality of the method interpolate. - """ - f = lambda t,y,yd: y**0.25-yd - - prob = Implicit_Problem(f,[1.0],[1.0]) - - sim = IDA(prob) - sim.simulate(10., 100) - y100 = sim.y_sol - t100 = sim.t_sol - sim.reset() - sim.simulate(10.) - nose.tools.assert_almost_equal(y100[-2], sim.interpolate(9.9,0),5) - - @testattr(stddist = True) - def test_handle_result(self): - """ - This function tests the handle result. - """ - f = lambda t,x,xd: x**0.25-xd - def handle_result(solver, t ,y, yd): - assert solver.t == t - - prob = Implicit_Problem(f, [1.0],[1.0]) - prob.handle_result = handle_result - - sim = IDA(prob) - - sim.report_continuously = True - - sim.simulate(10.) - - @testattr(stddist = True) - def test_max_order(self): - """ - This tests the functionality of the property maxord. - """ - nose.tools.assert_raises(Exception, self.simulator._set_max_ord, "Test") - nose.tools.assert_raises(Exception, self.simulator._set_max_ord, [1,1]) - - - self.simulator.maxord = -1 - assert self.simulator.maxord == 1 - self.simulator.maxord = 2 - assert self.simulator.maxord == 2 - self.simulator.maxord = 6 - assert self.simulator.maxord == 5 - - @testattr(stddist = True) - def test_tout1(self): - """ - This tests the functionality of the property tout1. - """ - nose.tools.assert_raises(Exception, self.simulator._set_tout1, 'Test') - nose.tools.assert_raises(Exception, self.simulator._set_tout1, [1,1]) - nose.tools.assert_raises(Exception, self.simulator._set_tout1, 'Test') - - assert self.simulator.tout1 == 0.0001 - self.simulator.tout1 = -0.001 - assert self.simulator.tout1 == -0.001 - self.simulator.tout1 = 1 - assert self.simulator.tout1 == 1.0 - - @testattr(stddist = True) - def test_lsoff(self): - """ - This tests the functionality of the property lsoff. - """ - assert self.simulator.lsoff == False - self.simulator.lsoff = True - assert self.simulator.lsoff == True - self.simulator.lsoff = False - assert self.simulator.lsoff == False - - @testattr(stddist = True) - def test_initstep(self): - """ - This tests the funtionality of the property initstep. - """ - - def f(t,y,yd): - res_0 = yd[0] - y[1] - res_1 = yd[1] +9.82-0.01*y[1]**2 - return N.array([res_0,res_1]) - - mod = Implicit_Problem(f,y0=[5.0,0.0], yd0=[0.0,9.82]) - - - sim = IDA(mod) - sim.simulate(2.0) - - nose.tools.assert_almost_equal(sim.y_sol[-1][0], -13.4746473811, places=7) - - sim.reset() - sim.inith = 1e-10 - sim.simulate(2.0) - - nose.tools.assert_almost_equal(sim.y_sol[-1][0], -13.4746596311, places=7) - - @testattr(stddist = True) - def test_time_event(self): - """ - This tests the functionality of the time event function. - """ - f = lambda t,x,xd,sw: xd-x - - def time_events(t, y, yd, sw): - if sw[0]: - return 1.0 - if sw[1]: - return 3.0 - return None - - def handle_event(solver, event_info): - - if event_info[1]: - solver.y = N.array([1.0]) - solver.yd = N.array([1.0]) - - if not solver.sw[0]: - solver.sw[1] = False - - if solver.sw[0]: - solver.sw[0] = False - - mod = Implicit_Problem(f,[1.0],[1.0]) - mod.time_events = time_events - mod.handle_event = handle_event - mod.switches0 = [True, True] - - sim = IDA(mod) - - sim.simulate(5.0) - - nose.tools.assert_almost_equal(sim.y_sol[38], 1.0000000, 5) - nose.tools.assert_almost_equal(sim.y_sol[87], 1.0000000, 5) - - sim = IDA(mod, [1.0],[1.0]) - sim.simulate(2.0) - - nose.tools.assert_almost_equal(sim.t_sol[-1], 2.0000000, 5) - - @testattr(stddist = True) - def test_clear_event_log(self): - """ - This tests the functionality of the time event function. - """ - f = lambda t,x,xd,sw: xd-x - - def time_events(t, y, yd, sw): - if sw[0]: - return 1.0 - if sw[1]: - return 3.0 - return None - - def handle_event(solver, event_info): - - if event_info[1]: - solver.y = N.array([1.0]) - solver.yd = N.array([1.0]) - - if not solver.sw[0]: - solver.sw[1] = False - - if solver.sw[0]: - solver.sw[0] = False - - mod = Implicit_Problem(f,[1.0],[1.0], sw0=[True, True]) - mod.time_events = time_events - mod.handle_event = handle_event - - sim = IDA(mod) - sim.verbosity = 10 - assert len(sim.event_data) == 0 - sim.simulate(5.0) - assert len(sim.event_data) > 0 - - sim.reset() - assert len(sim.event_data) == 0 - sim.simulate(5.0) - assert len(sim.event_data) > 0 - - @testattr(stddist = True) - def test_usejac(self): - """ - This tests the functionality of the property usejac. - """ - f = lambda t,x,xd: N.array([xd[0]-x[1], xd[1]-9.82]) #Defines the rhs - jac = lambda c,t,x,xd: N.array([[c,-1.],[0.,c]]) #Defines the jacobian - - imp_mod = Implicit_Problem(f,[1.0,0.0],[0.,-9.82]) - imp_mod.jac = jac - - imp_sim = IDA(imp_mod) - - imp_sim.simulate(3,100) - - assert imp_sim.statistics["nfcnjacs"] == 0 - nose.tools.assert_almost_equal(imp_sim.y_sol[-1][0], 45.1900000, 4) - - imp_sim.reset() - imp_sim.usejac=False - imp_sim.simulate(3.,100) - - nose.tools.assert_almost_equal(imp_sim.y_sol[-1][0], 45.1900000, 4) - assert imp_sim.statistics["nfcnjacs"] > 0 - - @testattr(stddist = True) - def test_terminate_simulation(self): - """ - This tests the functionality of raising TerminateSimulation exception in handle_result. - """ - class Extended_Problem(Implicit_Problem): - def __init__(self): - pass - def handle_event(self,solver, event_info): - if solver.t > 1.5: - raise TerminateSimulation - res = lambda self,t,y,yd,sw: N.array([y[0]-1.0]) - state_events = lambda self,t,y,yd,sw: N.array([t-1.0, t-2.0]) - y0 = [1.0] - yd0 = [1.0] - sw0 = [False] - - prob = Extended_Problem() - - sim = IDA(prob) - sim.simulate(2.5) - - nose.tools.assert_almost_equal(sim.t, 2.000000, 4) - - @testattr(stddist = True) - def test_algvar(self): - """ - This tests the functionality of the property algvar. - """ - #self.simulator.Integrator.dim = 3 - - #nose.tools.assert_raises(Exception, self.simulator._set_algvar, 1) - #nose.tools.assert_raises(Exception, self.simulator._set_algvar, 1.0) - nose.tools.assert_raises(Exception, self.simulator._set_algvar, [1,'hej',1]) - nose.tools.assert_raises(Exception, self.simulator._set_algvar, {'Test':'case'}) - nose.tools.assert_raises(Exception, self.simulator._set_algvar, [-1,0,1]) - nose.tools.assert_raises(Exception, self.simulator._set_algvar, [1.0,1.0]) - nose.tools.assert_raises(Exception, self.simulator._set_algvar, [3.0, 1.0, 1.0]) - - #vector = [1.0,0.0,1.0] - #vectorb = [True,False,True] - #vectori = [1,0,1] - - #self.simulator.algvar = vectorb - #self.simulator.algvar = vectori - #self.simulator.algvar = vector - #nose.tools.assert_equal(self.simulator.algvar[0], vector[0]) - #nose.tools.assert_equal(self.simulator.algvar[1], vector[1]) - #nose.tools.assert_equal(self.simulator.algvar[2], vector[2]) - - @testattr(stddist = True) - def test_time_event(self): - f = lambda t,y,yd: y-yd - global tnext - global nevent - tnext = 0.0 - nevent = 0 - def time_events(t,y,yd,sw): - global tnext,nevent - events = [1.0, 2.0, 2.5, 3.0] - for ev in events: - if t < ev: - tnext = ev - break - else: - tnext = None - nevent += 1 - return tnext - - def handle_event(solver, event_info): - solver.y+= 1.0 - global tnext - nose.tools.assert_almost_equal(solver.t, tnext) - assert event_info[0] == [] - assert event_info[1] == True - - exp_mod = Implicit_Problem(f,0.0,0.0) - exp_mod.time_events = time_events - exp_mod.handle_event = handle_event - - #CVode - exp_sim = IDA(exp_mod) - exp_sim(5.,100) - - assert nevent == 5 - - @testattr(stddist = True) - def test_suppress_alg(self): - """ - This tests the functionality of the property suppress_alg. - """ - self.simulator.suppress_alg = True - assert self.simulator.suppress_alg == True - self.simulator.suppress_alg = False - assert self.simulator.suppress_alg == False - - @testattr(stddist = True) - def test_make_consistency(self): - """ - This tests the functionality of the method make_consistency. - """ - def f(t,y,yd): - res_1 = y[0] + y[1]+1.0 - res_2 = y[1] - return N.array([res_1, res_2]) - y0 = [2.0, 2.0] - yd0 = [1.0 , 0.0] - - - my_Prob = Implicit_Problem(f, y0, yd0) - - simulator = IDA(my_Prob) - - [flag, y, yd] = simulator.make_consistent('IDA_Y_INIT') - - nose.tools.assert_almost_equal(y[1], 0.00000) - nose.tools.assert_almost_equal(y[0], -1.0000) - nose.tools.assert_almost_equal(yd[0], 1.0000) - nose.tools.assert_almost_equal(yd[1], 0.0000) - - @testattr(stddist = True) - def test_switches(self): - """ - This tests that the switches are actually turned when override. - """ - f = lambda t,x,xd,sw: N.array([xd[0]- 1.0]) - state_events = lambda t,x,xd,sw: N.array([x[0]-1.]) - def handle_event(solver, event_info): - solver.sw = [False] #Override the switches to point to another instance - - mod = Implicit_Problem(f, [0.0],[1.0]) - mod.f = f - mod.sw0 = [True] - mod.state_events = state_events - mod.handle_event = handle_event - - sim = IDA(mod) - assert sim.sw[0] == True - sim.simulate(3) - assert sim.sw[0] == False - - @testattr(stddist = True) - def test_completed_step(self): - """ - This tests the functionality of the method completed_step. - """ - global nsteps - nsteps = 0 - def f(t,y,yd): - res_1 = y[0] + y[1]+1.0 - res_2 = y[1] - return N.array([res_1, res_2]) - def completed_step(solver): - global nsteps - nsteps += 1 - - y0 = [-1.0, 0.0] - yd0 = [1.0 , 0.0] - - mod = Implicit_Problem(f, y0, yd0) - mod.step_events = completed_step - - sim = IDA(mod) - - sim.simulate(2., 100) - assert len(sim.t_sol) == 101 - assert nsteps == sim.statistics["nsteps"] - - sim = IDA(mod) - nsteps = 0 - sim.simulate(2.) - assert len(sim.t_sol) == sim.statistics["nsteps"] + 1 - assert nsteps == sim.statistics["nsteps"] - - - -class Test_Sundials: - - def setUp(self): - """ - This sets up the test case. - """ - class Prob_IDA(Implicit_Problem): - def __init__(self): - pass - res = lambda self,t,y,yd,sw: N.array([y[0]-1.0]) - state_events = lambda self,t,y,yd,sw: N.array([t-1.0, t]) - y0 = [1.0] - yd0 = [1.0] - sw0 = [False, True] - - res = Prob_IDA() - - class Prob_CVode(Explicit_Problem): - def __init__(self): - pass - rhs = lambda self,t,y,sw: N.array([1.0]) - state_events = lambda self,t,y,sw: N.array([t-1.0, t]) - y0 = [1.0] - sw0 = [False, True] - - f = Prob_CVode() - - self.simulators = [IDA(res), CVode(f)] - - - f = lambda t,y,yd,p: N.array([0.0]) - y0 = [1.0] - yd0 = [1.0] - p0 = [1.0] - - mod = Implicit_Problem(f, y0,yd0,p0=p0) - self.sim = IDA(mod) - - @testattr(stddist = True) - def test_atol(self): - """ - This tests the functionality of the property atol. - """ - assert self.simulators[1].atol == 1.0e-6 - assert self.simulators[0].atol == 1.0e-6 - - for i in range(len(self.simulators)): - nose.tools.assert_raises(Exception, self.simulators[i]._set_atol, -1.0) - nose.tools.assert_raises(Exception, self.simulators[i]._set_atol, [1.0, 1.0]) - nose.tools.assert_raises(Exception, self.simulators[i]._set_atol, "Test") - - self.simulators[i].atol = 1.0e-5 - assert self.simulators[i].atol == 1.0e-5 - self.simulators[i].atol = 1.0 - assert self.simulators[i].atol == 1.0 - self.simulators[i].atol = 1 - assert self.simulators[i].atol == 1.0 - self.simulators[i].atol = 1001.0 - assert self.simulators[i].atol == 1001.0 - self.simulators[i].atol = [N.array([1e-5])] - assert len(self.simulators[i].atol.shape) == 1 - assert self.simulators[i].atol == 1e-5 - """ - self.simulators[i].Integrator.dim = 3 - nose.tools.assert_raises(Exception, self.simulators[i]._set_atol, [1.0, 1.0]) - nose.tools.assert_raises(Exception, self.simulators[i]._set_atol, [1.0, 1.0, -1.0]) - self.simulators[i].atol = [1.0, 1.0, 1.0] - assert self.simulators[i].atol == [1.0, 1.0, 1.0] - self.simulators[i].atol = N.array([1.0, 1.0, 1.0]) - assert self.simulators[i].atol[0] == 1.0 - self.simulators[i].atol = N.array([1, 5, 1.0]) - assert self.simulators[i].atol[0] == 1.0 - """ - - - @testattr(stddist = True) - def test_rtol(self): - """ - This tests the functionality of the property rtol. - """ - for i in range(len(self.simulators)): - nose.tools.assert_raises(Exception, self.simulators[i]._set_rtol, -1.0) - nose.tools.assert_raises(Exception, self.simulators[i]._set_rtol, [1.0, 1.0]) - nose.tools.assert_raises(Exception, self.simulators[i]._set_rtol, "Test") - - self.simulators[i].rtol = 1.0e-5 - assert self.simulators[i].rtol == 1.0e-5 - self.simulators[i].rtol = 1.0 - assert self.simulators[i].rtol == 1.0 - self.simulators[i].rtol = 1001.0 - assert self.simulators[i].rtol == 1001.0 - self.simulators[i].rtol = 1001 - assert self.simulators[i].rtol == 1001.0 - - @testattr(stddist = True) - def test_maxh(self): - """ - This tests the functionality of the property maxh. - """ - for i in range(len(self.simulators)): - nose.tools.assert_raises(Exception, self.simulators[i]._set_max_h, [1.0, 1.0]) - nose.tools.assert_raises(Exception, self.simulators[i]._set_max_h, "Test") - - self.simulators[i].maxh = 1.0e-5 - assert self.simulators[i].maxh == 1.0e-5 - self.simulators[i].maxh = 1.0 - assert self.simulators[i].maxh == 1.0 - self.simulators[i].maxh = 1001.0 - assert self.simulators[i].maxh == 1001.0 - - @testattr(stddist = True) - def test_dqtype(self): - """ - Tests the property of dqtype. - """ - - assert self.sim.dqtype == 'CENTERED' #Test the default value. - - self.sim.dqtype = 'FORWARD' - assert self.sim.dqtype == 'FORWARD' - self.sim.dqtype = 'CENTERED' - assert self.sim.dqtype == 'CENTERED' - - self.sim.dqtype = 'forward' - assert self.sim.dqtype == 'FORWARD' - self.sim.dqtype = 'centered' - assert self.sim.dqtype == 'CENTERED' - - nose.tools.assert_raises(Exception,self.sim._set_dqtype, 1) - nose.tools.assert_raises(Exception,self.sim._set_dqtype, 'IDA_CE') - nose.tools.assert_raises(Exception,self.sim._set_dqtype, [1]) - nose.tools.assert_raises(Exception,self.sim._set_dqtype, -1) - - @testattr(stddist = True) - def test_dqrhomax(self): - """ - Tests the property of DQrhomax. - """ - assert self.sim.dqrhomax == 0.0 #Test the default value. - - self.sim.dqrhomax = 1.0 - assert self.sim.dqrhomax == 1.0 - self.sim.dqrhomax = 10 - assert self.sim.dqrhomax == 10 - - nose.tools.assert_raises(Exception,self.sim._set_dqrhomax, -1) - nose.tools.assert_raises(Exception,self.sim._set_dqrhomax, 'str') - nose.tools.assert_raises(Exception,self.sim._set_dqrhomax, []) - nose.tools.assert_raises(Exception,self.sim._set_dqrhomax, -10) - - @testattr(stddist = True) - def test_usesens(self): - """ - Tests the property of usesens. - """ - assert self.sim.usesens == True #Test the default value. - self.sim.usesens = False - assert self.sim.usesens == False - self.sim.usesens = 0 - assert self.sim.usesens == False - self.sim.usesens = 1 - assert self.sim.usesens == True - - @testattr(stddist = True) - def test_sensmethod(self): - """ - Tests the property of sensmethod. - """ - assert self.sim.sensmethod == 'STAGGERED' #Test the default value - - self.sim.sensmethod = 'SIMULTANEOUS' - assert self.sim.sensmethod == 'SIMULTANEOUS' - self.sim.sensmethod = 'STAGGERED' - assert self.sim.sensmethod == 'STAGGERED' - - self.sim.sensmethod = 'simultaneous' - assert self.sim.sensmethod == 'SIMULTANEOUS' - self.sim.sensmethod = 'staggered' - assert self.sim.sensmethod == 'STAGGERED' - - nose.tools.assert_raises(Exception,self.sim._set_sensitivity_method, 1) - nose.tools.assert_raises(Exception,self.sim._set_sensitivity_method, 'IDA_CE') - nose.tools.assert_raises(Exception,self.sim._set_sensitivity_method, [1]) - nose.tools.assert_raises(Exception,self.sim._set_sensitivity_method, -1) - - @testattr(stddist = True) - def test_suppress_sens(self): - """ - Tests the property of suppress_sens. - """ - assert self.sim.suppress_sens == False - self.sim.suppress_sens = False - assert self.sim.suppress_sens == False - self.sim.suppress_sens = 0 - assert self.sim.suppress_sens == False - self.sim.suppress_sens = 1 - assert self.sim.suppress_sens == True - - @testattr(stddist = True) - def test_maxsensiter(self): - """ - Tests the property of maxsensiter. - """ - assert self.sim.maxcorS == 3 #Test the default value - self.sim.maxcorS = 1 - assert self.sim.maxcorS == 1 - self.sim.maxcorS = 10.5 - assert self.sim.maxcorS == 10 - - #nose.tools.assert_raises(Exception, self.sim._set_max_cor_S, 0) - nose.tools.assert_raises(Exception, self.sim._set_max_cor_S, 'str') - nose.tools.assert_raises(Exception, self.sim._set_max_cor_S, []) - #nose.tools.assert_raises(Exception, self.sim._set_max_cor_S, -10) - - @testattr(stddist = True) - def test_pbar(self): - """ - Tests the property of pbar. - """ - f = lambda t,y,p:N.array([0.0]*len(y)) - y0 = [1.0]*2 - p0 = [1000.0, -100.0] - exp_mod = Explicit_Problem(f,y0,p0=p0) - - exp_sim = CVode(exp_mod) - - nose.tools.assert_almost_equal(exp_sim.pbar[0], 1000.00000,4) - nose.tools.assert_almost_equal(exp_sim.pbar[1], 100.000000,4) - - f = lambda t,y,yd,p: N.array([0.0]*len(y)) - yd0 = [0.0]*2 - imp_mod = Implicit_Problem(f,y0,yd0,p0=p0) - - imp_sim = IDA(imp_mod) - - nose.tools.assert_almost_equal(imp_sim.pbar[0], 1000.00000,4) - nose.tools.assert_almost_equal(imp_sim.pbar[1], 100.000000,4) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import nose +from assimulo import testattr +from assimulo.solvers.sundials import * +from assimulo.problem import Explicit_Problem +from assimulo.problem import Implicit_Problem +from assimulo.exception import * +import numpy as np +import scipy.sparse as sp + +class Extended_Problem(Explicit_Problem): + + #Sets the initial conditons directly into the problem + y0 = [0.0, -1.0, 0.0] + sw0 = [False,True,True] + event_array = N.array([0.0,0.0,0.0]) + rhs_array = N.array([0.0,0.0,0.0]) + + #The right-hand-side function (rhs) + def rhs(self,t,y,sw): + """ + This is our function we are trying to simulate. During simulation + the parameter sw should be fixed so that our function is continuous + over the interval. The parameters sw should only be changed when the + integrator has stopped. + """ + self.rhs_array[0] = (1.0 if sw[0] else -1.0) + self.rhs_array[1] = 0.0 + self.rhs_array[2] = 0.0 + + return self.rhs_array + + #Sets a name to our function + name = 'ODE with discontinuities and a function with consistency problem' + + #The event function + def state_events(self,t,y,sw): + """ + This is our function that keeps track of our events. When the sign + of any of the events has changed, we have an event. + """ + self.event_array[0] = y[1] - 1.0 + self.event_array[1] = -y[2] + 1.0 + self.event_array[2] = -t + 1.0 + + return self.event_array + + #Responsible for handling the events. + def handle_event(self, solver, event_info): + """ + Event handling. This functions is called when Assimulo finds an event as + specified by the event functions. + """ + event_info = event_info[0] #We only look at the state events information. + while True: #Event Iteration + self.event_switch(solver, event_info) #Turns the switches + + b_mode = self.state_events(solver.t, solver.y, solver.sw).copy() + self.init_mode(solver) #Pass in the solver to the problem specified init_mode + a_mode = self.state_events(solver.t, solver.y, solver.sw).copy() + + event_info = self.check_eIter(b_mode, a_mode) + + if not True in event_info: #Breaks the iteration loop + break + + #Helper function for handle_event + def event_switch(self, solver, event_info): + """ + Turns the switches. + """ + for i in range(len(event_info)): #Loop across all event functions + if event_info[i] != 0: + solver.sw[i] = not solver.sw[i] #Turn the switch + + #Helper function for handle_event + def check_eIter(self, before, after): + """ + Helper function for handle_event to determine if we have event + iteration. + + Input: Values of the event indicator functions (state_events) + before and after we have changed mode of operations. + """ + + eIter = [False]*len(before) + + for i in range(len(before)): + if (before[i] < 0.0 and after[i] > 0.0) or (before[i] > 0.0 and after[i] < 0.0): + eIter[i] = True + + return eIter + + def init_mode(self, solver): + """ + Initialize the DAE with the new conditions. + """ + solver.y[1] = (-1.0 if solver.sw[1] else 3.0) + solver.y[2] = (0.0 if solver.sw[2] else 2.0) + +class Test_CVode: + + def setUp(self): + """ + This function sets up the test case. + """ + f = lambda t,y:N.array(y) + y0 = [1.0] + + self.problem = Explicit_Problem(f,y0) + self.simulator = CVode(self.problem) + self.simulator.verbosity = 0 + + @testattr(stddist = True) + def test_backward_integration(self): + def f(t, y): + x, v = y + return [x, -x - 0.1*v] + + mod = Explicit_Problem(f, y0=[1, 0], t0=10) + sim = CVode(mod) + sim.backward = True + t, y = sim.simulate(0, ncp_list=np.arange(1, 10)) + + assert np.all(t == np.arange(0,11)[::-1]) + + mod = Explicit_Problem(f, y0=[1, 0], t0=10) + sim = CVode(mod) + sim.backward = True + t, y = sim.simulate(0, ncp_list=np.arange(1, 10)[::-1]) + + assert np.all(t == np.arange(0,11)[::-1]) + + @testattr(stddist = True) + def test_event_localizer(self): + exp_mod = Extended_Problem() #Create the problem + + exp_sim = CVode(exp_mod) #Create the solver + + exp_sim.verbosity = 0 + exp_sim.report_continuously = True + + #Simulate + t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points + + #Basic test + nose.tools.assert_almost_equal(y[-1][0],8.0) + nose.tools.assert_almost_equal(y[-1][1],3.0) + nose.tools.assert_almost_equal(y[-1][2],2.0) + + @testattr(stddist = True) + def test_get_error_weights(self): + nose.tools.assert_raises(CVodeError, self.simulator.get_error_weights) + + self.simulator.simulate(1.0) + + weights = self.simulator.get_error_weights() + assert weights[0] < 1e6 + + @testattr(stddist = True) + def test_get_used_initial_step(self): + self.simulator.simulate(1.0) + + step = self.simulator.get_used_initial_step() + nose.tools.assert_almost_equal(step, 0.001, 3) + + self.simulator.reset() + + self.simulator.inith = 1e-8 + self.simulator.simulate(1.0) + + step = self.simulator.get_used_initial_step() + assert N.abs(step-1e-8) < 1e-2 + + + @testattr(stddist = True) + def test_get_local_errors(self): + nose.tools.assert_raises(CVodeError, self.simulator.get_local_errors) + + self.simulator.simulate(1.0) + + err = self.simulator.get_local_errors() + assert err[0] < 1e-5 + + @testattr(stddist = True) + def test_get_last_order(self): + nose.tools.assert_raises(CVodeError, self.simulator.get_last_order) + + self.simulator.simulate(1.0) + + qlast = self.simulator.get_last_order() + assert qlast == 4 + + @testattr(stddist = True) + def test_max_convergence_failures(self): + assert self.simulator.maxncf == self.simulator.options["maxncf"] + self.simulator.maxncf = 15 + assert self.simulator.maxncf == 15 + + nose.tools.assert_raises(AssimuloException, self.simulator._set_max_conv_fails, -1) + + @testattr(stddist = True) + def test_max_error_tests_failures(self): + assert self.simulator.maxnef == self.simulator.options["maxnef"] + self.simulator.maxnef = 15 + assert self.simulator.maxnef == 15 + assert self.simulator.options["maxnef"] == 15 + + nose.tools.assert_raises(AssimuloException, self.simulator._set_max_err_fails, -1) + + @testattr(stddist = True) + def test_max_nonlinear_iterations(self): + assert self.simulator.maxcor == self.simulator.options["maxcor"] + self.simulator.maxcor = 15 + assert self.simulator.maxcor == 15 + assert self.simulator.options["maxcor"] == 15 + + #nose.tools.assert_raises(AssimuloException, self.simulator._set_max_err_fails, -1) + + @testattr(stddist = True) + def test_get_current_order(self): + + nose.tools.assert_raises(CVodeError, self.simulator.get_current_order) + + self.simulator.simulate(1.0) + + qcur = self.simulator.get_current_order() + assert qcur == 4 + + + + @testattr(stddist = True) + def test_init(self): + """ + This tests the functionality of the method __init__. + """ + #assert self.simulator.f == 'Test function' + assert self.simulator.y == 1.0 + assert self.simulator.discr == 'BDF' + assert self.simulator.iter == 'Newton' + assert self.simulator.maxord == 5 + + self.simulator.discr = 'Adams' + assert self.simulator.discr == 'Adams' + assert self.simulator.maxord == 12 + + @testattr(stddist = True) + def test_time_event(self): + f = lambda t,y: [1.0] + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Explicit_Problem(f,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = CVode(exp_mod) + exp_sim(5.,100) + + assert nevent == 5 + + @testattr(stddist = True) + def test_clear_event_log(self): + f = lambda t,y: [1.0] + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Explicit_Problem(f,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = CVode(exp_mod) + exp_sim.verbosity = 10 + exp_sim(5.,100) + + assert len(exp_sim.event_data) == 4 + + tnext = 0.0 + nevent = 0 + + exp_sim.reset() + assert len(exp_sim.event_data) == 0 + + exp_sim(5.,100) + assert len(exp_sim.event_data) == 4 + + @testattr(stddist = True) + def test_time_limit(self): + f = lambda t,y: -y + + exp_mod = Explicit_Problem(f,1.0) + exp_sim = CVode(exp_mod) + + exp_sim.maxh = 1e-8 + exp_sim.time_limit = 1 #One second + exp_sim.report_continuously = True + + nose.tools.assert_raises(TimeLimitExceeded, exp_sim.simulate, 1) + + @testattr(stddist = True) + def test_discr_method(self): + """ + This tests the functionality of the property discr. + """ + + nose.tools.assert_raises(Exception, self.simulator._set_discr_method, 'Test') + nose.tools.assert_raises(Exception, self.simulator._set_discr_method, 1) + nose.tools.assert_raises(Exception, self.simulator._set_discr_method, [1.0, 1]) + nose.tools.assert_raises(Exception, self.simulator._set_discr_method, {'Test':'case'}) + nose.tools.assert_raises(Exception, self.simulator._set_discr_method, 5.1) + nose.tools.assert_raises(Exception, self.simulator._set_discr_method, ['Test']) + + self.simulator.discr = 'BDF' + assert self.simulator.discr == 'BDF' + self.simulator.discr = 'Adams' + assert self.simulator.discr == 'Adams' + + @testattr(stddist = True) + def test_change_discr(self): + """ + This tests that the change from Functional to Newton works + """ + f = lambda t,y: N.array([1.0]) + y0 = 4.0 #Initial conditions + + exp_mod = Explicit_Problem(f,y0) + exp_sim = CVode(exp_mod) #Create a CVode solver + + exp_sim.iter = "FixedPoint" + exp_sim.simulate(1) + assert exp_sim.statistics["njacs"] == 0 + exp_sim.iter = "Newton" + exp_sim.simulate(2) + assert exp_sim.statistics["njacs"] > 0 + + @testattr(stddist = True) + def test_change_norm(self): + + assert self.simulator.options["norm"] == "WRMS" + self.simulator.norm = 'WRMS' + assert self.simulator.norm == 'WRMS' + self.simulator.norm = 'EUCLIDEAN' + assert self.simulator.options["norm"] == "EUCLIDEAN" + assert self.simulator.norm == 'EUCLIDEAN' + + f = lambda t,y: N.array([1.0]) + y0 = 4.0 #Initial conditions + + exp_mod = Explicit_Problem(f,y0) + exp_sim = CVode(exp_mod) #Create a CVode solver + + exp_sim.norm = "WRMS" + exp_sim.simulate(1) + + exp_mod = Explicit_Problem(f,y0) + exp_sim = CVode(exp_mod) #Create a CVode solver + + exp_sim.norm = "EUCLIDEAN" + exp_sim.simulate(1) + + @testattr(stddist = True) + def test_usejac(self): + """ + This tests the functionality of the property usejac. + """ + f = lambda t,x: N.array([x[1], -9.82]) #Defines the rhs + jac = lambda t,x: N.array([[0.,1.],[0.,0.]]) #Defines the jacobian + + exp_mod = Explicit_Problem(f, [1.0,0.0]) + exp_mod.jac = jac + + exp_sim = CVode(exp_mod) + exp_sim.discr='BDF' + exp_sim.iter='Newton' + exp_sim.simulate(5.,100) + + assert exp_sim.statistics["nfcnjacs"] == 0 + nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4) + + exp_sim.reset() + exp_sim.usejac=False + exp_sim.simulate(5.,100) + + nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4) + assert exp_sim.statistics["nfcnjacs"] > 0 + + @testattr(stddist = True) + def test_usejac_csc_matrix(self): + """ + This tests the functionality of the property usejac. + """ + f = lambda t,x: N.array([x[1], -9.82]) #Defines the rhs + jac = lambda t,x: sp.csc_matrix(N.array([[0.,1.],[0.,0.]])) #Defines the jacobian + + exp_mod = Explicit_Problem(f, [1.0,0.0]) + exp_mod.jac = jac + + exp_sim = CVode(exp_mod) + exp_sim.discr='BDF' + exp_sim.iter='Newton' + exp_sim.simulate(5.,100) + + assert exp_sim.statistics["nfcnjacs"] == 0 + nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4) + + exp_sim.reset() + exp_sim.usejac=False + exp_sim.simulate(5.,100) + + nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4) + assert exp_sim.statistics["nfcnjacs"] > 0 + + @testattr(stddist = True) + def test_switches(self): + """ + This tests that the switches are actually turned when override. + """ + f = lambda t,x,sw: N.array([1.0]) + state_events = lambda t,x,sw: N.array([x[0]-1.]) + def handle_event(solver, event_info): + solver.sw = [False] #Override the switches to point to another instance + + mod = Explicit_Problem(f,[0.0]) + mod.sw0 = [True] + + mod.state_events = state_events + mod.handle_event = handle_event + + sim = CVode(mod) + assert sim.sw[0] == True + sim.simulate(3) + assert sim.sw[0] == False + + @testattr(stddist = True) + def test_iter_method(self): + """ + This tests the functionality of the property iter. + """ + + nose.tools.assert_raises(Exception, self.simulator._set_iter_method, 'Test') + nose.tools.assert_raises(Exception, self.simulator._set_iter_method, 1) + nose.tools.assert_raises(Exception, self.simulator._set_iter_method, 0) + nose.tools.assert_raises(Exception, self.simulator._set_iter_method, ['Test']) + nose.tools.assert_raises(Exception, self.simulator._set_iter_method, [1.0, 1]) + nose.tools.assert_raises(Exception, self.simulator._set_iter_method, 11.1) + + self.simulator.iter = 'Newton' + assert self.simulator.iter == 'Newton' + self.simulator.iter = 'FixedPoint' + assert self.simulator.iter == 'FixedPoint' + + @testattr(stddist = True) + def test_initial_step(self): + """ + This tests the functionality of the property initstep. + """ + + nose.tools.assert_raises(Exception, self.simulator._set_initial_step, 'Test') + nose.tools.assert_raises(Exception, self.simulator._set_initial_step, ['Test']) + + assert self.simulator.inith == 0.0 + self.simulator.inith = 10.0 + assert self.simulator.inith == 10.0 + self.simulator.inith = 1 + assert self.simulator.inith == 1.0 + + @testattr(stddist = True) + def test_interpolate(self): + """ + This tests the functionality of the method interpolate. + """ + f = lambda t,x: N.array(x**0.25) + + prob = Explicit_Problem(f,[1.0]) + + sim = CVode(prob) + sim.simulate(10., 100) + y100 = sim.y_sol + t100 = sim.t_sol + sim.reset() + sim.simulate(10.) + nose.tools.assert_almost_equal(float(y100[-2]), float(sim.interpolate(9.9,0)),5) + + @testattr(stddist = True) + def test_ncp_list(self): + f = lambda t,y:N.array(-y) + y0 = [4.0] + + prob = Explicit_Problem(f,y0) + sim = CVode(prob) + + t, y = sim.simulate(7, ncp_list=N.arange(0, 7, 0.1)) #Simulate 5 seconds + + nose.tools.assert_almost_equal(float(y[-1]), 0.00364832, 4) + + @testattr(stddist = True) + def test_handle_result(self): + """ + This function tests the handle result. + """ + f = lambda t,x: x**0.25 + def handle_result(solver,t,y): + assert solver.t == t + + prob = Explicit_Problem(f, [1.0]) + prob.handle_result = handle_result + + sim = CVode(prob) + sim.report_continuously = True + sim.simulate(10.) + + @testattr(stddist = True) + def test_max_order(self): + """ + This tests the functionality of the property maxord. + """ + self.simulator.discr='Adams' + + nose.tools.assert_raises(Exception, self.simulator._set_max_ord, "Test") + nose.tools.assert_raises(Exception, self.simulator._set_max_ord, [1,1]) + + self.simulator.maxord = -1 + assert self.simulator.maxord == 1 + self.simulator.maxord = 2 + assert self.simulator.maxord == 2 + self.simulator.maxord = 13 + assert self.simulator.maxord == 12 + + self.simulator.discr='BDF' + + nose.tools.assert_raises(Exception, self.simulator._set_max_ord, "Test") + nose.tools.assert_raises(Exception, self.simulator._set_max_ord, [1,1]) + + self.simulator.maxord = -1 + assert self.simulator.maxord == 1 + self.simulator.maxord = 2 + assert self.simulator.maxord == 2 + self.simulator.maxord = 6 + assert self.simulator.maxord == 5 + + @testattr(stddist = True) + def test_spgmr(self): + f = lambda t,y: N.array([y[1], -9.82]) + fsw = lambda t,y,sw: N.array([y[1], -9.82]) + fp = lambda t,y,p: N.array([y[1], -9.82]) + fswp = lambda t,y,sw,p: N.array([y[1], -9.82]) + jacv = lambda t,y,fy,v: N.dot(N.array([[0,1.],[0,0]]),v) + jacvsw = lambda t,y,fy,v,sw: N.dot(N.array([[0,1.],[0,0]]),v) + jacvp = lambda t,y,fy,v,p: N.dot(N.array([[0,1.],[0,0]]),v) + jacvswp = lambda t,y,fy,v,sw,p: N.dot(N.array([[0,1.],[0,0]]),v) + y0 = [1.0,0.0] #Initial conditions + + def run_sim(exp_mod): + exp_sim = CVode(exp_mod) #Create a CVode solver + exp_sim.linear_solver = 'SPGMR' #Change linear solver + + #Simulate + t, y = exp_sim.simulate(5, 1000) #Simulate 5 seconds with 1000 communication points + + #Basic tests + nose.tools.assert_almost_equal(y[-1][0],-121.75000000,4) + nose.tools.assert_almost_equal(y[-1][1],-49.100000000) + + exp_mod = Explicit_Problem(f,y0) + exp_mod.jacv = jacv #Sets the jacobian + run_sim(exp_mod) + + #Need someway of suppressing error messages from deep down in the Cython wrapper + #See http://stackoverflow.com/questions/1218933/can-i-redirect-the-stdout-in-python-into-some-sort-of-string-buffer + try: + from cStringIO import StringIO + except ImportError: + from io import StringIO + import sys + stderr = sys.stderr + sys.stderr = StringIO() + + exp_mod = Explicit_Problem(f,y0) + exp_mod.jacv = jacvsw #Sets the jacobian + nose.tools.assert_raises(CVodeError,run_sim,exp_mod) + + exp_mod = Explicit_Problem(fswp,y0,sw0=[True],p0=1.0) + exp_mod.jacv = jacvsw #Sets the jacobian + nose.tools.assert_raises(CVodeError,run_sim,exp_mod) + + #Restore standard error + sys.stderr = stderr + + exp_mod = Explicit_Problem(fp,y0,p0=1.0) + exp_mod.jacv = jacvp #Sets the jacobian + run_sim(exp_mod) + + exp_mod = Explicit_Problem(fsw,y0,sw0=[True]) + exp_mod.jacv = jacvsw #Sets the jacobian + run_sim(exp_mod) + + exp_mod = Explicit_Problem(fswp,y0,sw0=[True],p0=1.0) + exp_mod.jacv = jacvswp #Sets the jacobian + run_sim(exp_mod) + + @testattr(stddist = True) + def test_max_order_discr(self): + """ + This tests the maximum order when the discretization is changed. + """ + self.simulator.discr = "Adams" + self.simulator.maxord = 7 + assert self.simulator.maxord == 7 + + self.simulator.discr = 'Adams' + assert self.simulator.maxord == 12 + self.simulator.discr = 'BDF' + assert self.simulator.maxord == 5 + self.simulator.discr = 'Adams' + assert self.simulator.maxord == 12 + self.simulator.maxord = 4 + self.simulator.discr = 'BDF' + assert self.simulator.maxord == 5 + self.simulator.discr = 'Adams' + assert self.simulator.maxord == 12 + + @testattr(stddist = True) + def test_pretype(self): + """ + This tests the precondition option. + """ + assert self.simulator.precond == 'PREC_NONE' + self.simulator.precond = 'prec_none' + assert self.simulator.precond == 'PREC_NONE' + + nose.tools.assert_raises(Exception, self.simulator._set_pre_cond, -1.0) + nose.tools.assert_raises(Exception, self.simulator._set_pre_cond, 'PREC_BOTH1') + + @testattr(stddist = True) + def test_maxkrylov(self): + """ + This test the maximum number of krylov subspaces. + """ + assert self.simulator.maxkrylov == 5 + self.simulator.maxkrylov = 3 + assert self.simulator.maxkrylov == 3 + self.simulator.maxkrylov = 4.5 + assert self.simulator.maxkrylov == 4 + + nose.tools.assert_raises(Exception, self.simulator._set_max_krylov, 'Test') + + @testattr(stddist = True) + def test_stablimit(self): + assert self.simulator.stablimit == False + self.simulator.stablimit = True + assert self.simulator.stablimit == True + assert self.simulator.options["stablimit"] == True + + @testattr(stddist = True) + def test_linearsolver(self): + """ + This test the choice of the linear solver. + """ + assert self.simulator.linear_solver == 'DENSE' + self.simulator.linear_solver = 'dense' + assert self.simulator.linear_solver == 'DENSE' + self.simulator.linear_solver = 'spgmr' + assert self.simulator.linear_solver == 'SPGMR' + + nose.tools.assert_raises(Exception, self.simulator._set_linear_solver, 'Test') + + @testattr(stddist = True) + def test_terminate_simulation(self): + """ + This tests the functionality of raising TerminateSimulation exception in handle_result. + """ + class Extended_Problem(Explicit_Problem): + def __init__(self): + pass + def handle_event(self, solver, event_info): + if solver.t > 1.5: + raise TerminateSimulation + rhs = lambda self,t,y,sw: N.array([1.0]) + y0 = [1.0] + sw0 = [False,True] + state_events = lambda self,t,y,sw: N.array([t-1.0, t-2.0]) + + exp_mod = Extended_Problem() + simulator = CVode(exp_mod) + simulator(3.) + + nose.tools.assert_almost_equal(simulator.t, 2.000000, 4) + + @testattr(stddist = True) + def test_completed_step(self): + """ + This tests the functionality of the method completed_step. + """ + global nsteps + nsteps = 0 + f = lambda t,x: x**0.25 + def completed_step(solver): + global nsteps + nsteps += 1 + mod = Explicit_Problem(f, 1.0) + mod.step_events = completed_step + + sim = CVode(mod) + + sim.simulate(2., 100) + assert len(sim.t_sol) == 101 + assert nsteps == sim.statistics["nsteps"] + + sim = CVode(mod) + nsteps = 0 + sim.simulate(2.) + assert len(sim.t_sol) == sim.statistics["nsteps"]+1 + assert nsteps == sim.statistics["nsteps"] + +class Test_IDA: + + def setUp(self): + """ + This function sets up the test case. + """ + f = lambda t,y,yd: y + y0 = [1.0] + yd0 = [1.0] + + self.problem = Implicit_Problem(f,y0,yd0) + self.simulator = IDA(self.problem) + + @testattr(stddist = True) + def test_time_limit(self): + f = lambda t,y,yd: yd-y + + exp_mod = Implicit_Problem(f,1.0,1.0) + exp_sim = IDA(exp_mod) + + exp_sim.maxh = 1e-8 + exp_sim.time_limit = 1 #One second + exp_sim.report_continuously = True + + nose.tools.assert_raises(TimeLimitExceeded, exp_sim.simulate, 1) + + @testattr(stddist = True) + def test_simulate_explicit(self): + """ + Test a simulation of an explicit problem using IDA. + """ + f = lambda t,y:N.array(-y) + y0 = [1.0] + + problem = Explicit_Problem(f,y0) + simulator = IDA(problem) + + assert simulator.yd0[0] == -simulator.y0[0] + + t,y = simulator.simulate(1.0) + + nose.tools.assert_almost_equal(float(y[-1]), float(N.exp(-1.0)),4) + + @testattr(stddist = True) + def test_init(self): + """ + This tests the functionality of the method __init__. + """ + assert self.simulator.suppress_alg == False + assert self.simulator.algvar[0] == 1.0 + assert self.simulator.sw == None + assert self.simulator.maxsteps == 10000 + assert self.simulator.y[0] == 1.0 + + @testattr(stddist = True) + def test_interpolate(self): + """ + This tests the functionality of the method interpolate. + """ + f = lambda t,y,yd: y**0.25-yd + + prob = Implicit_Problem(f,[1.0],[1.0]) + + sim = IDA(prob) + sim.simulate(10., 100) + y100 = sim.y_sol + t100 = sim.t_sol + sim.reset() + sim.simulate(10.) + nose.tools.assert_almost_equal(y100[-2], sim.interpolate(9.9,0),5) + + @testattr(stddist = True) + def test_handle_result(self): + """ + This function tests the handle result. + """ + f = lambda t,x,xd: x**0.25-xd + def handle_result(solver, t ,y, yd): + assert solver.t == t + + prob = Implicit_Problem(f, [1.0],[1.0]) + prob.handle_result = handle_result + + sim = IDA(prob) + + sim.report_continuously = True + + sim.simulate(10.) + + @testattr(stddist = True) + def test_max_order(self): + """ + This tests the functionality of the property maxord. + """ + nose.tools.assert_raises(Exception, self.simulator._set_max_ord, "Test") + nose.tools.assert_raises(Exception, self.simulator._set_max_ord, [1,1]) + + + self.simulator.maxord = -1 + assert self.simulator.maxord == 1 + self.simulator.maxord = 2 + assert self.simulator.maxord == 2 + self.simulator.maxord = 6 + assert self.simulator.maxord == 5 + + @testattr(stddist = True) + def test_tout1(self): + """ + This tests the functionality of the property tout1. + """ + nose.tools.assert_raises(Exception, self.simulator._set_tout1, 'Test') + nose.tools.assert_raises(Exception, self.simulator._set_tout1, [1,1]) + nose.tools.assert_raises(Exception, self.simulator._set_tout1, 'Test') + + assert self.simulator.tout1 == 0.0001 + self.simulator.tout1 = -0.001 + assert self.simulator.tout1 == -0.001 + self.simulator.tout1 = 1 + assert self.simulator.tout1 == 1.0 + + @testattr(stddist = True) + def test_lsoff(self): + """ + This tests the functionality of the property lsoff. + """ + assert self.simulator.lsoff == False + self.simulator.lsoff = True + assert self.simulator.lsoff == True + self.simulator.lsoff = False + assert self.simulator.lsoff == False + + @testattr(stddist = True) + def test_initstep(self): + """ + This tests the funtionality of the property initstep. + """ + + def f(t,y,yd): + res_0 = yd[0] - y[1] + res_1 = yd[1] +9.82-0.01*y[1]**2 + return N.array([res_0,res_1]) + + mod = Implicit_Problem(f,y0=[5.0,0.0], yd0=[0.0,9.82]) + + + sim = IDA(mod) + sim.simulate(2.0) + + nose.tools.assert_almost_equal(sim.y_sol[-1][0], -13.4746473811, places=7) + + sim.reset() + sim.inith = 1e-10 + sim.simulate(2.0) + + nose.tools.assert_almost_equal(sim.y_sol[-1][0], -13.4746596311, places=7) + + @testattr(stddist = True) + def test_time_event(self): + """ + This tests the functionality of the time event function. + """ + f = lambda t,x,xd,sw: xd-x + + def time_events(t, y, yd, sw): + if sw[0]: + return 1.0 + if sw[1]: + return 3.0 + return None + + def handle_event(solver, event_info): + + if event_info[1]: + solver.y = N.array([1.0]) + solver.yd = N.array([1.0]) + + if not solver.sw[0]: + solver.sw[1] = False + + if solver.sw[0]: + solver.sw[0] = False + + mod = Implicit_Problem(f,[1.0],[1.0]) + mod.time_events = time_events + mod.handle_event = handle_event + mod.switches0 = [True, True] + + sim = IDA(mod) + + sim.simulate(5.0) + + nose.tools.assert_almost_equal(sim.y_sol[38], 1.0000000, 5) + nose.tools.assert_almost_equal(sim.y_sol[87], 1.0000000, 5) + + sim = IDA(mod, [1.0],[1.0]) + sim.simulate(2.0) + + nose.tools.assert_almost_equal(sim.t_sol[-1], 2.0000000, 5) + + @testattr(stddist = True) + def test_clear_event_log(self): + """ + This tests the functionality of the time event function. + """ + f = lambda t,x,xd,sw: xd-x + + def time_events(t, y, yd, sw): + if sw[0]: + return 1.0 + if sw[1]: + return 3.0 + return None + + def handle_event(solver, event_info): + + if event_info[1]: + solver.y = N.array([1.0]) + solver.yd = N.array([1.0]) + + if not solver.sw[0]: + solver.sw[1] = False + + if solver.sw[0]: + solver.sw[0] = False + + mod = Implicit_Problem(f,[1.0],[1.0], sw0=[True, True]) + mod.time_events = time_events + mod.handle_event = handle_event + + sim = IDA(mod) + sim.verbosity = 10 + assert len(sim.event_data) == 0 + sim.simulate(5.0) + assert len(sim.event_data) > 0 + + sim.reset() + assert len(sim.event_data) == 0 + sim.simulate(5.0) + assert len(sim.event_data) > 0 + + @testattr(stddist = True) + def test_usejac(self): + """ + This tests the functionality of the property usejac. + """ + f = lambda t,x,xd: N.array([xd[0]-x[1], xd[1]-9.82]) #Defines the rhs + jac = lambda c,t,x,xd: N.array([[c,-1.],[0.,c]]) #Defines the jacobian + + imp_mod = Implicit_Problem(f,[1.0,0.0],[0.,-9.82]) + imp_mod.jac = jac + + imp_sim = IDA(imp_mod) + + imp_sim.simulate(3,100) + + assert imp_sim.statistics["nfcnjacs"] == 0 + nose.tools.assert_almost_equal(imp_sim.y_sol[-1][0], 45.1900000, 4) + + imp_sim.reset() + imp_sim.usejac=False + imp_sim.simulate(3.,100) + + nose.tools.assert_almost_equal(imp_sim.y_sol[-1][0], 45.1900000, 4) + assert imp_sim.statistics["nfcnjacs"] > 0 + + @testattr(stddist = True) + def test_terminate_simulation(self): + """ + This tests the functionality of raising TerminateSimulation exception in handle_result. + """ + class Extended_Problem(Implicit_Problem): + def __init__(self): + pass + def handle_event(self,solver, event_info): + if solver.t > 1.5: + raise TerminateSimulation + res = lambda self,t,y,yd,sw: N.array([y[0]-1.0]) + state_events = lambda self,t,y,yd,sw: N.array([t-1.0, t-2.0]) + y0 = [1.0] + yd0 = [1.0] + sw0 = [False] + + prob = Extended_Problem() + + sim = IDA(prob) + sim.simulate(2.5) + + nose.tools.assert_almost_equal(sim.t, 2.000000, 4) + + @testattr(stddist = True) + def test_algvar(self): + """ + This tests the functionality of the property algvar. + """ + #self.simulator.Integrator.dim = 3 + + #nose.tools.assert_raises(Exception, self.simulator._set_algvar, 1) + #nose.tools.assert_raises(Exception, self.simulator._set_algvar, 1.0) + nose.tools.assert_raises(Exception, self.simulator._set_algvar, [1,'hej',1]) + nose.tools.assert_raises(Exception, self.simulator._set_algvar, {'Test':'case'}) + nose.tools.assert_raises(Exception, self.simulator._set_algvar, [-1,0,1]) + nose.tools.assert_raises(Exception, self.simulator._set_algvar, [1.0,1.0]) + nose.tools.assert_raises(Exception, self.simulator._set_algvar, [3.0, 1.0, 1.0]) + + #vector = [1.0,0.0,1.0] + #vectorb = [True,False,True] + #vectori = [1,0,1] + + #self.simulator.algvar = vectorb + #self.simulator.algvar = vectori + #self.simulator.algvar = vector + #nose.tools.assert_equal(self.simulator.algvar[0], vector[0]) + #nose.tools.assert_equal(self.simulator.algvar[1], vector[1]) + #nose.tools.assert_equal(self.simulator.algvar[2], vector[2]) + + @testattr(stddist = True) + def test_time_event(self): + f = lambda t,y,yd: y-yd + global tnext + global nevent + tnext = 0.0 + nevent = 0 + def time_events(t,y,yd,sw): + global tnext,nevent + events = [1.0, 2.0, 2.5, 3.0] + for ev in events: + if t < ev: + tnext = ev + break + else: + tnext = None + nevent += 1 + return tnext + + def handle_event(solver, event_info): + solver.y+= 1.0 + global tnext + nose.tools.assert_almost_equal(solver.t, tnext) + assert event_info[0] == [] + assert event_info[1] == True + + exp_mod = Implicit_Problem(f,0.0,0.0) + exp_mod.time_events = time_events + exp_mod.handle_event = handle_event + + #CVode + exp_sim = IDA(exp_mod) + exp_sim(5.,100) + + assert nevent == 5 + + @testattr(stddist = True) + def test_suppress_alg(self): + """ + This tests the functionality of the property suppress_alg. + """ + self.simulator.suppress_alg = True + assert self.simulator.suppress_alg == True + self.simulator.suppress_alg = False + assert self.simulator.suppress_alg == False + + @testattr(stddist = True) + def test_make_consistency(self): + """ + This tests the functionality of the method make_consistency. + """ + def f(t,y,yd): + res_1 = y[0] + y[1]+1.0 + res_2 = y[1] + return N.array([res_1, res_2]) + y0 = [2.0, 2.0] + yd0 = [1.0 , 0.0] + + + my_Prob = Implicit_Problem(f, y0, yd0) + + simulator = IDA(my_Prob) + + [flag, y, yd] = simulator.make_consistent('IDA_Y_INIT') + + nose.tools.assert_almost_equal(y[1], 0.00000) + nose.tools.assert_almost_equal(y[0], -1.0000) + nose.tools.assert_almost_equal(yd[0], 1.0000) + nose.tools.assert_almost_equal(yd[1], 0.0000) + + @testattr(stddist = True) + def test_switches(self): + """ + This tests that the switches are actually turned when override. + """ + f = lambda t,x,xd,sw: N.array([xd[0]- 1.0]) + state_events = lambda t,x,xd,sw: N.array([x[0]-1.]) + def handle_event(solver, event_info): + solver.sw = [False] #Override the switches to point to another instance + + mod = Implicit_Problem(f, [0.0],[1.0]) + mod.f = f + mod.sw0 = [True] + mod.state_events = state_events + mod.handle_event = handle_event + + sim = IDA(mod) + assert sim.sw[0] == True + sim.simulate(3) + assert sim.sw[0] == False + + @testattr(stddist = True) + def test_completed_step(self): + """ + This tests the functionality of the method completed_step. + """ + global nsteps + nsteps = 0 + def f(t,y,yd): + res_1 = y[0] + y[1]+1.0 + res_2 = y[1] + return N.array([res_1, res_2]) + def completed_step(solver): + global nsteps + nsteps += 1 + + y0 = [-1.0, 0.0] + yd0 = [1.0 , 0.0] + + mod = Implicit_Problem(f, y0, yd0) + mod.step_events = completed_step + + sim = IDA(mod) + + sim.simulate(2., 100) + assert len(sim.t_sol) == 101 + assert nsteps == sim.statistics["nsteps"] + + sim = IDA(mod) + nsteps = 0 + sim.simulate(2.) + assert len(sim.t_sol) == sim.statistics["nsteps"] + 1 + assert nsteps == sim.statistics["nsteps"] + + + +class Test_Sundials: + + def setUp(self): + """ + This sets up the test case. + """ + class Prob_IDA(Implicit_Problem): + def __init__(self): + pass + res = lambda self,t,y,yd,sw: N.array([y[0]-1.0]) + state_events = lambda self,t,y,yd,sw: N.array([t-1.0, t]) + y0 = [1.0] + yd0 = [1.0] + sw0 = [False, True] + + res = Prob_IDA() + + class Prob_CVode(Explicit_Problem): + def __init__(self): + pass + rhs = lambda self,t,y,sw: N.array([1.0]) + state_events = lambda self,t,y,sw: N.array([t-1.0, t]) + y0 = [1.0] + sw0 = [False, True] + + f = Prob_CVode() + + self.simulators = [IDA(res), CVode(f)] + + + f = lambda t,y,yd,p: N.array([0.0]) + y0 = [1.0] + yd0 = [1.0] + p0 = [1.0] + + mod = Implicit_Problem(f, y0,yd0,p0=p0) + self.sim = IDA(mod) + + @testattr(stddist = True) + def test_atol(self): + """ + This tests the functionality of the property atol. + """ + assert self.simulators[1].atol == 1.0e-6 + assert self.simulators[0].atol == 1.0e-6 + + for i in range(len(self.simulators)): + nose.tools.assert_raises(Exception, self.simulators[i]._set_atol, -1.0) + nose.tools.assert_raises(Exception, self.simulators[i]._set_atol, [1.0, 1.0]) + nose.tools.assert_raises(Exception, self.simulators[i]._set_atol, "Test") + + self.simulators[i].atol = 1.0e-5 + assert self.simulators[i].atol == 1.0e-5 + self.simulators[i].atol = 1.0 + assert self.simulators[i].atol == 1.0 + self.simulators[i].atol = 1 + assert self.simulators[i].atol == 1.0 + self.simulators[i].atol = 1001.0 + assert self.simulators[i].atol == 1001.0 + self.simulators[i].atol = [N.array([1e-5])] + assert len(self.simulators[i].atol.shape) == 1 + assert self.simulators[i].atol == 1e-5 + """ + self.simulators[i].Integrator.dim = 3 + nose.tools.assert_raises(Exception, self.simulators[i]._set_atol, [1.0, 1.0]) + nose.tools.assert_raises(Exception, self.simulators[i]._set_atol, [1.0, 1.0, -1.0]) + self.simulators[i].atol = [1.0, 1.0, 1.0] + assert self.simulators[i].atol == [1.0, 1.0, 1.0] + self.simulators[i].atol = N.array([1.0, 1.0, 1.0]) + assert self.simulators[i].atol[0] == 1.0 + self.simulators[i].atol = N.array([1, 5, 1.0]) + assert self.simulators[i].atol[0] == 1.0 + """ + + + @testattr(stddist = True) + def test_rtol(self): + """ + This tests the functionality of the property rtol. + """ + for i in range(len(self.simulators)): + nose.tools.assert_raises(Exception, self.simulators[i]._set_rtol, -1.0) + nose.tools.assert_raises(Exception, self.simulators[i]._set_rtol, [1.0, 1.0]) + nose.tools.assert_raises(Exception, self.simulators[i]._set_rtol, "Test") + + self.simulators[i].rtol = 1.0e-5 + assert self.simulators[i].rtol == 1.0e-5 + self.simulators[i].rtol = 1.0 + assert self.simulators[i].rtol == 1.0 + self.simulators[i].rtol = 1001.0 + assert self.simulators[i].rtol == 1001.0 + self.simulators[i].rtol = 1001 + assert self.simulators[i].rtol == 1001.0 + + @testattr(stddist = True) + def test_maxh(self): + """ + This tests the functionality of the property maxh. + """ + for i in range(len(self.simulators)): + nose.tools.assert_raises(Exception, self.simulators[i]._set_max_h, [1.0, 1.0]) + nose.tools.assert_raises(Exception, self.simulators[i]._set_max_h, "Test") + + self.simulators[i].maxh = 1.0e-5 + assert self.simulators[i].maxh == 1.0e-5 + self.simulators[i].maxh = 1.0 + assert self.simulators[i].maxh == 1.0 + self.simulators[i].maxh = 1001.0 + assert self.simulators[i].maxh == 1001.0 + + @testattr(stddist = True) + def test_dqtype(self): + """ + Tests the property of dqtype. + """ + + assert self.sim.dqtype == 'CENTERED' #Test the default value. + + self.sim.dqtype = 'FORWARD' + assert self.sim.dqtype == 'FORWARD' + self.sim.dqtype = 'CENTERED' + assert self.sim.dqtype == 'CENTERED' + + self.sim.dqtype = 'forward' + assert self.sim.dqtype == 'FORWARD' + self.sim.dqtype = 'centered' + assert self.sim.dqtype == 'CENTERED' + + nose.tools.assert_raises(Exception,self.sim._set_dqtype, 1) + nose.tools.assert_raises(Exception,self.sim._set_dqtype, 'IDA_CE') + nose.tools.assert_raises(Exception,self.sim._set_dqtype, [1]) + nose.tools.assert_raises(Exception,self.sim._set_dqtype, -1) + + @testattr(stddist = True) + def test_dqrhomax(self): + """ + Tests the property of DQrhomax. + """ + assert self.sim.dqrhomax == 0.0 #Test the default value. + + self.sim.dqrhomax = 1.0 + assert self.sim.dqrhomax == 1.0 + self.sim.dqrhomax = 10 + assert self.sim.dqrhomax == 10 + + nose.tools.assert_raises(Exception,self.sim._set_dqrhomax, -1) + nose.tools.assert_raises(Exception,self.sim._set_dqrhomax, 'str') + nose.tools.assert_raises(Exception,self.sim._set_dqrhomax, []) + nose.tools.assert_raises(Exception,self.sim._set_dqrhomax, -10) + + @testattr(stddist = True) + def test_usesens(self): + """ + Tests the property of usesens. + """ + assert self.sim.usesens == True #Test the default value. + self.sim.usesens = False + assert self.sim.usesens == False + self.sim.usesens = 0 + assert self.sim.usesens == False + self.sim.usesens = 1 + assert self.sim.usesens == True + + @testattr(stddist = True) + def test_sensmethod(self): + """ + Tests the property of sensmethod. + """ + assert self.sim.sensmethod == 'STAGGERED' #Test the default value + + self.sim.sensmethod = 'SIMULTANEOUS' + assert self.sim.sensmethod == 'SIMULTANEOUS' + self.sim.sensmethod = 'STAGGERED' + assert self.sim.sensmethod == 'STAGGERED' + + self.sim.sensmethod = 'simultaneous' + assert self.sim.sensmethod == 'SIMULTANEOUS' + self.sim.sensmethod = 'staggered' + assert self.sim.sensmethod == 'STAGGERED' + + nose.tools.assert_raises(Exception,self.sim._set_sensitivity_method, 1) + nose.tools.assert_raises(Exception,self.sim._set_sensitivity_method, 'IDA_CE') + nose.tools.assert_raises(Exception,self.sim._set_sensitivity_method, [1]) + nose.tools.assert_raises(Exception,self.sim._set_sensitivity_method, -1) + + @testattr(stddist = True) + def test_suppress_sens(self): + """ + Tests the property of suppress_sens. + """ + assert self.sim.suppress_sens == False + self.sim.suppress_sens = False + assert self.sim.suppress_sens == False + self.sim.suppress_sens = 0 + assert self.sim.suppress_sens == False + self.sim.suppress_sens = 1 + assert self.sim.suppress_sens == True + + @testattr(stddist = True) + def test_maxsensiter(self): + """ + Tests the property of maxsensiter. + """ + assert self.sim.maxcorS == 3 #Test the default value + self.sim.maxcorS = 1 + assert self.sim.maxcorS == 1 + self.sim.maxcorS = 10.5 + assert self.sim.maxcorS == 10 + + #nose.tools.assert_raises(Exception, self.sim._set_max_cor_S, 0) + nose.tools.assert_raises(Exception, self.sim._set_max_cor_S, 'str') + nose.tools.assert_raises(Exception, self.sim._set_max_cor_S, []) + #nose.tools.assert_raises(Exception, self.sim._set_max_cor_S, -10) + + @testattr(stddist = True) + def test_pbar(self): + """ + Tests the property of pbar. + """ + f = lambda t,y,p:N.array([0.0]*len(y)) + y0 = [1.0]*2 + p0 = [1000.0, -100.0] + exp_mod = Explicit_Problem(f,y0,p0=p0) + + exp_sim = CVode(exp_mod) + + nose.tools.assert_almost_equal(exp_sim.pbar[0], 1000.00000,4) + nose.tools.assert_almost_equal(exp_sim.pbar[1], 100.000000,4) + + f = lambda t,y,yd,p: N.array([0.0]*len(y)) + yd0 = [0.0]*2 + imp_mod = Implicit_Problem(f,y0,yd0,p0=p0) + + imp_sim = IDA(imp_mod) + + nose.tools.assert_almost_equal(imp_sim.pbar[0], 1000.00000,4) + nose.tools.assert_almost_equal(imp_sim.pbar[1], 100.000000,4) diff --git a/tests/test_examples.py b/tests/test_examples.py index fa26a91d..efd76779 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,223 +1,223 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2010 Modelon AB -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import nose -from assimulo import testattr -from assimulo.exception import * -from assimulo.examples import * - -class Test_Examples: - - - @testattr(stddist = True) - def test_cvode_with_jac_sparse(self): - try: - cvode_with_jac_sparse.run_example(with_plots=False) - except AssimuloException: - pass #Handle the case when SuperLU is not installed - - @testattr(stddist = True) - def test_ida_with_user_defined_handle_result(self): - ida_with_user_defined_handle_result.run_example(with_plots=False) - - @testattr(stddist = True) - def test_radau5dae_time_events(self): - radau5dae_time_events.run_example(with_plots=False) - - @testattr(stddist = True) - def test_kinsol_basic(self): - kinsol_basic.run_example(with_plots=False) - - @testattr(stddist = True) - def test_kinsol_with_jac(self): - kinsol_with_jac.run_example(with_plots=False) - - @testattr(stddist = True) - def test_kinsol_ors(self): - kinsol_ors.run_example(with_plots=False) - - @testattr(stddist = True) - def test_cvode_with_preconditioning(self): - cvode_with_preconditioning.run_example(with_plots=False) - - @testattr(stddist = True) - def test_dasp3_basic(self): - print("Currently not running test_dasp3_basic. Numerically unstable problem.") - #dasp3_basic.run_example(with_plots=False) - - @testattr(stddist = True) - def test_cvode_gyro(self): - cvode_gyro.run_example(with_plots=False) - - @testattr(stddist = True) - def test_cvode_basic(self): - cvode_basic.run_example(with_plots=False) - - @testattr(stddist = True) - def test_cvode_with_disc(self): - cvode_with_disc.run_example(with_plots=False) - - @testattr(stddist = True) - def test_cvode_with_initial_sensitivity(self): - cvode_with_initial_sensitivity.run_example(with_plots=False) - - @testattr(stddist = True) - def test_cvode_with_jac(self): - cvode_with_jac.run_example(with_plots=False) - - @testattr(stddist = True) - def test_cvode_with_jac_spgmr(self): - cvode_with_jac_spgmr.run_example(with_plots=False) - - @testattr(stddist = True) - def test_ida_with_jac_spgmr(self): - ida_with_jac_spgmr.run_example(with_plots=False) - - @testattr(stddist = True) - def test_cvode_with_parameters(self): - cvode_with_parameters.run_example(with_plots=False) - - @testattr(stddist = True) - def test_cvode_with_parameters_fcn(self): - cvode_with_parameters_fcn.run_example(with_plots=False) - - @testattr(stddist = True) - def test_cvode_with_parameters_modified(self): - cvode_with_parameters_modified.run_example(with_plots=False) - - @testattr(stddist = True) - def test_euler_basic(self): - euler_basic.run_example(with_plots=False) - - @testattr(stddist = True) - def test_euler_with_disc(self): - euler_with_disc.run_example(with_plots=False) - - @testattr(stddist = True) - def test_rungekutta4_basic(self): - rungekutta4_basic.run_example(with_plots=False) - - @testattr(stddist = True) - def test_rungekutta34_basic(self): - rungekutta34_basic.run_example(with_plots=False) - - @testattr(stddist = True) - def test_rungekutta34_with_disc(self): - rungekutta34_with_disc.run_example(with_plots=False) - - @testattr(stddist = True) - def test_ida_with_disc(self): - ida_with_disc.run_example(with_plots=False) - - @testattr(stddist = True) - def test_ida_with_initial_sensitivity(self): - ida_with_initial_sensitivity.run_example(with_plots=False) - - @testattr(stddist = True) - def test_ida_with_jac(self): - ida_with_jac.run_example(with_plots=False) - - @testattr(stddist = True) - def test_ida_with_parameters(self): - ida_with_parameters.run_example(with_plots=False) - - @testattr(stddist = True) - def test_radau5ode_vanderpol(self): - radau5ode_vanderpol.run_example(with_plots=False) - - @testattr(stddist = True) - def test_radau5ode_with_disc(self): - radau5ode_with_disc.run_example(with_plots=False) - - @testattr(stddist = True) - def test_radau5dae_vanderpol(self): - radau5dae_vanderpol.run_example(with_plots=False) - - @testattr(stddist = True) - def test_dopri5_basic(self): - dopri5_basic.run_example(with_plots=False) - - @testattr(stddist = True) - def test_dopri5_with_disc(self): - dopri5_with_disc.run_example(with_plots=False) - - @testattr(stddist = True) - def test_rodasode_vanderpol(self): - rodasode_vanderpol.run_example(with_plots=False) - - @testattr(stddist = True) - def test_mech_system_pendulum1(self): - """ - This tests the class Mechanical_system together with ind1 and ida - """ - mech_system_pendulum.run_example('ind1',with_plots=False,with_test=True) - - @testattr(stddist = True) - def test_mech_system_pendulum2(self): - """ - This tests the class Mechanical_system together with ind2 and ida - """ - mech_system_pendulum.run_example('ind2',with_plots=False,with_test=True) - - @testattr(stddist = True) - def test_mech_system_pendulum3(self): - """ - This tests the class Mechanical_system together with ind3 and ida - """ - mech_system_pendulum.run_example('ind3',with_plots=False,with_test=True) - - @testattr(stddist = True) - def test_mech_system_pendulum_ggl2(self): - """ - This tests the class Mechanical_system together with ggl2 and ida - """ - mech_system_pendulum.run_example('ggl2',with_plots=False,with_test=True) - - @testattr(stddist = True) - def test_mech_system_pendulum_ovstab2(self): - """ - This tests the class Mechanical_system together with ovstab2 and odassl - """ - mech_system_pendulum.run_example('ovstab2',with_plots=False,with_test=True) - - @testattr(stddist = True) - def test_mech_system_pendulum_ovstab1(self): - """ - This tests the class Mechanical_system together with ovstab1 and odassl - """ - mech_system_pendulum.run_example('ovstab1',with_plots=False,with_test=True) - - - @testattr(stddist = True) - def test_lsodar_vanderpol(self): - lsodar_vanderpol.run_example(with_plots=False) - - @testattr(stddist = True) - def test_lsodar_with_disc(self): - lsodar_with_disc.run_example(with_plots=False) - - @testattr(stddist = True) - def test_euler_vanderpol(self): - euler_vanderpol.run_example(with_plots=False) - - @testattr(stddist = True) - def test_cvode_basic_backward(self): - cvode_basic_backward.run_example(with_plots=False) - - @testattr(stddist = True) - def test_ida_basic_backward(self): - ida_basic_backward.run_example(with_plots=False) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import nose +from assimulo import testattr +from assimulo.exception import * +from assimulo.examples import * + +class Test_Examples: + + + @testattr(stddist = True) + def test_cvode_with_jac_sparse(self): + try: + cvode_with_jac_sparse.run_example(with_plots=False) + except AssimuloException: + pass #Handle the case when SuperLU is not installed + + @testattr(stddist = True) + def test_ida_with_user_defined_handle_result(self): + ida_with_user_defined_handle_result.run_example(with_plots=False) + + @testattr(stddist = True) + def test_radau5dae_time_events(self): + radau5dae_time_events.run_example(with_plots=False) + + @testattr(stddist = True) + def test_kinsol_basic(self): + kinsol_basic.run_example(with_plots=False) + + @testattr(stddist = True) + def test_kinsol_with_jac(self): + kinsol_with_jac.run_example(with_plots=False) + + @testattr(stddist = True) + def test_kinsol_ors(self): + kinsol_ors.run_example(with_plots=False) + + @testattr(stddist = True) + def test_cvode_with_preconditioning(self): + cvode_with_preconditioning.run_example(with_plots=False) + + @testattr(stddist = True) + def test_dasp3_basic(self): + print("Currently not running test_dasp3_basic. Numerically unstable problem.") + #dasp3_basic.run_example(with_plots=False) + + @testattr(stddist = True) + def test_cvode_gyro(self): + cvode_gyro.run_example(with_plots=False) + + @testattr(stddist = True) + def test_cvode_basic(self): + cvode_basic.run_example(with_plots=False) + + @testattr(stddist = True) + def test_cvode_with_disc(self): + cvode_with_disc.run_example(with_plots=False) + + @testattr(stddist = True) + def test_cvode_with_initial_sensitivity(self): + cvode_with_initial_sensitivity.run_example(with_plots=False) + + @testattr(stddist = True) + def test_cvode_with_jac(self): + cvode_with_jac.run_example(with_plots=False) + + @testattr(stddist = True) + def test_cvode_with_jac_spgmr(self): + cvode_with_jac_spgmr.run_example(with_plots=False) + + @testattr(stddist = True) + def test_ida_with_jac_spgmr(self): + ida_with_jac_spgmr.run_example(with_plots=False) + + @testattr(stddist = True) + def test_cvode_with_parameters(self): + cvode_with_parameters.run_example(with_plots=False) + + @testattr(stddist = True) + def test_cvode_with_parameters_fcn(self): + cvode_with_parameters_fcn.run_example(with_plots=False) + + @testattr(stddist = True) + def test_cvode_with_parameters_modified(self): + cvode_with_parameters_modified.run_example(with_plots=False) + + @testattr(stddist = True) + def test_euler_basic(self): + euler_basic.run_example(with_plots=False) + + @testattr(stddist = True) + def test_euler_with_disc(self): + euler_with_disc.run_example(with_plots=False) + + @testattr(stddist = True) + def test_rungekutta4_basic(self): + rungekutta4_basic.run_example(with_plots=False) + + @testattr(stddist = True) + def test_rungekutta34_basic(self): + rungekutta34_basic.run_example(with_plots=False) + + @testattr(stddist = True) + def test_rungekutta34_with_disc(self): + rungekutta34_with_disc.run_example(with_plots=False) + + @testattr(stddist = True) + def test_ida_with_disc(self): + ida_with_disc.run_example(with_plots=False) + + @testattr(stddist = True) + def test_ida_with_initial_sensitivity(self): + ida_with_initial_sensitivity.run_example(with_plots=False) + + @testattr(stddist = True) + def test_ida_with_jac(self): + ida_with_jac.run_example(with_plots=False) + + @testattr(stddist = True) + def test_ida_with_parameters(self): + ida_with_parameters.run_example(with_plots=False) + + @testattr(stddist = True) + def test_radau5ode_vanderpol(self): + radau5ode_vanderpol.run_example(with_plots=False) + + @testattr(stddist = True) + def test_radau5ode_with_disc(self): + radau5ode_with_disc.run_example(with_plots=False) + + @testattr(stddist = True) + def test_radau5dae_vanderpol(self): + radau5dae_vanderpol.run_example(with_plots=False) + + @testattr(stddist = True) + def test_dopri5_basic(self): + dopri5_basic.run_example(with_plots=False) + + @testattr(stddist = True) + def test_dopri5_with_disc(self): + dopri5_with_disc.run_example(with_plots=False) + + @testattr(stddist = True) + def test_rodasode_vanderpol(self): + rodasode_vanderpol.run_example(with_plots=False) + + @testattr(stddist = True) + def test_mech_system_pendulum1(self): + """ + This tests the class Mechanical_system together with ind1 and ida + """ + mech_system_pendulum.run_example('ind1',with_plots=False,with_test=True) + + @testattr(stddist = True) + def test_mech_system_pendulum2(self): + """ + This tests the class Mechanical_system together with ind2 and ida + """ + mech_system_pendulum.run_example('ind2',with_plots=False,with_test=True) + + @testattr(stddist = True) + def test_mech_system_pendulum3(self): + """ + This tests the class Mechanical_system together with ind3 and ida + """ + mech_system_pendulum.run_example('ind3',with_plots=False,with_test=True) + + @testattr(stddist = True) + def test_mech_system_pendulum_ggl2(self): + """ + This tests the class Mechanical_system together with ggl2 and ida + """ + mech_system_pendulum.run_example('ggl2',with_plots=False,with_test=True) + + @testattr(stddist = True) + def test_mech_system_pendulum_ovstab2(self): + """ + This tests the class Mechanical_system together with ovstab2 and odassl + """ + mech_system_pendulum.run_example('ovstab2',with_plots=False,with_test=True) + + @testattr(stddist = True) + def test_mech_system_pendulum_ovstab1(self): + """ + This tests the class Mechanical_system together with ovstab1 and odassl + """ + mech_system_pendulum.run_example('ovstab1',with_plots=False,with_test=True) + + + @testattr(stddist = True) + def test_lsodar_vanderpol(self): + lsodar_vanderpol.run_example(with_plots=False) + + @testattr(stddist = True) + def test_lsodar_with_disc(self): + lsodar_with_disc.run_example(with_plots=False) + + @testattr(stddist = True) + def test_euler_vanderpol(self): + euler_vanderpol.run_example(with_plots=False) + + @testattr(stddist = True) + def test_cvode_basic_backward(self): + cvode_basic_backward.run_example(with_plots=False) + + @testattr(stddist = True) + def test_ida_basic_backward(self): + ida_basic_backward.run_example(with_plots=False) diff --git a/thirdparty/dasp3/DASP3.f b/thirdparty/dasp3/DASP3.f index feee74f3..4e992a6b 100644 --- a/thirdparty/dasp3/DASP3.f +++ b/thirdparty/dasp3/DASP3.f @@ -1,702 +1,702 @@ - SUBROUTINE DASP3 (DYDT, DZDT, OUTPDA, T,TEND,WSY,WSZ,N,M,TOL, - * ABSREL,WGHT,EPS, - * A,W,SLU,IPS,EQ,IND,LFLAG) -C -C AUTHOR G SODERLIND, DEPT OF NUMERICAL ANALYSIS, -C ROYAL INSTITUTE OF TECHNOLOGY, STOCKHOLM -C MAJOR REVISION G SODERLIND 1980-09-12 -C DOUBLE PRECISION VERSION: 1980-10-22 -C ------------------------------------------------------------------ -C -C THIS SUBROUTINE IS USED FOR THE NUMERICAL INTEGRATION OF -C THE PARTITIONED SYSTEM OF ORDINARY DIFFERENTIAL EQUATIONS -C -C DY/DT = F(T,Y,Z) (N EQUATIONS) -C -C EPS*DZ/DT = G(T,Y,Z) (M EQUATIONS) -C -C IT IS ASSUMED THAT THE FIRST SYSTEM IS NON-STIFF AND THAT -C THE STIFFNESS OF THE SECOND SYSTEM IS DUE TO THE PARAMETER -C EPS, A DIAGONAL "MATRIX" WITH SMALL ENTRIES. ONE OR MORE -C DIAGONAL ENTRIES OF EPS MAY BE SET EQUAL TO ZERO IF A -C DIFFERENTIAL-ALGEBRAIC SYSTEM IS TO BE SOLVED. THE SUB- -C ROUTINE DASP3 REQUIRES THREE SUBROUTINES NAMED -C -C OUTPDA(T,Y,Z,N,M,JSTOP) -C DYDT(T,Y,Z,F,N,M) -C DZDT(T,Y,Z,G,N,M) -C -C FOR OUTPUT AND EVALUATIONS OF THE FUNCTIONS F AND G. -C THE DERIVATIVES MUST BE COMPUTED TO DOUBLE PRECISION. -C THE PARAMETERS TO THE SUBROUTINE DASP3 ARE: -C -C T INDEPENDENT VARIABLE. ON CALL TO DASP3 IT SHOULD -C BE SET TO THE INITIAL TIME. DOUBLE PRECISION, -C -C TEND END OF INTEGRATION INTERVAL. TEND=1. -C -C TOL ERROR TOLERANCE PARAMETER. THE RELATIVE LOCAL -C ERROR PER STEP WILL BE APPROXIMATELY EQUAL -C TO TOL IN EACH STEP. -C -C ABSREL AN ARRAY OF N+M DEPENDENT VARIABLE NORMALIZING -C FACTORS USED FOR THE ESTIMATION OF LOCAL ERRORS. -C ABSOLUTE ERROR IS ESTIMATED FOR COMPONENTS -C SATISFYING ABS(Y(I)/ABSREL(I))<1, OTHERWISE THE -C RELATIVE ERROR IS ESTIMATED. (SIMILARLY FOR Z) -C NOTE: FOR EVERY I, ABSREL(I)>0. -C -C WGHT AN ARRAY OF N+M ERROR WEIGHTS WHICH CAN BE USED -C TO SET AN INDIVIDUAL ERROR TOLERANCE FOR EACH -C DEPENDENT VARIABLE: THE MAXIMUM NORM OF WGHT SHOULD -C EQUAL ONE. NOTE: O=0; -C -C A,W STORAGE FOR TWO M*M MATRICES. -C -C SLU A 2*M-VECTOR USED FOR LU-DECOMPOSITION OF THE -C ITERATION MATRIX AND FOR VARIOUS OTHER TASKS. -C -C IPS AN M-VECTOR OF INTEGERS USED FOR PARTIAL -C PIVOTING IN LU-DECOMPOSITION. -C -C EQ AN M-DIMENSIONAL (LOGICAL) ARRAY. -C -C IND A 2*M-DIMENSIONAL VECTOR OF INTEGERS. -C -C LFLAG ERROR FLAG USED IF THE INTEGRATION HAS TO BE -C TERMINATED BEFORE TEND IS REACHED. LFLAG=0 IF -C EVERYTHING IS OK. THE TERMINATION CODES HAVE -C THE FOLLOWING MEANINGS: -C -C =1 ZERO ROW FOUND IN DECOMP (SINGULAR MATRIX) -C =2 ZERO DIVIDE IN SOLVE -C =3 STEP-SIZE TOO SMALL (REQUESTED ACCURACY MAY -C NOT BE ATTAINABLE) -C =4 TOO MANY FAILURES TO PASS THE ERROR TEST (DYDT -C AND/OR DZDT MAY BE TOO ROUGH) -C =5 NO CONVERGENCE IN INIVAL ITERATIONS (ONLY FOR -C DIFFERENTIAL-ALGEBRAIC SYSTEMS) -C =6 N<0 OR M<1 -C -C----------------------------------------------------------------------- -C - EXTERNAL DYDT, DZDT, OUTPDA - DIMENSION WGHT(1),EPS(1),SLU(1),IPS(1),IND(1) - DIMENSION A(M,1),W(M,1),ABSREL(1) - DIMENSION BC(3),HH(3),HCOF(3) - DOUBLE PRECISION T,TEND,RUNIT,DTEMP - DOUBLE PRECISION WSY(1),WSZ(1) - DOUBLE PRECISION SAVE(2) - LOGICAL CHANGE,NEWJAC,INCORD,SKIP - LOGICAL EQ(1) - COMMON /COUNTS/ NYDER,NZDER,NSTEP,NREJ,MATEST,NREST,LUFACT - COMMON /STEPS/ HH - COMMON /JCIND/ IYN1,IZAUX,IZDIF,IZPC - COMMON /ROFF/ RUNIT,SQRUNT - EQUIVALENCE (H,HH(1)) - DATA BC/ 1.0, - A .666666667, - B .545454545 / -C -C INITIALIZING SECTION -C -C print *, 'Tol', tol - IF(N.GE.0 .AND. M.GE.1) GOTO 3 - LFLAG = 6 - RETURN - 3 CONTINUE - NREJ = 0 - NYDER = 0 - NZDER = 0 - NSTEP = 0 - NREST = 0 - LFLAG = 0 - JSTOP = 0 - MATEST = 0 - LUFACT = 0 - NOHMIN = 0 - NOSKIP = 0 -C - IYD1 = N - IYF1 = IYD1+N - IYF2 = IYF1+N - IYF3 = IYF2+N - IYF4 = IYF3+N - IYN1 = IYF4+N - IYK = IYN1+N - IYKD = IYK+N - IYERR = IYKD+N -C - IZD1 = M - IZD2 = IZD1+M - IZD3 = IZD2+M - IZPC = IZD3+M - IZDP = IZPC+M - IZDIF = IZDP+M - IZERR = IZDIF+M - IZAUX = IZERR+M -C -C CALCULATE ROUND-OFF UNIT -C - RUNIT = 1.0D0 - 10 RUNIT = .5D0*RUNIT - IF(1D0+RUNIT.GT.1D0) GOTO 10 - RUNIT = 2D0*RUNIT - SQRUNT = DSQRT(RUNIT) -C - NALG = 0 - TOL = ABS(TOL) - EMIN = 1.0 - HH(2) = 0. - HH(3) = 0. - DO 1 I=1,M - ABSREL(I) = ABS(ABSREL(I)) - WGHT(I) = ABS(WGHT(I)) - IND(I) = I - IND(I+M) = I+1 - EQ(I) = .FALSE. - U = ABS(EPS(I)) - IF(U.NE.0.0) GOTO 2 - EQ(I) = .TRUE. - NALG = NALG+1 - 2 IF(U.LT.EMIN) EMIN = U - 1 CONTINUE - IF(NALG.EQ.0) GOTO 9 - CALL INIVAL(DZDT, T,WSY,WSZ,ABSREL,N,M,A,NALG,EQ,SLU,IPS,TOL, - * LFLAG) - IF(LFLAG.NE.0) RETURN - 9 TEMP = AMAX1(10.0,1E7*TOL) - HMIN = TEMP*RUNIT*(TEND-T) - IF(N) 20,30,20 - 20 NYDER = 1 - CALL DYDT(T,WSY,WSZ,WSY(IYD1+1),N,M) - 30 STP = 1.0 - DO 40 I=1,M - IF(EPS(I).NE.0.0) STP = AMAX1(STP,ABS(1.0/EPS(I))) - 40 CONTINUE - CALL OUTPDA(T,WSY,WSZ,N,M,JSTOP) - ITRY = 1 - 1000 H = TEND-T - NZDER = NZDER+1 - CALL DZDT(T,WSY,WSZ,WSZ(IZAUX+1),N,M) - TEMP = ANORM(WSZ(IZAUX+1),WSZ,ABSREL(N+1),M) - I = ITRY/4 - U = 1E-3/1E2**I - HTRY = U/(STP*TEMP+1.0/ABS(H)) - HTRY = AMAX1(HTRY,ABS(1E2*HMIN)) -C -C CHECK DIRECTION OF INTEGRATION -C - IF(H.LT.0.0) HTRY = -HTRY - H = HTRY - IF(NALG.EQ.M) GOTO 165 - IF (N) 710,730,710 -C -C FORWARD EULER STEP ON NONSTIFF VARIABLES -C - 710 DO 720 I=1,N - 720 WSY(I+IYF1) = WSY(I)+DBLE(H)*WSY(I+IYD1) -C -C FORWARD EULER STEP, ON STIFF VARIABLES -C - 730 DO 740 I=1,M - WSZ(I+IZD1) = WSZ(I) -C -C SKIP IF EPS(I)=0 -C - IF(EPS(I).EQ.0.0) GOTO 740 - WSZ(I+IZD1) = WSZ(I)+DBLE(H/EPS(I))*WSZ(I+IZAUX) - 740 CONTINUE -C -C FORWARD EULER STEP ON STIFF VARIABLES BACK TO T=TO -C - NZDER = NZDER+1 - CALL DZDT(T+DBLE(H),WSY(IYF1+1),WSZ(IZD1+1),WSZ(IZD2+1),N,M) - DO 750 I=1,M - DTEMP = 0.0 - IF(EPS(I).NE.0.0) DTEMP = DBLE(H/EPS(I))*WSZ(I+IZD2) - WSZ(I+IZD2) = WSZ(I+IZD1)-DTEMP - 750 CONTINUE -C -C ESTIMATE NORM OF LOCAL ERROR ON FIRST STEP -C - DO 760 I=1,M - DTEMP = .5D0*DBLE(WGHT(I+N)) - 760 WSZ(I+IZERR) = DTEMP*(WSZ(I+IZD2)-WSZ(I)) - TESTFF = ANORM(WSZ(IZERR+1),WSZ,ABSREL(N+1),M) -C -C COMPUTE NEW STARTING STEP -C - H = 0.5*H*SQRT(TOL/(TESTFF+1E-5*TOL)) - TEMP = TEND-T - IF(H/TEMP.GT.1.0) H = 1E-2*TEMP - IF(H/HMIN.LT.1.0) H = HMIN - 165 HHALF = .5*H - JSTART = 1 - KORD = 1 - BETA = BC(1) - INCORD = .FALSE. -C -C INITIALIZE JMS (JACOBIAN MATRIX STATUS) -C JMS = 1: JACOBIAN ESTIMATED ON CURRENT STEP -C 0: OLD JACOBTAN IN USE -C -1: JACOBIAN NOT USED (FUNCTIONAL ITERATIONS) -C - JMS = 0 - NEWJAC = .TRUE. - IF(EMIN.EQ.0.0) GOTO 45 - NEWJAC = .FALSE. - JMS = -1 - 45 IDBL = 2 - LCH = 0 -C -C CLEAR WORKSPACE VECTORS (SKIP OLD DATA ON RESTART) -C - I0 = IZD1+1 - I1 = IZAUX - DO 50 I=I0,I1 - 50 WSZ(I) = 0D0 - HCOF(1) = H - IF(N) 60,80,60 - 60 I0 = IYF1+1 - I1 = IYERR+N - DO 70 I=I0,I1 - 70 WSY(I) = 0D0 - 80 CONTINUE - DO 100 I=1,M - U = EPS(I) - IF(U.NE.0.0) WSZ(I+IZD1) = WSZ(I+IZAUX)/DBLE(U) - 100 CONTINUE - IF(EMIN.EQ.0.0) GOTO 1100 - IF(JMS.EQ.-1) GOTO 2000 -C -C----------------------------------------------------------------------- -C NEW STEP ENTRY -C----------------------------------------------------------------------- -C - 1100 IF(N) 130,1400,130 -C -C----------------------------------------------------------------------- -C PERFORM ONE FULL RK4 STEP ON THE NON-STIFF COMPONENTS -C----------------------------------------------------------------------- -C -C COMPUTE YN+K0/6 AND YN+K0/2 -C - 130 DO 140 I=1,N - DTEMP = WSY(I+IYD1)*DBLE(HHALF) - WSY(I+IYN1) = DTEMP - 140 WSY(I+IYK) = WSY(I)+DTEMP -C -C COMPUTE YN+K0/6+K1/3+K2/3 AND YN+K2 -C - CALL PREPOL(.5,3,WSZ(IZPC+1),WSZ,M,1) - DO 150 K=1,2 - DTEMP = T+DBLE(HHALF) - CALL DYDT(DTEMP,WSY(IYK+1),WSZ(IZPC+1),WSY(IYKD+1),N,M) - DO 150 I=1,N - DTEMP = WSY(I+IYKD) - WSY(I+IYN1) = WSY(I+IYN1)+DBLE(H)*DTEMP - 150 WSY(I+IYK) = WSY(I)+DBLE(HHALF)*K*DTEMP -C -C COMPUTE YN1=YN+K0/6+K1/3+K2/3+K3/6 -C - CALL PREPOL(1.0,3,WSZ(IZPC+1),WSZ,M,1) - CALL DYDT(T+DBLE(H),WSY(IYK+1),WSZ(IZPC+1),WSY(IYKD+1),N,M) - DO 160 I=1,N - WSY(I+IYN1) = (WSY(I+IYN1)+DBLE(HHALF)*WSY(I+IYKD))/3D0 - 160 WSY(I+IYN1) = WSY(I)+WSY(I+IYN1) - NYDER = NYDER+3 -C -C----------------------------------------------------------------------- -C PREDICT Z FOR STIFF SUBSYSTEM -C----------------------------------------------------------------------- -C - 1400 CALL PREPOL(1.0,KORD,WSZ(IZPC+1),WSZ,M,1) - K = KORD-1 - CC = H*BETA -C - CALL PDERIV(1.0,K,WSZ(IZDP+1),WSZ(IZD1+1),M,1) - DO 170 I=1,M - WSZ(I+IZDP) = DBLE(CC)*WSZ(I+IZDP)-WSZ(I+IZPC) - 170 WSZ(I+IZERR) = 0D0 -C - IF(.NOT.NEWJAC) GOTO 1600 -C -C----------------------------------------------------------------------- -C NEW DIFFERENCE APPROXIMATION TO JACOBIAN MATRIX -C----------------------------------------------------------------------- -C - MATEST = MATEST+1 - JMS = 1 - CALL JACEST(DZDT,T,H,A,N,M,WSY,WSZ,ABSREL,SLU,IND) - IF(MATEST.EQ.1) CALL SPAPAT(M,A,IND,EQ,SLU) - IF(EMIN.EQ.0.) GOTO 2000 - RHOJAC = ABS(CC)*CTRACT(A,M,N,WSZ,ABSREL,SLU,EPS) - IF(RHOJAC.GT.0.25) GOTO 2000 - NQEST = 0 - Q = 100.0 - NEWJAC = .FALSE. - JMS = -1 -C -C----------------------------------------------------------------------- -C ENTER CORRECTOR LOOP -C----------------------------------------------------------------------- -C - 1600 ICMAX = 3 - IF (JMS.EQ.-1) ICMAX = 4 - DO 200 ICORR=1,ICMAX - RES = 1.0 - CALL DZDT(T+DBLE(H),WSY(IYN1+1),WSZ(IZPC+1),WSZ(IZAUX+1),N,M) - NZDER = NZDER+1 -C -C TEST IF ITERATION MATRIX IS IN USE. NEWTON ITERATION -C WILL BE USED IF JMS >= 0 -C - IF(JMS) 205,215,215 - 205 DO 210 I=1,M - WSZ(I+IZDIF) = DBLE(CC/EPS(I))*WSZ(I+IZAUX)-WSZ(I+IZDP) - 210 WSZ(I+IZAUX) = WSZ(I+IZPC)-WSZ(I+IZDIF) - GOTO 260 -C -C FORM RESIDUAL VECTOR AND STORE IN SINGLE PRECISION VECTOR SLU. -C - 215 DO 220 I=1,M - CDA = CC - IF(EPS(I).EQ.0.0) CDA = 1.0 - DTEMP = DBLE(EPS(I))*(WSZ(I+IZPC)+WSZ(I+IZDP)) - * -DBLE(CDA)*WSZ(I+IZAUX) - SLU(I) = DTEMP - 220 WSZ(I+IZDIF) = DTEMP -C -C COMPUTE NORM OF RESIDUAL VECTOR -C - RES = ANORM(WSZ(IZDIF+1),WSZ(IZPC+1),ABSREL(N+1),M) -C -C SOLVE THE LINEAR SYSTEM W*X-D, WHERE D TS THE RESIDUAL -C VECTOR AND W TS THE LU DECOMPOSITION OF THE MATRIX -C DIAG(EPS(I))-H*BETA*J. -C - IF(M-1) 240,230,240 - 230 WSZ(9) = WSZ(7)*DBLE(W(1,1)) - GOTO 260 - 240 CALL SOLVE(M,W,SLU,SLU(M+1),IPS) -C -C MOVE SOLUTION VECTOR (CONTAINED IN SLU(M+*)) TO WSZ(*+IZAUX). -C COMPUTE NORM OF SOLUTION VECTOR -C - DO 250 I=1,M - 250 WSZ(I+IZAUX) = DBLE(SLU(I+M)) - 260 CNORM = ANORM(WSZ(IZAUX+1),WSZ(IZPC+1),ABSREL(N+1),M) -C -C OMIT ESTIMATION OF CONVERGENCE RATE FIRST TIME -C - IF(ICORR.EQ.1) GOTO 280 - RHO = CNORM/CN1 - IF(RHO-1.0) 270,1700,1700 - 270 Q = RHO/(1 .0-RHO) - NQEST = 10 - 280 CN1 = CNORM -C -C COMPUTE NEW ITERATE AND ADD CORRECTION TO ERROR VECTOR. -C PREPARE FOR CONVERGENCE TEST. -C - SUP = 0.0 - SKIP = .TRUE. - DO 290 I=1,M - DTEMP = WSZ(I+IZAUX) - TEMP = DTEMP - DTEMP = WSZ(I+IZPC)-DTEMP - IF(JMS.EQ.-1) DTEMP = WSZ(I+IZDIF) - SKIP = SKIP .AND. DTEMP .EQ. WSZ(I+IZPC) - WSZ(I+IZPC) = DTEMP - WSZ(I+IZERR) = WSZ(I+IZERR)+TEMP - DEN = AMAX1(ABSREL(I+N),ABS(SNGL(DTEMP))) - CONV = WGHT(I+N)*ABS(Q*TEMP)/DEN - IF(CONV.GT.SUP) SUP = CONV - 290 CONTINUE -C -C CONVERGENCE TEST (SKIP IF THE CORRECTION WAS TOO SMALL) -C - IF(SKIP .OR. RES.LE.1E-6*TOL) GOTO 1800 - IF(SUP.LT.BND.AND.NQEST.GT.0) GOTO 1800 - 200 CONTINUE -C -C----------------------------------------------------------------------- -C THE CORRECTOR ITERATION FAILED TO CONVERGE. IF THE JACOBIAN -C MATRIX WAS ESTIMATED ON THE CURRENT STEP THE STEP-SIZE WILL -C BE REDUCED, OTHERWISE A NEW DIFFERENCE APPROXIMATION TO THE -C MATRIX WILL BE MADE. -C----------------------------------------------------------------------- -C TEST MATRIX STATUS -C - 1700 IF(JMS.EQ.1) GOTO 300 - HNEW = H - NEWJAC = .TRUE. - GOTO 1400 -C - 300 CHANGE = .TRUE. - HNEW = .25*H - JMS = 0 - GOTO 405 -C -C----------------------------------------------------------------------- -C COMPUTE ERROR ESTIMATED AND TEST IF THE STEP WAS OK. -C----------------------------------------------------------------------- -C - 1800 IF(SKIP .AND. ICORR.EQ.1) NOSKIP = NOSKIP+1 - IF(NOSKIP.GE.100) GOTO 2100 - DO 310 I=1,M - 310 WSZ(I+IZERR) = ERCON*WGHT(I+N)*WSZ(I+IZERR) - TESTFF = ANORM(WSZ(IZERR+1),WSZ(IZPC+1),ABSREL(N+1),M)/TOL - TESTFF = AMAX1(TESTFF,1.0E-8) - AZS = TESTFF**(-ERCON)*.95 - AYS = AZS - IF(JSTART.EQ.0.OR.JSTART.GE.4) GOTO 320 -C -C CAREFUL STEPSIZE CONTROL FOR FIRST STEPS -C - AG = AMIN1(.9*AZS,2.0) - TEST = TESTFF - GOTO 390 - 320 IF(N) 340,330,340 - 330 AYS = AZS - TEST = TESTFF - GOTO 380 - 340 CALL PREPOL(1.0,5,WSY(IYERR+1),WSY,N,2) - DO 370 I=1,N - DTEMP = WSY(I+IYN1) - 370 WSY(I+IYERR) = DBLE(WGHT(I))*(WSY(I+IYERR)-DTEMP) - TESTRK = ANORM(WSY(IYERR+1),WSY(IYN1+1),ABSREL,N)/TOL - TESTRK = AMAX1(TESTRK,1.0E-8) - AYS = TESTRK**(-.20)*.95 - TEST = AMAX1(TESTFF,TESTRK) -C -C COMPUTE MAXIMUM POSSIBLE STEP SIZE -C - 380 AMAX = 10.0 - IF(EMIN.EQ.0.0) AMAX = 20.0 - AG = AMIN1(AZS,AYS,AMAX) -C -C ERROR TEST -C - 390 HNEW = H - IF(TEST.GT.1.5) GOTO 400 - IF(TEST.LT.1.0) GOTO 410 - AG = .95*AG - GOTO 430 -C -C THE STEP WAS REJECTED DUE TO LARGE ERROR EXCESS -C - 400 HNEW = AG*H - IF(JSTART.NE.0) IDBL = KORD+1 - CHANGE = .TRUE. - 405 ITRY = ITRY+1 - NREJ = NREJ+1 - IF(MOD(ITRY,4).NE.0) GOTO 500 - NREST = NREST+1 - IF(NREST.LT.25) GOTO 1000 - GOTO 2105 -C -C TEST IF STEP-SIZE INCREASE IS POSSIBLE -C - 410 IF(JSTART.EQ.0) GOTO 420 - JSTART = JSTART+1 - IF(IDBL.NE.0) GOTO 1900 - KORD = KORD+1 - IF(KORD.EQ.2) GOTO 415 - JSTART = 0 - AG = AMIN1(10.0,AYS,2.0*AZS) - IF(AG.LT.1.2) AG = 1.0 - GOTO 430 - 415 INCORD = .TRUE. - CHANGE = .TRUE. - IDBL = 4 - GOTO 1900 - 420 IF(IDBL.GT.0.OR.AG.LT.1.2) GOTO 1900 -C -C INCREASE STEP-SIZE (REDUCE THE STEP-SIZE IF THERE WAS -C A SMALL ERROR EXCESS) -C - 430 HNEW = AG*H - CHANGE = .TRUE. - IDBL = 4 -C -C----------------------------------------------------------------------- -C THE STEP WAS SUCCESSFUL - UPDATE DIVIDED DIFFERENCES &C. -C----------------------------------------------------------------------- -C - 1900 T = T+DBLE(H) - ITRY = 1 - IF(N) 440,470,440 - 440 DTEMP = HCOF(2) - IF(JSTART.EQ.2) DTEMP = 1.0 - DO 450 I=1,N - WSY(I+IYK) = (WSY(I+IYN1)-WSY(I))/DBLE(H) - WSY(I+IYKD) = (WSY(I+IYK)-WSY(I+IYD1))/DBLE(H) - 450 WSY(I) = WSY(I+IYN1) -C -C EVALUATE THE DERIVATIVE AT THE BEGINNING OF THE NEXT STEP -C - NYDER = NYDER+1 - CALL DYDT(T,WSY,WSZ(IZPC+1),WSY(IYD1+1),N,M) - DO 460 I=1,N - SAVE(1) = (WSY(I+IYKD)-WSY(I+IYF1))/DTEMP - WSY(I+IYF1) = (WSY(I+IYD1)-WSY(I+IYK))/DBLE(H) - SAVE(2) = (SAVE(1)-WSY(I+IYF2))/DTEMP - WSY(I+IYF2) = (WSY(I+IYF1)-WSY(I+IYKD))/DBLE(H) - WSY(I+IYF3) = (WSY(I+IYF2)-SAVE(1))/DTEMP - 460 WSY(I+IYF4) = (WSY(I+IYF3)-SAVE(2))/DTEMP - 470 KK = KORD - IF(JSTART.EQ.0) GOTO 445 - KK = KORD+JSTART-2 - KK = MIN0(KK,3) - 445 DO 480 I=1,M - IPL = 2 - INEW = IZD1 - IOLD = 0 - SAVE(1) = WSZ(I) - WSZ(I) = WSZ(I+IZPC) - DO 480 K=1,KK - SAVE(IPL) = WSZ(I+INEW) - IPL = 3-IPL - WSZ(I+INEW) = (WSZ(I+IOLD)-SAVE(IPL))/DBLE(HCOF(K)) - IOLD = INEW - 480 INEW = INEW + M -C -C COMPUTE STEP-SIZE FOR KORD = 2 -C - IF(.NOT.INCORD) GOTO 7800 - INCORD = .FALSE. - SUP = 0.0 - DO 7600 I=1,M - TEMP = WSZ(I) - DEN = AMAX1(ABSREL(I+N),ABS(TEMP)) - TEMP = WSZ(I+IZD3) - TEMP = ABS(WGHT(I+N)*TEMP/DEN) - IF(TEMP.GT.SUP) SUP = TEMP - 7600 CONTINUE - TEMP = TOL/(2.0*SUP+1.0E-5*TOL) - AZS = .5*TEMP**.33 - AG = AMIN1(20.0,AZS) - AG = AMAX1(0.1,AG) - HNEW = AG*H - 7800 CONTINUE -C -C UPDATE COUNTS -C - IDBL = IDBL-1 - NSTEP = NSTEP+1 - IF(JMS.EQ.1) JMS = 0 - NQEST = NQEST-1 - LCH = LCH+1 -C -C OUTPUT -C - CALL OUTPDA(T,WSY,WSZ,N,M,JSTOP) -C -C TEST FOR TERMINATION FORCED BY USER -C - IF(JSTOP.EQ.-1) RETURN -C -C LIMIT STEPSIZE USING FUNCTION HMAX -C - TEMP = ABS(HMAX(T,TEND,WSY,WSZ,N,M)) - U = TEND-T - IF(JSTOP.EQ.1) GOTO 485 - IF(U.LT.0.0) TEMP = -TEMP - IF(HNEW/TEMP.LE.1.0) GOTO 485 - HNEW = TEMP - CHANGE = .TRUE. - 485 U = U/HNEW - IF(JSTOP.EQ.1 .AND. U.LT.1E-6) GOTO 2110 - IF(U-1.0) 455,465,475 - 455 HNEW = TEND-T - CHANGE = .TRUE. - 465 JSTOP = 1 - 475 CONTINUE - IF(.NOT.CHANGE.AND.LCH.GE.3) GOTO 1100 -C -C UPDATE STEP-SIZE DEPENDENT ARRAYS -C - HH(3) = HH(2) - HH(2) = HH(1) - IF(HNEW/HMIN.GT.1.0) GOTO 500 - NOHMIN = NOHMIN+1 - IF(NOHMIN.EQ.100) GOTO 2100 - HNEW = HMIN - 500 HH(1) = HNEW - HCOF(1) = HNEW - HHALF = .5*HNEW - HCOF(2) = HCOF(1)+HH(2) - HCOF(3) = HCOF(2)+HH(3) - IF(.NOT.CHANGE) GOTO 1100 - LCH = 0 - IF(EMIN.EQ.0 .OR. JMS.EQ.-1) GOTO 2000 - IF(RHOJAC*H/HH(2).LT.0.25) JMS = -1 -C -C----------------------------------------------------------------------- -C FORM DIAG(EPS(I))-H*BETA*J MATRIX AND DECOMPOSE INTO LU -C FORM, COMPUTE ERROR TOLERANCE &C. -C----------------------------------------------------------------------- -C - 2000 CONTINUE - BND = .5*TOL/FLOAT(KORD+2) - ERCON = 1.0/FLOAT(KORD+1) - CHANGE = .FALSE. - BETA = BC(KORD) - IF(JMS.EQ.-1) GOTO 610 - CC = H*BETA - DO 560 J=1,M - DO 560 I=1,M - CDA = CC - IF(EPS(I).EQ.0.0) CDA = 1.0 - 560 W(I,J) = -CDA*A(I,J) - DO 570 I=1,M - 570 W(I,I) = W(I,I)+EPS(I) - IF(M-1) 590,580,590 - 580 IF(W(1,1).EQ.0.0) GOTO 585 - W(1,1) = 1.0/W(1,1) - GOTO 610 - 590 CALL DECOMP(M,W,SLU,IPS,LFLAG) - LUFACT = LUFACT+1 - IF(LFLAG) 2110,610,2110 -C -C FORCE ESTIMATION OF RHO -C - 610 Q = 100.0 - NQEST = 0 - IF(.NOT.NEWJAC) GOTO 1100 - NEWJAC = .FALSE. - GOTO 1600 -C -C RETURN -C - 585 LFLAG = 2 - RETURN - 2100 LFLAG = 3 - RETURN - 2105 LFLAG = 4 - RETURN - 2110 CONTINUE - RETURN - END -C -C * * * END DASP3 * * * -C + SUBROUTINE DASP3 (DYDT, DZDT, OUTPDA, T,TEND,WSY,WSZ,N,M,TOL, + * ABSREL,WGHT,EPS, + * A,W,SLU,IPS,EQ,IND,LFLAG) +C +C AUTHOR G SODERLIND, DEPT OF NUMERICAL ANALYSIS, +C ROYAL INSTITUTE OF TECHNOLOGY, STOCKHOLM +C MAJOR REVISION G SODERLIND 1980-09-12 +C DOUBLE PRECISION VERSION: 1980-10-22 +C ------------------------------------------------------------------ +C +C THIS SUBROUTINE IS USED FOR THE NUMERICAL INTEGRATION OF +C THE PARTITIONED SYSTEM OF ORDINARY DIFFERENTIAL EQUATIONS +C +C DY/DT = F(T,Y,Z) (N EQUATIONS) +C +C EPS*DZ/DT = G(T,Y,Z) (M EQUATIONS) +C +C IT IS ASSUMED THAT THE FIRST SYSTEM IS NON-STIFF AND THAT +C THE STIFFNESS OF THE SECOND SYSTEM IS DUE TO THE PARAMETER +C EPS, A DIAGONAL "MATRIX" WITH SMALL ENTRIES. ONE OR MORE +C DIAGONAL ENTRIES OF EPS MAY BE SET EQUAL TO ZERO IF A +C DIFFERENTIAL-ALGEBRAIC SYSTEM IS TO BE SOLVED. THE SUB- +C ROUTINE DASP3 REQUIRES THREE SUBROUTINES NAMED +C +C OUTPDA(T,Y,Z,N,M,JSTOP) +C DYDT(T,Y,Z,F,N,M) +C DZDT(T,Y,Z,G,N,M) +C +C FOR OUTPUT AND EVALUATIONS OF THE FUNCTIONS F AND G. +C THE DERIVATIVES MUST BE COMPUTED TO DOUBLE PRECISION. +C THE PARAMETERS TO THE SUBROUTINE DASP3 ARE: +C +C T INDEPENDENT VARIABLE. ON CALL TO DASP3 IT SHOULD +C BE SET TO THE INITIAL TIME. DOUBLE PRECISION, +C +C TEND END OF INTEGRATION INTERVAL. TEND=1. +C +C TOL ERROR TOLERANCE PARAMETER. THE RELATIVE LOCAL +C ERROR PER STEP WILL BE APPROXIMATELY EQUAL +C TO TOL IN EACH STEP. +C +C ABSREL AN ARRAY OF N+M DEPENDENT VARIABLE NORMALIZING +C FACTORS USED FOR THE ESTIMATION OF LOCAL ERRORS. +C ABSOLUTE ERROR IS ESTIMATED FOR COMPONENTS +C SATISFYING ABS(Y(I)/ABSREL(I))<1, OTHERWISE THE +C RELATIVE ERROR IS ESTIMATED. (SIMILARLY FOR Z) +C NOTE: FOR EVERY I, ABSREL(I)>0. +C +C WGHT AN ARRAY OF N+M ERROR WEIGHTS WHICH CAN BE USED +C TO SET AN INDIVIDUAL ERROR TOLERANCE FOR EACH +C DEPENDENT VARIABLE: THE MAXIMUM NORM OF WGHT SHOULD +C EQUAL ONE. NOTE: O=0; +C +C A,W STORAGE FOR TWO M*M MATRICES. +C +C SLU A 2*M-VECTOR USED FOR LU-DECOMPOSITION OF THE +C ITERATION MATRIX AND FOR VARIOUS OTHER TASKS. +C +C IPS AN M-VECTOR OF INTEGERS USED FOR PARTIAL +C PIVOTING IN LU-DECOMPOSITION. +C +C EQ AN M-DIMENSIONAL (LOGICAL) ARRAY. +C +C IND A 2*M-DIMENSIONAL VECTOR OF INTEGERS. +C +C LFLAG ERROR FLAG USED IF THE INTEGRATION HAS TO BE +C TERMINATED BEFORE TEND IS REACHED. LFLAG=0 IF +C EVERYTHING IS OK. THE TERMINATION CODES HAVE +C THE FOLLOWING MEANINGS: +C +C =1 ZERO ROW FOUND IN DECOMP (SINGULAR MATRIX) +C =2 ZERO DIVIDE IN SOLVE +C =3 STEP-SIZE TOO SMALL (REQUESTED ACCURACY MAY +C NOT BE ATTAINABLE) +C =4 TOO MANY FAILURES TO PASS THE ERROR TEST (DYDT +C AND/OR DZDT MAY BE TOO ROUGH) +C =5 NO CONVERGENCE IN INIVAL ITERATIONS (ONLY FOR +C DIFFERENTIAL-ALGEBRAIC SYSTEMS) +C =6 N<0 OR M<1 +C +C----------------------------------------------------------------------- +C + EXTERNAL DYDT, DZDT, OUTPDA + DIMENSION WGHT(1),EPS(1),SLU(1),IPS(1),IND(1) + DIMENSION A(M,1),W(M,1),ABSREL(1) + DIMENSION BC(3),HH(3),HCOF(3) + DOUBLE PRECISION T,TEND,RUNIT,DTEMP + DOUBLE PRECISION WSY(1),WSZ(1) + DOUBLE PRECISION SAVE(2) + LOGICAL CHANGE,NEWJAC,INCORD,SKIP + LOGICAL EQ(1) + COMMON /COUNTS/ NYDER,NZDER,NSTEP,NREJ,MATEST,NREST,LUFACT + COMMON /STEPS/ HH + COMMON /JCIND/ IYN1,IZAUX,IZDIF,IZPC + COMMON /ROFF/ RUNIT,SQRUNT + EQUIVALENCE (H,HH(1)) + DATA BC/ 1.0, + A .666666667, + B .545454545 / +C +C INITIALIZING SECTION +C +C print *, 'Tol', tol + IF(N.GE.0 .AND. M.GE.1) GOTO 3 + LFLAG = 6 + RETURN + 3 CONTINUE + NREJ = 0 + NYDER = 0 + NZDER = 0 + NSTEP = 0 + NREST = 0 + LFLAG = 0 + JSTOP = 0 + MATEST = 0 + LUFACT = 0 + NOHMIN = 0 + NOSKIP = 0 +C + IYD1 = N + IYF1 = IYD1+N + IYF2 = IYF1+N + IYF3 = IYF2+N + IYF4 = IYF3+N + IYN1 = IYF4+N + IYK = IYN1+N + IYKD = IYK+N + IYERR = IYKD+N +C + IZD1 = M + IZD2 = IZD1+M + IZD3 = IZD2+M + IZPC = IZD3+M + IZDP = IZPC+M + IZDIF = IZDP+M + IZERR = IZDIF+M + IZAUX = IZERR+M +C +C CALCULATE ROUND-OFF UNIT +C + RUNIT = 1.0D0 + 10 RUNIT = .5D0*RUNIT + IF(1D0+RUNIT.GT.1D0) GOTO 10 + RUNIT = 2D0*RUNIT + SQRUNT = DSQRT(RUNIT) +C + NALG = 0 + TOL = ABS(TOL) + EMIN = 1.0 + HH(2) = 0. + HH(3) = 0. + DO 1 I=1,M + ABSREL(I) = ABS(ABSREL(I)) + WGHT(I) = ABS(WGHT(I)) + IND(I) = I + IND(I+M) = I+1 + EQ(I) = .FALSE. + U = ABS(EPS(I)) + IF(U.NE.0.0) GOTO 2 + EQ(I) = .TRUE. + NALG = NALG+1 + 2 IF(U.LT.EMIN) EMIN = U + 1 CONTINUE + IF(NALG.EQ.0) GOTO 9 + CALL INIVAL(DZDT, T,WSY,WSZ,ABSREL,N,M,A,NALG,EQ,SLU,IPS,TOL, + * LFLAG) + IF(LFLAG.NE.0) RETURN + 9 TEMP = AMAX1(10.0,1E7*TOL) + HMIN = TEMP*RUNIT*(TEND-T) + IF(N) 20,30,20 + 20 NYDER = 1 + CALL DYDT(T,WSY,WSZ,WSY(IYD1+1),N,M) + 30 STP = 1.0 + DO 40 I=1,M + IF(EPS(I).NE.0.0) STP = AMAX1(STP,ABS(1.0/EPS(I))) + 40 CONTINUE + CALL OUTPDA(T,WSY,WSZ,N,M,JSTOP) + ITRY = 1 + 1000 H = TEND-T + NZDER = NZDER+1 + CALL DZDT(T,WSY,WSZ,WSZ(IZAUX+1),N,M) + TEMP = ANORM(WSZ(IZAUX+1),WSZ,ABSREL(N+1),M) + I = ITRY/4 + U = 1E-3/1E2**I + HTRY = U/(STP*TEMP+1.0/ABS(H)) + HTRY = AMAX1(HTRY,ABS(1E2*HMIN)) +C +C CHECK DIRECTION OF INTEGRATION +C + IF(H.LT.0.0) HTRY = -HTRY + H = HTRY + IF(NALG.EQ.M) GOTO 165 + IF (N) 710,730,710 +C +C FORWARD EULER STEP ON NONSTIFF VARIABLES +C + 710 DO 720 I=1,N + 720 WSY(I+IYF1) = WSY(I)+DBLE(H)*WSY(I+IYD1) +C +C FORWARD EULER STEP, ON STIFF VARIABLES +C + 730 DO 740 I=1,M + WSZ(I+IZD1) = WSZ(I) +C +C SKIP IF EPS(I)=0 +C + IF(EPS(I).EQ.0.0) GOTO 740 + WSZ(I+IZD1) = WSZ(I)+DBLE(H/EPS(I))*WSZ(I+IZAUX) + 740 CONTINUE +C +C FORWARD EULER STEP ON STIFF VARIABLES BACK TO T=TO +C + NZDER = NZDER+1 + CALL DZDT(T+DBLE(H),WSY(IYF1+1),WSZ(IZD1+1),WSZ(IZD2+1),N,M) + DO 750 I=1,M + DTEMP = 0.0 + IF(EPS(I).NE.0.0) DTEMP = DBLE(H/EPS(I))*WSZ(I+IZD2) + WSZ(I+IZD2) = WSZ(I+IZD1)-DTEMP + 750 CONTINUE +C +C ESTIMATE NORM OF LOCAL ERROR ON FIRST STEP +C + DO 760 I=1,M + DTEMP = .5D0*DBLE(WGHT(I+N)) + 760 WSZ(I+IZERR) = DTEMP*(WSZ(I+IZD2)-WSZ(I)) + TESTFF = ANORM(WSZ(IZERR+1),WSZ,ABSREL(N+1),M) +C +C COMPUTE NEW STARTING STEP +C + H = 0.5*H*SQRT(TOL/(TESTFF+1E-5*TOL)) + TEMP = TEND-T + IF(H/TEMP.GT.1.0) H = 1E-2*TEMP + IF(H/HMIN.LT.1.0) H = HMIN + 165 HHALF = .5*H + JSTART = 1 + KORD = 1 + BETA = BC(1) + INCORD = .FALSE. +C +C INITIALIZE JMS (JACOBIAN MATRIX STATUS) +C JMS = 1: JACOBIAN ESTIMATED ON CURRENT STEP +C 0: OLD JACOBTAN IN USE +C -1: JACOBIAN NOT USED (FUNCTIONAL ITERATIONS) +C + JMS = 0 + NEWJAC = .TRUE. + IF(EMIN.EQ.0.0) GOTO 45 + NEWJAC = .FALSE. + JMS = -1 + 45 IDBL = 2 + LCH = 0 +C +C CLEAR WORKSPACE VECTORS (SKIP OLD DATA ON RESTART) +C + I0 = IZD1+1 + I1 = IZAUX + DO 50 I=I0,I1 + 50 WSZ(I) = 0D0 + HCOF(1) = H + IF(N) 60,80,60 + 60 I0 = IYF1+1 + I1 = IYERR+N + DO 70 I=I0,I1 + 70 WSY(I) = 0D0 + 80 CONTINUE + DO 100 I=1,M + U = EPS(I) + IF(U.NE.0.0) WSZ(I+IZD1) = WSZ(I+IZAUX)/DBLE(U) + 100 CONTINUE + IF(EMIN.EQ.0.0) GOTO 1100 + IF(JMS.EQ.-1) GOTO 2000 +C +C----------------------------------------------------------------------- +C NEW STEP ENTRY +C----------------------------------------------------------------------- +C + 1100 IF(N) 130,1400,130 +C +C----------------------------------------------------------------------- +C PERFORM ONE FULL RK4 STEP ON THE NON-STIFF COMPONENTS +C----------------------------------------------------------------------- +C +C COMPUTE YN+K0/6 AND YN+K0/2 +C + 130 DO 140 I=1,N + DTEMP = WSY(I+IYD1)*DBLE(HHALF) + WSY(I+IYN1) = DTEMP + 140 WSY(I+IYK) = WSY(I)+DTEMP +C +C COMPUTE YN+K0/6+K1/3+K2/3 AND YN+K2 +C + CALL PREPOL(.5,3,WSZ(IZPC+1),WSZ,M,1) + DO 150 K=1,2 + DTEMP = T+DBLE(HHALF) + CALL DYDT(DTEMP,WSY(IYK+1),WSZ(IZPC+1),WSY(IYKD+1),N,M) + DO 150 I=1,N + DTEMP = WSY(I+IYKD) + WSY(I+IYN1) = WSY(I+IYN1)+DBLE(H)*DTEMP + 150 WSY(I+IYK) = WSY(I)+DBLE(HHALF)*K*DTEMP +C +C COMPUTE YN1=YN+K0/6+K1/3+K2/3+K3/6 +C + CALL PREPOL(1.0,3,WSZ(IZPC+1),WSZ,M,1) + CALL DYDT(T+DBLE(H),WSY(IYK+1),WSZ(IZPC+1),WSY(IYKD+1),N,M) + DO 160 I=1,N + WSY(I+IYN1) = (WSY(I+IYN1)+DBLE(HHALF)*WSY(I+IYKD))/3D0 + 160 WSY(I+IYN1) = WSY(I)+WSY(I+IYN1) + NYDER = NYDER+3 +C +C----------------------------------------------------------------------- +C PREDICT Z FOR STIFF SUBSYSTEM +C----------------------------------------------------------------------- +C + 1400 CALL PREPOL(1.0,KORD,WSZ(IZPC+1),WSZ,M,1) + K = KORD-1 + CC = H*BETA +C + CALL PDERIV(1.0,K,WSZ(IZDP+1),WSZ(IZD1+1),M,1) + DO 170 I=1,M + WSZ(I+IZDP) = DBLE(CC)*WSZ(I+IZDP)-WSZ(I+IZPC) + 170 WSZ(I+IZERR) = 0D0 +C + IF(.NOT.NEWJAC) GOTO 1600 +C +C----------------------------------------------------------------------- +C NEW DIFFERENCE APPROXIMATION TO JACOBIAN MATRIX +C----------------------------------------------------------------------- +C + MATEST = MATEST+1 + JMS = 1 + CALL JACEST(DZDT,T,H,A,N,M,WSY,WSZ,ABSREL,SLU,IND) + IF(MATEST.EQ.1) CALL SPAPAT(M,A,IND,EQ,SLU) + IF(EMIN.EQ.0.) GOTO 2000 + RHOJAC = ABS(CC)*CTRACT(A,M,N,WSZ,ABSREL,SLU,EPS) + IF(RHOJAC.GT.0.25) GOTO 2000 + NQEST = 0 + Q = 100.0 + NEWJAC = .FALSE. + JMS = -1 +C +C----------------------------------------------------------------------- +C ENTER CORRECTOR LOOP +C----------------------------------------------------------------------- +C + 1600 ICMAX = 3 + IF (JMS.EQ.-1) ICMAX = 4 + DO 200 ICORR=1,ICMAX + RES = 1.0 + CALL DZDT(T+DBLE(H),WSY(IYN1+1),WSZ(IZPC+1),WSZ(IZAUX+1),N,M) + NZDER = NZDER+1 +C +C TEST IF ITERATION MATRIX IS IN USE. NEWTON ITERATION +C WILL BE USED IF JMS >= 0 +C + IF(JMS) 205,215,215 + 205 DO 210 I=1,M + WSZ(I+IZDIF) = DBLE(CC/EPS(I))*WSZ(I+IZAUX)-WSZ(I+IZDP) + 210 WSZ(I+IZAUX) = WSZ(I+IZPC)-WSZ(I+IZDIF) + GOTO 260 +C +C FORM RESIDUAL VECTOR AND STORE IN SINGLE PRECISION VECTOR SLU. +C + 215 DO 220 I=1,M + CDA = CC + IF(EPS(I).EQ.0.0) CDA = 1.0 + DTEMP = DBLE(EPS(I))*(WSZ(I+IZPC)+WSZ(I+IZDP)) + * -DBLE(CDA)*WSZ(I+IZAUX) + SLU(I) = DTEMP + 220 WSZ(I+IZDIF) = DTEMP +C +C COMPUTE NORM OF RESIDUAL VECTOR +C + RES = ANORM(WSZ(IZDIF+1),WSZ(IZPC+1),ABSREL(N+1),M) +C +C SOLVE THE LINEAR SYSTEM W*X-D, WHERE D TS THE RESIDUAL +C VECTOR AND W TS THE LU DECOMPOSITION OF THE MATRIX +C DIAG(EPS(I))-H*BETA*J. +C + IF(M-1) 240,230,240 + 230 WSZ(9) = WSZ(7)*DBLE(W(1,1)) + GOTO 260 + 240 CALL SOLVE(M,W,SLU,SLU(M+1),IPS) +C +C MOVE SOLUTION VECTOR (CONTAINED IN SLU(M+*)) TO WSZ(*+IZAUX). +C COMPUTE NORM OF SOLUTION VECTOR +C + DO 250 I=1,M + 250 WSZ(I+IZAUX) = DBLE(SLU(I+M)) + 260 CNORM = ANORM(WSZ(IZAUX+1),WSZ(IZPC+1),ABSREL(N+1),M) +C +C OMIT ESTIMATION OF CONVERGENCE RATE FIRST TIME +C + IF(ICORR.EQ.1) GOTO 280 + RHO = CNORM/CN1 + IF(RHO-1.0) 270,1700,1700 + 270 Q = RHO/(1 .0-RHO) + NQEST = 10 + 280 CN1 = CNORM +C +C COMPUTE NEW ITERATE AND ADD CORRECTION TO ERROR VECTOR. +C PREPARE FOR CONVERGENCE TEST. +C + SUP = 0.0 + SKIP = .TRUE. + DO 290 I=1,M + DTEMP = WSZ(I+IZAUX) + TEMP = DTEMP + DTEMP = WSZ(I+IZPC)-DTEMP + IF(JMS.EQ.-1) DTEMP = WSZ(I+IZDIF) + SKIP = SKIP .AND. DTEMP .EQ. WSZ(I+IZPC) + WSZ(I+IZPC) = DTEMP + WSZ(I+IZERR) = WSZ(I+IZERR)+TEMP + DEN = AMAX1(ABSREL(I+N),ABS(SNGL(DTEMP))) + CONV = WGHT(I+N)*ABS(Q*TEMP)/DEN + IF(CONV.GT.SUP) SUP = CONV + 290 CONTINUE +C +C CONVERGENCE TEST (SKIP IF THE CORRECTION WAS TOO SMALL) +C + IF(SKIP .OR. RES.LE.1E-6*TOL) GOTO 1800 + IF(SUP.LT.BND.AND.NQEST.GT.0) GOTO 1800 + 200 CONTINUE +C +C----------------------------------------------------------------------- +C THE CORRECTOR ITERATION FAILED TO CONVERGE. IF THE JACOBIAN +C MATRIX WAS ESTIMATED ON THE CURRENT STEP THE STEP-SIZE WILL +C BE REDUCED, OTHERWISE A NEW DIFFERENCE APPROXIMATION TO THE +C MATRIX WILL BE MADE. +C----------------------------------------------------------------------- +C TEST MATRIX STATUS +C + 1700 IF(JMS.EQ.1) GOTO 300 + HNEW = H + NEWJAC = .TRUE. + GOTO 1400 +C + 300 CHANGE = .TRUE. + HNEW = .25*H + JMS = 0 + GOTO 405 +C +C----------------------------------------------------------------------- +C COMPUTE ERROR ESTIMATED AND TEST IF THE STEP WAS OK. +C----------------------------------------------------------------------- +C + 1800 IF(SKIP .AND. ICORR.EQ.1) NOSKIP = NOSKIP+1 + IF(NOSKIP.GE.100) GOTO 2100 + DO 310 I=1,M + 310 WSZ(I+IZERR) = ERCON*WGHT(I+N)*WSZ(I+IZERR) + TESTFF = ANORM(WSZ(IZERR+1),WSZ(IZPC+1),ABSREL(N+1),M)/TOL + TESTFF = AMAX1(TESTFF,1.0E-8) + AZS = TESTFF**(-ERCON)*.95 + AYS = AZS + IF(JSTART.EQ.0.OR.JSTART.GE.4) GOTO 320 +C +C CAREFUL STEPSIZE CONTROL FOR FIRST STEPS +C + AG = AMIN1(.9*AZS,2.0) + TEST = TESTFF + GOTO 390 + 320 IF(N) 340,330,340 + 330 AYS = AZS + TEST = TESTFF + GOTO 380 + 340 CALL PREPOL(1.0,5,WSY(IYERR+1),WSY,N,2) + DO 370 I=1,N + DTEMP = WSY(I+IYN1) + 370 WSY(I+IYERR) = DBLE(WGHT(I))*(WSY(I+IYERR)-DTEMP) + TESTRK = ANORM(WSY(IYERR+1),WSY(IYN1+1),ABSREL,N)/TOL + TESTRK = AMAX1(TESTRK,1.0E-8) + AYS = TESTRK**(-.20)*.95 + TEST = AMAX1(TESTFF,TESTRK) +C +C COMPUTE MAXIMUM POSSIBLE STEP SIZE +C + 380 AMAX = 10.0 + IF(EMIN.EQ.0.0) AMAX = 20.0 + AG = AMIN1(AZS,AYS,AMAX) +C +C ERROR TEST +C + 390 HNEW = H + IF(TEST.GT.1.5) GOTO 400 + IF(TEST.LT.1.0) GOTO 410 + AG = .95*AG + GOTO 430 +C +C THE STEP WAS REJECTED DUE TO LARGE ERROR EXCESS +C + 400 HNEW = AG*H + IF(JSTART.NE.0) IDBL = KORD+1 + CHANGE = .TRUE. + 405 ITRY = ITRY+1 + NREJ = NREJ+1 + IF(MOD(ITRY,4).NE.0) GOTO 500 + NREST = NREST+1 + IF(NREST.LT.25) GOTO 1000 + GOTO 2105 +C +C TEST IF STEP-SIZE INCREASE IS POSSIBLE +C + 410 IF(JSTART.EQ.0) GOTO 420 + JSTART = JSTART+1 + IF(IDBL.NE.0) GOTO 1900 + KORD = KORD+1 + IF(KORD.EQ.2) GOTO 415 + JSTART = 0 + AG = AMIN1(10.0,AYS,2.0*AZS) + IF(AG.LT.1.2) AG = 1.0 + GOTO 430 + 415 INCORD = .TRUE. + CHANGE = .TRUE. + IDBL = 4 + GOTO 1900 + 420 IF(IDBL.GT.0.OR.AG.LT.1.2) GOTO 1900 +C +C INCREASE STEP-SIZE (REDUCE THE STEP-SIZE IF THERE WAS +C A SMALL ERROR EXCESS) +C + 430 HNEW = AG*H + CHANGE = .TRUE. + IDBL = 4 +C +C----------------------------------------------------------------------- +C THE STEP WAS SUCCESSFUL - UPDATE DIVIDED DIFFERENCES &C. +C----------------------------------------------------------------------- +C + 1900 T = T+DBLE(H) + ITRY = 1 + IF(N) 440,470,440 + 440 DTEMP = HCOF(2) + IF(JSTART.EQ.2) DTEMP = 1.0 + DO 450 I=1,N + WSY(I+IYK) = (WSY(I+IYN1)-WSY(I))/DBLE(H) + WSY(I+IYKD) = (WSY(I+IYK)-WSY(I+IYD1))/DBLE(H) + 450 WSY(I) = WSY(I+IYN1) +C +C EVALUATE THE DERIVATIVE AT THE BEGINNING OF THE NEXT STEP +C + NYDER = NYDER+1 + CALL DYDT(T,WSY,WSZ(IZPC+1),WSY(IYD1+1),N,M) + DO 460 I=1,N + SAVE(1) = (WSY(I+IYKD)-WSY(I+IYF1))/DTEMP + WSY(I+IYF1) = (WSY(I+IYD1)-WSY(I+IYK))/DBLE(H) + SAVE(2) = (SAVE(1)-WSY(I+IYF2))/DTEMP + WSY(I+IYF2) = (WSY(I+IYF1)-WSY(I+IYKD))/DBLE(H) + WSY(I+IYF3) = (WSY(I+IYF2)-SAVE(1))/DTEMP + 460 WSY(I+IYF4) = (WSY(I+IYF3)-SAVE(2))/DTEMP + 470 KK = KORD + IF(JSTART.EQ.0) GOTO 445 + KK = KORD+JSTART-2 + KK = MIN0(KK,3) + 445 DO 480 I=1,M + IPL = 2 + INEW = IZD1 + IOLD = 0 + SAVE(1) = WSZ(I) + WSZ(I) = WSZ(I+IZPC) + DO 480 K=1,KK + SAVE(IPL) = WSZ(I+INEW) + IPL = 3-IPL + WSZ(I+INEW) = (WSZ(I+IOLD)-SAVE(IPL))/DBLE(HCOF(K)) + IOLD = INEW + 480 INEW = INEW + M +C +C COMPUTE STEP-SIZE FOR KORD = 2 +C + IF(.NOT.INCORD) GOTO 7800 + INCORD = .FALSE. + SUP = 0.0 + DO 7600 I=1,M + TEMP = WSZ(I) + DEN = AMAX1(ABSREL(I+N),ABS(TEMP)) + TEMP = WSZ(I+IZD3) + TEMP = ABS(WGHT(I+N)*TEMP/DEN) + IF(TEMP.GT.SUP) SUP = TEMP + 7600 CONTINUE + TEMP = TOL/(2.0*SUP+1.0E-5*TOL) + AZS = .5*TEMP**.33 + AG = AMIN1(20.0,AZS) + AG = AMAX1(0.1,AG) + HNEW = AG*H + 7800 CONTINUE +C +C UPDATE COUNTS +C + IDBL = IDBL-1 + NSTEP = NSTEP+1 + IF(JMS.EQ.1) JMS = 0 + NQEST = NQEST-1 + LCH = LCH+1 +C +C OUTPUT +C + CALL OUTPDA(T,WSY,WSZ,N,M,JSTOP) +C +C TEST FOR TERMINATION FORCED BY USER +C + IF(JSTOP.EQ.-1) RETURN +C +C LIMIT STEPSIZE USING FUNCTION HMAX +C + TEMP = ABS(HMAX(T,TEND,WSY,WSZ,N,M)) + U = TEND-T + IF(JSTOP.EQ.1) GOTO 485 + IF(U.LT.0.0) TEMP = -TEMP + IF(HNEW/TEMP.LE.1.0) GOTO 485 + HNEW = TEMP + CHANGE = .TRUE. + 485 U = U/HNEW + IF(JSTOP.EQ.1 .AND. U.LT.1E-6) GOTO 2110 + IF(U-1.0) 455,465,475 + 455 HNEW = TEND-T + CHANGE = .TRUE. + 465 JSTOP = 1 + 475 CONTINUE + IF(.NOT.CHANGE.AND.LCH.GE.3) GOTO 1100 +C +C UPDATE STEP-SIZE DEPENDENT ARRAYS +C + HH(3) = HH(2) + HH(2) = HH(1) + IF(HNEW/HMIN.GT.1.0) GOTO 500 + NOHMIN = NOHMIN+1 + IF(NOHMIN.EQ.100) GOTO 2100 + HNEW = HMIN + 500 HH(1) = HNEW + HCOF(1) = HNEW + HHALF = .5*HNEW + HCOF(2) = HCOF(1)+HH(2) + HCOF(3) = HCOF(2)+HH(3) + IF(.NOT.CHANGE) GOTO 1100 + LCH = 0 + IF(EMIN.EQ.0 .OR. JMS.EQ.-1) GOTO 2000 + IF(RHOJAC*H/HH(2).LT.0.25) JMS = -1 +C +C----------------------------------------------------------------------- +C FORM DIAG(EPS(I))-H*BETA*J MATRIX AND DECOMPOSE INTO LU +C FORM, COMPUTE ERROR TOLERANCE &C. +C----------------------------------------------------------------------- +C + 2000 CONTINUE + BND = .5*TOL/FLOAT(KORD+2) + ERCON = 1.0/FLOAT(KORD+1) + CHANGE = .FALSE. + BETA = BC(KORD) + IF(JMS.EQ.-1) GOTO 610 + CC = H*BETA + DO 560 J=1,M + DO 560 I=1,M + CDA = CC + IF(EPS(I).EQ.0.0) CDA = 1.0 + 560 W(I,J) = -CDA*A(I,J) + DO 570 I=1,M + 570 W(I,I) = W(I,I)+EPS(I) + IF(M-1) 590,580,590 + 580 IF(W(1,1).EQ.0.0) GOTO 585 + W(1,1) = 1.0/W(1,1) + GOTO 610 + 590 CALL DECOMP(M,W,SLU,IPS,LFLAG) + LUFACT = LUFACT+1 + IF(LFLAG) 2110,610,2110 +C +C FORCE ESTIMATION OF RHO +C + 610 Q = 100.0 + NQEST = 0 + IF(.NOT.NEWJAC) GOTO 1100 + NEWJAC = .FALSE. + GOTO 1600 +C +C RETURN +C + 585 LFLAG = 2 + RETURN + 2100 LFLAG = 3 + RETURN + 2105 LFLAG = 4 + RETURN + 2110 CONTINUE + RETURN + END +C +C * * * END DASP3 * * * +C diff --git a/thirdparty/dasp3/JACEST.f b/thirdparty/dasp3/JACEST.f index 6257ef08..13a67b0e 100644 --- a/thirdparty/dasp3/JACEST.f +++ b/thirdparty/dasp3/JACEST.f @@ -1,59 +1,59 @@ -C======================================================================= - SUBROUTINE JACEST(DZDT,T,H,A,N,M,WSY,WSZ,ABSREL,SLU,IND) -C======================================================================= - EXTERNAL DZDT - DIMENSION A(M,1),ABSREL(1),SLU(1),IND(1) - DOUBLE PRECISION WSY(1),WSZ(1),T,RUNIT - COMMON /JCIND/ IYN1,IZAUX,IZDIF,IZPC - COMMON /COUNTS/ NYDER,NZDER,NSTEP,NREJ,MATEST,NREST,LUFACT - COMMON /ROFF/ RUNIT,SQRUNT -C -C SUBROUTINE JACEST ESTIMATES THE JACOBIAN MATRIX -C OF THE SYSTEM BY NUMERICAL DIFFERENCING, -C -C REVISED G SODERLIND 1980-05-29 -C DOUBLE PRECISION VERSION: 1980-10-22 -C - NZDER = NZDER+1 - CALL DZDT(T+DBLE(H),WSY(IYN1+1),WSZ(IZPC+1),WSZ(IZAUX+1),N,M) - K0 = 1 - DO 30 II=1,M - K = IND(II+M) - KM1 = K-1 - DO 10 JJ=K0,KM1 - J = IND(JJ) - TEMP = WSZ(J+IZPC) -C *** The next line was changed: I --> II (Claus Fuhrer) - SLU(J) = SQRUNT*AMAX1(ABSREL(II+N),ABS(TEMP)) - 10 WSZ(J+IZPC) = WSZ(J+IZPC)+DBLE(SLU(J)) -C - NZDER = NZDER+1 - CALL DZDT(T+DBLE(H),WSY(IYN1+1),WSZ(IZPC+1),WSZ(IZDIF+1),N,M) -C - DO 20 JJ=K0,KM1 - J = IND(JJ) - WSZ(J+IZPC) = WSZ(J+IZPC)-DBLE(SLU(J)) - DO 20 I=1,M - IF(A(I,J).EQ.0.0 .AND. MATEST.NE.1) GOTO 20 - A(I,J) = (WSZ(I+IZDIF)-WSZ(I+IZAUX))/DBLE(SLU(J)) - 20 CONTINUE - IF(KM1.EQ.M) RETURN - 30 K0 = K - RETURN - END -C -C * * * END JACEST * * * -C - - - - - - - - - - - - - +C======================================================================= + SUBROUTINE JACEST(DZDT,T,H,A,N,M,WSY,WSZ,ABSREL,SLU,IND) +C======================================================================= + EXTERNAL DZDT + DIMENSION A(M,1),ABSREL(1),SLU(1),IND(1) + DOUBLE PRECISION WSY(1),WSZ(1),T,RUNIT + COMMON /JCIND/ IYN1,IZAUX,IZDIF,IZPC + COMMON /COUNTS/ NYDER,NZDER,NSTEP,NREJ,MATEST,NREST,LUFACT + COMMON /ROFF/ RUNIT,SQRUNT +C +C SUBROUTINE JACEST ESTIMATES THE JACOBIAN MATRIX +C OF THE SYSTEM BY NUMERICAL DIFFERENCING, +C +C REVISED G SODERLIND 1980-05-29 +C DOUBLE PRECISION VERSION: 1980-10-22 +C + NZDER = NZDER+1 + CALL DZDT(T+DBLE(H),WSY(IYN1+1),WSZ(IZPC+1),WSZ(IZAUX+1),N,M) + K0 = 1 + DO 30 II=1,M + K = IND(II+M) + KM1 = K-1 + DO 10 JJ=K0,KM1 + J = IND(JJ) + TEMP = WSZ(J+IZPC) +C *** The next line was changed: I --> II (Claus Fuhrer) + SLU(J) = SQRUNT*AMAX1(ABSREL(II+N),ABS(TEMP)) + 10 WSZ(J+IZPC) = WSZ(J+IZPC)+DBLE(SLU(J)) +C + NZDER = NZDER+1 + CALL DZDT(T+DBLE(H),WSY(IYN1+1),WSZ(IZPC+1),WSZ(IZDIF+1),N,M) +C + DO 20 JJ=K0,KM1 + J = IND(JJ) + WSZ(J+IZPC) = WSZ(J+IZPC)-DBLE(SLU(J)) + DO 20 I=1,M + IF(A(I,J).EQ.0.0 .AND. MATEST.NE.1) GOTO 20 + A(I,J) = (WSZ(I+IZDIF)-WSZ(I+IZAUX))/DBLE(SLU(J)) + 20 CONTINUE + IF(KM1.EQ.M) RETURN + 30 K0 = K + RETURN + END +C +C * * * END JACEST * * * +C + + + + + + + + + + + + + diff --git a/thirdparty/glimda/glimda_complete.pyf b/thirdparty/glimda/glimda_complete.pyf index 37eb1e67..21ec5972 100644 --- a/thirdparty/glimda/glimda_complete.pyf +++ b/thirdparty/glimda/glimda_complete.pyf @@ -1,108 +1,108 @@ -! -*- f90 -*- -! Note: the context of this file is case sensitive. - -python module glimda__user__routines - interface glimda_user_interface - subroutine fevl(m,n,qprime,xin,t,fzxt,rpar,ipar,ierr) ! in :glimda_complete:glimda_complete.f:glimda:unknown_interface - integer, optional,check(len(xin)>=m),depend(xin) :: m=len(xin) - integer, optional,check(len(qprime)>=n),depend(qprime) :: n=len(qprime) - real*8 dimension(n) :: qprime - real*8 dimension(m) :: xin - real*8 :: t - real*8 dimension(m),depend(m),intent(out) :: fzxt - real*8 dimension(1),intent(in,hide) :: rpar - integer dimension(1),intent(in,hide) :: ipar - integer :: ierr - end subroutine fevl - subroutine qevl(m,n,xin,t,q,rpar,ipar,ierr) ! in :glimda_complete:glimda_complete.f:glimda:unknown_interface - integer, optional,check(len(xin)>=m),depend(xin) :: m=len(xin) - integer, optional,check(len(q)>=n),depend(q) :: n=len(q) - real*8 dimension(m) :: xin - real*8 :: t - real*8 dimension(n),intent(out) :: q - real*8 dimension(1),intent(in,hide) :: rpar - integer dimension(1),intent(in,hide) :: ipar - integer :: ierr - end subroutine qevl - subroutine dqxevl(m,n,x,t,mat_d,rpar,ipar,ierr) ! in :glimda_complete:glimda_complete.f:get_d:unknown_interface - integer, optional,check(len(x)>=m),depend(x) :: m=len(x) - integer, optional,check(shape(mat_d,0)==n),depend(mat_d) :: n=shape(mat_d,0) - real*8 dimension(m) :: x - real*8 :: t - real*8 dimension(n,m),depend(m) :: mat_d - real*8 dimension(1),intent(in,hide) :: rpar - integer dimension(1),intent(in,hide) :: ipar - integer :: ierr - end subroutine dqxevl - subroutine dfyevl(m,n,z,x,t,mat_a,rpar,ipar,ierr) ! in :glimda_complete:glimda_complete.f:get_a:unknown_interface - integer, optional,check(len(x)>=m),depend(x) :: m=len(x) - integer, optional,check(len(z)>=n),depend(z) :: n=len(z) - real*8 dimension(n) :: z - real*8 dimension(m) :: x - real*8 :: t - real*8 dimension(m,n),depend(m,n) :: mat_a - real*8 dimension(1),intent(in,hide) :: rpar - integer dimension(1),intent(in,hide) :: ipar - integer :: ierr - end subroutine dfyevl - subroutine dfxevl(m,n,z,x,t,jac,rpar,ipar,ierr) ! in :glimda_complete:glimda_complete.f:get_j:unknown_interface - integer, optional,check(len(x)>=m),depend(x) :: m=len(x) - integer, optional,check(len(z)>=n),depend(z) :: n=len(z) - real*8 dimension(n) :: z - real*8 dimension(m) :: x - real*8 :: t - real*8 dimension(m,m),depend(m,m) :: jac - real*8 dimension(1),intent(in,hide) :: rpar - integer dimension(1),intent(in,hide) :: ipar - integer :: ierr - end subroutine dfxevl - subroutine solout(f_sol,tph,h,p,y,m,yd,n) ! in :glimda_complete:glimda_complete.f:get_j:unknown_interface - integer :: f_sol - real*8 :: tph - real*8 :: h - integer :: p - real*8 dimension(m) :: y - integer, optional,check(len(y)>=m),depend(y) :: m=len(y) - real*8 dimension(n) :: yd - integer, optional,check(len(yd)>=n),depend(yd) :: n=len(yd) - end subroutine solout - end interface glimda_user_interface -end python module glimda__user__routines - -python module glimda ! in - interface ! in :glimda - subroutine glimda(m,n,fevl,qevl,dfyevl,dfxevl,dqxevl,t0,tend,xin,qprime,h0,atolx,rtolx,tolvec,num_a,num_d,num_b,ode,adcnst,icount,iopt,ropt,ipar,rpar,ierr,solout) ! in :glimda:glimda_complete.f - use glimda__user__routines - integer, optional,check(len(xin)>=m),depend(xin) :: m=len(xin) - integer, optional,check(len(qprime)>=n),depend(qprime) :: n=len(qprime) - external fevl - external qevl - external dfyevl - external dfxevl - external dqxevl - real*8 :: t0 - real*8 :: tend - real*8 dimension(m),intent(in,out) :: xin - real*8 dimension(n),intent(in,out) :: qprime - real*8 :: h0 - real*8 dimension(m),depend(m) :: atolx - real*8 dimension(m),depend(m) :: rtolx - logical :: tolvec - logical :: num_a - logical :: num_d - logical :: num_b - logical :: ode - logical :: adcnst - integer dimension(6),intent(out) :: icount - integer dimension(9) :: iopt - real*8 dimension(11) :: ropt - integer dimension(1),intent(in,hide) :: ipar - real*8 dimension(1),intent(in,hide) :: rpar - integer,intent(out) :: ierr - external solout - end subroutine glimda - end interface -end python module glimda - -! This file was auto-generated with f2py (version:2). -! See http://cens.ioc.ee/projects/f2py2e/ +! -*- f90 -*- +! Note: the context of this file is case sensitive. + +python module glimda__user__routines + interface glimda_user_interface + subroutine fevl(m,n,qprime,xin,t,fzxt,rpar,ipar,ierr) ! in :glimda_complete:glimda_complete.f:glimda:unknown_interface + integer, optional,check(len(xin)>=m),depend(xin) :: m=len(xin) + integer, optional,check(len(qprime)>=n),depend(qprime) :: n=len(qprime) + real*8 dimension(n) :: qprime + real*8 dimension(m) :: xin + real*8 :: t + real*8 dimension(m),depend(m),intent(out) :: fzxt + real*8 dimension(1),intent(in,hide) :: rpar + integer dimension(1),intent(in,hide) :: ipar + integer :: ierr + end subroutine fevl + subroutine qevl(m,n,xin,t,q,rpar,ipar,ierr) ! in :glimda_complete:glimda_complete.f:glimda:unknown_interface + integer, optional,check(len(xin)>=m),depend(xin) :: m=len(xin) + integer, optional,check(len(q)>=n),depend(q) :: n=len(q) + real*8 dimension(m) :: xin + real*8 :: t + real*8 dimension(n),intent(out) :: q + real*8 dimension(1),intent(in,hide) :: rpar + integer dimension(1),intent(in,hide) :: ipar + integer :: ierr + end subroutine qevl + subroutine dqxevl(m,n,x,t,mat_d,rpar,ipar,ierr) ! in :glimda_complete:glimda_complete.f:get_d:unknown_interface + integer, optional,check(len(x)>=m),depend(x) :: m=len(x) + integer, optional,check(shape(mat_d,0)==n),depend(mat_d) :: n=shape(mat_d,0) + real*8 dimension(m) :: x + real*8 :: t + real*8 dimension(n,m),depend(m) :: mat_d + real*8 dimension(1),intent(in,hide) :: rpar + integer dimension(1),intent(in,hide) :: ipar + integer :: ierr + end subroutine dqxevl + subroutine dfyevl(m,n,z,x,t,mat_a,rpar,ipar,ierr) ! in :glimda_complete:glimda_complete.f:get_a:unknown_interface + integer, optional,check(len(x)>=m),depend(x) :: m=len(x) + integer, optional,check(len(z)>=n),depend(z) :: n=len(z) + real*8 dimension(n) :: z + real*8 dimension(m) :: x + real*8 :: t + real*8 dimension(m,n),depend(m,n) :: mat_a + real*8 dimension(1),intent(in,hide) :: rpar + integer dimension(1),intent(in,hide) :: ipar + integer :: ierr + end subroutine dfyevl + subroutine dfxevl(m,n,z,x,t,jac,rpar,ipar,ierr) ! in :glimda_complete:glimda_complete.f:get_j:unknown_interface + integer, optional,check(len(x)>=m),depend(x) :: m=len(x) + integer, optional,check(len(z)>=n),depend(z) :: n=len(z) + real*8 dimension(n) :: z + real*8 dimension(m) :: x + real*8 :: t + real*8 dimension(m,m),depend(m,m) :: jac + real*8 dimension(1),intent(in,hide) :: rpar + integer dimension(1),intent(in,hide) :: ipar + integer :: ierr + end subroutine dfxevl + subroutine solout(f_sol,tph,h,p,y,m,yd,n) ! in :glimda_complete:glimda_complete.f:get_j:unknown_interface + integer :: f_sol + real*8 :: tph + real*8 :: h + integer :: p + real*8 dimension(m) :: y + integer, optional,check(len(y)>=m),depend(y) :: m=len(y) + real*8 dimension(n) :: yd + integer, optional,check(len(yd)>=n),depend(yd) :: n=len(yd) + end subroutine solout + end interface glimda_user_interface +end python module glimda__user__routines + +python module glimda ! in + interface ! in :glimda + subroutine glimda(m,n,fevl,qevl,dfyevl,dfxevl,dqxevl,t0,tend,xin,qprime,h0,atolx,rtolx,tolvec,num_a,num_d,num_b,ode,adcnst,icount,iopt,ropt,ipar,rpar,ierr,solout) ! in :glimda:glimda_complete.f + use glimda__user__routines + integer, optional,check(len(xin)>=m),depend(xin) :: m=len(xin) + integer, optional,check(len(qprime)>=n),depend(qprime) :: n=len(qprime) + external fevl + external qevl + external dfyevl + external dfxevl + external dqxevl + real*8 :: t0 + real*8 :: tend + real*8 dimension(m),intent(in,out) :: xin + real*8 dimension(n),intent(in,out) :: qprime + real*8 :: h0 + real*8 dimension(m),depend(m) :: atolx + real*8 dimension(m),depend(m) :: rtolx + logical :: tolvec + logical :: num_a + logical :: num_d + logical :: num_b + logical :: ode + logical :: adcnst + integer dimension(6),intent(out) :: icount + integer dimension(9) :: iopt + real*8 dimension(11) :: ropt + integer dimension(1),intent(in,hide) :: ipar + real*8 dimension(1),intent(in,hide) :: rpar + integer,intent(out) :: ierr + external solout + end subroutine glimda + end interface +end python module glimda + +! This file was auto-generated with f2py (version:2). +! See http://cens.ioc.ee/projects/f2py2e/ diff --git a/thirdparty/glimda/glimda_diff.patch b/thirdparty/glimda/glimda_diff.patch index 552c465a..ae781896 100644 --- a/thirdparty/glimda/glimda_diff.patch +++ b/thirdparty/glimda/glimda_diff.patch @@ -1,6 +1,6 @@ ---- C:/Development/Assimulo/trunk/thirdparty/voigtmann/glimda_complete.f Tue Mar 6 11:26:33 2012 -+++ C:/Development/Assimulo/trunk/thirdparty/voigtmann/glimda_complete _original.f Mon Mar 5 15:58:51 2012 -@@ -101,7 +101,7 @@ +--- C:/Development/Assimulo/trunk/thirdparty/voigtmann/glimda_complete.f Tue Mar 6 11:26:33 2012 ++++ C:/Development/Assimulo/trunk/thirdparty/voigtmann/glimda_complete _original.f Mon Mar 5 15:58:51 2012 +@@ -101,7 +101,7 @@ $ dqxevl, t0 , tend , xin , qprime, h0 , $ atolX , rtolX , tolvec, num_A , num_D , num_B , $ ode , ADcnst, icount, iopt , ropt , ipar , @@ -9,7 +9,7 @@ C#define DEBUG C -@@ -112,7 +112,7 @@ +@@ -112,7 +112,7 @@ integer m , n , icount(6), iopt(9) , ipar(*),ierr real*8 t0 , tend , xin(m) , qprime(n), h0 , $ atolX(m), rtolX(m), ropt(11) , rpar(*) @@ -18,7 +18,7 @@ C C options of the integrator (see iniopt for details) C -@@ -168,15 +168,12 @@ +@@ -168,15 +168,12 @@ real*8 dlamch, dnrm2 character*2 ifmt @@ -38,7 +38,7 @@ call iniopt(t0 , tend , iopt , ropt , MD , atolX , $ rtolX , tolvec, m , PRTLEV, NEWTIT, MAXORD, -@@ -298,13 +295,11 @@ +@@ -298,13 +295,11 @@ call dcopy(m,xin,1,X_vals,1) X_use = 1 @@ -55,7 +55,7 @@ do while (.true.) -@@ -320,30 +315,26 @@ +@@ -320,30 +315,26 @@ C check for last timestep if (abs((t+h-tend)/(tend-t0)).le.eps) LAST = .true. @@ -96,7 +96,7 @@ C C . the order is allowed to change provided that C . (i) the last step was accepted (change.eq..true.) -@@ -401,28 +392,24 @@ +@@ -401,28 +392,24 @@ endif @@ -134,7 +134,7 @@ C C . evaluate Jacobian at least once per step C -@@ -436,27 +423,23 @@ +@@ -436,27 +423,23 @@ C do k=1,p @@ -169,7 +169,7 @@ C C this part of the residual is independent of the current stage X_k C omega = h*Qp(:,1:k-1)*m.A(k,1:k-1)' + qn_in*m.U(k,:)' -@@ -464,13 +447,11 @@ +@@ -464,13 +447,11 @@ call dgemv('n',n,p,1d0,qn_in,n,U(k,1),MD,0d0,omega,1) call dgemv('n',n,k-1,h,Qp,n,A(k,1),MD,1d0,omega,1) @@ -186,7 +186,7 @@ C C predict a value for X_k (interpolation of X_use+k-1 past stages: C X_use old (accepted) stages are saved in X_vals, k-1 more are -@@ -491,13 +472,11 @@ +@@ -491,13 +472,11 @@ 3000 continue @@ -203,7 +203,7 @@ conv = 0 nresx = 0d0 theta = 0d0 -@@ -545,12 +524,9 @@ +@@ -545,12 +524,9 @@ C . check norm of the residual C if ((resold.gt.0d0).and.(nrmres.ge.resold)) then @@ -219,7 +219,7 @@ conv = 0 ierr = 0 jacnew = .true. -@@ -559,12 +535,10 @@ +@@ -559,12 +535,10 @@ tolfok = chktol(res,m,scaacX,TOLF) @@ -236,7 +236,7 @@ resold = nrmres -@@ -572,11 +546,9 @@ +@@ -572,11 +546,9 @@ C evaluate and decompose the jacobian: Jac = AD+h*lam*B C if (caljac) then @@ -251,7 +251,7 @@ call get_j(m , n , Qp(1,k),X(1,k), t+c(k)*h, $ h , lam , num_A , num_D , num_B , $ dfyevl, dqxevl, dfxevl, fevl , qevl , -@@ -641,24 +613,20 @@ +@@ -641,24 +613,20 @@ C call convrt(psi,l,res,m,scaacX,nresx,nresx1,theta,theta1) if (psi.ge.1d0) then @@ -282,7 +282,7 @@ C C . check for convergence C -@@ -678,13 +646,11 @@ +@@ -678,13 +646,11 @@ if (conv.eq.0) then @@ -299,7 +299,7 @@ nnocon = nnocon + 1 if (.not.jacnew.and.psi.le.1d0) then -@@ -708,11 +674,9 @@ +@@ -708,11 +674,9 @@ C C . no convergence after order change ... retry with old order C @@ -314,7 +314,7 @@ h = hnw_ac p = p_acc call getmet(p,MD,A,U,B,V,c,c1beta,delta,P3METH, -@@ -730,11 +694,9 @@ +@@ -730,11 +694,9 @@ C ... reduce stepsize h and order p C hnew = CUTFAC * h @@ -329,7 +329,7 @@ if (varord.and.(p.gt.MINORD)) godown = .true. C C . scale and modify -@@ -749,13 +711,11 @@ +@@ -749,13 +711,11 @@ else @@ -346,7 +346,7 @@ C C . stage number k converged ... compute next stage C -@@ -769,34 +729,28 @@ +@@ -769,34 +729,28 @@ C . ----------------------------- @@ -390,7 +390,7 @@ C error estimate do k=1,m scalX(k) = atolX(k)+rtolX(k)*abs(X(k,p)) -@@ -812,13 +766,11 @@ +@@ -812,13 +766,11 @@ Cccccccc test a parameter for a modified error constant cccccccccc Cc err = 1d0 * abs(c1beta(1)) * dnrm2(n,errvec,1) @@ -407,7 +407,7 @@ C predict new stepsize fac = 0.85d0 * err**(-1d0/real(p+1)) C do not increase by more than INCFAC -@@ -828,12 +780,10 @@ +@@ -828,12 +780,10 @@ C do not use stepsize larger than HMAX hnew = min(fac*h,HMAX) @@ -423,7 +423,7 @@ C C . check for end of integration interval C -@@ -845,23 +795,19 @@ +@@ -845,23 +795,19 @@ endif @@ -453,7 +453,7 @@ LAST = .false. nrefus = nrefus + 1 -@@ -869,11 +815,9 @@ +@@ -869,11 +815,9 @@ C C . refuse after changing the order ... try again with old order C @@ -468,7 +468,7 @@ h = hnw_ac p = p_acc call getmet(p,MD,A,U,B,V,c,c1beta,delta,P3METH,ierr) -@@ -885,11 +829,9 @@ +@@ -885,11 +829,9 @@ call sclmod(qn_in,n,p,h,h_acc,scdrv0,scdrv1,c1beta) else @@ -483,7 +483,7 @@ C$$$ if(retry.eq.5) hnew = max(CUTFAC * hnew, HMIN) call dcopy(n,sd0_ac,1 ,scdrv0,1) call dcopy(n,sd1_ac,1 ,scdrv1,1) -@@ -903,13 +845,11 @@ +@@ -903,13 +845,11 @@ else @@ -500,7 +500,7 @@ C C . update X_vals used for stage prediction (interpolation) C -@@ -932,18 +872,16 @@ +@@ -932,18 +872,16 @@ call dcopy(n ,scalQ ,1,scaacQ,1) call dcopy(m ,scalX ,1,scaacX,1) @@ -525,7 +525,7 @@ if (t.ge.tend-eps) goto 9999 call dcopy(n*p,qn_out ,1,qn_in,1) -@@ -1005,14 +943,12 @@ +@@ -1005,14 +943,12 @@ 9999 continue @@ -544,7 +544,7 @@ 71 format(3x,a,I6,a,I6,a,I6,a,I6) 73 format(7x,a,1P,E12.5,' (min=',E12.5,', max=',E12.5,' )') -@@ -3110,21 +3046,21 @@ +@@ -3110,21 +3046,21 @@ C C SOURCE C diff --git a/thirdparty/hairer/dopri5.pyf b/thirdparty/hairer/dopri5.pyf index ba963e5b..1d105422 100644 --- a/thirdparty/hairer/dopri5.pyf +++ b/thirdparty/hairer/dopri5.pyf @@ -1,66 +1,66 @@ -! -*- f90 -*- -! Note: the context of this file is case sensitive. - -python module dopcor__user__routines - interface dopcor_user_interface - subroutine fcn(n,x,y,k1,rpar,ipar) ! in :dopri5:dopri5.f:dopcor:unknown_interface - integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) - double precision :: x - double precision dimension(n) :: y - double precision dimension(n),depend(n),intent(out) :: k1 - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - end subroutine fcn - subroutine solout(e_naccpt_1_err,xold,x,y,n,cont,icomp,nrd,rpar,ipar,irtrn) ! in :dopri5:dopri5.f:dopcor:unknown_interface - double precision :: e_naccpt_1_err - double precision :: xold - double precision :: x - double precision dimension(n) :: y - integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) - double precision dimension(5 * nrd) :: cont - integer dimension(nrd),depend(nrd) :: icomp - integer, optional,check((len(cont))/(5)>=nrd),depend(cont),intent(hide) :: nrd=(len(cont))/(5) - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - integer,intent(in,out) :: irtrn - end subroutine solout - end interface dopcor_user_interface -end python module dopcor__user__routines -python module dopri5 ! in - interface ! in :dopri5 - subroutine dopri5(n,fcn,x,y,xend,rtol,atol,itol,solout,iout,work,lwork,iwork,liwork,rpar,ipar,idid) ! in :dopri5:dopri5.f - use dopcor__user__routines - integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) - external fcn - double precision,intent(in,out) :: x - double precision dimension(n),intent(in,out) :: y - double precision :: xend - double precision dimension(n) :: rtol - double precision dimension(n) :: atol - integer :: itol - external solout - integer :: iout - double precision dimension(lwork) :: work - integer, optional,check(len(work)>=lwork),depend(work),intent(hide) :: lwork=len(work) - integer dimension(liwork),intent(in,out) :: iwork - integer, optional,check(len(iwork)>=liwork),depend(iwork),intent(hide) :: liwork=len(iwork) - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - integer,intent(out) :: idid - end subroutine dopri5 - function contd5(ii,x,con,icomp,nd) ! in :dopri5:dopri5.f - integer :: ii - double precision :: x - double precision dimension(5 * nd) :: con - integer dimension(nd),depend(nd) :: icomp - integer, optional,check((len(con))/(5)>=nd),depend(con),intent(hide) :: nd=(len(con))/(5) - double precision :: xold - double precision :: h - double precision :: contd5 - common /condo5/ xold,h - end function contd5 - end interface -end python module dopri5 - -! This file was auto-generated with f2py (version:2). -! See http://cens.ioc.ee/projects/f2py2e/ +! -*- f90 -*- +! Note: the context of this file is case sensitive. + +python module dopcor__user__routines + interface dopcor_user_interface + subroutine fcn(n,x,y,k1,rpar,ipar) ! in :dopri5:dopri5.f:dopcor:unknown_interface + integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) + double precision :: x + double precision dimension(n) :: y + double precision dimension(n),depend(n),intent(out) :: k1 + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + end subroutine fcn + subroutine solout(e_naccpt_1_err,xold,x,y,n,cont,icomp,nrd,rpar,ipar,irtrn) ! in :dopri5:dopri5.f:dopcor:unknown_interface + double precision :: e_naccpt_1_err + double precision :: xold + double precision :: x + double precision dimension(n) :: y + integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) + double precision dimension(5 * nrd) :: cont + integer dimension(nrd),depend(nrd) :: icomp + integer, optional,check((len(cont))/(5)>=nrd),depend(cont),intent(hide) :: nrd=(len(cont))/(5) + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + integer,intent(in,out) :: irtrn + end subroutine solout + end interface dopcor_user_interface +end python module dopcor__user__routines +python module dopri5 ! in + interface ! in :dopri5 + subroutine dopri5(n,fcn,x,y,xend,rtol,atol,itol,solout,iout,work,lwork,iwork,liwork,rpar,ipar,idid) ! in :dopri5:dopri5.f + use dopcor__user__routines + integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) + external fcn + double precision,intent(in,out) :: x + double precision dimension(n),intent(in,out) :: y + double precision :: xend + double precision dimension(n) :: rtol + double precision dimension(n) :: atol + integer :: itol + external solout + integer :: iout + double precision dimension(lwork) :: work + integer, optional,check(len(work)>=lwork),depend(work),intent(hide) :: lwork=len(work) + integer dimension(liwork),intent(in,out) :: iwork + integer, optional,check(len(iwork)>=liwork),depend(iwork),intent(hide) :: liwork=len(iwork) + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + integer,intent(out) :: idid + end subroutine dopri5 + function contd5(ii,x,con,icomp,nd) ! in :dopri5:dopri5.f + integer :: ii + double precision :: x + double precision dimension(5 * nd) :: con + integer dimension(nd),depend(nd) :: icomp + integer, optional,check((len(con))/(5)>=nd),depend(con),intent(hide) :: nd=(len(con))/(5) + double precision :: xold + double precision :: h + double precision :: contd5 + common /condo5/ xold,h + end function contd5 + end interface +end python module dopri5 + +! This file was auto-generated with f2py (version:2). +! See http://cens.ioc.ee/projects/f2py2e/ diff --git a/thirdparty/hairer/radar5.diff b/thirdparty/hairer/radar5.diff index a60119e5..70d9462a 100644 --- a/thirdparty/hairer/radar5.diff +++ b/thirdparty/hairer/radar5.diff @@ -1,6 +1,6 @@ ---- C:/Development/Assimulo/branches/Assimulo-Delay-201202/thirdparty/hairer/radar5.f90 ti jun 26 13:37:26 2012 -+++ C:/Development/Assimulo/branches/Assimulo-Delay-201202/thirdparty/hairer/radar5.f on jul 20 15:26:52 2005 -@@ -43,7 +43,7 @@ +--- C:/Development/Assimulo/branches/Assimulo-Delay-201202/thirdparty/hairer/radar5.f90 ti jun 26 13:37:26 2012 ++++ C:/Development/Assimulo/branches/Assimulo-Delay-201202/thirdparty/hairer/radar5.f on jul 20 15:26:52 2005 +@@ -43,7 +43,7 @@ C ALLOWED DELAYS; IF A LARGER NUMBER OF RETARDED ARGUMENT IS C REQUIRED CHANGE THE DIMENSION TO THE DESIRED VALUE AND RECOMPILE C HERE THE DIMENSION IS SET TO 100 @@ -9,7 +9,7 @@ END MODULE IP_ARRAY C SUBROUTINE RADAR5(N,FCN,PHI,ARGLAG,X,Y,XEND,H, -@@ -52,8 +52,7 @@ +@@ -52,8 +52,7 @@ & JACLAG,NLAGS,NJACL, & IMAS,SOLOUT,IOUT, & WORK,IWORK,RPAR,IPAR,IDID, @@ -19,7 +19,7 @@ C ---------------------------------------------------------- C INPUT PARAMETERS C -------------------- -@@ -483,19 +482,16 @@ +@@ -483,19 +482,16 @@ INTEGER, PARAMETER :: DP=kind(1D0) REAL(kind=DP), dimension(N), intent(inout) :: & Y @@ -41,7 +41,7 @@ LOGICAL IMPLCT,NEUTRAL,JBAND,ARRET,STARTN,PRED LOGICAL FLAGS,FLAGN -@@ -524,7 +520,7 @@ +@@ -524,7 +520,7 @@ FLAGS=.FALSE. FLAGN=.FALSE. @@ -50,7 +50,7 @@ C C ------> OPERATIONS RELEVANT TO THE DELAY DEPENDENCE <------ C -@@ -538,20 +534,20 @@ +@@ -538,20 +534,20 @@ C -------- NGRID NUMBER OF PRESCRIBED GRID-POINTS NGRID=IWORK(13) IF (NGRID.LT.0) NGRID=0 @@ -81,7 +81,7 @@ C ------- LIPAST DIMENSION OF VECTOR IPAST LIPAST=NRDENS+1 IF(NRDENS.LT.0.OR.NRDENS.GT.N) THEN -@@ -567,7 +563,7 @@ +@@ -567,7 +563,7 @@ DO 16 I=1,NRDS 16 IPAST(I)=I END IF @@ -90,7 +90,7 @@ C ------- LRPAST DIMENSION OF VECTOR PAST MXST=IWORK(12) C ------- CONTROL OF LENGTH OF PAST ------- -@@ -578,8 +574,7 @@ +@@ -578,8 +574,7 @@ END IF C ------- DIM. of PAST -------- IDIF=4*NRDS+2 @@ -100,7 +100,7 @@ C ------------------------------------------------- C ------- CONTROL OF SIMPLE NEWTON ITERATION ------- ISWJL=IWORK(14) -@@ -740,11 +735,6 @@ +@@ -740,11 +735,6 @@ NGRID=NGRID+1 END IF GRID(NGRID)=XEND @@ -112,7 +112,7 @@ C ------------------------------------------------------- C -------> MAXIMAL STEP SIZE <------- -@@ -784,9 +774,9 @@ +@@ -784,9 +774,9 @@ END IF C -------> PARAMETER FOR CONTROLLING THE SEARCH OF BP <------- TCKBP=WORK(11) @@ -125,7 +125,7 @@ C *** *** *** *** *** *** *** *** *** *** *** *** *** C COMPUTATION OF ARRAY ENTRIES -@@ -847,11 +837,9 @@ +@@ -847,11 +837,9 @@ IDID=-1 RETURN END IF @@ -139,7 +139,7 @@ C -------- CALL TO CORE INTEGRATOR ------------ CALL RADCOR(N,X,Y,XEND,H,FCN,PHI,ARGLAG,RTOL,ATOL,ITOL, & JAC,IJAC,MLJAC,MUJAC,JACLAG,MAS,MLMAS,MUMAS,SOLOUT,IOUT,IDID, -@@ -860,8 +848,7 @@ +@@ -860,8 +848,7 @@ & IMPLCT,NEUTRAL,NDIMN,JBAND,LDJAC,LDE1,LDMAS2, & NFCN,NJAC,NSTEP,NACCPT,NREJCT,NDEC,NSOL,NFULL,RPAR,IPAR, & IPAST,GRID,NRDS,NLAGS,NJACL, @@ -149,7 +149,7 @@ IWORK(13)=NFULL IWORK(14)=NFCN IWORK(15)=NJAC -@@ -900,8 +887,7 @@ +@@ -900,8 +887,7 @@ & IMPLCT,NEUTRAL,NDIMN,BANDED,LDJAC,LDE1,LDMAS, & NFCN,NJAC,NSTEP,NACCPT,NREJCT,NDEC,NSOL,NFULL,RPAR,IPAR, & IPAST,GRID,NRDS,NLAGS,NJACL, @@ -159,7 +159,7 @@ C ---------------------------------------------------------- C CORE INTEGRATOR FOR RADAR5 C PARAMETERS SAME AS IN RADAR5 WITH WORKSPACE ADDED -@@ -914,8 +900,7 @@ +@@ -914,8 +900,7 @@ INTEGER, PARAMETER :: DP=kind(1D0) REAL(kind=DP), dimension(1), intent(inout) :: Y REAL(kind=DP), dimension(:), allocatable :: @@ -169,7 +169,7 @@ REAL(kind=DP), dimension(:), allocatable :: BPV,UCONT REAL(kind=DP), dimension(:,:), allocatable :: & FJAC,FJACS,FMAS,E1,E2R,E2I -@@ -936,8 +921,6 @@ +@@ -936,8 +921,6 @@ & IVL,IVE,IVC,ILS INTEGER, dimension(:), allocatable :: & IP1,IP2,IPHES,IPJ @@ -178,7 +178,7 @@ LOGICAL FLAGS,FLAGN,FLAGUS LOGICAL QUADR -@@ -965,9 +948,7 @@ +@@ -965,9 +948,7 @@ IF (IMPLCT) ALLOCATE(FMAS(LDMAS,NM1)) ALLOCATE (IP1(NM1),IP2(NM1),IPHES(NM1)) ALLOCATE (E1(LDE1,NM1),E2R(LDE1,NM1),E2I(LDE1,NM1)) @@ -189,7 +189,7 @@ IF (NLAGS.GT.0) THEN ALLOCATE (FJACS(LDJAC,N),FJACLAG(NJACL)) ALLOCATE (IVL(NJACL),IVE(NJACL),IVC(NJACL), -@@ -983,8 +964,8 @@ +@@ -983,8 +964,8 @@ ALLOCATE (CONT(LRC)) ALLOCATE (UCONT(LRC+2)) C --- @@ -200,7 +200,7 @@ C ------------------------------------------------- BPC=.FALSE. -@@ -995,11 +976,9 @@ +@@ -995,11 +976,9 @@ CALLAG=.FALSE. IACT=1 IPOS=1 @@ -213,7 +213,7 @@ X0B=X DO I=1,NGRID IF (GRID(I).GT.X0B) THEN -@@ -1019,21 +998,18 @@ +@@ -1019,21 +998,18 @@ ERRACC=1.D0 IRTRN=2 @@ -240,7 +240,7 @@ C -------- CHECK THE INDEX OF THE PROBLEM ----- INDEX1=NIND1.NE.0 -@@ -1106,15 +1082,12 @@ +@@ -1106,15 +1082,12 @@ REJECT=.FALSE. FIRST=.TRUE. LAST=.FALSE. @@ -258,7 +258,7 @@ DO 3 I=0,MXST-1 PAST(1+IDIF*I)=X 3 CONTINUE -@@ -1126,8 +1099,6 @@ +@@ -1126,8 +1099,6 @@ PAST(J+2*NRDS+IPA)=0.D0 PAST(J+3*NRDS+IPA)=0.D0 ENDDO @@ -267,7 +267,7 @@ PAST(IPA+IDIF-1)=H C --- END OF THE INITIALIZATION FACCON=1.D0 -@@ -1167,26 +1138,7 @@ +@@ -1167,26 +1138,7 @@ HHFAC=H NFCN=NFCN+1 @@ -295,7 +295,7 @@ C -------------------------- C --- BASIC INTEGRATION STEP C -------------------------- -@@ -1200,9 +1152,9 @@ +@@ -1200,9 +1152,9 @@ C ----------------------- ALOPT=0.D0 NJAC=NJAC+1 @@ -308,7 +308,7 @@ END IF IF (IJAC.EQ.0) THEN C --- COMPUTE JACOBIAN MATRIX NUMERICALLY -@@ -1217,12 +1169,9 @@ +@@ -1217,12 +1169,9 @@ F2(J)=DSQRT(UROUND*MAX(1.D-5,ABS(Y(J)))) Y(J)=Y(J)+F2(J) J=J+MD @@ -323,7 +323,7 @@ J=K+(MM-1)*M2 J1=K LBEG=MAX(1,J1-MUJAC)+M1 -@@ -1242,14 +1191,11 @@ +@@ -1242,14 +1191,11 @@ C --- JACOBIAN IS FULL DO I=1,N YSAFE=Y(I) @@ -342,7 +342,7 @@ FJAC(J-M1,I)=(CONT(J)-Y0(J))/DELT END DO Y(I)=YSAFE -@@ -1257,12 +1203,11 @@ +@@ -1257,12 +1203,11 @@ END IF ELSE C --- COMPUTE JACOBIAN MATRIX ANALYTICALLY @@ -359,7 +359,7 @@ END IF CALJAC=.TRUE. CALHES=.TRUE. -@@ -1350,14 +1295,11 @@ +@@ -1350,14 +1295,11 @@ C --- LOOP ON LAG TERMS DO IL=1,NLAGS C --- DELAYED ARGUMENTS ARE COMPUTED @@ -377,7 +377,7 @@ IF (XLAG(3,IL).GT.X) ICOUN(3,IL)=1 IF (ICOUN(1,IL)+ICOUN(2,IL)+ICOUN(3,IL).GE.1) CALJACL=.TRUE. END DO -@@ -1431,7 +1373,7 @@ +@@ -1431,7 +1373,7 @@ IK=IVE(KK) JK=IVC(KK) FJAC(IK,JK)=FJAC(IK,JK)+ALOPT*FJACLAG(KK) @@ -386,7 +386,7 @@ END DO ELSE CALJACL=.FALSE. -@@ -1550,42 +1492,30 @@ +@@ -1550,42 +1492,30 @@ ZL(I+N2)=A1+Z3(I) END DO C COMPUTATION OF STAGE VALUES @@ -436,7 +436,7 @@ 42 CONTINUE NFCN=NFCN+3 -@@ -1606,10 +1536,10 @@ +@@ -1606,10 +1536,10 @@ NSOL=NSOL+1 NEWT=NEWT+1 C --- NORM OF DY @@ -450,7 +450,7 @@ DENOM=SCAL(I) DYNO=DYNO+(Z1(I)/DENOM)**2+(Z2(I)/DENOM)**2 & +(Z3(I)/DENOM)**2 -@@ -1619,12 +1549,10 @@ +@@ -1619,12 +1549,10 @@ C --- BAD CONVERGENCE OR NUMBER OF ITERATIONS TO LARGE C -------------------------------------------------------- IF (NEWT.GT.1.AND.NEWT.LT.NIT) THEN @@ -463,7 +463,7 @@ THETA=SQRT(THQ*THQOLD) END IF THQOLD=THQ -@@ -1658,7 +1586,6 @@ +@@ -1658,7 +1586,6 @@ BPD=.FALSE. END IF HP=H*0.99D0 @@ -471,7 +471,7 @@ CALL BPDTCT(N,X,HP,Y,ARGLAG,RPAR,IPAR,UCONT,GRID,NLAGS, & FIRST,LAST,XEND,IGRID,BPV,IBP,ILBP,BPP,BPD, & KMAX,PHI,PAST,IPAST,NRDS) -@@ -1699,7 +1626,7 @@ +@@ -1699,7 +1626,7 @@ QNEWT=DMAX1(1.0D-4,DMIN1(20.0D0,DYTH)) HHFAC=.8D0*QNEWT**(-1.0D0/(4.0D0+NIT-1-NEWT)) H=HHFAC*H @@ -480,7 +480,7 @@ ELSE IF (INREJ.EQ.2) THEN H=H*0.55D0 HHFAC=0.55D0 -@@ -1743,29 +1670,29 @@ +@@ -1743,29 +1670,29 @@ C --- ITERATIVE CORRECTION OF THE BREAKING POINT C ---------------------------------------------------------------- IF (BPD) THEN @@ -518,7 +518,7 @@ END IF END IF C ---------------------------------------------------------------- -@@ -1961,19 +1888,14 @@ +@@ -1961,19 +1888,14 @@ END IF C --- COMPUTE THE RIGHT-HAND SIDE CONT(1:N)=Y(1:N)+Z1(1:N) @@ -541,7 +541,7 @@ NFCN=NFCN+3 C CCC ---> RHS COMPUTATION -@@ -2094,7 +2016,7 @@ +@@ -2094,7 +2016,7 @@ & FCN,NFCN,Y0,Y,IJOB,X,M1,M2,NM1,E1,LDE1,ALPHA, & Z1,Z2,Z3,CONT,F1,F2,F3,IP1,IPHES,SCAL,SERR,CERR, & FIRST,REJECT,FAC1,ARGLAG,PHI,RPAR,IPAR, @@ -550,7 +550,7 @@ FAC=MIN(SAFE,CFAC/(NEWT+2*NIT)) -@@ -2151,7 +2073,6 @@ +@@ -2151,7 +2073,6 @@ ELSE C --- LOOK FOR A BP HP=H*0.99D0 @@ -558,7 +558,7 @@ CALL BPDTCT(N,X,HP,Y,ARGLAG,RPAR,IPAR,UCONT,GRID,NLAGS, & FIRST,LAST,XEND,IGRID,BPV,IBP,ILBP,BPP,BPD, & KMAX,PHI,PAST,IPAST,NRDS) -@@ -2177,7 +2098,6 @@ +@@ -2177,7 +2098,6 @@ C --------> PREDICTIVE CONTROLLER OF GUSTAFSSON IF (NACCPT.GT.1) THEN IF (FLAGUS) THEN @@ -566,7 +566,7 @@ FACGUS=(HACC/H)*(ERR**2/ERRACC)**0.25D0/SAFE FACGUS=MAX(FACR,MIN(FACL,FACGUS)) QUOT=MAX(QUOT,FACGUS) -@@ -2221,7 +2141,7 @@ +@@ -2221,7 +2141,7 @@ CONT(I+N3)=A2-ACONT3 ELSE C --- QUADRATIC APPROXIMATION @@ -575,7 +575,7 @@ C --- INVECE DI: C --- CONT(I+N3)=CONT(I+N2)-ACONT3 END IF -@@ -2267,9 +2187,8 @@ +@@ -2267,9 +2187,8 @@ IF (LAST) THEN C --- LAST HAS TO BE RE/DEFINED IF (BPD) THEN @@ -586,7 +586,7 @@ C --- IF (.NOT.IMPLCT.OR.NEUTRAL) THEN ! EXPLICIT PROBLEM HE=DMAX1(H/1.D4,10.D0*UROUND) -@@ -2278,80 +2197,70 @@ +@@ -2278,80 +2197,70 @@ C -------------------- C --- C --- EULER STEP @@ -698,7 +698,7 @@ C --- BPD=.FALSE. END IF -@@ -2385,21 +2294,16 @@ +@@ -2385,21 +2294,16 @@ FIRST=.FALSE. IF (LAST) FIRST=.TRUE. C --- COMPUTATION OF Y0 @@ -723,7 +723,7 @@ H=HOPT IDID=1 C --- END OF COMPUTATION -@@ -2411,10 +2315,10 @@ +@@ -2411,10 +2315,10 @@ C LEFT=.FALSE. FIRST=.TRUE. XEND=GRID(IGRID) @@ -738,7 +738,7 @@ FLAGUS=.FALSE. IF (WORK7.EQ.0.D0) THEN HMAXN=XEND-X -@@ -2423,8 +2327,7 @@ +@@ -2423,8 +2327,7 @@ HNEW=MIN(HNEW,H) END IF END IF @@ -748,7 +748,7 @@ HNEW=MIN(HNEW,HMAXN) HOPT=MIN(H,HNEW) IF (REJECT) HNEW=MIN(HNEW,H) -@@ -2551,13 +2454,13 @@ +@@ -2551,13 +2454,13 @@ C --- RETURN LABEL 980 CONTINUE @@ -769,7 +769,7 @@ C --- DEALLOCATION OF THE MEMORY DEALLOCATE (Z1,Z2,Z3,Y0,SCAL,F1,F2,F3) -@@ -2566,7 +2469,7 @@ +@@ -2566,7 +2469,7 @@ IF (IMPLCT) DEALLOCATE(FMAS) DEALLOCATE (IP1,IP2,IPHES) DEALLOCATE (E1,E2R,E2I) @@ -778,7 +778,7 @@ IF (NLAGS.GT.0) THEN DEALLOCATE (FJACS,FJACLAG) DEALLOCATE (IVL,IVE,IVC,ILS,ICOUN) -@@ -2584,8 +2487,7 @@ +@@ -2584,8 +2487,7 @@ C *********************************************************** C SUBROUTINE LAGR5(IL,X,Y,ARGLAG,PAST,THETA,IPOS,RPAR,IPAR, @@ -788,7 +788,7 @@ C ---------------------------------------------------------- C THIS FUNCTION CAN BE USED FOR CONTINUOUS OUTPUT IN CONNECTION C WITH THE OUTPUT-SUBROUTINE FOR RADAR5. IT PROVIDES THE -@@ -2594,31 +2496,21 @@ +@@ -2594,31 +2496,21 @@ USE IP_ARRAY IMPLICIT DOUBLE PRECISION (A-H,O-Z) INTEGER, PARAMETER :: DP=kind(1D0) @@ -825,7 +825,7 @@ C --- INITIAL PHASE THETA=XLAG IPOS=-1 -@@ -2628,51 +2520,50 @@ +@@ -2628,51 +2520,50 @@ COMPAR=UROUND*MAX(ABS(XLAG),ABS(X0B)) EPSACT=10.D0*COMPAR @@ -913,7 +913,7 @@ C --- COMPUTE THE POSITION OF XLAG IPA = IACT+IDIF -@@ -2680,7 +2571,7 @@ +@@ -2680,7 +2571,7 @@ IF (XLAG-PAST(IPA).LT.0.D0) THEN WRITE (6,*) ' MEMORY FULL, MXST = ',MXST IRTRN=-1 @@ -922,7 +922,7 @@ C RETURN END IF -@@ -2690,9 +2581,7 @@ +@@ -2690,9 +2581,7 @@ C -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- C --- INITIALIZE POSITION INSIDE THE MEMORY @@ -933,7 +933,7 @@ C --- STEP AND DELAYS IF (XLAG-XRIGHT.GT.0.D0) THEN IF (.NOT.FLAGN) THEN -@@ -2725,7 +2614,7 @@ +@@ -2725,7 +2614,7 @@ IPOS=INEXT GOTO 2 END IF @@ -942,7 +942,7 @@ IPREV=(MXST-1)*IDIF+1 ELSE IPREV=IPOS-IDIF -@@ -2737,45 +2626,45 @@ +@@ -2737,45 +2626,45 @@ IF (.NOT.((IL.EQ.ILBP).AND.(BPD.OR.FIRST))) GOTO 10 IF (BPP.EQ.X0B) THEN @@ -1013,7 +1013,7 @@ C THETA=BPP*(1.D0-100*UROUND) C ELSE C THETA=BPP*(1.D0+100*UROUND) -@@ -2783,12 +2672,12 @@ +@@ -2783,12 +2672,12 @@ C SE PERO' IL DATO INIZIALE E' ESTENDIBILE THETA=XLAG RETURN @@ -1030,7 +1030,7 @@ IF (LEFT) THEN C --- PREVIOUS INTERVAL HAS TO BE SELECTED IPOS=IPOSB-IDIF -@@ -2800,18 +2689,12 @@ +@@ -2800,18 +2689,12 @@ C --------------------------------------------------------- C ----- COMPUTE THETA (<0): SITUAZIONE PIU' TIPICA @@ -1052,7 +1052,7 @@ RETURN END C -@@ -2821,8 +2704,7 @@ +@@ -2821,8 +2704,7 @@ C *********************************************************** C @@ -1062,7 +1062,7 @@ C ---------------------------------------------------------- C THIS FUNCTION CAN BE USED FOR CONTINUOUS OUTPUT IN CONNECTION C WITH THE SUBROUTINE LAGR5. IT PROVIDES AN APPROXIMATION -@@ -2834,11 +2716,8 @@ +@@ -2834,11 +2716,8 @@ REAL(kind=DP), dimension(1), intent(in) :: RPAR INTEGER, dimension(1), intent(in) :: IPAR C --- @@ -1076,7 +1076,7 @@ LOGICAL FLAGS,FLAGN C---- COMMON BLOCKS COMMON /CONSTN/C1,C2,C1M1,C2M1,C1MC2 -@@ -2873,8 +2752,7 @@ +@@ -2873,8 +2752,7 @@ C *********************************************************** C @@ -1086,7 +1086,7 @@ C ---------------------------------------------------------- C THIS FUNCTION CAN BE USED FOR CONTINUOUS OUTPUT IN CONNECTION C WITH THE SUBROUTINE LAGR5. IT PROVIDES AN APPROXIMATION -@@ -2885,12 +2763,9 @@ +@@ -2885,12 +2763,9 @@ C --- REAL(kind=DP) PHI,DLAGR5 REAL(kind=DP), dimension(1), intent(in) :: RPAR INTEGER, dimension(1), intent(in) :: IPAR @@ -1102,7 +1102,7 @@ LOGICAL FLAGS,FLAGN C---- COMMON BLOCKS COMMON /CONSTN/C1,C2,C1M1,C2M1,C1MC2 -@@ -2918,8 +2793,7 @@ +@@ -2918,8 +2793,7 @@ I=I+IPOS DLAGR5=PAST(NRDS+I)+(THETA-C2M1)*(PAST(2*NRDS+I) & +(THETA-C1M1)*PAST(3*NRDS+I)) @@ -1112,7 +1112,7 @@ DLAGR5=DLAGR5/H RETURN END -@@ -2946,38 +2820,32 @@ +@@ -2946,38 +2820,32 @@ REAL(kind=DP), dimension(1) :: UCONT REAL(kind=DP), dimension(1), intent(inout) :: GRID REAL(kind=DP), dimension(1) :: BPV @@ -1157,7 +1157,7 @@ C ----- DEVIATING ARGUMENT AT X+H IF (ABS(ALS-ALD).LE.COMPAR) GO TO 33 -@@ -3013,10 +2881,8 @@ +@@ -3013,10 +2881,8 @@ XA=X+THNEW*H DO IC=1,N YADV(IC)=CONTR5(IC,N,XA,UCONT,XLAST,HLAST) @@ -1170,7 +1170,7 @@ IF ((ALS-BPP)*(ALN-BPP).LE.0.D0) THEN ALD=ALN THRIGH=THNEW -@@ -3052,7 +2918,7 @@ +@@ -3052,7 +2918,7 @@ 37 CONTINUE DEALLOCATE(YADV) @@ -1179,7 +1179,7 @@ RETURN END -@@ -3064,23 +2930,20 @@ +@@ -3064,23 +2930,20 @@ C THIS SUBROUTINE CAN BE USED FOR APPROXIMATING BREAKING POINTS C IN TANDEM WITH THE SIMPLIFIED NEWTON ITERATION.. C ---------------------------------------------------------- @@ -1217,7 +1217,7 @@ C C --- ALLOCATE(YCONT(4*N),YAPP(N)) -@@ -3114,15 +2977,11 @@ +@@ -3114,15 +2977,11 @@ XL=X+THLEFT*H DO I=1,N YAPP(I)=CONTR5(I,N,XL,YCONT,XSOL,HSOL) @@ -1236,7 +1236,7 @@ DO K=1,KMAX THNEW = THRIGH - (ALD-BPP)*(THRIGH-THLEFT)/(ALD-ALS) THLEFT= THRIGH -@@ -3137,10 +2996,8 @@ +@@ -3137,10 +2996,8 @@ ALS=ALD DO I=1,N YAPP(I)=CONTR5(I,N,XAP,YCONT,XSOL,HSOL) diff --git a/thirdparty/hairer/radau_decsol.patch b/thirdparty/hairer/radau_decsol.patch index f1cd4c90..ae149abc 100644 --- a/thirdparty/hairer/radau_decsol.patch +++ b/thirdparty/hairer/radau_decsol.patch @@ -84,11 +84,11 @@ Index: radau_decsol.pyf --- radau_decsol.pyf (revision 829) +++ radau_decsol.pyf (working copy) @@ -9,7 +9,7 @@ - double precision dimension(n) :: y - double precision dimension(n),depend(n),intent(out) :: y0 - double precision dimension(1),intent(hide) :: rpar -- integer dimension(1),intent(hide) :: ipar -+ integer dimension(1),intent(out) :: ipar - end subroutine fcn - subroutine mas(n,am,lmas,rpar,ipar) ! in :radau5:radau_decsol.f:radcor:unknown_interface - integer, optional,check(len(am)>=n),depend(am) :: n=shape(am,0) + double precision dimension(n) :: y + double precision dimension(n),depend(n),intent(out) :: y0 + double precision dimension(1),intent(hide) :: rpar +- integer dimension(1),intent(hide) :: ipar ++ integer dimension(1),intent(out) :: ipar + end subroutine fcn + subroutine mas(n,am,lmas,rpar,ipar) ! in :radau5:radau_decsol.f:radcor:unknown_interface + integer, optional,check(len(am)>=n),depend(am) :: n=shape(am,0) diff --git a/thirdparty/hairer/radau_decsol.pyf b/thirdparty/hairer/radau_decsol.pyf index 86d77801..835650b8 100644 --- a/thirdparty/hairer/radau_decsol.pyf +++ b/thirdparty/hairer/radau_decsol.pyf @@ -1,98 +1,98 @@ -! -*- f90 -*- -! Note: the context of this file is case sensitive. - -python module radcor__user__routines - interface radcor_user_interface - subroutine fcn(n,x,y,y0,rpar,ipar) ! in :radau5:radau_decsol.f:radcor:unknown_interface - integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) - double precision :: x - double precision dimension(n) :: y - double precision dimension(n),depend(n),intent(out) :: y0 - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(out) :: ipar - end subroutine fcn - subroutine mas(n,am,lmas,rpar,ipar) ! in :radau5:radau_decsol.f:radcor:unknown_interface - integer, optional,check(len(am)>=n),depend(am) :: n=shape(am,0) - double precision dimension(lmas,n),depend(lmas,n),intent(in,out) :: am - !double precision dimension(n),depend(n),intent(hide) :: lmas - integer, optional,intent(hide) :: lmas=1 - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - end subroutine mas - subroutine solout(nrsol,xosol,xsol,y,cont,werr,lrc,nsolu,rpar,ipar,irtrn) ! in :radau5:radau_decsol.f:radcor:unknown_interface - integer :: nrsol - double precision :: xosol - double precision :: xsol - double precision dimension(nsolu) :: y - double precision dimension(4*nsolu) :: cont - double precision dimension(nsolu) :: werr - integer :: lrc - integer, optional,check(len(y)>=nsolu),depend(y),intent(hide) :: nsolu=len(y) - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - integer,intent(in,out) :: irtrn - end subroutine solout - subroutine jac(n,x,y,fjac,ldjac,rpar,ipar) ! in :radau5:radau_decsol.f:radcor:unknown_interface - integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) - double precision :: x - double precision dimension(n) :: y - double precision dimension(ldjac,n),depend(n,ldjac),intent(out) :: fjac - !integer, optional,check(shape(fjac,0)==ldjac),depend(fjac),intent(hide) :: ldjac=shape(fjac,0) - integer, optional,check(len(y)==ldjac),depend(y),intent(hide) :: ldjac=len(y) - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - end subroutine jac - end interface radcor_user_interface -end python module radcor__user__routines -python module radau5 ! in - interface ! in :radau5 - subroutine radau5(n,fcn,x,y,xend,h,rtol,atol,itol,jac,ijac,mljac,mujac,mas,imas,mlmas,mumas,solout,iout,work,lwork,iwork,liwork,rpar,ipar,idid) ! in :radau5:radau_decsol.f - use radcor__user__routines - integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) - external fcn - double precision,intent(in,out) :: x - double precision dimension(n),intent(in,out) :: y - double precision :: xend - double precision,intent(in,out) :: h - double precision dimension(n),intent(in) :: rtol - double precision dimension(n),intent(in) :: atol - integer :: itol - external jac - integer :: ijac - integer :: mljac - integer :: mujac - external mas - integer :: imas - integer :: mlmas - integer :: mumas - external solout - integer :: iout - double precision dimension(lwork),intent(in) :: work - integer, optional,check(len(work)>=lwork),depend(work),intent(hide) :: lwork=len(work) - integer dimension(liwork),intent(in,out) :: iwork - integer, optional,check(len(iwork)>=liwork),depend(iwork),intent(hide) :: liwork=len(iwork) - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - integer,intent(out) :: idid - end subroutine radau5 - function contr5(i,x,cont,lrc) ! in :radau5:radau_decsol.f - integer :: i - double precision :: x - double precision dimension(lrc) :: cont - integer, optional,check(len(cont)>=lrc),depend(cont),intent(hide) :: lrc=len(cont) - integer :: nn - integer :: nn2 - integer :: nn3 - integer :: nn4 - double precision :: xsol - double precision :: hsol - double precision :: c2m1 - double precision :: c1m1 - double precision :: contr5 - common /conra5/ nn,nn2,nn3,nn4,xsol,hsol,c2m1,c1m1 - end function contr5 - end interface -end python module radau5 - -! This file was auto-generated with f2py (version:2). -! See http://cens.ioc.ee/projects/f2py2e/ +! -*- f90 -*- +! Note: the context of this file is case sensitive. + +python module radcor__user__routines + interface radcor_user_interface + subroutine fcn(n,x,y,y0,rpar,ipar) ! in :radau5:radau_decsol.f:radcor:unknown_interface + integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) + double precision :: x + double precision dimension(n) :: y + double precision dimension(n),depend(n),intent(out) :: y0 + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(out) :: ipar + end subroutine fcn + subroutine mas(n,am,lmas,rpar,ipar) ! in :radau5:radau_decsol.f:radcor:unknown_interface + integer, optional,check(len(am)>=n),depend(am) :: n=shape(am,0) + double precision dimension(lmas,n),depend(lmas,n),intent(in,out) :: am + !double precision dimension(n),depend(n),intent(hide) :: lmas + integer, optional,intent(hide) :: lmas=1 + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + end subroutine mas + subroutine solout(nrsol,xosol,xsol,y,cont,werr,lrc,nsolu,rpar,ipar,irtrn) ! in :radau5:radau_decsol.f:radcor:unknown_interface + integer :: nrsol + double precision :: xosol + double precision :: xsol + double precision dimension(nsolu) :: y + double precision dimension(4*nsolu) :: cont + double precision dimension(nsolu) :: werr + integer :: lrc + integer, optional,check(len(y)>=nsolu),depend(y),intent(hide) :: nsolu=len(y) + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + integer,intent(in,out) :: irtrn + end subroutine solout + subroutine jac(n,x,y,fjac,ldjac,rpar,ipar) ! in :radau5:radau_decsol.f:radcor:unknown_interface + integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) + double precision :: x + double precision dimension(n) :: y + double precision dimension(ldjac,n),depend(n,ldjac),intent(out) :: fjac + !integer, optional,check(shape(fjac,0)==ldjac),depend(fjac),intent(hide) :: ldjac=shape(fjac,0) + integer, optional,check(len(y)==ldjac),depend(y),intent(hide) :: ldjac=len(y) + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + end subroutine jac + end interface radcor_user_interface +end python module radcor__user__routines +python module radau5 ! in + interface ! in :radau5 + subroutine radau5(n,fcn,x,y,xend,h,rtol,atol,itol,jac,ijac,mljac,mujac,mas,imas,mlmas,mumas,solout,iout,work,lwork,iwork,liwork,rpar,ipar,idid) ! in :radau5:radau_decsol.f + use radcor__user__routines + integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) + external fcn + double precision,intent(in,out) :: x + double precision dimension(n),intent(in,out) :: y + double precision :: xend + double precision,intent(in,out) :: h + double precision dimension(n),intent(in) :: rtol + double precision dimension(n),intent(in) :: atol + integer :: itol + external jac + integer :: ijac + integer :: mljac + integer :: mujac + external mas + integer :: imas + integer :: mlmas + integer :: mumas + external solout + integer :: iout + double precision dimension(lwork),intent(in) :: work + integer, optional,check(len(work)>=lwork),depend(work),intent(hide) :: lwork=len(work) + integer dimension(liwork),intent(in,out) :: iwork + integer, optional,check(len(iwork)>=liwork),depend(iwork),intent(hide) :: liwork=len(iwork) + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + integer,intent(out) :: idid + end subroutine radau5 + function contr5(i,x,cont,lrc) ! in :radau5:radau_decsol.f + integer :: i + double precision :: x + double precision dimension(lrc) :: cont + integer, optional,check(len(cont)>=lrc),depend(cont),intent(hide) :: lrc=len(cont) + integer :: nn + integer :: nn2 + integer :: nn3 + integer :: nn4 + double precision :: xsol + double precision :: hsol + double precision :: c2m1 + double precision :: c1m1 + double precision :: contr5 + common /conra5/ nn,nn2,nn3,nn4,xsol,hsol,c2m1,c1m1 + end function contr5 + end interface +end python module radau5 + +! This file was auto-generated with f2py (version:2). +! See http://cens.ioc.ee/projects/f2py2e/ diff --git a/thirdparty/hairer/rodas_decsol.pyf b/thirdparty/hairer/rodas_decsol.pyf index c8f91440..6cd666c7 100644 --- a/thirdparty/hairer/rodas_decsol.pyf +++ b/thirdparty/hairer/rodas_decsol.pyf @@ -1,103 +1,103 @@ -! -*- f90 -*- -! Note: the context of this file is case sensitive. - -python module roscor__user__routines - interface roscor_user_interface - subroutine solout(e_naccpt_1_err,xold,x,y,cont,lrc,n,rpar,ipar,irtrn) ! in :rodas:rodas_decsol.f:roscor:unknown_interface - double precision :: e_naccpt_1_err - double precision :: xold - double precision :: x - double precision dimension(n) :: y - double precision dimension(4 * n),depend(n) :: cont - integer :: lrc - integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - integer,intent(in,out) :: irtrn - end subroutine solout - subroutine fcn(n,x,y,dy1,rpar,ipar) ! in :rodas:rodas_decsol.f:roscor:unknown_interface - integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) - double precision :: x - double precision dimension(n) :: y - double precision dimension(n),depend(n),intent(out) :: dy1 - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - end subroutine fcn - subroutine mas(n,am,lmas,rpar,ipar) ! in :rodas:rodas_decsol.f:roscor:unknown_interface - integer, optional,check(len(am)>=n),depend(am) :: n=shape(am,0) - double precision dimension(lmas,n),depend(lmas,n),intent(in,out) :: am - !double precision dimension(n),depend(n),intent(hide) :: lmas - integer, optional,intent(hide) :: lmas=1 - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - end subroutine mas - subroutine jac(n,x,y,fjac,ldjac,rpar,ipar) ! in :rodas:rodas_decsol.f:roscor:unknown_interface - integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) - double precision :: x - double precision dimension(n) :: y - double precision dimension(ldjac,n),depend(n,ldjac),intent(out) :: fjac - integer, optional,check(len(y)==ldjac),depend(y),intent(hide) :: ldjac=len(y) - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - end subroutine jac - subroutine dfx(n,x,y,fx,rpar,ipar) ! in :rodas:rodas_decsol.f:roscor:unknown_interface - integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) - double precision :: x - double precision dimension(n) :: y - double precision dimension(n),depend(n) :: fx - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - end subroutine dfx - end interface roscor_user_interface -end python module roscor__user__routines - -python module rodas ! in - interface ! in :rodas - subroutine rodas(n,fcn,ifcn,x,y,xend,h,rtol,atol,itol,jac,ijac,mljac,mujac,dfx,idfx,mas,imas,mlmas,mumas,solout,iout,work,lwork,iwork,liwork,rpar,ipar,idid) ! in :rodas:rodas_decsol.f - use roscor__user__routines - integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) - external fcn - integer :: ifcn - double precision,intent(in,out) :: x - double precision dimension(n),intent(in,out) :: y - double precision :: xend - double precision,intent(in,out) :: h - double precision dimension(n) :: rtol - double precision dimension(n) :: atol - integer :: itol - external jac - integer :: ijac - integer :: mljac - integer :: mujac - external dfx - integer :: idfx - external mas - integer :: imas - integer :: mlmas - integer :: mumas - external solout - integer :: iout - double precision dimension(lwork) :: work - integer, optional,check(len(work)>=lwork),depend(work),intent(hide) :: lwork=len(work) - integer dimension(liwork),intent(in,out) :: iwork - integer, optional,check(len(iwork)>=liwork),depend(iwork),intent(hide) :: liwork=len(iwork) - double precision dimension(1),intent(hide) :: rpar - integer dimension(1),intent(hide) :: ipar - integer,intent(out) :: idid - end subroutine rodas - function contro(i,x,cont,lrc) ! in :rodas:rodas_decsol.f - integer :: i - double precision :: x - double precision dimension(lrc) :: cont - integer, optional,check(len(cont)>=lrc),depend(cont),intent(hide) :: lrc=len(cont) - double precision :: xold - double precision :: h - integer :: n - double precision :: contro - common /conros/ xold,h,n - end function contro - end interface -end python module rodas - -! This file was auto-generated with f2py (version:2). -! See http://cens.ioc.ee/projects/f2py2e/ +! -*- f90 -*- +! Note: the context of this file is case sensitive. + +python module roscor__user__routines + interface roscor_user_interface + subroutine solout(e_naccpt_1_err,xold,x,y,cont,lrc,n,rpar,ipar,irtrn) ! in :rodas:rodas_decsol.f:roscor:unknown_interface + double precision :: e_naccpt_1_err + double precision :: xold + double precision :: x + double precision dimension(n) :: y + double precision dimension(4 * n),depend(n) :: cont + integer :: lrc + integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + integer,intent(in,out) :: irtrn + end subroutine solout + subroutine fcn(n,x,y,dy1,rpar,ipar) ! in :rodas:rodas_decsol.f:roscor:unknown_interface + integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) + double precision :: x + double precision dimension(n) :: y + double precision dimension(n),depend(n),intent(out) :: dy1 + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + end subroutine fcn + subroutine mas(n,am,lmas,rpar,ipar) ! in :rodas:rodas_decsol.f:roscor:unknown_interface + integer, optional,check(len(am)>=n),depend(am) :: n=shape(am,0) + double precision dimension(lmas,n),depend(lmas,n),intent(in,out) :: am + !double precision dimension(n),depend(n),intent(hide) :: lmas + integer, optional,intent(hide) :: lmas=1 + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + end subroutine mas + subroutine jac(n,x,y,fjac,ldjac,rpar,ipar) ! in :rodas:rodas_decsol.f:roscor:unknown_interface + integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) + double precision :: x + double precision dimension(n) :: y + double precision dimension(ldjac,n),depend(n,ldjac),intent(out) :: fjac + integer, optional,check(len(y)==ldjac),depend(y),intent(hide) :: ldjac=len(y) + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + end subroutine jac + subroutine dfx(n,x,y,fx,rpar,ipar) ! in :rodas:rodas_decsol.f:roscor:unknown_interface + integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) + double precision :: x + double precision dimension(n) :: y + double precision dimension(n),depend(n) :: fx + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + end subroutine dfx + end interface roscor_user_interface +end python module roscor__user__routines + +python module rodas ! in + interface ! in :rodas + subroutine rodas(n,fcn,ifcn,x,y,xend,h,rtol,atol,itol,jac,ijac,mljac,mujac,dfx,idfx,mas,imas,mlmas,mumas,solout,iout,work,lwork,iwork,liwork,rpar,ipar,idid) ! in :rodas:rodas_decsol.f + use roscor__user__routines + integer, optional,check(len(y)>=n),depend(y),intent(hide) :: n=len(y) + external fcn + integer :: ifcn + double precision,intent(in,out) :: x + double precision dimension(n),intent(in,out) :: y + double precision :: xend + double precision,intent(in,out) :: h + double precision dimension(n) :: rtol + double precision dimension(n) :: atol + integer :: itol + external jac + integer :: ijac + integer :: mljac + integer :: mujac + external dfx + integer :: idfx + external mas + integer :: imas + integer :: mlmas + integer :: mumas + external solout + integer :: iout + double precision dimension(lwork) :: work + integer, optional,check(len(work)>=lwork),depend(work),intent(hide) :: lwork=len(work) + integer dimension(liwork),intent(in,out) :: iwork + integer, optional,check(len(iwork)>=liwork),depend(iwork),intent(hide) :: liwork=len(iwork) + double precision dimension(1),intent(hide) :: rpar + integer dimension(1),intent(hide) :: ipar + integer,intent(out) :: idid + end subroutine rodas + function contro(i,x,cont,lrc) ! in :rodas:rodas_decsol.f + integer :: i + double precision :: x + double precision dimension(lrc) :: cont + integer, optional,check(len(cont)>=lrc),depend(cont),intent(hide) :: lrc=len(cont) + double precision :: xold + double precision :: h + integer :: n + double precision :: contro + common /conros/ xold,h,n + end function contro + end interface +end python module rodas + +! This file was auto-generated with f2py (version:2). +! See http://cens.ioc.ee/projects/f2py2e/ diff --git a/thirdparty/kvaernoe/SDIRK-DAE.c b/thirdparty/kvaernoe/SDIRK-DAE.c index 6c11bf99..9166721f 100644 --- a/thirdparty/kvaernoe/SDIRK-DAE.c +++ b/thirdparty/kvaernoe/SDIRK-DAE.c @@ -1,991 +1,991 @@ -#include -#include -#include -#include - -/* Definition of macros and constants. */ -/*-------------------------------------*/ -#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) -#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) - -#define HMIN 1.e-8 -#define EPS sqrt(DBL_EPSILON) -#define ROCK 0.5 /* Max allowed rate_of_convergence. */ -#define PES_FAC 0.9 /* Pessismist factor, used for stepsize */ -#define KAPPA 0.5 /* Iteration error < KAPPA*tolerance. */ - -/* problem : */ -/*---------------------------------------------------------------------*/ -struct problem -{ int n; /* The dimension of the system. */ - double *v; /* The dependent variables. */ - double *vp; /* The derivatives of the dependent variables. */ - double x; /* The independent variable. */ -}; - -/* information : */ -/*---------------------------------------------------------------------*/ -struct information -{ int num_jacobian; /* 0 if exact jacobian is supplied, */ - /* 1 if not. */ - double *abserr; /* Absolute and */ - double relerr; /* relative tolerance. */ - double xend; /* End point for the integration. */ - double h0; /* Starting stepsize. */ - double hmax; /* Max allowed stepsize. */ -}; - -/* etcetera: work arrays and variables. */ -/*-------------------------------------------------------------------------*/ -struct etcetera -{ - double *dfdv; /* The Jacobi matrix df/dv. */ - double *dfdvp; /* The Jacobi matrix df/dv'. */ - double *newton; /* The (factorised) Newton matrix. */ - int *pivot; /* The pivot elements to newton. */ - double *tolerance; /* The array : abserr(i)+relerr*v(i). */ - double min_tolerance; /* Used for the residual error test. */ - double *b; /* The b in f(Vi',h*g*VP+b,xi) = O. */ - double rate_of_convergence; - double h; /* The stepsize. */ - double gh; /* gh = a(i,i)*stepsize. */ - double *temp1; /* Work arrays of dimension n. */ - double *temp2; - int jacobi_status; /* 0 if the jacobian is new, else 1. */ - int newton_status; /* 0 if the Newton matrix is fact. */ - /* with current stepsize, else 1. */ - int new_jacobian; /* Updated jacobians required. */ - int new_fact; /* New LU-factorisation is required. */ - int steps_red; /* Number of steps before the */ - /* stepsize is allowed to increase. */ - double *vp_stage[4]; /* Stage values. */ -}; - - -/* method : The parameters of the method. */ -/*------------------------------------------------------------------*/ -struct method -{ - int s; /* Number of stages. */ - double a[4][4]; - double b_error[4]; /* The b-vector for error estimate. */ - double c[4]; -}; - -/* statistic : */ -/*------------------------------------------------------------------*/ -struct statistic -{ - int steps; - int rejected_steps; - int notkonv_steps; - int function_evaluations; - int jacobi_evaluations; - int factorisations; /* of the Newton matrix. */ - int solutions; /* of the linear system. */ -} number_of; -/************ End of the header. ************************************/ - - - -/*------------------------------------------------------------------*/ -/* sdirk_dae : Solves fully implicit uniform index 1 DAEs with a */ -/* SDIRK method of order 3, embedded with a second */ -/* order error estimating method. */ -/*------------------------------------------------------------------*/ - -int sdirk_dae(struct problem *prob, struct information *info) - -/*------------------------------------------------------------------*/ -/* var : On entry : Function values at the starting point. */ -/* On exit : Function values at the end point. */ -/* info : User supplied information. Not altered by the function. */ -/* */ -/* returns : 0 :The integration has been successfully performed. */ -/* 1 : The Newton matrix is singular. */ -/* 2 : The Newton-iterations fails to converge. */ -/* 3 : The local error criteria fails to be satisfied, */ -/* the step-size becomes too small. */ -/* 4 : The error tolerance is too stringent. */ -/*------------------------------------------------------------------*/ -{ - int i, flag; - double step_fact; - struct method rk; - struct problem prob_old, *var_old, *var; - struct etcetera etc; - extern struct statistic number_of; - - /* Allocation of memory, and initialization of variables. */ - /*--------------------------------------------------------*/ - work_alloc(prob->n, &prob_old, &etc); - var_old = &prob_old; - var = prob; - var_old->n = var->n; - for (i = 0; i < var->n; ++i) - { - var_old->v[i] = var->v[i]; - var_old->vp[i] = var->vp[i]; - } - var_old->x = var->x - HMIN; - etc.new_jacobian = 1; - etc.new_fact = 1; - etc.jacobi_status = 1; - etc.steps_red = 0; - etc.h = info->h0; - - set_coeff(&rk); - zero_stat(); - - etc.gh = info->h0 * rk.a[0][0]; - - /* Main loop. */ - /*--------------------------------------------------------*/ - while (info->xend - var->x > HMIN) - { - if ( flag = one_step(&var, &var_old, info, &rk, &etc) ) - return flag; - if ( info->xend < var->x + etc.h ) - { - step_fact = (info->xend - var->x)/etc.h; - adjust_stepsize(step_fact, &etc); - } - ++number_of.steps; - } - - prob->x = var->x; - for (i = 0; i < var->n; ++i) - { - prob->v[i] = var->v[i]; - prob->vp[i] = var->vp[i]; - } - - work_free(var_old, &etc); - return 0; -} -/************ End of sdirk_dae **************************************/ - - -/*---------------------------------------------------------------*/ -/* one_step : Takes one step. If appropriate, the stepsize is */ -/* changed. */ -/*---------------------------------------------------------------*/ - -int one_step(struct problem **var, struct problem **var_old, - struct information *info, struct method *rk, - struct etcetera *etc) - -/*---------------------------------------------------------------*/ -/* var : On entry : Function variables for step n. */ -/* On exit : The variables for step n+1 */ -/* var_old : On entry : Function variables for step n-1. */ -/* On exit : The variables for step n. */ -/* */ -/* returns : 0 : if one step is taken successfully. */ -/* 1 : The Newton matrix is singular. */ -/* 2 : The Newton-iterations fails to converge. */ -/* 3 : The local error criteria fails to be satisfied, */ -/* the step-size becomes too small. */ -/* 4 : The error tolerance is too stringent. */ -/*---------------------------------------------------------------*/ -{ - int i, j, stage, no_success; - double loc_error, step_fact, roc_fact, sum, error; - int n, s1; - double *h, *gh, *roc, *tol, *v, **vp_stage; - struct problem *v_swap; - extern struct statistic number_of; -/* Set local names on structure variables. */ -/*-----------------------------------------*/ -v = (*var)->v; -n = (*var)->n; -s1 = rk->s - 1; -h = &etc->h; -gh = &etc->gh; -roc = &etc->rate_of_convergence; -vp_stage = etc->vp_stage; -tol = etc->tolerance; - -if ( compute_tolerance_vector(*var, info, etc) ) - return 4; - -do -{ - - /* If appropriate, the newton matrix is factorized. */ - /* */ - if (etc ->new_fact) - { - if ( make_newton(*var, etc, info) ) - { - if ( adjust_stepsize(0.5, etc) ) - return 3; - if ( make_newton(*var, etc, info) ) - return 1; - } - } - - /* Predict starting values for the iterations. */ - /*---------------------------------------------*/ - prediction(*var, *var_old, rk, etc); - - /* Computes the stage values. */ - /*----------------------------*/ - no_success = solve_stages(*var, rk, etc); - roc_fact = ROCK*PES_FAC/(*roc); - roc_fact = MAX(0.1, roc_fact); - if (no_success) - { - ++number_of.notkonv_steps; - - /* The iterations diverge, update the jacobians, or, */ - /* if the jacobians is updated, reduce the stepsize. */ - /* */ - if (etc->jacobi_status) - { - etc->new_jacobian = 1; - etc->new_fact = 1; - } - else - { - if ( adjust_stepsize( roc_fact, etc) ) - return 2; - etc->steps_red = 2; - } - } - else - { - /* The stage values are successfully computed. */ - /* Compute the local error. */ - /*---------------------------------------------*/ - loc_error = 0.0; - for (i = 0; i < n; ++i) - { - sum = 0.0; - for (stage = 0; stage <= s1; ++stage) - sum += rk->b_error[stage]*vp_stage[stage][i]; - sum *= *h; - error = fabs(sum)/tol[i]; - loc_error = MAX(loc_error, error); - } - - /* Assumes 0.5*error from the local error */ - /* and 0.5*error from iterations. */ - /*--------------------------------------------*/ - loc_error *= 2.0; - - if (64.0*loc_error < 1.0) - step_fact = PES_FAC*4.0; - else - { - step_fact = PES_FAC*pow(loc_error, -1.0/3.0); - step_fact = MAX(0.1, step_fact); - } - if (step_fact <= PES_FAC) - { - /* The step is rejected. The stepsize is reduced */ - /* */ - ++number_of.rejected_steps; - step_fact = MIN(step_fact, roc_fact); - if ( adjust_stepsize(step_fact, etc) ) - return 3; - etc->steps_red = 2; - no_success = 1; - } - } -} -while (no_success); - -/* The step is accepted. */ -/* Place the old values of the variables in var in var_old. */ -/*----------------------------------------------------------*/ -v_swap = *var_old; -*var_old = *var; -*var = v_swap; - -/* Calculate the new value for v. */ -/* Store it in var, together with a new approximation of v', */ -/* and x(n+1) */ -/*-----------------------------------------------------------*/ -for (i = 0; i < n; ++i) -{ - sum = 0.0; - for (stage = 0; stage <= s1; ++stage) - sum += rk->a[s1] [stage]*vp_stage[stage][i]; - (*var)->v[i] = v[i] + (*h)*sum; - (*var)->vp[i] = vp_stage[s1][i]; -} -(*var)->x = (*var_old)->x + *h; - /* If appropriate, change the stepsize. */ - /*---------------------------------------------------------*/ - step_fact = MIN(step_fact, roc_fact); - --etc->steps_red; - - if (etc->h*step_fact > info->hmax) - step_fact = info->hmax/etc->h; - if (step_fact > 1.5 && etc->steps_red < 0) - { - adjust_stepsize(step_fact, etc); - } - else if (step_fact < 1) - { - if ( adjust_stepsize(step_fact, etc) ) - return 3; - } - else - { - etc->new_fact = 0; - etc->new_jacobian = 0; - } - /* The jacobians are not updated. */ - /*--------------------------------*/ - etc->jacobi_status = 1; - - return 0; -} -/************ End of one_step ***************************************/ - - -/*------------------------------------------------------------------*/ -/* solve_stages : Solves the Runge-Kutta equations */ -/* f(Vi',v + h*a(i,1)*V1,+...+h*a(i,i)*Vi',x+ci*h)=0 */ -/* for the stage values Vi' .....Vi'. */ -/*------------------------------------------------------------------*/ - -int solve_stages(struct problem *var, struct method *rk, - struct etcetera *etc) - -/*------------------------------------------------------------------*/ -/* returns : 0 if the iterations converges successfully. */ -/* 1 if not. */ -/*------------------------------------------------------------------*/ -{ - int i, j, stg; - double xi, roc; - double max_roc = 0; - int s, n; - double *b, gh, **vp_stage, h; - double *v, *vp, x; - extern struct statistic number_of; - /* Set local names on structure variables. */ - /*-----------------------------------------*/ - n = var->n; - v = var->v; - vp = var->vp; - x = var->x; - - b = etc->b; - gh = etc->gh; - vp_stage = etc->vp_stage; - h = etc->h; - - /* Main loop. */ - /*------------*/ - for (stg = 0; stg < rk->s; ++stg) - { - - /* Computes b = v + h*a(i,1)*V(1) +... h*a(i,i-1)*V(i-1). */ - /*--------------------------------------------------------*/ - for (i = 0; i < n; ++i) - { - b[i] = v[i]; - for (j = 0; j < stg; ++j) - b[i] += h * rk->a[stg][j] * vp_stage[j][i]; - } - xi = x + rk->c[stg] * h; - - /* Solves the equation. Return if not successfully. */ - /*--------------------------------------------------*/ - if ( solve_nonlinear_equations(n, vp_stage[stg], xi, &roc, etc) ) - { - etc->rate_of_convergence = roc; - return 1; - } - max_roc = MAX(max_roc, roc); - } - etc->rate_of_convergence = max_roc; - return 0; -} -/************ End of solve_stages ***********************************/ - - -/*--------------------------------------------------------------*/ -/* solve_nonlinear_equation : solves the nonlinear equations */ -/* f(vp, gh*vp + b, xi) */ -/* with a modified Newton method. */ -/*--------------------------------------------------------------*/ - -int solve_nonlinear_equations(int n, double *vp, double xi, - double *roc, struct etcetera *etc) - -/*--------------------------------------------------------------*/ -/* n : The dimension of the system. */ -/* vp : The dependent variable. */ -/* On entry : Some starting values for the iterations. */ -/* On exit : If successfull, the solution of the eqautions. */ -/* xi : xi = xn + c(i)*h. */ -/* */ -/* returns : 0 if the iterations converges successfully. */ -/* 1 if the iterations diverges or converges too slow. */ -/*--------------------------------------------------------------*/ -{ - int first_time = 1, near_conv = 0; - int i, j; - double error, kappa, last_error, res_error, err_i; - double *res, *b, *v, *tol, mintol, gh, h; - extern struct statistic number_of; - /* Set local names of structure variables. */ - /*-----------------------------------------*/ - - gh = etc->gh; - h = etc->h; - b = etc->b; - mintol = etc->min_tolerance; - tol =etc->tolerance; - res = etc->temp1; - v = etc->temp2; - - /* The iteration loop. */ - /*---------------------*/ - for (*roc = 0.05; (*roc < 1.0) && (*roc < ROCK || near_conv < 2); ) - { - /* Computes the residual error. */ - /*------------------------------*/ - for (i = 0; i < n; ++i) - v[i] = b[i] + gh*vp[i]; - func(vp, v, xi, res); - ++number_of.function_evaluations; - - /* Solves the linear system. */ - /*---------------------------*/ - backsub(n, etc->newton, etc->pivot, res); - ++number_of.solutions; - - /* vp is updated. */ - /* Computes the displacement error. */ - /*----------------------------------*/ - error = 0.0; - for (i = 0; i < n; ++i) - { - vp[i] -= res[i]; - err_i = fabs(res[i])*h/tol[i]; - error = MAX(error, err_i); - } - - /* Computes rate of convergence. */ - /*-------------------------------*/ - if (first_time) - { - first_time = 0; - if (error < KAPPA) - return 0; - } - else - { - *roc = MAX(0.05,error/last_error); - if ( fabs(*roc/(1.0 - *roc))*error < KAPPA) - return 0; - if (*roc >= ROCK) - ++near_conv; - } - last_error = error; - } - return 1; -} -/************ End of solve_nonlinear_equations **********************/ - -/*------------------------------------------------------------------*/ -/* make_newton : Computes the Newton-matrix df/dv, + g*h*df/dv, */ -/* and performs the LU-decomposition of the matrix. */ -/*------------------------------------------------------------------*/ - -int make_newton(struct problem *var, - struct etcetera *etc, struct information *info) -/*------------------------------------------------------------------*/ -/* returns: 0 if the factorisation was perfomed successfully. */ -/* 1 if the Newton matrix is singular. */ -/*------------------------------------------------------------------*/ - -{ - int i, j; - int *jacobi_status, *pivot, num_jacobian, new_jacobian, n; - double *dfdvp, *dfdv, *newton, gh; - extern struct statistic number_of; - - /* Set local names of structure variables. */ - /*-----------------------------------------*/ - n = var->n; - newton = etc->newton; - pivot = etc->pivot; - gh = etc->gh; - dfdvp = etc->dfdvp; - dfdv = etc->dfdv; - jacobi_status = &(etc->jacobi_status); - new_jacobian = etc->new_jacobian; - num_jacobian = info->num_jacobian; - - /* Computes new jacobian if necessary. */ - /*-------------------------------------*/ - if (new_jacobian) - { if (num_jacobian) - jacobi(var, etc); - else - exact_jacobi(var->v, var->vp, var->x, dfdvp, dfdv); - *jacobi_status = 0; - ++number_of.jacobi_evaluations; - } - - /* Construct the Newton-matrix. */ - /*------------------------------*/ - for (i = 0; i < n; ++i) - for (j = 0; j < n; ++j) - newton[n*i+j] = dfdvp[n*i+j] + gh*dfdv[n*i+j]; - - /* Performs the LU-decomposition of the Newton-matrix. */ - /*-----------------------------------------------------*/ - ++number_of.factorisations; - if (decomp(n, newton, pivot)) - return 1; - else - { etc->newton_status = 0; - return 0; - } -} -/************ End of make_newton ************************************/ - -/*----------------------------------------------------------*/ -/* jacobi : Computes an aproximation to the jacobi-matrices */ -/* df/dv and df/dvp. */ -/*----------------------------------------------------------*/ - -int jacobi(struct problem *var, struct etcetera *etc) -{ - int i, j; - double delta, absv; - int n; - double *v, *vp, x; - double *res, *res_delta, *dfdv, *dfdvp; - extern struct statistic nuMber_of; - - /* Set local names of structure variables. */ - /*-----------------------------------------*/ - n = var->n; - v = var->v; - vp = var->vp; - x = var->x; - dfdv = etc->dfdv; - dfdvp = etc->dfdvp; - res = etc->temp1; - res_delta = etc->temp2; - - /* Start of the computation. */ - /*---------------------------*/ - func(vp, v, x, res); - ++number_of.function_evaluations; - - for (j = 0; j < n; ++j) - { - /* Computation of df/dvp(j). */ - /*---------------------------*/ - absv = fabs(vp[j]); - delta = (MAX(1.0, absv))*EPS; - vp[j] += delta; - func(vp, v, x, res_delta); - ++number_of.function_evaluations; - for (i = 0; i < n; ++i) - dfdvp[n*i+j] = (res_delta[i] - res[i])/delta; - vp[j] -= delta; - - /* Computation of df/dv(j). */ - /*--------------------------*/ - absv = fabs(v[j]); - delta = (MAX(1.0, absv))*EPS; - v[j] += delta; - func(vp, v, x, res_delta); - ++number_of.function_evaluations; - for (i = 0; i < n; ++i) - dfdv[n*i+j] = (res_delta[i] - res[i])/delta; - v[j] -= delta; - } - return 0; -} -/************ End of Jacobi *****************************************/ - -/*----------------------------------------------------------------*/ -/* compute_tolerance_vector : Set tolerance = abserr + relerr*vn. */ -/*----------------------------------------------------------------*/ - -int compute_tolerance_vector(struct problem *var, - struct information *info, - struct etcetera *etc) - -/*------------------------------------------------------------------*/ -/* returns : 0 : All elements in the vector are sufficiently large. */ -/* 1 : The tolerance is near 0 for at least one variables.*/ -/*------------------------------------------------------------------*/ -{ - int i, n; - double mintol = 1000.0; - double *tol, *abserr, relerr, *v; - - /* Set local names on structure variables. */ - /*-----------------------------------------*/ - - n = var->n; - v = var->v; - abserr = info->abserr; - relerr = info->relerr; - tol = etc->tolerance; - - for (i = 0; i < n; ++i) - { - tol[i] = abserr[i] + relerr*fabs(v[i]); - mintol = MIN(mintol, tol[i]); - } - if (mintol < EPS) - return 1; - else - etc->min_tolerance = mintol; - - return 0; -} -/************ End of compute_tolerance_vector ***********************/ - - -/*--------------------------------------------------------------*/ -/* prediction : Prediction of the stage_values. */ -/* Used as starting values for the iterations */ -/* 1.edition. : Vi = vn. */ -/*--------------------------------------------------------------*/ - -int prediction( struct problem *var, struct problem *var_old, - struct method *rk, struct etcetera *etc) - -/*--------------------------------------------------------------*/ -/* var : Function variables for step n. */ -/* var_old : Function variables for step n-1. */ -/*--------------------------------------------------------------*/ -{ - int i, stage, n, s; - double **vp_stage, *vp, h; - double delta; - - /* Set local names to structure variables. */ - /*-----------------------------------------*/ - n = var->n; - vp = var->vp; - s = rk->s; - vp_stage = etc->vp_stage; - h = etc->h; - /* Main loop. */ - /*------------*/ - for (i = 0; i < n; ++i) - { - delta = (var->vp[i] - var_old->vp[i])/(var->x - var_old->x); - for (stage = 0; stage < s; ++stage) - vp_stage[stage][i] = delta*rk->c[stage]*h + vp[i]; - } - - return 0; -} -/************ End of predict ****************************************/ - - - -/*---------------------------------------------------------------*/ -/* adjust_stepsize : Increase (or decrease) the stepsize with a */ -/* factor step_fact. */ -/*---------------------------------------------------------------*/ - -int adjust_stepsize(double step_fact, struct etcetera *etc) -{ - etc->h *= step_fact; - if (etc->h < HMIN) - return 1; - etc->gh *= step_fact; - etc->new_fact = 1; - etc->new_jacobian = 0; - return 0; -} -/************ End of adjust_stepsize ********************************/ - - - -/*---------------------------------------------------------------*/ -/* decomp: Gaussian Elimination with Partial Pivoting. */ -/* References : Golub & van Loan "Matrix Computations", */ -/* Algorithm 4.4-2. */ -/*---------------------------------------------------------------*/ - -int decomp(int n, double *a, int *piv) - -/*---------------------------------------------------------------*/ -/* n : The dimension of the matrix. */ -/* a : Pointer to an array of dimension n*n. */ -/* On entry : The matrix to be factorised. */ -/* On exit : The LU-decomposition of the matrix. */ -/* piv : The pivot array, of dimension n. */ -/*---------------------------------------------------------------*/ -{ - int i, j, k, p, nml; - double maxpivot, temp; - nml = n-1; - - /* Start of the factorisation. */ - /*-----------------------------*/ - for (k = 0; k < nml; ++k) - { - maxpivot = 0.0; - - /* Find index of max element in column below the diagonal. */ - /*---------------------------------------------------------*/ - for (p = k; p < n; ++p) - if (maxpivot < fabs(a[n*p+k])) - { - maxpivot = fabs(a[n*p+k]); - piv[k] = p; - } - /* Test for singularity. */ - /*-----------------------*/ - if (maxpivot < EPS) - return 1; - - /* If necessary, swap a(k,j) and a(piv[k],j) for j = 0,..,n-1 */ - /*------------------------------------------------------------*/ - if (piv[k] != k) - for (j = 0; j < n; ++j) - { - temp = a[n*piv[k]+j]; - a[n*piv[k]+j] = a[n*k+j]; - a[n*k+j] = temp; - } - - /* Column elimination. */ - /*---------------------*/ - for (i = k+1; i < n; ++i) - { a[n*i+k] /= a[n*k+k]; - for (j = k+1; j < n; ++j) - a[n*i+j] -= a[n*i+k] * a[n*k+j]; - } - } - - if (fabs(a[n*n-1]) < EPS) return 1; - - /* Everything seems to work, return. */ - /*-----------------------------------*/ - return 0; -} -/************ End of decomp *****************************************/ - - -/*--------------------------------------------------------------*/ -/* backsub: solves the linear equation a*x = b, where a is the */ -/* factorised matrix, given from the function decomp. */ -/*--------------------------------------------------------------*/ - -int backsub(int n, double *a, int *piv, double *b) - -/*--------------------------------------------------------------*/ -/* it : The dimension of the system. */ -/* a : Pointer to an array of dimension n*n. */ -/* On entry : The LU-decomposition of the matrix. */ -/* piv : The pivot array of dimension n. */ -/* b : Pointer to an array of dimension n. */ -/* On entry : The vector b on the right hand side. */ -/* On exit : The solution of the equation. */ -/*--------------------------------------------------------------*/ -{ int i, j, nm1; - double temp; - nm1 = n-1; - - /* Swap b[i] and b[piv[i]] */ - /*-------------------------*/ - for (i = 0; i < nm1; ++i) - if (piv[i] != i) - { - temp = b[piv[i]]; - b[piv[i]] = b[i]; - b[i] = temp; - } - /* Forward elimination. Ly = b. */ - /*------------------------------*/ - for (i = 0; i < n; ++i) - for (j = 0; j < i; ++j) - b[i] -= a[n*i+j]*b[j]; - - /* Back substitution. Ux = y*/ - /*--------------------------*/ - for (i = nm1; i >= 0; --i) - { - for (j = i+1; j < n; ++j) - b[i] -= a[n*i+j]*b[j]; - b[i] /= a[n*i+i]; - } - return 0; -} -/************ End of backsub ****************************************/ - - -/*--------------------------------------------------------------*/ -/* set_coeff : Set the method coefficients. */ -/*--------------------------------------------------------------*/ - -int set_coeff(struct method *rk) -{ - rk ->s = 4; - - rk->a[0][0]= 1.0/4.0; - rk->a[1][0]= 1.0/7.0; - rk->a[1][1]= 1.0/4.0; - rk->a[2][0]= 61.0/144.0; - rk->a[2][1]= -49.0/144.0; - rk->a[2][2]= 1.0/4.0; - rk->a[3][0]= 0.0; - rk->a[3][1]= 0.0; - rk->a[3][2]= 3.0/4.0; - rk->a[3][3]= 1.0/4.0; - - rk->c[0]= 1.0/4.0; - rk->c[1]= 11.0/28.0; - rk->c[2]= 1.0/3.0; - rk->c[3]= 1.0; - - rk->b_error[0]= -61.0/704.0; - rk->b_error[1]= 49.0/704.0; - rk->b_error[2]= 3.0/88.0; - rk->b_error[3]= -3.0/176.0; - - return 0; -} -/************ End of sat_cssff **************************************/ - -/*--------------------------------------------------------------*/ -/* zero_stat : Set all the statistic variables to zero. */ -/*--------------------------------------------------------------*/ - -int zero_stat() -{ - extern struct statistic number_of; - number_of.steps = 0; - number_of.rejected_steps = 0; - number_of.notkonv_steps = 0; - number_of.function_evaluations = 0; - number_of.jacobi_evaluations = 0; - number_of.factorisations = 0; - number_of.solutions = 0; - - return 0; -} -/************ End of zero_stat **************************************/ - - -/*--------------------------------------------------------------*/ -/* work_alloc : Allocates memory for the work structures. */ -/*--------------------------------------------------------------*/ - -int work_alloc(int n, struct problem *var, struct etcetera *etc) -{ - int i; - - /* Allocates memory for the arrays in the structure var. */ - /*-------------------------------------------------------*/ - var->v = (double *) malloc( n*sizeof(double) ); - if (!var->v) - { - printf("Error in work_alloc: Out of memory: \n"); - exit(1); - } - var->vp = (double *) malloc( n*sizeof(double) ); - if (!var->vp) - { - printf("Error in work_alloc: Out of memory\n"); - exit (1); - } - - /* Allocates memory for the arrays in the structure etc. */ - /*-------------------------------------------------------*/ - etc->dfdv = (double *) malloc( (n*n)*sizeof(double) ); - if (!etc->dfdv) - { - printf("Error in work_alloc: Out of memory\n"); - exit(1); - } - etc->dfdvp = (double *) malloc( (n*n)*sizeof(double) ); - if (!etc->dfdvp) - { - printf("Error in work_alloc: Out of memory\n"); - exit (1); - } - etc->newton = (double *) malloc( (n*n)*sizeof(double) ); - if (!etc->newton) - { - printf("Error in work_alloc: Out of memory\n"); - exit (1); - } - etc->pivot = (int *) malloc( n*sizeof(int) ); - if (!etc->pivot) - { - printf("Error in work_alloc: Out of memory\n"); - exit (1); - } - etc->tolerance = (double *) malloc( n*sizeof(double) ); - if (!etc->tolerance) - { - printf("Error in work_alloc: Out of memory\n"); - exit (1); - } - etc->b = (double *) malloc( n*sizeof(double) ); - if (!etc->b) - { - printf("Error in work_alloc: Out of memory\n"); - exit(1); - } - etc->temp1 = (double *) malloc( n*sizeof(double) ); - if (!etc->temp1) - { - printf("Error in work_alloc: Out of memory\n"); - exit (1); - } - etc->temp2 = (double *) malloc( n*sizeof(double) ); - if (!etc->temp2) - { - printf("Error in work_alloc: Out of memory\n"); - exit (1); - } - for (i = 0; i < 4; ++i) - { - etc->vp_stage[i] = (double *) malloc( n*sizeof(double) ); - if (!etc->vp_stage[i]) - { - printf("Error in work_alloc: Out of memory\n"); - exit (1); - } - } - return 0; -} -/************ End of nork_allno *************************************/ - - -/*--------------------------------------------------------------*/ -/* work_free : Free the memory used for the work structures. */ -/*--------------------------------------------------------------*/ - -int work_free(struct problem *var, struct etcetera *etc) -{ - int i; -/* free(var->v); - free(var->vp); */ - - free(etc->dfdv); - free(etc->dfdvp); - free(etc->newton); - free(etc->pivot); - free(etc->tolerance); - free(etc->b); - free(etc->temp1); - free(etc->temp2); - for (i = 0; i < 4; ++i) - free(etc->vp_stage[i]); - return 0; -} -/************ End of work_free **********************************/ +#include +#include +#include +#include + +/* Definition of macros and constants. */ +/*-------------------------------------*/ +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) + +#define HMIN 1.e-8 +#define EPS sqrt(DBL_EPSILON) +#define ROCK 0.5 /* Max allowed rate_of_convergence. */ +#define PES_FAC 0.9 /* Pessismist factor, used for stepsize */ +#define KAPPA 0.5 /* Iteration error < KAPPA*tolerance. */ + +/* problem : */ +/*---------------------------------------------------------------------*/ +struct problem +{ int n; /* The dimension of the system. */ + double *v; /* The dependent variables. */ + double *vp; /* The derivatives of the dependent variables. */ + double x; /* The independent variable. */ +}; + +/* information : */ +/*---------------------------------------------------------------------*/ +struct information +{ int num_jacobian; /* 0 if exact jacobian is supplied, */ + /* 1 if not. */ + double *abserr; /* Absolute and */ + double relerr; /* relative tolerance. */ + double xend; /* End point for the integration. */ + double h0; /* Starting stepsize. */ + double hmax; /* Max allowed stepsize. */ +}; + +/* etcetera: work arrays and variables. */ +/*-------------------------------------------------------------------------*/ +struct etcetera +{ + double *dfdv; /* The Jacobi matrix df/dv. */ + double *dfdvp; /* The Jacobi matrix df/dv'. */ + double *newton; /* The (factorised) Newton matrix. */ + int *pivot; /* The pivot elements to newton. */ + double *tolerance; /* The array : abserr(i)+relerr*v(i). */ + double min_tolerance; /* Used for the residual error test. */ + double *b; /* The b in f(Vi',h*g*VP+b,xi) = O. */ + double rate_of_convergence; + double h; /* The stepsize. */ + double gh; /* gh = a(i,i)*stepsize. */ + double *temp1; /* Work arrays of dimension n. */ + double *temp2; + int jacobi_status; /* 0 if the jacobian is new, else 1. */ + int newton_status; /* 0 if the Newton matrix is fact. */ + /* with current stepsize, else 1. */ + int new_jacobian; /* Updated jacobians required. */ + int new_fact; /* New LU-factorisation is required. */ + int steps_red; /* Number of steps before the */ + /* stepsize is allowed to increase. */ + double *vp_stage[4]; /* Stage values. */ +}; + + +/* method : The parameters of the method. */ +/*------------------------------------------------------------------*/ +struct method +{ + int s; /* Number of stages. */ + double a[4][4]; + double b_error[4]; /* The b-vector for error estimate. */ + double c[4]; +}; + +/* statistic : */ +/*------------------------------------------------------------------*/ +struct statistic +{ + int steps; + int rejected_steps; + int notkonv_steps; + int function_evaluations; + int jacobi_evaluations; + int factorisations; /* of the Newton matrix. */ + int solutions; /* of the linear system. */ +} number_of; +/************ End of the header. ************************************/ + + + +/*------------------------------------------------------------------*/ +/* sdirk_dae : Solves fully implicit uniform index 1 DAEs with a */ +/* SDIRK method of order 3, embedded with a second */ +/* order error estimating method. */ +/*------------------------------------------------------------------*/ + +int sdirk_dae(struct problem *prob, struct information *info) + +/*------------------------------------------------------------------*/ +/* var : On entry : Function values at the starting point. */ +/* On exit : Function values at the end point. */ +/* info : User supplied information. Not altered by the function. */ +/* */ +/* returns : 0 :The integration has been successfully performed. */ +/* 1 : The Newton matrix is singular. */ +/* 2 : The Newton-iterations fails to converge. */ +/* 3 : The local error criteria fails to be satisfied, */ +/* the step-size becomes too small. */ +/* 4 : The error tolerance is too stringent. */ +/*------------------------------------------------------------------*/ +{ + int i, flag; + double step_fact; + struct method rk; + struct problem prob_old, *var_old, *var; + struct etcetera etc; + extern struct statistic number_of; + + /* Allocation of memory, and initialization of variables. */ + /*--------------------------------------------------------*/ + work_alloc(prob->n, &prob_old, &etc); + var_old = &prob_old; + var = prob; + var_old->n = var->n; + for (i = 0; i < var->n; ++i) + { + var_old->v[i] = var->v[i]; + var_old->vp[i] = var->vp[i]; + } + var_old->x = var->x - HMIN; + etc.new_jacobian = 1; + etc.new_fact = 1; + etc.jacobi_status = 1; + etc.steps_red = 0; + etc.h = info->h0; + + set_coeff(&rk); + zero_stat(); + + etc.gh = info->h0 * rk.a[0][0]; + + /* Main loop. */ + /*--------------------------------------------------------*/ + while (info->xend - var->x > HMIN) + { + if ( flag = one_step(&var, &var_old, info, &rk, &etc) ) + return flag; + if ( info->xend < var->x + etc.h ) + { + step_fact = (info->xend - var->x)/etc.h; + adjust_stepsize(step_fact, &etc); + } + ++number_of.steps; + } + + prob->x = var->x; + for (i = 0; i < var->n; ++i) + { + prob->v[i] = var->v[i]; + prob->vp[i] = var->vp[i]; + } + + work_free(var_old, &etc); + return 0; +} +/************ End of sdirk_dae **************************************/ + + +/*---------------------------------------------------------------*/ +/* one_step : Takes one step. If appropriate, the stepsize is */ +/* changed. */ +/*---------------------------------------------------------------*/ + +int one_step(struct problem **var, struct problem **var_old, + struct information *info, struct method *rk, + struct etcetera *etc) + +/*---------------------------------------------------------------*/ +/* var : On entry : Function variables for step n. */ +/* On exit : The variables for step n+1 */ +/* var_old : On entry : Function variables for step n-1. */ +/* On exit : The variables for step n. */ +/* */ +/* returns : 0 : if one step is taken successfully. */ +/* 1 : The Newton matrix is singular. */ +/* 2 : The Newton-iterations fails to converge. */ +/* 3 : The local error criteria fails to be satisfied, */ +/* the step-size becomes too small. */ +/* 4 : The error tolerance is too stringent. */ +/*---------------------------------------------------------------*/ +{ + int i, j, stage, no_success; + double loc_error, step_fact, roc_fact, sum, error; + int n, s1; + double *h, *gh, *roc, *tol, *v, **vp_stage; + struct problem *v_swap; + extern struct statistic number_of; +/* Set local names on structure variables. */ +/*-----------------------------------------*/ +v = (*var)->v; +n = (*var)->n; +s1 = rk->s - 1; +h = &etc->h; +gh = &etc->gh; +roc = &etc->rate_of_convergence; +vp_stage = etc->vp_stage; +tol = etc->tolerance; + +if ( compute_tolerance_vector(*var, info, etc) ) + return 4; + +do +{ + + /* If appropriate, the newton matrix is factorized. */ + /* */ + if (etc ->new_fact) + { + if ( make_newton(*var, etc, info) ) + { + if ( adjust_stepsize(0.5, etc) ) + return 3; + if ( make_newton(*var, etc, info) ) + return 1; + } + } + + /* Predict starting values for the iterations. */ + /*---------------------------------------------*/ + prediction(*var, *var_old, rk, etc); + + /* Computes the stage values. */ + /*----------------------------*/ + no_success = solve_stages(*var, rk, etc); + roc_fact = ROCK*PES_FAC/(*roc); + roc_fact = MAX(0.1, roc_fact); + if (no_success) + { + ++number_of.notkonv_steps; + + /* The iterations diverge, update the jacobians, or, */ + /* if the jacobians is updated, reduce the stepsize. */ + /* */ + if (etc->jacobi_status) + { + etc->new_jacobian = 1; + etc->new_fact = 1; + } + else + { + if ( adjust_stepsize( roc_fact, etc) ) + return 2; + etc->steps_red = 2; + } + } + else + { + /* The stage values are successfully computed. */ + /* Compute the local error. */ + /*---------------------------------------------*/ + loc_error = 0.0; + for (i = 0; i < n; ++i) + { + sum = 0.0; + for (stage = 0; stage <= s1; ++stage) + sum += rk->b_error[stage]*vp_stage[stage][i]; + sum *= *h; + error = fabs(sum)/tol[i]; + loc_error = MAX(loc_error, error); + } + + /* Assumes 0.5*error from the local error */ + /* and 0.5*error from iterations. */ + /*--------------------------------------------*/ + loc_error *= 2.0; + + if (64.0*loc_error < 1.0) + step_fact = PES_FAC*4.0; + else + { + step_fact = PES_FAC*pow(loc_error, -1.0/3.0); + step_fact = MAX(0.1, step_fact); + } + if (step_fact <= PES_FAC) + { + /* The step is rejected. The stepsize is reduced */ + /* */ + ++number_of.rejected_steps; + step_fact = MIN(step_fact, roc_fact); + if ( adjust_stepsize(step_fact, etc) ) + return 3; + etc->steps_red = 2; + no_success = 1; + } + } +} +while (no_success); + +/* The step is accepted. */ +/* Place the old values of the variables in var in var_old. */ +/*----------------------------------------------------------*/ +v_swap = *var_old; +*var_old = *var; +*var = v_swap; + +/* Calculate the new value for v. */ +/* Store it in var, together with a new approximation of v', */ +/* and x(n+1) */ +/*-----------------------------------------------------------*/ +for (i = 0; i < n; ++i) +{ + sum = 0.0; + for (stage = 0; stage <= s1; ++stage) + sum += rk->a[s1] [stage]*vp_stage[stage][i]; + (*var)->v[i] = v[i] + (*h)*sum; + (*var)->vp[i] = vp_stage[s1][i]; +} +(*var)->x = (*var_old)->x + *h; + /* If appropriate, change the stepsize. */ + /*---------------------------------------------------------*/ + step_fact = MIN(step_fact, roc_fact); + --etc->steps_red; + + if (etc->h*step_fact > info->hmax) + step_fact = info->hmax/etc->h; + if (step_fact > 1.5 && etc->steps_red < 0) + { + adjust_stepsize(step_fact, etc); + } + else if (step_fact < 1) + { + if ( adjust_stepsize(step_fact, etc) ) + return 3; + } + else + { + etc->new_fact = 0; + etc->new_jacobian = 0; + } + /* The jacobians are not updated. */ + /*--------------------------------*/ + etc->jacobi_status = 1; + + return 0; +} +/************ End of one_step ***************************************/ + + +/*------------------------------------------------------------------*/ +/* solve_stages : Solves the Runge-Kutta equations */ +/* f(Vi',v + h*a(i,1)*V1,+...+h*a(i,i)*Vi',x+ci*h)=0 */ +/* for the stage values Vi' .....Vi'. */ +/*------------------------------------------------------------------*/ + +int solve_stages(struct problem *var, struct method *rk, + struct etcetera *etc) + +/*------------------------------------------------------------------*/ +/* returns : 0 if the iterations converges successfully. */ +/* 1 if not. */ +/*------------------------------------------------------------------*/ +{ + int i, j, stg; + double xi, roc; + double max_roc = 0; + int s, n; + double *b, gh, **vp_stage, h; + double *v, *vp, x; + extern struct statistic number_of; + /* Set local names on structure variables. */ + /*-----------------------------------------*/ + n = var->n; + v = var->v; + vp = var->vp; + x = var->x; + + b = etc->b; + gh = etc->gh; + vp_stage = etc->vp_stage; + h = etc->h; + + /* Main loop. */ + /*------------*/ + for (stg = 0; stg < rk->s; ++stg) + { + + /* Computes b = v + h*a(i,1)*V(1) +... h*a(i,i-1)*V(i-1). */ + /*--------------------------------------------------------*/ + for (i = 0; i < n; ++i) + { + b[i] = v[i]; + for (j = 0; j < stg; ++j) + b[i] += h * rk->a[stg][j] * vp_stage[j][i]; + } + xi = x + rk->c[stg] * h; + + /* Solves the equation. Return if not successfully. */ + /*--------------------------------------------------*/ + if ( solve_nonlinear_equations(n, vp_stage[stg], xi, &roc, etc) ) + { + etc->rate_of_convergence = roc; + return 1; + } + max_roc = MAX(max_roc, roc); + } + etc->rate_of_convergence = max_roc; + return 0; +} +/************ End of solve_stages ***********************************/ + + +/*--------------------------------------------------------------*/ +/* solve_nonlinear_equation : solves the nonlinear equations */ +/* f(vp, gh*vp + b, xi) */ +/* with a modified Newton method. */ +/*--------------------------------------------------------------*/ + +int solve_nonlinear_equations(int n, double *vp, double xi, + double *roc, struct etcetera *etc) + +/*--------------------------------------------------------------*/ +/* n : The dimension of the system. */ +/* vp : The dependent variable. */ +/* On entry : Some starting values for the iterations. */ +/* On exit : If successfull, the solution of the eqautions. */ +/* xi : xi = xn + c(i)*h. */ +/* */ +/* returns : 0 if the iterations converges successfully. */ +/* 1 if the iterations diverges or converges too slow. */ +/*--------------------------------------------------------------*/ +{ + int first_time = 1, near_conv = 0; + int i, j; + double error, kappa, last_error, res_error, err_i; + double *res, *b, *v, *tol, mintol, gh, h; + extern struct statistic number_of; + /* Set local names of structure variables. */ + /*-----------------------------------------*/ + + gh = etc->gh; + h = etc->h; + b = etc->b; + mintol = etc->min_tolerance; + tol =etc->tolerance; + res = etc->temp1; + v = etc->temp2; + + /* The iteration loop. */ + /*---------------------*/ + for (*roc = 0.05; (*roc < 1.0) && (*roc < ROCK || near_conv < 2); ) + { + /* Computes the residual error. */ + /*------------------------------*/ + for (i = 0; i < n; ++i) + v[i] = b[i] + gh*vp[i]; + func(vp, v, xi, res); + ++number_of.function_evaluations; + + /* Solves the linear system. */ + /*---------------------------*/ + backsub(n, etc->newton, etc->pivot, res); + ++number_of.solutions; + + /* vp is updated. */ + /* Computes the displacement error. */ + /*----------------------------------*/ + error = 0.0; + for (i = 0; i < n; ++i) + { + vp[i] -= res[i]; + err_i = fabs(res[i])*h/tol[i]; + error = MAX(error, err_i); + } + + /* Computes rate of convergence. */ + /*-------------------------------*/ + if (first_time) + { + first_time = 0; + if (error < KAPPA) + return 0; + } + else + { + *roc = MAX(0.05,error/last_error); + if ( fabs(*roc/(1.0 - *roc))*error < KAPPA) + return 0; + if (*roc >= ROCK) + ++near_conv; + } + last_error = error; + } + return 1; +} +/************ End of solve_nonlinear_equations **********************/ + +/*------------------------------------------------------------------*/ +/* make_newton : Computes the Newton-matrix df/dv, + g*h*df/dv, */ +/* and performs the LU-decomposition of the matrix. */ +/*------------------------------------------------------------------*/ + +int make_newton(struct problem *var, + struct etcetera *etc, struct information *info) +/*------------------------------------------------------------------*/ +/* returns: 0 if the factorisation was perfomed successfully. */ +/* 1 if the Newton matrix is singular. */ +/*------------------------------------------------------------------*/ + +{ + int i, j; + int *jacobi_status, *pivot, num_jacobian, new_jacobian, n; + double *dfdvp, *dfdv, *newton, gh; + extern struct statistic number_of; + + /* Set local names of structure variables. */ + /*-----------------------------------------*/ + n = var->n; + newton = etc->newton; + pivot = etc->pivot; + gh = etc->gh; + dfdvp = etc->dfdvp; + dfdv = etc->dfdv; + jacobi_status = &(etc->jacobi_status); + new_jacobian = etc->new_jacobian; + num_jacobian = info->num_jacobian; + + /* Computes new jacobian if necessary. */ + /*-------------------------------------*/ + if (new_jacobian) + { if (num_jacobian) + jacobi(var, etc); + else + exact_jacobi(var->v, var->vp, var->x, dfdvp, dfdv); + *jacobi_status = 0; + ++number_of.jacobi_evaluations; + } + + /* Construct the Newton-matrix. */ + /*------------------------------*/ + for (i = 0; i < n; ++i) + for (j = 0; j < n; ++j) + newton[n*i+j] = dfdvp[n*i+j] + gh*dfdv[n*i+j]; + + /* Performs the LU-decomposition of the Newton-matrix. */ + /*-----------------------------------------------------*/ + ++number_of.factorisations; + if (decomp(n, newton, pivot)) + return 1; + else + { etc->newton_status = 0; + return 0; + } +} +/************ End of make_newton ************************************/ + +/*----------------------------------------------------------*/ +/* jacobi : Computes an aproximation to the jacobi-matrices */ +/* df/dv and df/dvp. */ +/*----------------------------------------------------------*/ + +int jacobi(struct problem *var, struct etcetera *etc) +{ + int i, j; + double delta, absv; + int n; + double *v, *vp, x; + double *res, *res_delta, *dfdv, *dfdvp; + extern struct statistic nuMber_of; + + /* Set local names of structure variables. */ + /*-----------------------------------------*/ + n = var->n; + v = var->v; + vp = var->vp; + x = var->x; + dfdv = etc->dfdv; + dfdvp = etc->dfdvp; + res = etc->temp1; + res_delta = etc->temp2; + + /* Start of the computation. */ + /*---------------------------*/ + func(vp, v, x, res); + ++number_of.function_evaluations; + + for (j = 0; j < n; ++j) + { + /* Computation of df/dvp(j). */ + /*---------------------------*/ + absv = fabs(vp[j]); + delta = (MAX(1.0, absv))*EPS; + vp[j] += delta; + func(vp, v, x, res_delta); + ++number_of.function_evaluations; + for (i = 0; i < n; ++i) + dfdvp[n*i+j] = (res_delta[i] - res[i])/delta; + vp[j] -= delta; + + /* Computation of df/dv(j). */ + /*--------------------------*/ + absv = fabs(v[j]); + delta = (MAX(1.0, absv))*EPS; + v[j] += delta; + func(vp, v, x, res_delta); + ++number_of.function_evaluations; + for (i = 0; i < n; ++i) + dfdv[n*i+j] = (res_delta[i] - res[i])/delta; + v[j] -= delta; + } + return 0; +} +/************ End of Jacobi *****************************************/ + +/*----------------------------------------------------------------*/ +/* compute_tolerance_vector : Set tolerance = abserr + relerr*vn. */ +/*----------------------------------------------------------------*/ + +int compute_tolerance_vector(struct problem *var, + struct information *info, + struct etcetera *etc) + +/*------------------------------------------------------------------*/ +/* returns : 0 : All elements in the vector are sufficiently large. */ +/* 1 : The tolerance is near 0 for at least one variables.*/ +/*------------------------------------------------------------------*/ +{ + int i, n; + double mintol = 1000.0; + double *tol, *abserr, relerr, *v; + + /* Set local names on structure variables. */ + /*-----------------------------------------*/ + + n = var->n; + v = var->v; + abserr = info->abserr; + relerr = info->relerr; + tol = etc->tolerance; + + for (i = 0; i < n; ++i) + { + tol[i] = abserr[i] + relerr*fabs(v[i]); + mintol = MIN(mintol, tol[i]); + } + if (mintol < EPS) + return 1; + else + etc->min_tolerance = mintol; + + return 0; +} +/************ End of compute_tolerance_vector ***********************/ + + +/*--------------------------------------------------------------*/ +/* prediction : Prediction of the stage_values. */ +/* Used as starting values for the iterations */ +/* 1.edition. : Vi = vn. */ +/*--------------------------------------------------------------*/ + +int prediction( struct problem *var, struct problem *var_old, + struct method *rk, struct etcetera *etc) + +/*--------------------------------------------------------------*/ +/* var : Function variables for step n. */ +/* var_old : Function variables for step n-1. */ +/*--------------------------------------------------------------*/ +{ + int i, stage, n, s; + double **vp_stage, *vp, h; + double delta; + + /* Set local names to structure variables. */ + /*-----------------------------------------*/ + n = var->n; + vp = var->vp; + s = rk->s; + vp_stage = etc->vp_stage; + h = etc->h; + /* Main loop. */ + /*------------*/ + for (i = 0; i < n; ++i) + { + delta = (var->vp[i] - var_old->vp[i])/(var->x - var_old->x); + for (stage = 0; stage < s; ++stage) + vp_stage[stage][i] = delta*rk->c[stage]*h + vp[i]; + } + + return 0; +} +/************ End of predict ****************************************/ + + + +/*---------------------------------------------------------------*/ +/* adjust_stepsize : Increase (or decrease) the stepsize with a */ +/* factor step_fact. */ +/*---------------------------------------------------------------*/ + +int adjust_stepsize(double step_fact, struct etcetera *etc) +{ + etc->h *= step_fact; + if (etc->h < HMIN) + return 1; + etc->gh *= step_fact; + etc->new_fact = 1; + etc->new_jacobian = 0; + return 0; +} +/************ End of adjust_stepsize ********************************/ + + + +/*---------------------------------------------------------------*/ +/* decomp: Gaussian Elimination with Partial Pivoting. */ +/* References : Golub & van Loan "Matrix Computations", */ +/* Algorithm 4.4-2. */ +/*---------------------------------------------------------------*/ + +int decomp(int n, double *a, int *piv) + +/*---------------------------------------------------------------*/ +/* n : The dimension of the matrix. */ +/* a : Pointer to an array of dimension n*n. */ +/* On entry : The matrix to be factorised. */ +/* On exit : The LU-decomposition of the matrix. */ +/* piv : The pivot array, of dimension n. */ +/*---------------------------------------------------------------*/ +{ + int i, j, k, p, nml; + double maxpivot, temp; + nml = n-1; + + /* Start of the factorisation. */ + /*-----------------------------*/ + for (k = 0; k < nml; ++k) + { + maxpivot = 0.0; + + /* Find index of max element in column below the diagonal. */ + /*---------------------------------------------------------*/ + for (p = k; p < n; ++p) + if (maxpivot < fabs(a[n*p+k])) + { + maxpivot = fabs(a[n*p+k]); + piv[k] = p; + } + /* Test for singularity. */ + /*-----------------------*/ + if (maxpivot < EPS) + return 1; + + /* If necessary, swap a(k,j) and a(piv[k],j) for j = 0,..,n-1 */ + /*------------------------------------------------------------*/ + if (piv[k] != k) + for (j = 0; j < n; ++j) + { + temp = a[n*piv[k]+j]; + a[n*piv[k]+j] = a[n*k+j]; + a[n*k+j] = temp; + } + + /* Column elimination. */ + /*---------------------*/ + for (i = k+1; i < n; ++i) + { a[n*i+k] /= a[n*k+k]; + for (j = k+1; j < n; ++j) + a[n*i+j] -= a[n*i+k] * a[n*k+j]; + } + } + + if (fabs(a[n*n-1]) < EPS) return 1; + + /* Everything seems to work, return. */ + /*-----------------------------------*/ + return 0; +} +/************ End of decomp *****************************************/ + + +/*--------------------------------------------------------------*/ +/* backsub: solves the linear equation a*x = b, where a is the */ +/* factorised matrix, given from the function decomp. */ +/*--------------------------------------------------------------*/ + +int backsub(int n, double *a, int *piv, double *b) + +/*--------------------------------------------------------------*/ +/* it : The dimension of the system. */ +/* a : Pointer to an array of dimension n*n. */ +/* On entry : The LU-decomposition of the matrix. */ +/* piv : The pivot array of dimension n. */ +/* b : Pointer to an array of dimension n. */ +/* On entry : The vector b on the right hand side. */ +/* On exit : The solution of the equation. */ +/*--------------------------------------------------------------*/ +{ int i, j, nm1; + double temp; + nm1 = n-1; + + /* Swap b[i] and b[piv[i]] */ + /*-------------------------*/ + for (i = 0; i < nm1; ++i) + if (piv[i] != i) + { + temp = b[piv[i]]; + b[piv[i]] = b[i]; + b[i] = temp; + } + /* Forward elimination. Ly = b. */ + /*------------------------------*/ + for (i = 0; i < n; ++i) + for (j = 0; j < i; ++j) + b[i] -= a[n*i+j]*b[j]; + + /* Back substitution. Ux = y*/ + /*--------------------------*/ + for (i = nm1; i >= 0; --i) + { + for (j = i+1; j < n; ++j) + b[i] -= a[n*i+j]*b[j]; + b[i] /= a[n*i+i]; + } + return 0; +} +/************ End of backsub ****************************************/ + + +/*--------------------------------------------------------------*/ +/* set_coeff : Set the method coefficients. */ +/*--------------------------------------------------------------*/ + +int set_coeff(struct method *rk) +{ + rk ->s = 4; + + rk->a[0][0]= 1.0/4.0; + rk->a[1][0]= 1.0/7.0; + rk->a[1][1]= 1.0/4.0; + rk->a[2][0]= 61.0/144.0; + rk->a[2][1]= -49.0/144.0; + rk->a[2][2]= 1.0/4.0; + rk->a[3][0]= 0.0; + rk->a[3][1]= 0.0; + rk->a[3][2]= 3.0/4.0; + rk->a[3][3]= 1.0/4.0; + + rk->c[0]= 1.0/4.0; + rk->c[1]= 11.0/28.0; + rk->c[2]= 1.0/3.0; + rk->c[3]= 1.0; + + rk->b_error[0]= -61.0/704.0; + rk->b_error[1]= 49.0/704.0; + rk->b_error[2]= 3.0/88.0; + rk->b_error[3]= -3.0/176.0; + + return 0; +} +/************ End of sat_cssff **************************************/ + +/*--------------------------------------------------------------*/ +/* zero_stat : Set all the statistic variables to zero. */ +/*--------------------------------------------------------------*/ + +int zero_stat() +{ + extern struct statistic number_of; + number_of.steps = 0; + number_of.rejected_steps = 0; + number_of.notkonv_steps = 0; + number_of.function_evaluations = 0; + number_of.jacobi_evaluations = 0; + number_of.factorisations = 0; + number_of.solutions = 0; + + return 0; +} +/************ End of zero_stat **************************************/ + + +/*--------------------------------------------------------------*/ +/* work_alloc : Allocates memory for the work structures. */ +/*--------------------------------------------------------------*/ + +int work_alloc(int n, struct problem *var, struct etcetera *etc) +{ + int i; + + /* Allocates memory for the arrays in the structure var. */ + /*-------------------------------------------------------*/ + var->v = (double *) malloc( n*sizeof(double) ); + if (!var->v) + { + printf("Error in work_alloc: Out of memory: \n"); + exit(1); + } + var->vp = (double *) malloc( n*sizeof(double) ); + if (!var->vp) + { + printf("Error in work_alloc: Out of memory\n"); + exit (1); + } + + /* Allocates memory for the arrays in the structure etc. */ + /*-------------------------------------------------------*/ + etc->dfdv = (double *) malloc( (n*n)*sizeof(double) ); + if (!etc->dfdv) + { + printf("Error in work_alloc: Out of memory\n"); + exit(1); + } + etc->dfdvp = (double *) malloc( (n*n)*sizeof(double) ); + if (!etc->dfdvp) + { + printf("Error in work_alloc: Out of memory\n"); + exit (1); + } + etc->newton = (double *) malloc( (n*n)*sizeof(double) ); + if (!etc->newton) + { + printf("Error in work_alloc: Out of memory\n"); + exit (1); + } + etc->pivot = (int *) malloc( n*sizeof(int) ); + if (!etc->pivot) + { + printf("Error in work_alloc: Out of memory\n"); + exit (1); + } + etc->tolerance = (double *) malloc( n*sizeof(double) ); + if (!etc->tolerance) + { + printf("Error in work_alloc: Out of memory\n"); + exit (1); + } + etc->b = (double *) malloc( n*sizeof(double) ); + if (!etc->b) + { + printf("Error in work_alloc: Out of memory\n"); + exit(1); + } + etc->temp1 = (double *) malloc( n*sizeof(double) ); + if (!etc->temp1) + { + printf("Error in work_alloc: Out of memory\n"); + exit (1); + } + etc->temp2 = (double *) malloc( n*sizeof(double) ); + if (!etc->temp2) + { + printf("Error in work_alloc: Out of memory\n"); + exit (1); + } + for (i = 0; i < 4; ++i) + { + etc->vp_stage[i] = (double *) malloc( n*sizeof(double) ); + if (!etc->vp_stage[i]) + { + printf("Error in work_alloc: Out of memory\n"); + exit (1); + } + } + return 0; +} +/************ End of nork_allno *************************************/ + + +/*--------------------------------------------------------------*/ +/* work_free : Free the memory used for the work structures. */ +/*--------------------------------------------------------------*/ + +int work_free(struct problem *var, struct etcetera *etc) +{ + int i; +/* free(var->v); + free(var->vp); */ + + free(etc->dfdv); + free(etc->dfdvp); + free(etc->newton); + free(etc->pivot); + free(etc->tolerance); + free(etc->b); + free(etc->temp1); + free(etc->temp2); + for (i = 0; i < 4; ++i) + free(etc->vp_stage[i]); + return 0; +} +/************ End of work_free **********************************/ diff --git a/thirdparty/odassl/read.me b/thirdparty/odassl/read.me index ffc9bb21..a6c3a37c 100755 --- a/thirdparty/odassl/read.me +++ b/thirdparty/odassl/read.me @@ -1,33 +1,33 @@ -This zip-file contains the FORTRAN code ODASSL and all -related subroutines. -ODASSL is an experimental code for solving overdetermined -differential-algebraic equation as they occur in mechanical -multibody systems. (These systems become overdetermined -when considering position constraints *and* velocity constraints -*and* perhaps even accelaration constraints, see the example) - -ODASSL is written on the basis of DASSL (Linda Petzold, Minnesota, USA) -The theoretical background of the extensions, the "O", can be found -in Führer/Leimkuhler: Numerical Solution of differential-algebraic equations -for constrained mechanical motion, Num.Math. 59, 55-69, 1991 -and in a less compressed form in the textbook: -Eich-Soellner: Numerical Methods in Multibody Dynamics, Teubner 1998 - -The calling sequence of ODASSL is described in the heading comments -of ODASSL.F -A demonstration example and a sample driver can be found in DEMO.F . - -I would like to emphasize that ODASSL is not a state-of-the-art -production code. It was written as a demonstration code to verify -a -at these days- new numerical approach. - -In case of questions please contact: - -Claus Führer -Numerical Analysis -Centre for Methematical Sciences -Lund University -POB 117 -SE-22100 Lund - -e-mail: claus.fuhrer@na.lu.se +This zip-file contains the FORTRAN code ODASSL and all +related subroutines. +ODASSL is an experimental code for solving overdetermined +differential-algebraic equation as they occur in mechanical +multibody systems. (These systems become overdetermined +when considering position constraints *and* velocity constraints +*and* perhaps even accelaration constraints, see the example) + +ODASSL is written on the basis of DASSL (Linda Petzold, Minnesota, USA) +The theoretical background of the extensions, the "O", can be found +in Führer/Leimkuhler: Numerical Solution of differential-algebraic equations +for constrained mechanical motion, Num.Math. 59, 55-69, 1991 +and in a less compressed form in the textbook: +Eich-Soellner: Numerical Methods in Multibody Dynamics, Teubner 1998 + +The calling sequence of ODASSL is described in the heading comments +of ODASSL.F +A demonstration example and a sample driver can be found in DEMO.F . + +I would like to emphasize that ODASSL is not a state-of-the-art +production code. It was written as a demonstration code to verify +a -at these days- new numerical approach. + +In case of questions please contact: + +Claus Führer +Numerical Analysis +Centre for Methematical Sciences +Lund University +POB 117 +SE-22100 Lund + +e-mail: claus.fuhrer@na.lu.se diff --git a/thirdparty/odepack/odepack.pyf b/thirdparty/odepack/odepack.pyf index 681517ee..bd1badd6 100644 --- a/thirdparty/odepack/odepack.pyf +++ b/thirdparty/odepack/odepack.pyf @@ -1,1085 +1,1085 @@ -! -*- f90 -*- -! Note: the context of this file is case sensitive. - -python module dlsode__user__routines - interface dlsode_user_interface - subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsode:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq),intent(out) :: ydot - end subroutine f - subroutine jac(neq,t,y,ml,mu,pd,nrowpd) ! in :odepack:opkdmain.f:dlsodar:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - integer :: ml - integer :: mu - double precision dimension(nrowpd,neq),intent(out) :: pd - integer :: nrowpd - end subroutine jac - end interface dlsode_user_interface -end python module dlsode__user__routines -python module dlsodes__user__routines - interface dlsodes_user_interface - subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsodes:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq),intent(out) :: ydot - end subroutine f - subroutine jac(neq,t,y,j,ian,jan,pdj) ! in :odepack:opkdmain.f:dlsodar:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - integer :: j - double precision dimension(neq) :: ian - double precision dimension(neq) :: jan - double precision dimension(neq),intent(out) :: pdj - end subroutine jac - end interface dlsodes_user_interface -end python module dlsodes__user__routines -python module dlsoda__user__routines - interface dlsoda_user_interface - subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsoda:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq),intent(out) :: ydot - end subroutine f - subroutine jac(neq,t,y,ml,mu,pd,nrowpd) ! in :odepack:opkdmain.f:dlsodar:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - integer :: ml - integer :: mu - double precision dimension(nrowpd,neq),intent(out) :: pd - integer :: nrowpd - end subroutine jac - end interface dlsoda_user_interface -end python module dlsoda__user__routines -python module dlsodar__user__routines - interface dlsodar_user_interface - subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsodar:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq),intent(out) :: ydot - end subroutine f - subroutine g(neq,t,y,ng,gout) ! in :odepack:opkdmain.f:dlsodar:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - integer :: ng - double precision dimension(ng),intent(out) :: gout - end subroutine g - subroutine jac(neq,t,y,ml,mu,pd,nrowpd) ! in :odepack:opkdmain.f:dlsodar:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - integer,intent(hide) :: ml - integer,intent(hide) :: mu - double precision dimension(nrowpd,neq),intent(out) :: pd - integer,intent(hide) :: nrowpd - end subroutine jac - end interface dlsodar_user_interface -end python module dlsodar__user__routines -python module dlsodpk__user__routines - interface dlsodpk_user_interface - subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsodpk:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq),intent(out) :: ydot - end subroutine f - subroutine jac(f,neq,t,y,ysv,rewt,fty,v,hlo,wp,iwp,ier) - external f - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq) :: ysv - double precision dimension(neq) :: rewt - double precision dimension(neq) :: fty - double precision dimension(neq) :: v - double precision :: hlo - double precision dimension (*) :: wp - integer dimension(*) :: iwp - integer :: ier - end subroutine jac - subroutine psol(neq,t,y,fty,wk,hlo,wp,iwp,b,lr,ier) - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq) :: fty - double precision dimension(neq) :: wk - double precision :: hlo - double precision dimension (*) :: wp - integer dimension(*) :: iwp - double precision dimension(neq),intent(out) :: b - integer :: lr - integer :: ier - end subroutine psol - end interface dlsodpk_user_interface -end python module dlsodpk__user__routines -python module dlsodkr__user__routines - interface dlsodkr_user_interface - subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsodkr:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq),intent(out) :: ydot - end subroutine f - subroutine g(neq,t,y,ng,gout) ! in :odepack:opkdmain.f:dlsodar:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - integer :: ng - double precision dimension(ng),intent(out) :: gout - end subroutine g - subroutine jac(f,neq,t,y,ysv,rewt,fty,v,hlo,jok,wp,iwp,ier) - external f - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq) :: ysv - double precision dimension(neq) :: rewt - double precision dimension(neq) :: fty - double precision dimension(neq) :: v - double precision :: hlo - integer :: jok - double precision dimension (*) :: wp - integer dimension(*) :: iwp - integer :: ier - end subroutine jac - subroutine psol(neq,t,y,fty,wk,hlo,wp,iwp,b,lr,ier) - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq) :: fty - double precision dimension(neq) :: wk - double precision :: hlo - double precision dimension (*) :: wp - integer dimension(*) :: iwp - double precision dimension(neq),intent(out) :: b - integer :: lr - integer :: ier - end subroutine psol - end interface dlsodkr_user_interface -end python module dlsodkr__user__routines -python module dlsodi__user__routines - interface dlsodi_user_interface - subroutine res(neq,tn,y,s,r,ires) ! in :odepack:opkdmain.f:dlsodi:unknown_interface - integer :: neq - double precision :: tn - double precision dimension(neq) :: y - double precision dimension(neq) :: s - double precision dimension(neq),intent(out) :: r - integer :: ires - end subroutine res - subroutine adda(neq,tn,y,ml,mu,p,nrowp) ! in :odepack:opkdmain.f:dlsodi:unknown_interface - integer :: neq - double precision :: tn - double precision dimension(neq) :: y - integer :: ml - integer :: mu - double precision dimension(nrowp,neq),intent(out) :: p - integer :: nrowp - end subroutine adda - subroutine jac(neq,t,y,s,ml,mu,p,nrowp) ! in :odepack:opkdmain.f:dlsodar:unknown_interface - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq) :: s - integer :: ml - integer :: mu - double precision dimension(nrowp,neq),intent(out) :: p - integer :: nrowp - end subroutine jac - end interface dlsodi_user_interface -end python module dlsodi__user__routines -python module dlsoibt__user__routines - interface dlsoibt_user_interface - subroutine res(neq,tn,y,s,r,ires) ! in :odepack:opkdmain.f:dlsoibt:unknown_interface - integer :: neq - double precision :: tn - double precision dimension(neq) :: y - double precision dimension(neq) :: s - double precision dimension(neq),intent(out) :: r - integer :: ires - end subroutine res - subroutine adda(neq,t,y,mb,nb,pa,pb,pc) - integer :: neq - double precision :: t - double precision dimension(neq) :: y - integer :: mb - integer :: nb - double precision dimension(mb,mb,nb),intent(out) :: pa - double precision dimension(mb,mb,nb),intent(out) :: pb - double precision dimension(mb,mb,nb),intent(out) :: pc - end subroutine adda - subroutine jac(neq,t,y,s,mb,nb,pa,pb,pc) - integer :: neq - double precision :: t - double precision dimension(neq) :: y - double precision dimension(neq) :: s - integer :: mb - integer :: nb - double precision dimension(mb,mb,nb),intent(out) :: pa - double precision dimension(mb,mb,nb),intent(out) :: pb - double precision dimension(mb,mb,nb),intent(out) :: pc - end subroutine jac - end interface dlsoibt_user_interface -end python module dlsoibt__user__routines -python module dlsodis__user__routines - interface dlsodis_user_interface - subroutine res(neq,tn,y,s,r,ires) ! in :odepack:opkdmain.f:dlsodis:unknown_interface - integer :: neq - double precision :: tn - double precision dimension(neq) :: y - double precision dimension(neq) :: s - double precision dimension(neq),intent(out) :: r - integer :: ires - end subroutine res - subroutine adda(neq,tn,y,j,ian,jan,p) ! in :odepack:opkdmain.f:dlsodi:unknown_interface - integer :: neq - double precision :: tn - double precision dimension(neq) :: y - integer :: j - integer dimension(*) :: ian - integer dimension(*) :: jan - double precision dimension(neq),intent(out) :: p - end subroutine adda - subroutine jac(neq,tn,y,s,j,ian,jan,pdj) - integer :: neq - double precision :: tn - double precision dimension(neq) :: y - double precision dimension(neq) :: s - integer :: j - integer dimension(*) :: ian - integer dimension(*) :: jan - double precision dimension(neq),intent(out) :: pdj - end subroutine jac - end interface dlsodis_user_interface -end python module dlsodis__user__routines - -python module odepack ! in - interface ! in :odepack - subroutine dlsode(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,mf) ! in :odepack:opkdmain.f - use dlsode__user__routines - external f - integer :: neq - double precision dimension(neq) :: y - double precision :: t - double precision :: tout - integer :: itol - double precision dimension(neq) :: rtol - double precision dimension(neq) :: atol - integer :: itask - integer :: istate - integer :: iopt - double precision dimension(lrw) :: rwork - integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) - integer dimension(liw) :: iwork - integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) - external jac - integer :: mf - double precision dimension(209) :: rowns - double precision :: ccmax - double precision :: el0 - double precision :: h - double precision :: hmin - double precision :: hmxi - double precision :: hu - double precision :: rc - double precision :: tn - double precision :: uround - integer :: init - integer :: mxstep - integer :: mxhnil - integer :: nhnil - integer :: nslast - integer :: nyh - integer :: icf - integer :: ierpj - integer :: iersl - integer :: jcur - integer :: jstart - integer :: kflag - integer :: l - integer :: lyh - integer :: lewt - integer :: lacor - integer :: lsavf - integer :: lwm - integer :: liwm - integer :: meth - integer :: miter - integer :: maxord - integer :: maxcor - integer :: msbp - integer :: mxncf - integer :: n - integer :: nq - integer :: nst - integer :: nfe - integer :: nje - integer :: nqu - integer :: init,mxstep,mxhnil,nhnil,nslast,nyh - !common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu - common /dls001/ conit, crate, el(13), elco(13,12), hold, & - rmax, tesco(3,12),ccmax, el0, h, hmin, & - hmxi, hu, rc, tn, uround, & - init,mxstep,mxhnil,nhnil,nslast,nyh, & !iownd(6), - ialth, ipup, lmax, meo, nqnyh, nslp, & !iowns(6) - icf, ierpj, iersl, jcur, jstart, kflag, & - l,lyh, lewt, lacor, lsavf, lwm, liwm, & - meth, miter,maxord, maxcor, msbp, mxncf, & - n, nq, nst, nfe, nje, nqu - end subroutine dlsode - subroutine dlsodes(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,mf) ! in :odepack:opkdmain.f - use dlsodes__user__routines - external f - integer :: neq - double precision dimension(neq) :: y - double precision :: t - double precision :: tout - integer :: itol - double precision dimension(neq) :: rtol - double precision dimension(neq) :: atol - integer :: itask - integer :: istate - integer :: iopt - double precision dimension(lrw) :: rwork - integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) - integer dimension(liw) :: iwork - integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) - external jac - integer :: mf - double precision :: con0 - double precision :: conmin - double precision :: ccmxj - double precision :: psmall - double precision :: rbig - double precision :: seth - integer :: iplost - integer :: iesp - integer :: istatc - integer :: iys - integer :: iba - integer :: ibian - integer :: ibjan - integer :: ibjgp - integer :: ipian - integer :: ipjan - integer :: ipjgp - integer :: ipigp - integer :: ipr - integer :: ipc - integer :: ipic - integer :: ipisp - integer :: iprsp - integer :: ipa - integer :: lenyh - integer :: lenyhm - integer :: lenwk - integer :: lreq - integer :: lrat - integer :: lrest - integer :: lwmin - integer :: moss - integer :: msbj - integer :: nslj - integer :: ngp - integer :: nlu - integer :: nnz - integer :: nsp - integer :: nzl - integer :: nzu - double precision dimension(209) :: rowns - double precision :: ccmax - double precision :: el0 - double precision :: h - double precision :: hmin - double precision :: hmxi - double precision :: hu - double precision :: rc - double precision :: tn - double precision :: uround - integer :: init - integer :: mxstep - integer :: mxhnil - integer :: nhnil - integer :: nslast - integer :: nyh - integer dimension(6) :: iowns - integer :: icf - integer :: ierpj - integer :: iersl - integer :: jcur - integer :: jstart - integer :: kflag - integer :: l - integer :: lyh - integer :: lewt - integer :: lacor - integer :: lsavf - integer :: lwm - integer :: liwm - integer :: meth - integer :: miter - integer :: maxord - integer :: maxcor - integer :: msbp - integer :: mxncf - integer :: n - integer :: nq - integer :: nst - integer :: nfe - integer :: nje - integer :: nqu - common /dlss01/ con0,conmin,ccmxj,psmall,rbig,seth,iplost,iesp,istatc,iys,iba,ibian,ibjan,ibjgp,ipian,ipjan,ipjgp,ipigp,ipr,ipc,ipic,ipisp,iprsp,ipa,lenyh,lenyhm,lenwk,lreq,lrat,lrest,lwmin,moss,msbj,nslj,ngp,nlu,nnz,nsp,nzl,nzu - common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu - end subroutine dlsodes - subroutine dlsoda(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,jt) ! in :odepack:opkdmain.f - use dlsoda__user__routines - external f - integer :: neq - double precision dimension(neq) :: y - double precision :: t - double precision :: tout - integer :: itol - double precision dimension(neq) :: rtol - double precision dimension(neq) :: atol - integer :: itask - integer :: istate - integer :: iopt - double precision dimension(lrw) :: rwork - integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) - integer dimension(liw) :: iwork - integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) - external jac - integer :: jt - double precision dimension(209) :: rowns - double precision :: ccmax - double precision :: el0 - double precision :: h - double precision :: hmin - double precision :: hmxi - double precision :: hu - double precision :: rc - double precision :: tn - double precision :: uround - integer :: init - integer :: mxstep - integer :: mxhnil - integer :: nhnil - integer :: nslast - integer :: nyh - integer dimension(6) :: iowns - integer :: icf - integer :: ierpj - integer :: iersl - integer :: jcur - integer :: jstart - integer :: kflag - integer :: l - integer :: lyh - integer :: lewt - integer :: lacor - integer :: lsavf - integer :: lwm - integer :: liwm - integer :: meth - integer :: miter - integer :: maxord - integer :: maxcor - integer :: msbp - integer :: mxncf - integer :: n - integer :: nq - integer :: nst - integer :: nfe - integer :: nje - integer :: nqu - double precision :: tsw - double precision dimension(20) :: rowns2 - double precision :: pdnorm - integer :: insufr - integer :: insufi - integer :: ixpr - integer dimension(2) :: iowns2 - integer :: jtyp - integer :: mused - integer :: mxordn - integer :: mxords - common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu - common /dlsa01/ tsw,rowns2,pdnorm,insufr,insufi,ixpr,iowns2,jtyp,mused,mxordn,mxords - end subroutine dlsoda - subroutine dlsodar(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,jt,g,ng,jroot) ! in :odepack:opkdmain.f - use dlsodar__user__routines - external f - integer :: neq - double precision dimension(neq),intent(in,out) :: y - double precision,intent(in,out) :: t - double precision :: tout - integer :: itol - double precision dimension(neq) :: rtol - double precision dimension(neq) :: atol - integer :: itask - integer,intent(in,out) :: istate - integer :: iopt - double precision dimension(lrw), intent(in,out) :: rwork - integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) - integer dimension(liw),intent(in,out) :: iwork - integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) - external jac - integer :: jt - external g - integer, optional,check(len(jroot)>=ng),depend(jroot) :: ng=len(jroot) - integer dimension(ng),intent(in,out) :: jroot - double precision dimension(2) :: rownr3 - double precision :: t0 - double precision :: tlast - double precision :: toutc - integer :: lg0 - integer :: lg1 - integer :: lgx - integer dimension(2) :: iownr3 - integer :: irfnd - integer :: itaskc - integer :: ngc - integer :: nge - double precision dimension(209) :: rowns - double precision :: ccmax - double precision :: el0 - double precision :: h - double precision :: hmin - double precision :: hmxi - double precision :: hu - double precision :: rc - double precision :: tn - double precision :: uround - integer :: init - integer :: mxstep - integer :: mxhnil - integer :: nhnil - integer :: nslast - integer :: nyh - integer dimension(6) :: iowns - integer :: icf - integer :: ierpj - integer :: iersl - integer :: jcur - integer :: jstart - integer :: kflag - integer :: l - integer :: lyh - integer :: lewt - integer :: lacor - integer :: lsavf - integer :: lwm - integer :: liwm - integer :: meth - integer :: miter - integer :: maxord - integer :: maxcor - integer :: msbp - integer :: mxncf - integer :: n - integer :: nq - integer :: nst - integer :: nfe - integer :: nje - integer :: nqu - double precision :: tsw - double precision dimension(20) :: rowns2 - double precision :: pdnorm - integer :: insufr - integer :: insufi - integer :: ixpr - integer dimension(2) :: iowns2 - integer :: jtyp - integer :: mused - integer :: mxordn - integer :: mxords - common /dlsr01/ rownr3,t0,tlast,toutc,lg0,lg1,lgx,iownr3,irfnd,itaskc,ngc,nge - common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu - common /dlsa01/ tsw,rowns2,pdnorm,insufr,insufi,ixpr,iowns2,jtyp,mused,mxordn,mxords - end subroutine dlsodar - subroutine dlsodpk(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,psol,mf) ! in :odepack:opkdmain.f - use dlsodpk__user__routines - external f - integer :: neq - double precision dimension(neq) :: y - double precision :: t - double precision :: tout - integer :: itol - double precision dimension(neq) :: rtol - double precision dimension(neq) :: atol - integer :: itask - integer :: istate - integer :: iopt - double precision dimension(lrw) :: rwork - integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) - integer dimension(liw) :: iwork - integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) - external jac - external psol - integer :: mf - double precision :: delt - double precision :: epcon - double precision :: sqrtn - double precision :: rsqrtn - integer :: jpre - integer :: jacflg - integer :: locwp - integer :: lociwp - integer :: lsavx - integer :: kmp - integer :: maxl - integer :: mnewt - integer :: nni - integer :: nli - integer :: nps - integer :: ncfn - integer :: ncfl - double precision dimension(209) :: rowns - double precision :: ccmax - double precision :: el0 - double precision :: h - double precision :: hmin - double precision :: hmxi - double precision :: hu - double precision :: rc - double precision :: tn - double precision :: uround - integer :: init - integer :: mxstep - integer :: mxhnil - integer :: nhnil - integer :: nslast - integer :: nyh - integer dimension(6) :: iowns - integer :: icf - integer :: ierpj - integer :: iersl - integer :: jcur - integer :: jstart - integer :: kflag - integer :: l - integer :: lyh - integer :: lewt - integer :: lacor - integer :: lsavf - integer :: lwm - integer :: liwm - integer :: meth - integer :: miter - integer :: maxord - integer :: maxcor - integer :: msbp - integer :: mxncf - integer :: n - integer :: nq - integer :: nst - integer :: nfe - integer :: nje - integer :: nqu - common /dlpk01/ delt,epcon,sqrtn,rsqrtn,jpre,jacflg,locwp,lociwp,lsavx,kmp,maxl,mnewt,nni,nli,nps,ncfn,ncfl - common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu - end subroutine dlsodpk - subroutine dlsodkr(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,psol,mf,g,ng,jroot) ! in :odepack:opkdmain.f - use dlsodkr__user__routines - external f - integer :: neq - double precision dimension(neq) :: y - double precision :: t - double precision :: tout - integer :: itol - double precision dimension(neq) :: rtol - double precision dimension(neq) :: atol - integer :: itask - integer :: istate - integer :: iopt - double precision dimension(lrw) :: rwork - integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) - integer dimension(liw) :: iwork - integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) - external jac - external psol - integer :: mf - external g - integer :: ng - integer dimension(*) :: jroot - double precision :: delt - double precision :: epcon - double precision :: sqrtn - double precision :: rsqrtn - integer :: jpre - integer :: jacflg - integer :: locwp - integer :: lociwp - integer :: lsavx - integer :: kmp - integer :: maxl - integer :: mnewt - integer :: nni - integer :: nli - integer :: nps - integer :: ncfn - integer :: ncfl - double precision dimension(2) :: rownr3 - double precision :: t0 - double precision :: tlast - double precision :: toutc - integer :: lg0 - integer :: lg1 - integer :: lgx - integer dimension(2) :: iownr3 - integer :: irfnd - integer :: itaskc - integer :: ngc - integer :: nge - double precision :: stifr - integer :: newt - integer :: nsfi - integer :: nslj - integer :: njev - double precision dimension(209) :: rowns - double precision :: ccmax - double precision :: el0 - double precision :: h - double precision :: hmin - double precision :: hmxi - double precision :: hu - double precision :: rc - double precision :: tn - double precision :: uround - integer :: init - integer :: mxstep - integer :: mxhnil - integer :: nhnil - integer :: nslast - integer :: nyh - integer dimension(6) :: iowns - integer :: icf - integer :: ierpj - integer :: iersl - integer :: jcur - integer :: jstart - integer :: kflag - integer :: l - integer :: lyh - integer :: lewt - integer :: lacor - integer :: lsavf - integer :: lwm - integer :: liwm - integer :: meth - integer :: miter - integer :: maxord - integer :: maxcor - integer :: msbp - integer :: mxncf - integer :: n - integer :: nq - integer :: nst - integer :: nfe - integer :: nje - integer :: nqu - common /dlpk01/ delt,epcon,sqrtn,rsqrtn,jpre,jacflg,locwp,lociwp,lsavx,kmp,maxl,mnewt,nni,nli,nps,ncfn,ncfl - common /dlsr01/ rownr3,t0,tlast,toutc,lg0,lg1,lgx,iownr3,irfnd,itaskc,ngc,nge - common /dls002/ stifr,newt,nsfi,nslj,njev - common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu - end subroutine dlsodkr - subroutine dlsodi(res,adda,jac,neq,y,ydoti,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,mf) ! in :odepack:opkdmain.f - use dlsodi__user__routines - external res - external adda - external jac - integer :: neq - double precision dimension(neq) :: y - double precision dimension(neq) :: ydoti - double precision :: t - double precision :: tout - integer :: itol - double precision dimension(neq) :: rtol - double precision dimension(neq) :: atol - integer :: itask - integer :: istate - integer :: iopt - double precision dimension(lrw) :: rwork - integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) - integer dimension(liw) :: iwork - integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) - integer :: mf - double precision dimension(209) :: rowns - double precision :: ccmax - double precision :: el0 - double precision :: h - double precision :: hmin - double precision :: hmxi - double precision :: hu - double precision :: rc - double precision :: tn - double precision :: uround - integer :: init - integer :: mxstep - integer :: mxhnil - integer :: nhnil - integer :: nslast - integer :: nyh - integer dimension(6) :: iowns - integer :: icf - integer :: ierpj - integer :: iersl - integer :: jcur - integer :: jstart - integer :: kflag - integer :: l - integer :: lyh - integer :: lewt - integer :: lacor - integer :: lsavf - integer :: lwm - integer :: liwm - integer :: meth - integer :: miter - integer :: maxord - integer :: maxcor - integer :: msbp - integer :: mxncf - integer :: n - integer :: nq - integer :: nst - integer :: nfe - integer :: nje - integer :: nqu - common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu - end subroutine dlsodi - subroutine dlsoibt(res,adda,jac,neq,y,ydoti,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,mf) ! in :odepack:opkdmain.f - use dlsoibt__user__routines - external res - external adda - external jac - integer :: neq - double precision dimension(neq) :: y - double precision dimension(neq) :: ydoti - double precision :: t - double precision :: tout - integer :: itol - double precision dimension(neq) :: rtol - double precision dimension(neq) :: atol - integer :: itask - integer :: istate - integer :: iopt - double precision dimension(lrw) :: rwork - integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) - integer dimension(liw) :: iwork - integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) - integer :: mf - double precision dimension(209) :: rowns - double precision :: ccmax - double precision :: el0 - double precision :: h - double precision :: hmin - double precision :: hmxi - double precision :: hu - double precision :: rc - double precision :: tn - double precision :: uround - integer :: init - integer :: mxstep - integer :: mxhnil - integer :: nhnil - integer :: nslast - integer :: nyh - integer dimension(6) :: iowns - integer :: icf - integer :: ierpj - integer :: iersl - integer :: jcur - integer :: jstart - integer :: kflag - integer :: l - integer :: lyh - integer :: lewt - integer :: lacor - integer :: lsavf - integer :: lwm - integer :: liwm - integer :: meth - integer :: miter - integer :: maxord - integer :: maxcor - integer :: msbp - integer :: mxncf - integer :: n - integer :: nq - integer :: nst - integer :: nfe - integer :: nje - integer :: nqu - common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu - end subroutine dlsoibt - subroutine dlsodis(res,adda,jac,neq,y,ydoti,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,mf) ! in :odepack:opkdmain.f - use dlsodis__user__routines - external res - external adda - external jac - integer :: neq - double precision dimension(neq) :: y - double precision dimension(neq) :: ydoti - double precision :: t - double precision :: tout - integer :: itol - double precision dimension(neq) :: rtol - double precision dimension(neq) :: atol - integer :: itask - integer :: istate - integer :: iopt - double precision dimension(lrw) :: rwork - integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) - integer dimension(liw) :: iwork - integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) - integer :: mf - double precision :: con0 - double precision :: conmin - double precision :: ccmxj - double precision :: psmall - double precision :: rbig - double precision :: seth - integer :: iplost - integer :: iesp - integer :: istatc - integer :: iys - integer :: iba - integer :: ibian - integer :: ibjan - integer :: ibjgp - integer :: ipian - integer :: ipjan - integer :: ipjgp - integer :: ipigp - integer :: ipr - integer :: ipc - integer :: ipic - integer :: ipisp - integer :: iprsp - integer :: ipa - integer :: lenyh - integer :: lenyhm - integer :: lenwk - integer :: lreq - integer :: lrat - integer :: lrest - integer :: lwmin - integer :: moss - integer :: msbj - integer :: nslj - integer :: ngp - integer :: nlu - integer :: nnz - integer :: nsp - integer :: nzl - integer :: nzu - double precision dimension(209) :: rowns - double precision :: ccmax - double precision :: el0 - double precision :: h - double precision :: hmin - double precision :: hmxi - double precision :: hu - double precision :: rc - double precision :: tn - double precision :: uround - integer :: init - integer :: mxstep - integer :: mxhnil - integer :: nhnil - integer :: nslast - integer :: nyh - integer dimension(6) :: iowns - integer :: icf - integer :: ierpj - integer :: iersl - integer :: jcur - integer :: jstart - integer :: kflag - integer :: l - integer :: lyh - integer :: lewt - integer :: lacor - integer :: lsavf - integer :: lwm - integer :: liwm - integer :: meth - integer :: miter - integer :: maxord - integer :: maxcor - integer :: msbp - integer :: mxncf - integer :: n - integer :: nq - integer :: nst - integer :: nfe - integer :: nje - integer :: nqu - common /dlss01/ con0,conmin,ccmxj,psmall,rbig,seth,iplost,iesp,istatc,iys,iba,ibian,ibjan,ibjgp,ipian,ipjan,ipjgp,ipigp,ipr,ipc,ipic,ipisp,iprsp,ipa,lenyh,lenyhm,lenwk,lreq,lrat,lrest,lwmin,moss,msbj,nslj,ngp,nlu,nnz,nsp,nzl,nzu - common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu - end subroutine dlsodis - subroutine dsrcar(rsav, isav, job) - ! Save/restore LSODAR COMMON blocks - integer, intent(inout), dimension(55):: isav - integer, intent(in):: job - double precision, intent(inout), dimension(245):: rsav - end subroutine dsrcar - subroutine dcfode(meth, elco, tesco) - ! set method and test coefficients for odepack routines - integer, intent(in):: meth - double precision, intent(out),dimension(13,12):: elco - double precision, intent(out),dimension(3,12)::tesco - end subroutine dcfode - end interface - interface ! in :odepack_aux - subroutine set_lsod_common(meth,nq,nqu,miter,maxord,meo,nqnyh,nst,nfe,nje,init,tn, conit,el, nge, hu, jstart) ! in :odepack_aux:odepack_aux.f90 - integer intent(in), optional :: meth - integer intent(in), optional :: nq - integer intent(in), optional :: nqu - integer intent(in), optional :: miter - integer intent(in), optional :: maxord - integer intent(in), optional :: meo - integer intent(in), optional :: nqnyh - integer intent(in), optional :: nst - integer intent(in), optional :: nfe - integer intent(in), optional :: nje - integer intent(in), optional :: nge - integer intent(in), optional :: init - integer intent(in), optional :: jstart - double precision intent(in), optional :: tn - double precision intent(in), optional :: conit - double precision intent(in), optional :: hu - double precision dimension(13),intent(in), optional :: el - end subroutine set_lsod_common - subroutine get_lsod_common(hu_,nqu_,nq_,nyh_, nqnyh_) ! in :odepack_aux:odepack_aux.f90 - double precision intent(out) :: hu_ - integer intent(out) :: nqu_ - integer intent(out) :: nq_ - integer intent(out) :: nyh_ - integer intent(out) :: nqnyh_ - end subroutine get_lsod_common - end interface - interface ! in odpkda1 - subroutine dintdy (t, k, yh, nyh, dky, iflag) ! in: odpkda1.f - double precision, intent(in) :: t - integer, intent(in) :: k - double precision, depend(nyh), intent(in) :: yh(nyh,*) - integer, intent(in) :: nyh - double precision, intent(out), depend(nyh) :: dky(nyh) - integer, intent(out) :: iflag - end subroutine dintdy - end interface -end python module odepack - -! This file was auto-generated with f2py (version:2) and edited. -! See http://cens.ioc.ee/projects/f2py2e/ +! -*- f90 -*- +! Note: the context of this file is case sensitive. + +python module dlsode__user__routines + interface dlsode_user_interface + subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsode:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq),intent(out) :: ydot + end subroutine f + subroutine jac(neq,t,y,ml,mu,pd,nrowpd) ! in :odepack:opkdmain.f:dlsodar:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + integer :: ml + integer :: mu + double precision dimension(nrowpd,neq),intent(out) :: pd + integer :: nrowpd + end subroutine jac + end interface dlsode_user_interface +end python module dlsode__user__routines +python module dlsodes__user__routines + interface dlsodes_user_interface + subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsodes:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq),intent(out) :: ydot + end subroutine f + subroutine jac(neq,t,y,j,ian,jan,pdj) ! in :odepack:opkdmain.f:dlsodar:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + integer :: j + double precision dimension(neq) :: ian + double precision dimension(neq) :: jan + double precision dimension(neq),intent(out) :: pdj + end subroutine jac + end interface dlsodes_user_interface +end python module dlsodes__user__routines +python module dlsoda__user__routines + interface dlsoda_user_interface + subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsoda:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq),intent(out) :: ydot + end subroutine f + subroutine jac(neq,t,y,ml,mu,pd,nrowpd) ! in :odepack:opkdmain.f:dlsodar:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + integer :: ml + integer :: mu + double precision dimension(nrowpd,neq),intent(out) :: pd + integer :: nrowpd + end subroutine jac + end interface dlsoda_user_interface +end python module dlsoda__user__routines +python module dlsodar__user__routines + interface dlsodar_user_interface + subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsodar:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq),intent(out) :: ydot + end subroutine f + subroutine g(neq,t,y,ng,gout) ! in :odepack:opkdmain.f:dlsodar:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + integer :: ng + double precision dimension(ng),intent(out) :: gout + end subroutine g + subroutine jac(neq,t,y,ml,mu,pd,nrowpd) ! in :odepack:opkdmain.f:dlsodar:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + integer,intent(hide) :: ml + integer,intent(hide) :: mu + double precision dimension(nrowpd,neq),intent(out) :: pd + integer,intent(hide) :: nrowpd + end subroutine jac + end interface dlsodar_user_interface +end python module dlsodar__user__routines +python module dlsodpk__user__routines + interface dlsodpk_user_interface + subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsodpk:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq),intent(out) :: ydot + end subroutine f + subroutine jac(f,neq,t,y,ysv,rewt,fty,v,hlo,wp,iwp,ier) + external f + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq) :: ysv + double precision dimension(neq) :: rewt + double precision dimension(neq) :: fty + double precision dimension(neq) :: v + double precision :: hlo + double precision dimension (*) :: wp + integer dimension(*) :: iwp + integer :: ier + end subroutine jac + subroutine psol(neq,t,y,fty,wk,hlo,wp,iwp,b,lr,ier) + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq) :: fty + double precision dimension(neq) :: wk + double precision :: hlo + double precision dimension (*) :: wp + integer dimension(*) :: iwp + double precision dimension(neq),intent(out) :: b + integer :: lr + integer :: ier + end subroutine psol + end interface dlsodpk_user_interface +end python module dlsodpk__user__routines +python module dlsodkr__user__routines + interface dlsodkr_user_interface + subroutine f(neq,t,y,ydot) ! in :odepack:opkdmain.f:dlsodkr:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq),intent(out) :: ydot + end subroutine f + subroutine g(neq,t,y,ng,gout) ! in :odepack:opkdmain.f:dlsodar:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + integer :: ng + double precision dimension(ng),intent(out) :: gout + end subroutine g + subroutine jac(f,neq,t,y,ysv,rewt,fty,v,hlo,jok,wp,iwp,ier) + external f + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq) :: ysv + double precision dimension(neq) :: rewt + double precision dimension(neq) :: fty + double precision dimension(neq) :: v + double precision :: hlo + integer :: jok + double precision dimension (*) :: wp + integer dimension(*) :: iwp + integer :: ier + end subroutine jac + subroutine psol(neq,t,y,fty,wk,hlo,wp,iwp,b,lr,ier) + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq) :: fty + double precision dimension(neq) :: wk + double precision :: hlo + double precision dimension (*) :: wp + integer dimension(*) :: iwp + double precision dimension(neq),intent(out) :: b + integer :: lr + integer :: ier + end subroutine psol + end interface dlsodkr_user_interface +end python module dlsodkr__user__routines +python module dlsodi__user__routines + interface dlsodi_user_interface + subroutine res(neq,tn,y,s,r,ires) ! in :odepack:opkdmain.f:dlsodi:unknown_interface + integer :: neq + double precision :: tn + double precision dimension(neq) :: y + double precision dimension(neq) :: s + double precision dimension(neq),intent(out) :: r + integer :: ires + end subroutine res + subroutine adda(neq,tn,y,ml,mu,p,nrowp) ! in :odepack:opkdmain.f:dlsodi:unknown_interface + integer :: neq + double precision :: tn + double precision dimension(neq) :: y + integer :: ml + integer :: mu + double precision dimension(nrowp,neq),intent(out) :: p + integer :: nrowp + end subroutine adda + subroutine jac(neq,t,y,s,ml,mu,p,nrowp) ! in :odepack:opkdmain.f:dlsodar:unknown_interface + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq) :: s + integer :: ml + integer :: mu + double precision dimension(nrowp,neq),intent(out) :: p + integer :: nrowp + end subroutine jac + end interface dlsodi_user_interface +end python module dlsodi__user__routines +python module dlsoibt__user__routines + interface dlsoibt_user_interface + subroutine res(neq,tn,y,s,r,ires) ! in :odepack:opkdmain.f:dlsoibt:unknown_interface + integer :: neq + double precision :: tn + double precision dimension(neq) :: y + double precision dimension(neq) :: s + double precision dimension(neq),intent(out) :: r + integer :: ires + end subroutine res + subroutine adda(neq,t,y,mb,nb,pa,pb,pc) + integer :: neq + double precision :: t + double precision dimension(neq) :: y + integer :: mb + integer :: nb + double precision dimension(mb,mb,nb),intent(out) :: pa + double precision dimension(mb,mb,nb),intent(out) :: pb + double precision dimension(mb,mb,nb),intent(out) :: pc + end subroutine adda + subroutine jac(neq,t,y,s,mb,nb,pa,pb,pc) + integer :: neq + double precision :: t + double precision dimension(neq) :: y + double precision dimension(neq) :: s + integer :: mb + integer :: nb + double precision dimension(mb,mb,nb),intent(out) :: pa + double precision dimension(mb,mb,nb),intent(out) :: pb + double precision dimension(mb,mb,nb),intent(out) :: pc + end subroutine jac + end interface dlsoibt_user_interface +end python module dlsoibt__user__routines +python module dlsodis__user__routines + interface dlsodis_user_interface + subroutine res(neq,tn,y,s,r,ires) ! in :odepack:opkdmain.f:dlsodis:unknown_interface + integer :: neq + double precision :: tn + double precision dimension(neq) :: y + double precision dimension(neq) :: s + double precision dimension(neq),intent(out) :: r + integer :: ires + end subroutine res + subroutine adda(neq,tn,y,j,ian,jan,p) ! in :odepack:opkdmain.f:dlsodi:unknown_interface + integer :: neq + double precision :: tn + double precision dimension(neq) :: y + integer :: j + integer dimension(*) :: ian + integer dimension(*) :: jan + double precision dimension(neq),intent(out) :: p + end subroutine adda + subroutine jac(neq,tn,y,s,j,ian,jan,pdj) + integer :: neq + double precision :: tn + double precision dimension(neq) :: y + double precision dimension(neq) :: s + integer :: j + integer dimension(*) :: ian + integer dimension(*) :: jan + double precision dimension(neq),intent(out) :: pdj + end subroutine jac + end interface dlsodis_user_interface +end python module dlsodis__user__routines + +python module odepack ! in + interface ! in :odepack + subroutine dlsode(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,mf) ! in :odepack:opkdmain.f + use dlsode__user__routines + external f + integer :: neq + double precision dimension(neq) :: y + double precision :: t + double precision :: tout + integer :: itol + double precision dimension(neq) :: rtol + double precision dimension(neq) :: atol + integer :: itask + integer :: istate + integer :: iopt + double precision dimension(lrw) :: rwork + integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) + integer dimension(liw) :: iwork + integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) + external jac + integer :: mf + double precision dimension(209) :: rowns + double precision :: ccmax + double precision :: el0 + double precision :: h + double precision :: hmin + double precision :: hmxi + double precision :: hu + double precision :: rc + double precision :: tn + double precision :: uround + integer :: init + integer :: mxstep + integer :: mxhnil + integer :: nhnil + integer :: nslast + integer :: nyh + integer :: icf + integer :: ierpj + integer :: iersl + integer :: jcur + integer :: jstart + integer :: kflag + integer :: l + integer :: lyh + integer :: lewt + integer :: lacor + integer :: lsavf + integer :: lwm + integer :: liwm + integer :: meth + integer :: miter + integer :: maxord + integer :: maxcor + integer :: msbp + integer :: mxncf + integer :: n + integer :: nq + integer :: nst + integer :: nfe + integer :: nje + integer :: nqu + integer :: init,mxstep,mxhnil,nhnil,nslast,nyh + !common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu + common /dls001/ conit, crate, el(13), elco(13,12), hold, & + rmax, tesco(3,12),ccmax, el0, h, hmin, & + hmxi, hu, rc, tn, uround, & + init,mxstep,mxhnil,nhnil,nslast,nyh, & !iownd(6), + ialth, ipup, lmax, meo, nqnyh, nslp, & !iowns(6) + icf, ierpj, iersl, jcur, jstart, kflag, & + l,lyh, lewt, lacor, lsavf, lwm, liwm, & + meth, miter,maxord, maxcor, msbp, mxncf, & + n, nq, nst, nfe, nje, nqu + end subroutine dlsode + subroutine dlsodes(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,mf) ! in :odepack:opkdmain.f + use dlsodes__user__routines + external f + integer :: neq + double precision dimension(neq) :: y + double precision :: t + double precision :: tout + integer :: itol + double precision dimension(neq) :: rtol + double precision dimension(neq) :: atol + integer :: itask + integer :: istate + integer :: iopt + double precision dimension(lrw) :: rwork + integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) + integer dimension(liw) :: iwork + integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) + external jac + integer :: mf + double precision :: con0 + double precision :: conmin + double precision :: ccmxj + double precision :: psmall + double precision :: rbig + double precision :: seth + integer :: iplost + integer :: iesp + integer :: istatc + integer :: iys + integer :: iba + integer :: ibian + integer :: ibjan + integer :: ibjgp + integer :: ipian + integer :: ipjan + integer :: ipjgp + integer :: ipigp + integer :: ipr + integer :: ipc + integer :: ipic + integer :: ipisp + integer :: iprsp + integer :: ipa + integer :: lenyh + integer :: lenyhm + integer :: lenwk + integer :: lreq + integer :: lrat + integer :: lrest + integer :: lwmin + integer :: moss + integer :: msbj + integer :: nslj + integer :: ngp + integer :: nlu + integer :: nnz + integer :: nsp + integer :: nzl + integer :: nzu + double precision dimension(209) :: rowns + double precision :: ccmax + double precision :: el0 + double precision :: h + double precision :: hmin + double precision :: hmxi + double precision :: hu + double precision :: rc + double precision :: tn + double precision :: uround + integer :: init + integer :: mxstep + integer :: mxhnil + integer :: nhnil + integer :: nslast + integer :: nyh + integer dimension(6) :: iowns + integer :: icf + integer :: ierpj + integer :: iersl + integer :: jcur + integer :: jstart + integer :: kflag + integer :: l + integer :: lyh + integer :: lewt + integer :: lacor + integer :: lsavf + integer :: lwm + integer :: liwm + integer :: meth + integer :: miter + integer :: maxord + integer :: maxcor + integer :: msbp + integer :: mxncf + integer :: n + integer :: nq + integer :: nst + integer :: nfe + integer :: nje + integer :: nqu + common /dlss01/ con0,conmin,ccmxj,psmall,rbig,seth,iplost,iesp,istatc,iys,iba,ibian,ibjan,ibjgp,ipian,ipjan,ipjgp,ipigp,ipr,ipc,ipic,ipisp,iprsp,ipa,lenyh,lenyhm,lenwk,lreq,lrat,lrest,lwmin,moss,msbj,nslj,ngp,nlu,nnz,nsp,nzl,nzu + common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu + end subroutine dlsodes + subroutine dlsoda(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,jt) ! in :odepack:opkdmain.f + use dlsoda__user__routines + external f + integer :: neq + double precision dimension(neq) :: y + double precision :: t + double precision :: tout + integer :: itol + double precision dimension(neq) :: rtol + double precision dimension(neq) :: atol + integer :: itask + integer :: istate + integer :: iopt + double precision dimension(lrw) :: rwork + integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) + integer dimension(liw) :: iwork + integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) + external jac + integer :: jt + double precision dimension(209) :: rowns + double precision :: ccmax + double precision :: el0 + double precision :: h + double precision :: hmin + double precision :: hmxi + double precision :: hu + double precision :: rc + double precision :: tn + double precision :: uround + integer :: init + integer :: mxstep + integer :: mxhnil + integer :: nhnil + integer :: nslast + integer :: nyh + integer dimension(6) :: iowns + integer :: icf + integer :: ierpj + integer :: iersl + integer :: jcur + integer :: jstart + integer :: kflag + integer :: l + integer :: lyh + integer :: lewt + integer :: lacor + integer :: lsavf + integer :: lwm + integer :: liwm + integer :: meth + integer :: miter + integer :: maxord + integer :: maxcor + integer :: msbp + integer :: mxncf + integer :: n + integer :: nq + integer :: nst + integer :: nfe + integer :: nje + integer :: nqu + double precision :: tsw + double precision dimension(20) :: rowns2 + double precision :: pdnorm + integer :: insufr + integer :: insufi + integer :: ixpr + integer dimension(2) :: iowns2 + integer :: jtyp + integer :: mused + integer :: mxordn + integer :: mxords + common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu + common /dlsa01/ tsw,rowns2,pdnorm,insufr,insufi,ixpr,iowns2,jtyp,mused,mxordn,mxords + end subroutine dlsoda + subroutine dlsodar(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,jt,g,ng,jroot) ! in :odepack:opkdmain.f + use dlsodar__user__routines + external f + integer :: neq + double precision dimension(neq),intent(in,out) :: y + double precision,intent(in,out) :: t + double precision :: tout + integer :: itol + double precision dimension(neq) :: rtol + double precision dimension(neq) :: atol + integer :: itask + integer,intent(in,out) :: istate + integer :: iopt + double precision dimension(lrw), intent(in,out) :: rwork + integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) + integer dimension(liw),intent(in,out) :: iwork + integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) + external jac + integer :: jt + external g + integer, optional,check(len(jroot)>=ng),depend(jroot) :: ng=len(jroot) + integer dimension(ng),intent(in,out) :: jroot + double precision dimension(2) :: rownr3 + double precision :: t0 + double precision :: tlast + double precision :: toutc + integer :: lg0 + integer :: lg1 + integer :: lgx + integer dimension(2) :: iownr3 + integer :: irfnd + integer :: itaskc + integer :: ngc + integer :: nge + double precision dimension(209) :: rowns + double precision :: ccmax + double precision :: el0 + double precision :: h + double precision :: hmin + double precision :: hmxi + double precision :: hu + double precision :: rc + double precision :: tn + double precision :: uround + integer :: init + integer :: mxstep + integer :: mxhnil + integer :: nhnil + integer :: nslast + integer :: nyh + integer dimension(6) :: iowns + integer :: icf + integer :: ierpj + integer :: iersl + integer :: jcur + integer :: jstart + integer :: kflag + integer :: l + integer :: lyh + integer :: lewt + integer :: lacor + integer :: lsavf + integer :: lwm + integer :: liwm + integer :: meth + integer :: miter + integer :: maxord + integer :: maxcor + integer :: msbp + integer :: mxncf + integer :: n + integer :: nq + integer :: nst + integer :: nfe + integer :: nje + integer :: nqu + double precision :: tsw + double precision dimension(20) :: rowns2 + double precision :: pdnorm + integer :: insufr + integer :: insufi + integer :: ixpr + integer dimension(2) :: iowns2 + integer :: jtyp + integer :: mused + integer :: mxordn + integer :: mxords + common /dlsr01/ rownr3,t0,tlast,toutc,lg0,lg1,lgx,iownr3,irfnd,itaskc,ngc,nge + common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu + common /dlsa01/ tsw,rowns2,pdnorm,insufr,insufi,ixpr,iowns2,jtyp,mused,mxordn,mxords + end subroutine dlsodar + subroutine dlsodpk(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,psol,mf) ! in :odepack:opkdmain.f + use dlsodpk__user__routines + external f + integer :: neq + double precision dimension(neq) :: y + double precision :: t + double precision :: tout + integer :: itol + double precision dimension(neq) :: rtol + double precision dimension(neq) :: atol + integer :: itask + integer :: istate + integer :: iopt + double precision dimension(lrw) :: rwork + integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) + integer dimension(liw) :: iwork + integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) + external jac + external psol + integer :: mf + double precision :: delt + double precision :: epcon + double precision :: sqrtn + double precision :: rsqrtn + integer :: jpre + integer :: jacflg + integer :: locwp + integer :: lociwp + integer :: lsavx + integer :: kmp + integer :: maxl + integer :: mnewt + integer :: nni + integer :: nli + integer :: nps + integer :: ncfn + integer :: ncfl + double precision dimension(209) :: rowns + double precision :: ccmax + double precision :: el0 + double precision :: h + double precision :: hmin + double precision :: hmxi + double precision :: hu + double precision :: rc + double precision :: tn + double precision :: uround + integer :: init + integer :: mxstep + integer :: mxhnil + integer :: nhnil + integer :: nslast + integer :: nyh + integer dimension(6) :: iowns + integer :: icf + integer :: ierpj + integer :: iersl + integer :: jcur + integer :: jstart + integer :: kflag + integer :: l + integer :: lyh + integer :: lewt + integer :: lacor + integer :: lsavf + integer :: lwm + integer :: liwm + integer :: meth + integer :: miter + integer :: maxord + integer :: maxcor + integer :: msbp + integer :: mxncf + integer :: n + integer :: nq + integer :: nst + integer :: nfe + integer :: nje + integer :: nqu + common /dlpk01/ delt,epcon,sqrtn,rsqrtn,jpre,jacflg,locwp,lociwp,lsavx,kmp,maxl,mnewt,nni,nli,nps,ncfn,ncfl + common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu + end subroutine dlsodpk + subroutine dlsodkr(f,neq,y,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,jac,psol,mf,g,ng,jroot) ! in :odepack:opkdmain.f + use dlsodkr__user__routines + external f + integer :: neq + double precision dimension(neq) :: y + double precision :: t + double precision :: tout + integer :: itol + double precision dimension(neq) :: rtol + double precision dimension(neq) :: atol + integer :: itask + integer :: istate + integer :: iopt + double precision dimension(lrw) :: rwork + integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) + integer dimension(liw) :: iwork + integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) + external jac + external psol + integer :: mf + external g + integer :: ng + integer dimension(*) :: jroot + double precision :: delt + double precision :: epcon + double precision :: sqrtn + double precision :: rsqrtn + integer :: jpre + integer :: jacflg + integer :: locwp + integer :: lociwp + integer :: lsavx + integer :: kmp + integer :: maxl + integer :: mnewt + integer :: nni + integer :: nli + integer :: nps + integer :: ncfn + integer :: ncfl + double precision dimension(2) :: rownr3 + double precision :: t0 + double precision :: tlast + double precision :: toutc + integer :: lg0 + integer :: lg1 + integer :: lgx + integer dimension(2) :: iownr3 + integer :: irfnd + integer :: itaskc + integer :: ngc + integer :: nge + double precision :: stifr + integer :: newt + integer :: nsfi + integer :: nslj + integer :: njev + double precision dimension(209) :: rowns + double precision :: ccmax + double precision :: el0 + double precision :: h + double precision :: hmin + double precision :: hmxi + double precision :: hu + double precision :: rc + double precision :: tn + double precision :: uround + integer :: init + integer :: mxstep + integer :: mxhnil + integer :: nhnil + integer :: nslast + integer :: nyh + integer dimension(6) :: iowns + integer :: icf + integer :: ierpj + integer :: iersl + integer :: jcur + integer :: jstart + integer :: kflag + integer :: l + integer :: lyh + integer :: lewt + integer :: lacor + integer :: lsavf + integer :: lwm + integer :: liwm + integer :: meth + integer :: miter + integer :: maxord + integer :: maxcor + integer :: msbp + integer :: mxncf + integer :: n + integer :: nq + integer :: nst + integer :: nfe + integer :: nje + integer :: nqu + common /dlpk01/ delt,epcon,sqrtn,rsqrtn,jpre,jacflg,locwp,lociwp,lsavx,kmp,maxl,mnewt,nni,nli,nps,ncfn,ncfl + common /dlsr01/ rownr3,t0,tlast,toutc,lg0,lg1,lgx,iownr3,irfnd,itaskc,ngc,nge + common /dls002/ stifr,newt,nsfi,nslj,njev + common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu + end subroutine dlsodkr + subroutine dlsodi(res,adda,jac,neq,y,ydoti,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,mf) ! in :odepack:opkdmain.f + use dlsodi__user__routines + external res + external adda + external jac + integer :: neq + double precision dimension(neq) :: y + double precision dimension(neq) :: ydoti + double precision :: t + double precision :: tout + integer :: itol + double precision dimension(neq) :: rtol + double precision dimension(neq) :: atol + integer :: itask + integer :: istate + integer :: iopt + double precision dimension(lrw) :: rwork + integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) + integer dimension(liw) :: iwork + integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) + integer :: mf + double precision dimension(209) :: rowns + double precision :: ccmax + double precision :: el0 + double precision :: h + double precision :: hmin + double precision :: hmxi + double precision :: hu + double precision :: rc + double precision :: tn + double precision :: uround + integer :: init + integer :: mxstep + integer :: mxhnil + integer :: nhnil + integer :: nslast + integer :: nyh + integer dimension(6) :: iowns + integer :: icf + integer :: ierpj + integer :: iersl + integer :: jcur + integer :: jstart + integer :: kflag + integer :: l + integer :: lyh + integer :: lewt + integer :: lacor + integer :: lsavf + integer :: lwm + integer :: liwm + integer :: meth + integer :: miter + integer :: maxord + integer :: maxcor + integer :: msbp + integer :: mxncf + integer :: n + integer :: nq + integer :: nst + integer :: nfe + integer :: nje + integer :: nqu + common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu + end subroutine dlsodi + subroutine dlsoibt(res,adda,jac,neq,y,ydoti,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,mf) ! in :odepack:opkdmain.f + use dlsoibt__user__routines + external res + external adda + external jac + integer :: neq + double precision dimension(neq) :: y + double precision dimension(neq) :: ydoti + double precision :: t + double precision :: tout + integer :: itol + double precision dimension(neq) :: rtol + double precision dimension(neq) :: atol + integer :: itask + integer :: istate + integer :: iopt + double precision dimension(lrw) :: rwork + integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) + integer dimension(liw) :: iwork + integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) + integer :: mf + double precision dimension(209) :: rowns + double precision :: ccmax + double precision :: el0 + double precision :: h + double precision :: hmin + double precision :: hmxi + double precision :: hu + double precision :: rc + double precision :: tn + double precision :: uround + integer :: init + integer :: mxstep + integer :: mxhnil + integer :: nhnil + integer :: nslast + integer :: nyh + integer dimension(6) :: iowns + integer :: icf + integer :: ierpj + integer :: iersl + integer :: jcur + integer :: jstart + integer :: kflag + integer :: l + integer :: lyh + integer :: lewt + integer :: lacor + integer :: lsavf + integer :: lwm + integer :: liwm + integer :: meth + integer :: miter + integer :: maxord + integer :: maxcor + integer :: msbp + integer :: mxncf + integer :: n + integer :: nq + integer :: nst + integer :: nfe + integer :: nje + integer :: nqu + common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu + end subroutine dlsoibt + subroutine dlsodis(res,adda,jac,neq,y,ydoti,t,tout,itol,rtol,atol,itask,istate,iopt,rwork,lrw,iwork,liw,mf) ! in :odepack:opkdmain.f + use dlsodis__user__routines + external res + external adda + external jac + integer :: neq + double precision dimension(neq) :: y + double precision dimension(neq) :: ydoti + double precision :: t + double precision :: tout + integer :: itol + double precision dimension(neq) :: rtol + double precision dimension(neq) :: atol + integer :: itask + integer :: istate + integer :: iopt + double precision dimension(lrw) :: rwork + integer, optional,check(len(rwork)>=lrw),depend(rwork) :: lrw=len(rwork) + integer dimension(liw) :: iwork + integer, optional,check(len(iwork)>=liw),depend(iwork) :: liw=len(iwork) + integer :: mf + double precision :: con0 + double precision :: conmin + double precision :: ccmxj + double precision :: psmall + double precision :: rbig + double precision :: seth + integer :: iplost + integer :: iesp + integer :: istatc + integer :: iys + integer :: iba + integer :: ibian + integer :: ibjan + integer :: ibjgp + integer :: ipian + integer :: ipjan + integer :: ipjgp + integer :: ipigp + integer :: ipr + integer :: ipc + integer :: ipic + integer :: ipisp + integer :: iprsp + integer :: ipa + integer :: lenyh + integer :: lenyhm + integer :: lenwk + integer :: lreq + integer :: lrat + integer :: lrest + integer :: lwmin + integer :: moss + integer :: msbj + integer :: nslj + integer :: ngp + integer :: nlu + integer :: nnz + integer :: nsp + integer :: nzl + integer :: nzu + double precision dimension(209) :: rowns + double precision :: ccmax + double precision :: el0 + double precision :: h + double precision :: hmin + double precision :: hmxi + double precision :: hu + double precision :: rc + double precision :: tn + double precision :: uround + integer :: init + integer :: mxstep + integer :: mxhnil + integer :: nhnil + integer :: nslast + integer :: nyh + integer dimension(6) :: iowns + integer :: icf + integer :: ierpj + integer :: iersl + integer :: jcur + integer :: jstart + integer :: kflag + integer :: l + integer :: lyh + integer :: lewt + integer :: lacor + integer :: lsavf + integer :: lwm + integer :: liwm + integer :: meth + integer :: miter + integer :: maxord + integer :: maxcor + integer :: msbp + integer :: mxncf + integer :: n + integer :: nq + integer :: nst + integer :: nfe + integer :: nje + integer :: nqu + common /dlss01/ con0,conmin,ccmxj,psmall,rbig,seth,iplost,iesp,istatc,iys,iba,ibian,ibjan,ibjgp,ipian,ipjan,ipjgp,ipigp,ipr,ipc,ipic,ipisp,iprsp,ipa,lenyh,lenyhm,lenwk,lreq,lrat,lrest,lwmin,moss,msbj,nslj,ngp,nlu,nnz,nsp,nzl,nzu + common /dls001/ rowns,ccmax,el0,h,hmin,hmxi,hu,rc,tn,uround,init,mxstep,mxhnil,nhnil,nslast,nyh,iowns,icf,ierpj,iersl,jcur,jstart,kflag,l,lyh,lewt,lacor,lsavf,lwm,liwm,meth,miter,maxord,maxcor,msbp,mxncf,n,nq,nst,nfe,nje,nqu + end subroutine dlsodis + subroutine dsrcar(rsav, isav, job) + ! Save/restore LSODAR COMMON blocks + integer, intent(inout), dimension(55):: isav + integer, intent(in):: job + double precision, intent(inout), dimension(245):: rsav + end subroutine dsrcar + subroutine dcfode(meth, elco, tesco) + ! set method and test coefficients for odepack routines + integer, intent(in):: meth + double precision, intent(out),dimension(13,12):: elco + double precision, intent(out),dimension(3,12)::tesco + end subroutine dcfode + end interface + interface ! in :odepack_aux + subroutine set_lsod_common(meth,nq,nqu,miter,maxord,meo,nqnyh,nst,nfe,nje,init,tn, conit,el, nge, hu, jstart) ! in :odepack_aux:odepack_aux.f90 + integer intent(in), optional :: meth + integer intent(in), optional :: nq + integer intent(in), optional :: nqu + integer intent(in), optional :: miter + integer intent(in), optional :: maxord + integer intent(in), optional :: meo + integer intent(in), optional :: nqnyh + integer intent(in), optional :: nst + integer intent(in), optional :: nfe + integer intent(in), optional :: nje + integer intent(in), optional :: nge + integer intent(in), optional :: init + integer intent(in), optional :: jstart + double precision intent(in), optional :: tn + double precision intent(in), optional :: conit + double precision intent(in), optional :: hu + double precision dimension(13),intent(in), optional :: el + end subroutine set_lsod_common + subroutine get_lsod_common(hu_,nqu_,nq_,nyh_, nqnyh_) ! in :odepack_aux:odepack_aux.f90 + double precision intent(out) :: hu_ + integer intent(out) :: nqu_ + integer intent(out) :: nq_ + integer intent(out) :: nyh_ + integer intent(out) :: nqnyh_ + end subroutine get_lsod_common + end interface + interface ! in odpkda1 + subroutine dintdy (t, k, yh, nyh, dky, iflag) ! in: odpkda1.f + double precision, intent(in) :: t + integer, intent(in) :: k + double precision, depend(nyh), intent(in) :: yh(nyh,*) + integer, intent(in) :: nyh + double precision, intent(out), depend(nyh) :: dky(nyh) + integer, intent(out) :: iflag + end subroutine dintdy + end interface +end python module odepack + +! This file was auto-generated with f2py (version:2) and edited. +! See http://cens.ioc.ee/projects/f2py2e/